当前位置:文档之家› 基于stm32的双缓冲的实现

基于stm32的双缓冲的实现

基于stm32的双缓冲的实现
基于stm32的双缓冲的实现

终于开荤了~~~DMA 先抄一小段DMA的说明。

对于没玩过DMA 的朋友,这里简单说一下DMA,用自己的语言说吧,那就是,从某个位置

传输数据到某个位置,如果不用DMA,那要CPU参与操作,一个字节一个字节地搬,效率高

点的,就一个字一个字地搬.但当你用了DMA 后,那就是只需要设置:A.从哪里开始搬; B,

搬到哪里去;C以字节方式搬还是半字还是字;D:一共搬多少个.之后,启动DMA.CPU内部

就会开始搬数据了,整个搬数据的过程都不需要指令的参与,唯一要做的,就是检测什么时

候搬完.你可以扫描寄存器,也可以用中断.这里,我使用了中断.

具体设置功能看注释就可以明白了.注意一点就是,有一个设置:

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

这个是外设的地址不递增.也就是说,每次搬动,都是从源头,也就是USART1的DR寄存器

搬,但内存地址却是递增的:

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

这个历程实现了接受串口的数据写到FLASH 之中工作,而DMA的作用在于将串口收寄存器USART1->D u8 USART1_DMA_Buf1[512]; 写满512个字节之后将进入DMA中断(通道5)在这里修改DMA 的内存写入u8 USART1_DMA_Buf2[512]; ,同时标记下次的入口Free_Buf_No=BUF_NO1; 与Buf_Ok=TRUE; 证明已USART1_DMA_Buf1中的数据写入FLASH .

又抄了一点

这次使用的是双缓冲,也有人

叫乒乓缓冲.因为一般情况下,串口的数据DMA 传输进BUF1 的过程中,是不建议对

BUF1 进行操作的.但由于串口数据是不会等待的直传,所以你总不能等BUF1 满了,

才往FLASH 上写,因为这时候串口数据依旧是源源不断.于是,使用双缓冲就变的理

所当然了.当BUF1 满了的时候,就马上设置DMA的目标为BUF2,并且BUF1的数据

往25F080上灌.当串口DMA写满了BUF2的时候,再设置DMA的目标为BUF1,此时

再操作BUF2写进25F080.如此一直循环,就好像打乒乓球那样吧,所以就叫乒乓缓冲.

用这个方法的速度极限就是,你必须确保两点a.DMA 灌满了BUF1 的时候,会发生中

断,此时切换DMA 的目标缓冲为BUF2,而且切换的过程必须在新的串口数据溢出之

前完成.b.在DMA的BUF1满之前,另外一个有数据的BUF2必须能全部写进25F080,

其中包括了遇到新的扇区边界而要刷除扇区的操作时间!!

可以看出,BUF的增大,并不能够很大程度的提升速度极限.

假设USART 与FLASH 的底层驱动已经写好了。点击查看。

/************DMA方式传输***************************/

#define SRC_USART1_DR (&(USART1->DR)) //串口接收寄存器作为源头

//DMA目标缓冲,这里使用双缓冲

u8 USART1_DMA_Buf1[512];

u8 USART1_DMA_Buf2[512];

bool Buf_Ok; //BUF是否已经可用

BUF_NO Free_Buf_No; //空闲的BUF号typedef enum {BUF_NO1=0,BUF_NO2=1}BUF_NO;

DMA_InitTypeDef DMA_InitStructure;

void USART_DMAToBuf1(void)

{

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开DMA时钟

DMA_DeInit(DMA1_Channel5); //将DMA的通道1寄存器DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_USART1_DR; //源头BUF既是(&(U DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1; //目标BUF 既是要写在DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头//外设是DMA_InitStructure.DMA_BufferSize = 512; //DMA缓存的大小单位在下边设定DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4优先级之一的(高优先)VeryH DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存

DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根据DMA_InitStruct中指定的参DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); //DMA5传输完成中断

USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //使能USART1的接 /************************************************************************************************************************* //初始化BUF标志

Free_Buf_No=BUF_NO2; //因为DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1 Buf_Ok=FALSE; //此时没有数据准备完成当然FALSE

DMA_Cmd(DMA1_Channel5, ENABLE); //正式允许DMA

}

再来看看DMA中断:

//u16 DataCounter;

extern DMA_InitTypeDef DMA_InitStructure;

void DMA1_Channel5_IRQHandler(void)

{

if(DMA_GetITStatus(DMA1_IT_TC5)) //通道5传输完成中断TC还有传输过半中断HT 错误中断TE 全局{

//DataCounter = DMA_GetCurrDataCounter(DMA1_Channel5);//获取剩余长度,一般都为0,调试用

DMA_ClearITPendingBit(DMA1_IT_GL5); //清除全部中断标志

//转换可操作BUF

if(Free_Buf_No==BUF_NO1)

{

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;

DMA_Init(DMA1_Channel5, &DMA_InitStructure);

Free_Buf_No=BUF_NO2;

}

else

{

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf2;

DMA_Init(DMA1_Channel5, &DMA_InitStructure);

Free_Buf_No=BUF_NO1;

}

Buf_Ok=TRUE; //有准备好的数据了

}

}

写FLASH的操作

while(1)

{

if(Buf_Ok==TRUE)

{

LED1_ON; //一个标记

Buf_Ok=FALSE; //操作了准备好的数据

if((addr%4096)==0) //跨越一个扇区,则需要先刷除

{

SST25SectorErase(addr);

sector_count++;

}

if(Free_Buf_No==BUF_NO1)

SST25Write(addr,USART1_DMA_Buf1,512);

else

SST25Write(addr,USART1_DMA_Buf2,512);

addr+=512;

Timer1=5000; //时间重置

LED1_OFF;

}

//检测超时开了定时器

if(Timer1==0) //五秒内没准备好的数据

{

//获取长度

len=512-DMA_GetCurrDataCounter(DMA1_Channel5);

//写入最后数据

if(Free_Buf_No==BUF_NO1)

SST25Write(addr,USART1_DMA_Buf2,len);

else

SST25Write(addr,USART1_DMA_Buf1,len);

addr+=len;

break;

}

}

还是很简单的。

有一点比较困扰就是FlagStatus标志位与ITStatus中断标志位的区别。其实就DMA 来说DMA_IT值甚至2者值的获取都是读DMA ISR register 的值清除也是设置DMA_IFCR 寄存器来清除的所以貌似没有在但我还不可保证IT 与FLAG 的值总是相同的这个存在也许是为了兼容但一定有其意义务必不可混用即使有

相关主题
相关文档 最新文档