一.乒乓缓冲原理
一般情况下,串口数据 DMA 传输到 BUF1(内存上的一片连续的缓冲区)的过程中,是不
建议对 BUF1 进行操作的。但由于串口数据是一个连续传输的过程,比如接收 GPS 数据,不
能等待 BUF1 满了才处理数据,你 CPU 在处理数据的同时,串口源源不断接收数据,此时会
造成串口数据丢失,而乒乓缓冲就完美地解决了这个问题。
具体过程是:当串口 BUF1 满了时,DMA 的目标地址迅速切换到 BUF2,此时可以处理
BUF1 的数据;当串口 BUF2 满了时,DMA 的目标地址迅速切换到 BUF1,此时可以处理 BUF2
的数据。如此一直循环下去,就像打乒乓球一样,你推我挡,故称作乒乓缓冲。二 . DMA就不在这里班门弄斧的讲解了,分享一下程序吧。
DMA配置代码:
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头
DMA_InitStructure.DMA_BufferSize = dma_len; //BUF 大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
这两项的配置还是很好理解的,比如在这里我们是要将内存里边的东西发到 USART1 中去,每次发送 8 位,
那么外设地址当然不能改变,而每一次发送内容都是不一样的,而且数组在内存中的存放就是递增的,所以内存地址寄存器要递增。
下边是设置数据宽度:
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为 8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //正常缓存模式(非连续传输)
一些基本定义:
#define dma_len 100 //定义串口 DMA 传输数据长度(如果串口一次接收数据没有达到 dma_len 个 byte,则不会发生 DMA 中断)
extern u8 USART1_DMA_Buf1[dma_len]; //BUF1
extern u8 USART1_DMA_Buf2[dma_len]; //BUF2
typedef enum {BUF_NO1=0,BUF_NO2=1}BUF_NO;
extern BUF_NO Free_Buf_Now;
extern bool Buf_Ok;
乒乓思想在这里体现:
void DMA1_Channel5_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC5))
{
//DataCounter = DMA_GetCurrDataCounter(DMA1_Channel5);
//获取剩余长度,一般都为 0,调试用
DMA_ClearITPendingBit(DMA1_IT_GL5); //清除全部中断标志
//转换可操作 BUF
if(Free_Buf_Now==BUF_NO1) //如果 BUF1 空闲,将 DMA 接收数据赋值给 BUF1
{
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
Free_Buf_Now=BUF_NO2;
}
else //如果 BUF2 空闲,将 DMA 接收数据赋值给 BUF2
{
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf2;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
Free_Buf_Now=BUF_NO1;
}
Buf_Ok=TRUE;
}
}
当发现有一个缓冲接收满的时候,可以上传数据。将另外一个缓冲作为接收数据的目的地,周而复始。屡试不爽。
|