串口DMA方式发送&接收

串口DMA方式收发

        笔者使用的是STM32F407VET6,共包含6路串口,页尾处程序已将全部串口的DMA收发配置完成,本文仅以串口1为例进行讲解。(查看代码可直接跳至第二节或页尾处下载)

1 STM32F4 DMA  简介

        DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
        STM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。

它可以处理一下事务:

  • 外设到储存器的传输
  • 储存器到外设的传输
  • 储存器到储存器的传输

注意:DMA1 控制器 AHB 外设端口与 DMA2 控制器的情况不同,不连接到总线矩阵,因此,仅 DMA2 数据流能够执行存储器到
存储器的传输。

        其中,数据流的多通道选择,是通过 DMA_SxCR 寄存器控制的,如图1所示:

                                                                                   图1 通道选择

        上图可以看出,DMA_SxCR 控制数据流到底使用哪一个通道,每个数据流有 8 个通道可供选择,但每次只能选择其中一个通道进行 DMA 传输,DMA2 的各数据流通道映射表,如表 1 所示

                                                                             表1 DMA2数据流映射表

        上表就列出了 DMA2 所有可能的选择情况,来总共 64 种组合,比如本章我们要实现串口1的 DMA 发送,即USART1_TX,就必须选择 DMA2 的数据流 7,通道 4,来进行 DMA 传输。这里注意一下,有的外设(比如 USART1_RX)可能有多个通道可以选择,随意选择一个就可以。

重要寄存器简介

(1) DMA 中断状态寄存器

        该寄存器总共有 2 个:DMA_LISR 和 DMA_HISR,每个寄存器管理 4 数据流(总共 8 个),DMA_LISR 寄存器用于管理数据流 0~3,而 DMA_HISR 用于管理数据流 4~7。如果开启了 DMA_LISR 中这些位对应的中断,则在达到条件后就会跳到中断服务函数里面去,即使没开启,也可以通过查询这些位来获得当前 DMA 传输的状态。这里常用的是 TCIFx位,即数据流 x 的 DMA 传输完成与否标志。

注意:此寄存器为只读寄存器,所以在这些位被置位之后,只能通过【中断标志清除寄存器】来清除。

(2)DMA 中断标志清除寄存器

        该寄存器同样有 2 个:DMA_LIFCR 和 DMA_HIFCR,同样是每个寄存器控制 4 个数据流。该寄存器为只写寄存器,其各位就是用来清除 【中断状态寄存器】的对应位的,通过写 1 清除。

(3) DMA 数据流 x 配置寄存器(DMA_SxCR)

        该寄存器控制着 DMA 的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以 DMA_ SxCR 是 DMA 传输的核心控制寄存器。

(4)DMA 数据流 x 数据项

  • 24
    点赞
  • 151
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
使用串口 DMA 中断方式接收发送可以大大降低 CPU 的占用率,提高系统的响应速度。下面我简单介绍一下 STM32 使用串口 DMA 中断方式接收发送的步骤。 1. 初始化串口 首先需要初始化串口,设置波特率、数据位、停止位等参数。这里我们以 USART1 为例: ``` // 使能 USART1 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 初始化 USART1 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); ``` 2. 配置 DMA 接下来配置 DMA,设置 DMA 的传输方向、数据宽度、缓冲区地址等参数。这里我们以 USART1 的 DMA 接收为例: ``` // 使能 DMA1 时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 配置 DMA1_Channel5(USART1_RX)为普通模式,从外设接收数据,内存地址自增,传输数据宽度为 8 位 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = RX_BUFFER_SIZE; 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_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); // 使能 DMA1_Channel5(USART1_RX)传输完成中断 DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); // 使能 DMA1_Channel5(USART1_RX)传输 DMA_Cmd(DMA1_Channel5, ENABLE); ``` 3. 配置中断 最后配置中断,使能串口DMA 的中断。这里我们以 USART1 的 DMA 接收中断为例: ``` // 使能 USART1 的中断 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 使能 DMA1_Channel5(USART1_RX)的中断 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); ``` 4. 接收数据 现在我们可以通过 DMA 接收数据了。当 DMA 接收到数据后,会触发 DMA1_Channel5 的传输完成中断,我们可以在中断处理函数中读取接收到的数据: ``` uint8_t rx_buffer[RX_BUFFER_SIZE]; void DMA1_Channel5_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC5)) { // 读取接收到的数据 uint16_t rx_length = RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5); for(int i = 0; i < rx_length; i++) { uint8_t ch = rx_buffer[i]; // 处理接收到的数据 } // 清除传输完成中断标志 DMA_ClearITPendingBit(DMA1_IT_TC5); } } ``` 5. 发送数据 除了接收数据以外,我们也可以使用 DMA 发送数据。这里我们以 USART1 的 DMA 发送为例: ``` // 配置 DMA1_Channel4(USART1_TX)为普通模式,从内存发送数据,外设地址自增,传输数据宽度为 8 位 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tx_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = tx_length; 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_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); // 使能 DMA1_Channel4(USART1_TX)传输完成中断 DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // 使能 DMA1_Channel4(USART1_TX)传输 DMA_Cmd(DMA1_Channel4, ENABLE); // 等待 DMA1_Channel4(USART1_TX)传输完成 while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET); // 清除传输完成标志 DMA_ClearFlag(DMA1_FLAG_TC4); ``` 以上就是 STM32 使用串口 DMA 中断方式接收发送的基本步骤。需要注意的是,配置 DMA 时,要根据实际情况选择传输模式和优先级,以保证数据的正确传输。另外,在中断处理函数中,要及时清除相应的中断标志,以免影响下一次的中断处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值