串口DMA的配置和使用
串口DMA
DMA利用好无疑会让串口使用起来更加高效,同时CPU还能处理自己的事情,但是DMA的使用却让代码变得更加的复杂,也使串口配置变得更加麻烦!麻烦???也许只是学习的方式不对,代码是变长了,但是思路清晰的话,也就是在原来串口加了点东西而已。本文让你轻轻松松学会串口DMA!定长数据传输与不定长数据传输都教会你!通过此文,希望能让更多人了解和会使用串口DMA,希望大家多多支持,有无问题都可以参与评论,如果写得好,希望可以给个赞,谢谢! 读本文前,如果你对DMA不是很了解的可以看我的这篇博文《 STM32 | DMA配置和使用如此简单(超详细)》,里面详细介绍了DMA的一些基础知识。
测试平台:STM32F103C8T6
库版本:官方库3.5版本
关键词:串口DMA;定长数据传输;不定长数据传输;中断方式
代码下载地址: (超详细、附讲解)串口DMA.zip (点击链接跳转)
(https://download.csdn.net/download/weixin_44524484/12417316)
一、串口DMA的配置
本文针对串口2(USART2)如何进行DMA传输进行讲解,如果采用其他串口,注意要修改相应端口配置以及DMA通道。
1、串口的DMA请求映像
通过我之前的博文《 STM32 | DMA配置和使用如此简单(超详细)》可以知道,串口2(USART2)的接收(USART2_RX)和发送(USART2_TX)分别在DMA1控制器的通道6和通道7。下图为DMA1控制器的请求映像图。
2、资源说明
STM32F1中,USART2使用的是PA2(USART2_TX)和PA3(USART2_RX),为了方便阅读,使用到的资源列在下表。
外设 | GPIO口 | DMA请求映像通道 | 备注 |
---|---|---|---|
USART2_TX | PA2 | DMA1通道7 | RAM->USART2的数据传输 |
USART2_RX | PA3 | DMA1通道6 | USART2->RAM的数据传输 |
3、DMA初始化配置
USART2的DMA配置在《 STM32 | DMA配置和使用如此简单(超详细)》中分别讲了库函数版和寄存器版两种配置,我这里直接搬用,不理解的可以看看《 STM32 | DMA配置和使用如此简单(超详细)》这篇文章。要注意的是库函数最大优势就是便于阅读,所以在DMA初始化配置中我们使用库函数。
首先,我们要先定义三个缓冲区(作全局定义),一个发送缓冲区,两个接收缓冲区,两个接收缓冲区是为了做双缓冲区,目的是为了防止后一次传输的数据覆盖前一次传输的数据,并且留出足够的时间让CPU处理缓冲区数据。双缓冲在串口DMA中有着很重要的意义并起着很大的作用!
//USART2_MAX_TX_LEN和USART2_MAX_RX_LEN在头文件进行了宏定义,分别指USART2最大发送长度和最大接收长度
u8 USART2_TX_BUF[USART2_MAX_TX_LEN]; //发送缓冲,最大USART2_MAX_TX_LEN字节
u8 u1rxbuf[USART2_MAX_RX_LEN]; //发送数据缓冲区1
u8 u2rxbuf[USART2_MAX_RX_LEN]; //发送数据缓冲区2
u8 witchbuf=0; //标记当前使用的是哪个缓冲区,0:使用u1rxbuf;1:使用u2rxbuf
u8 USART2_TX_FLAG=0; //USART2发送标志,启动发送时置1
u8 USART2_RX_FLAG=0; //USART2接收标志,启动接收时置1
要说明的是,实际上发送缓冲区可能用不上,因为我们要发送的数据内容和大小都不一定每次都是固定的,可以是变化的,我们只需重新指派新的发送缓冲区地址即可,但是在初始化中我们还是要指定一个发送缓冲区地址。
下面是DMA1_USART2的初始化函数。
void DMA1_USART2_Init(void)
{
DMA_InitTypeDef DMA1_Init;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //使能DMA1时钟
//DMA_USART2_RX USART2->RAM的数据传输
DMA_DeInit(DMA1_Channel6); //将DMA的通道6寄存器重设为缺省值
DMA1_Init.DMA_PeripheralBaseAddr = (u32)(&USART2->DR); //启动传输前装入实际RAM地址
DMA1_Init.DMA_MemoryBaseAddr = (u32)u1rxbuf; //设置接收缓冲区首地址
DMA1_Init.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读取到内存
DMA1_Init.DMA_BufferSize = USART2_MAX_RX_LEN; //DMA通道的DMA缓存的大小
DMA1_Init.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA1_Init.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA1_Init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA1_Init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA1_Init.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA1_Init.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
DMA1_Init.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA1_Channel6,&DMA1_Init); //对DMA通道6进行初始化
//DMA_USART2_TX RAM->USART2的数据传输
DMA_DeInit(DMA1_Channel7); //将DMA的通道7寄存器重设为缺省值
DMA1_Init.DMA_PeripheralBaseAddr = (u32)(&USART2->DR); //启动传输前装入实际RAM地址
DMA1_Init.DMA_MemoryBaseAddr = (u32)USART2_TX_BUF; //设置发送缓冲区首地址
DMA1_Init.DMA