加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

实现STM32中USART的DMA(转)

发布时间:2020-12-15 06:36:46 所属栏目:百科 来源:网络整理
导读:? ? 对于没玩过DMA 的朋友,这里简单说一下DMA,用自己的语言说吧,那就是,从某个位置 传输数据到某个位置,如果不用DMA,那要CPU参与操作,一个字节一个字节地搬,效率高 点的,就一个字一个字地搬.但当你用了DMA 后,那就是只需要设置:A.从哪里开始搬; B, 搬到哪里

?

?

对于没玩过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->DR 的 数据写到内存之中 比如某个数组之中 u8 USART1_DMA_Buf1[512]; 写满512个字节之后将进入DMA中断(通道5)在这里修改DMA 的内存写入入口
u8 USART1_DMA_Buf2[512]; ,同时标记 下次的入口Free_Buf_No=BUF_NO1; 与 Buf_Ok=TRUE; 证明已有数据准备完成。这时CUP将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 的底层驱动已经写好了。?? 点击查看。


#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 既是 (&(USART1->DR))
??? 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优先级之一的(高优先) VeryHigh/High/Medium/Low
??? DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;??????????????????????????????? //非内存到内存
??? DMA_Init(DMA1_Channel5,&DMA_InitStructure);??????????????????????????????????? //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器
??
??? DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);??????????????????????????????? //DMA5传输完成中断

???? USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);?????????????????????????????? //使能USART1的接收DMA请求
??
??? //初始化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 全局中断GL
???? {
?????? //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@96)==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,len);
??????????? else
??????????????? SST25Write(addr,len);
??????????? addr+=len;
??????????
??????????? break;
??????? }
??? }

?

还是很简单的。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读