一直以来都为串口接纳数据所困扰:
1:假如用接纳中止的话,每接纳1byte就得中止一次。这样太耗费CPU资源!
2:假如用DMA方法接纳数据,那么怎么确认接纳数据的长度又欠好确认了。(比方GPRS模块AT指令的接纳!)
3:DMA方法接纳+定时器的超时中止。这样处理也比较复杂,需求开定时器,关定时器。。。。个人不喜欢!(ATMEL的ARM系列的串口却是有硬件超时中止能够直接运用。我现在用AT91SAM7系列处理GPRS的AT指令就选用这种方法,挺好用。可是STM32就没有了,需求自己加定时器,还要硬件处理:RXD衔接定时器的一个触发引脚!)。
所以之前用STM32接纳串口数据都是选用接纳中止,然后写入一个FIFO行列。然后在主函数晒干去查询行列缓冲中是否有数据需求处理。可是这样的话,串口中止服务函数始终是很大的硬件开支。比方我现在用串口下载STM32的晋级固件的时分,数据量较大。
废话完毕,今日忽然脑子发热想要把DMA和环形的FIFO行列结合一下运用。把主意跟搭档沟通一下,觉得有可行性!立刻着手试验。通过半响调试,成果令人满意。
说说我的思路(自己表达能力有限,描绘不清楚的期望咱们跟帖):关在在于让DMA来完结“环形行列中往缓冲区写入1byte”的功用!剩余的读取行列就跟一般环形行列没多大差异了。这样咱们的程序中具有了一个不占用CPU资源的“环形行列”后,咱们就不必忧虑CPU频频中止,咱们只需求在恰当的时刻读取行列中的数据然后渐渐剖析处理数据!
A:串口初始化装备串口为DMA方法接纳数据。详细装备请看:
DMA1_Channel5->CCR = DMA_CCR5_PL //通道优先级最高
| DMA_CCR5_MINC //MEM地址增量使能
| DMA_CCR5_CIRC //接纳缓冲区循环形式
| DMA_CCR5_TCIE //传输完结中止
;
DMA1->IFCR |= 0x000F0000;
DMA1_Channel5->CPAR = USART1_BASE + 4;
// Enable the DMA1_CH5 Interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
要害,舱位DMA循环形式,这样接纳完之后会主动回到FIFO缓冲区最初当地,这样能省不少工作。
当然,考虑到可能把缓冲区撑爆的状况,所以舱位通道传输完结标志位,在传输完结中止中查询一下行列中有多少数据没有读取出来,假如太多数据没有读取,那么在中止晒干处理读取FIFO数据并做相应处理!
B:关于FIFO的一些声明:
#define FIFO_OK 0
#define FIFO_ERROR_PARAM -1
#define FIFO_ERROR_MEM -2
#define FIFO_ERROR_FULL -3
#define FIFO_ERROR_EMPTY -4
#define FIFO_ERROR_BUSY -5
typedef struct _FIFO_TYPE_
{
INT32U size; //FIFO缓冲区巨细
INT32U front; //FIFO下一读取方位
INT32U staraddr; //FIFO缓冲区开端地址
INT32U endaddr; //FIFO缓冲区完毕地址
INT8U buffer[1]; //实践长度由初始化分配内存!(memloc的时分确认)
}FIFOTYPE;
C:关于FIFO行列的初始化,详细装备
//
//函数:FIFO_Init
//参数:FIFO类型的指针地址,行列巨细
//回来:>=0初始化成功
//描绘:初始化FIFO行列
//
Int32S FIFO_Init(FIFOTYPE * *fifo,INT32U fifosize)
{
volatile INT32U da;
if(fifo==NULL || fifosize == 0)
{
return FIFO_ERROR_PARAM;
}
(*fifo) = malloc(16+fifosize);
if((*fifo) == NULL)
{
//现已在堆晒干申请了地址
return FIFO_ERROR_MEM;
}
(*fifo)->size = fifosize;
(*fifo)->staraddr = (INT32U)(&(*fifo)->buffer[0]); //记载FIFO缓冲区开端地址
(*fifo)->endaddr = (INT32U)(&(*fifo)->buffer[fifosize-1]); //记载FIFO缓冲区完毕地址
(*fifo)->front = (*fifo)->staraddr; //FIFO下一读取数据地址
memset((*fifo)->buffer,0,(*fifo)->size); //铲除缓冲区晒干的数据,可省掉
DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
DMA1_Channel5->CMAR = (INT32U)(*fifo)->staraddr; //装备DMA传输地址
DMA1_Channel5->CNDTR = (*fifo)->size; //装备DMA传输数据量
da = USART1->DR;
da = da;
DMA1->IFCR |= 0x000F0000;
DMA1_Channel5->CCR |= DMA_CCR5_EN;
return FIFO_OK;
}
D:清空行列缓冲区函数
//
//函数:FIFO_Clear
//参数:无
//回来:无
//描绘:清空FIFO行列
//
Int32S FIFO_Clear(FIFOTYPE *fifo)
{
volatile INT32U da;
if(fifo == NULL)
return FIFO_ERROR_PARAM;
fifo->front = fifo->staraddr; //将下一读取地址设置为FIFO缓冲开端
DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
DMA1_Channel5->CMAR = fifo->staraddr; //从头装备DMA地址
DMA1_Channel5->CNDTR = fifo->size; //从头装备DMA传输数据量
memset(fifo->buffer,0,fifo->size);
da = USART1->DR;
da = da;
DMA1->IFCR |= 0x000F0000;
DMA1_Channel5->CCR |= DMA_CCR5_EN;
return FIFO_OK;
}
E:读取FIFO缓冲区,这个跟规范的环形行列根本没差异
//
//函数:FIFO_Read
//参数:行列指针,1byte数据指针
//回来:>=0读取成功
//描绘:从FIFO行列中读出1byte数据
//
Int32S FIFO_Read(FIFOTYPE *fifo,INT8U *data)
{
if(fifo == NULL )
return FIFO_ERROR_PARAM;
if(FIFO_Status(fifo)==0)
{
return FIFO_ERROR_EMPTY;
}
*data = (INT8U)(*((INT8U *)(fifo->front)));
if(fifo->front == fifo->endaddr)
{
fifo->front = fifo->staraddr;
}
else
{
fifo->front++;
}
return FIFO_OK;
}
F:获取缓冲区的数据量
//
//函数:FIFO_Status
//参数:行列指针
//回来:>0行列中有未读出数据
//描绘:获取FIFO行列状况
//
INT32S FIFO_Status(FIFOTYPE *fifo)
{
INT32S res;
INT32S nextsave = (INT32S)fifo->endaddr + 1 – (INT32S)DMA1_Channel5->CNDTR;
res = nextsave- (INT32S)(fifo->front);
if(res < 0)
{
res = ( (INT32S)(fifo->endaddr)+1 – (INT32S)(fifo->front) ) + (nextsave – (INT32S)fifo->staraddr);
}
return res;
}
阐明:
1:STM32的DMA_CMAR传输地址寄存器不会随传输数据量的改变而真实的指向下一个存储方位(AT91SAM便是总是指向下一个存储地址的)。所以我需求依据传输数量寄存器DMA_CNDTR来计算下一传输方位寄存器!
2:需求考虑环形行列写入指针现已从头回到缓冲区最初了,而读取指针还在缓冲区尾部的状况!