STM32F207 USART+DMA代码+个人理解

来源:互联网 发布:学网络营销好还是java 编辑:程序博客网 时间:2024/06/18 14:34

环境:STM32F207

目的USART通过DMA通信+DMA初步理解


1.之前发送数据的方式

①数据放到串口数据寄存器里面

②等待一个字节发送完成

③重复第一二步。 

看到我们平时的方式我们就会有个想法,如果我们发送五百个字节,我们就需要让CPU在这里等待五百次。也就是在等待过程中我们是不能够去做其他事情的,只能够通过一个while循环去查看串口状态寄存器里面对应的发送完成标志是否为1。


2.现在我们大致描述一下DMA传输模式:

①数据写好放在内存  

②告诉DMA,我们发送多少数据

③干其他事情

④有想要发送数据了,先看看发送完了没有,没有发送完需要等待

⑤重复①②③④步骤

我们看到其实DMA也需要等,很多同学就说,这不是一样吗,我们都需要等待。其实这个情况不一样,这就像一岁小孩吃饭和十岁小孩吃饭一个道理,一岁小孩吃饭需要我们大人一口一口的喂他,在这期间,我们不能够去干其他事情,只能够看到他,第一口吃完了才能够喂第二口。  但是十岁小孩子吃饭就不一样了,你把饭给弄好,然后给他,让他自己吃,这个期间我们就可以去洗衣服了,如果你还想让小孩子吃完这一碗饭再吃一碗,那么你等一会儿去看看就可以了。

言归正传,如果我们用普通方式去做的话,相当于软件去循环查询,但是DMA的话,相当于硬件来做,到底谁快谁慢,我们就不用说了。

其实说了那么多,就是想给大家普及一下DMA大致的工作方式,到底好不好,还是要看具体的项目,对时间的要求等等。下面我对我的代码进行一个讲解。


总体如下:

void usart1_Init(void){usart1_PortInit();//相关管脚初始化usart1_IT_Init();//中断初始化usart1_DmaInit();//DMA初始化}
其中PortInit和IT_Init就不需要详细说明了,和一般模式一样,只不过需要在PortInit里面加一个DMA使能,如下图所示:


这里调用了一个USART_DMACmd去使能对应的DMA功能,现在我们跟踪进去看看,这个函数到底做了什么,对应哪个寄存器哪一位,有什么用:

跟踪进去发现两个东西:


①USART_DMAReq 参数的值是什么,我们进到assert_param看到如下:


②再看看寄存器的说明:


其实关于DMAT,我个人的理解就是,使能了这个,我们就可以让USART控制器明白发送的数据可以从DMA通道过来,不一定要我们人为一个一个给DR寄存器里面放。其实我觉得这样理解也符合情理。


好了,上面将串口使能DMA说了,下面将要说说DMA配置了,这个也是我们这篇文章的重中之重,我个人理解,如果这个理解到了的话,比如AD+DMA什么的都应该能够理解了。


DMA学前普及:

在我们具体把一大堆代码搬上来之前,我们先说说DMA相关知识

1.流和通道的关系,直接上图


初学者看到这里一定会问,为哈先来这个图,因为F1系列和F2系列不同,F1系列只有通道(channel)一说,但是F2多了一个流(Stream),为啥?很显然,因为F2支持的DMA通道更加多了。我们要用USART1_TX,我们就马上能够看出来我们需要定位到Stream7-Channel4. 然后通过一大堆的设置,我们就完成了设置。


2.配置DMA需要想到哪些东西?所以我们应该有以下问题:

①我们使用的外设对应DMA 的StreamX channelX

②我们的外设地址是多少

③我们的内存地址是多少

④我们数据传输方向是什么,是内存->外设还是外设->内存

⑤内存的大小是多少

⑥外设地址是否需要增加,比如ADDR ADDR+1 ADDR+2

⑦内存地址是否需要增加,比如ADDR ADDR+1 ADDR+2

⑧内存和外设分别用的是byte,short,还是Int,也就是位宽是多少

⑨我们的DMA是一直运转还是只需要运转一次就停止

⑩当前DMA请求的等级高度

11.是否使用FIFO

12.如果使用FIFO,当FIFO数据存量为多少的时候将数据发送出去

13.还有就是对内存和外设burst模式的配置,这个地方我也不是很懂,具体的话,可以看看链接点击打开链接


如果能够理解上面我说的词汇的话,DMA设置就应该没多大问题了,下面可以上代码了,我都进行了备注,如下:

void usart1_DmaInit(void){NVIC_InitTypeDef NVIC_InitStructure;DMA_InitTypeDef DMA_InitStructure;//使能DMA总线时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//去初始化STREAM 7 ,USART1_TX流 将使用CHANNEL 4DMA_DeInit(DMA2_Stream7);//wait the DMA reset OKwhile(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}//Configure DMA StreamDMA_InitStructure.DMA_Channel = DMA_Channel_4;//通道4DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//数据寄存器DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)usartBuf;//内存地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//外设地址DMA_InitStructure.DMA_BufferSize = 200;//传输数据个数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_MemoryDataSize_Byte;//数据位宽DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//单次传输模式DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA等级高度DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//不使用FIFODMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;//FIFO溢出个数DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次突发模式DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//单次突发模式DMA_Init(DMA2_Stream7,&DMA_InitStructure);//使能DMA Stream7的传输中断DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);//中断等级NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);DMA_Cmd(DMA2_Stream7,DISABLE);}

上面既然使用了NVIC配置,肯定少不了中断处理函数,其实这个中断函数就是等DMA把数据发送完了就会产生。如下:

extern unsigned  char Usart1DmaSendOverFlag;void DMA2_Stream7_IRQHandler(void){#if 1if(DMA_GetITStatus(DMA2_Stream7,DMA_IT_TCIF7) == SET){DMA_ClearITPendingBit(DMA2_Stream7,DMA_IT_TCIF7);Usart1DmaSendOverFlag = 1;}#endif}


中断函数就不需要怎么说了吧,也很简单,如果有不懂得,请留言。

下面是我们怎么调用DMA来发送:


void msgOut(unsigned short len){volatile int count = 0;if(len > 200)return;DMA_SetCurrDataCounter(DMA2_Stream7,len);DMA_Cmd(DMA2_Stream7 , ENABLE);}//输出void strout(char *p){int len = 0;len = strlen(p);msgOut(len);}void printk(char *msg,...){va_list argp;while(!Usart1DmaSendOverFlag);//等待上次数据发送完Usart1DmaSendOverFlag = 0;va_start(argp,msg);vsnprintf((char*)usartBuf , sizeof(usartBuf) , msg , argp);va_end(argp);strout((char*)usartBuf);}

其实,我们调用DMA也就是直接发送请求,然后就OK了


0 0