IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> M-Arch(8)第七个示例:串口和DMA -> 正文阅读

[嵌入式]M-Arch(8)第七个示例:串口和DMA

前言

回顾下之前的章节:

  • 第一章节中我们描述了整个框架的核心设计思路以及主要的文件架构

  • 第二章节中我们基于一个简单的定时器OS实现了串口的数据打印,并完成了通用crc模块的设计和测试

  • 第三章节中我们给出了真随机数和伪随机数的概念和代码示例,并在架构上对接口进行了重构

  • 第四章节中我们回顾了FMC的基本知识,并给出了示例,后面我们将在设计IAP的时候再次使用到FMC

  • 第五章节中我们使用ADC和DMA搭建了一个通用的采样框架,并通过串口给出了采样的数据示例

  • 第六章节中我们总结了DAC的基本使用方法,并通过DAC生成了任意频率的正弦波,三角波和方波

  • 第七章节中我们总结下时钟的概念,并给出了获取系统中各模块的时钟频率的代码

本文我们介绍如何通过串口的DMA来实现串口数据的收发。

由于之前我们已经使用了串口1(STM中是USART1,GD中是USART0),本文中以串口2作为示例(STM中是USART2,GD中是USART1)。

串口DMA设计

前文中我们已经讲过了DMA的概念和原理:

DMA即直接存储器访问控制器,DMA提供了一种硬件的方式在外设和存储器之间或者存储器和存储器之间传输数据,而无需CPU的介入,避免了CPU多次进入中断进行大规模的数据拷贝,最终提高整体的系统性能。

简单而言,DMA相当于是外请(DMA硬件)的搬运工(数据拷贝),节约宝贵的CPU资源。

DMA一般需要配置的内容包括:

  1. IO配置(时钟)

  2. DMA参数配置(拷贝的方向,内容,地址,通道,模式和数量等)

  3. 中断(使能)配置

DMA的配置需要注意通道的匹配:

9f0f2364a65b78e979d1180c06a582ed.png fdda9b2b9b89ed6f172a72a6183764a1.png

这里我们就不赘叙,直接给出串口DMA的设计思路:

98f704890541020551e93dcbb92aff4f.png

简单一句话理解:把DMA缓存当做二级缓存!

串口发送和接收的DMA流程

串口发送和接收的DMA流程(手册中的):

05cf4cb841e8213e53833ec55de0629a.png

注意的几个点手册中有描述,我在图中也给出来了,这几个坑描述如下:

  1. DMA的几个标志位(传输完成,半传输完成等)需要软件自己清除。

  2. DMA的配置在使能的情况下不可写,需要先失能才能写。

  3. 串口的空闲中断需要先读状态寄存器,再读数据寄存器才能清!

串口基本配置代码

代码中包括:

时钟配置,GPIO配置,串口参数配置,收发使能,DMA使能,使能空闲中断

GD32代码:

void?uart2_init(uint32_t?baudrate)
{
????uint32_t?com?=?USART1;

????usart_deinit(com);
????rcu_periph_clock_enable(RCU_USART1);
????rcu_periph_clock_enable(RCU_GPIOA);

????///<??USART1_TX???PA.2
????gpio_af_set(GPIOA,?GPIO_AF_7,?GPIO_PIN_2);
????gpio_mode_set(GPIOA,?GPIO_MODE_AF,?GPIO_PUPD_PULLUP,?GPIO_PIN_2);
????gpio_output_options_set(GPIOA,?GPIO_OTYPE_PP,?GPIO_OSPEED_50MHZ,?GPIO_PIN_2);
????
????///<??USART1_RX???PA.3
????gpio_af_set(GPIOA,?GPIO_AF_7,?GPIO_PIN_3);
????gpio_mode_set(GPIOA,?GPIO_MODE_AF,?GPIO_PUPD_PULLUP,?GPIO_PIN_3);
????gpio_output_options_set(GPIOA,?GPIO_OTYPE_PP,?GPIO_OSPEED_50MHZ,?GPIO_PIN_3);???
????
????usart_baudrate_set(com,?baudrate);
????usart_word_length_set(com,?USART_WL_8BIT);
????usart_stop_bit_set(com,?USART_STB_1BIT);
????usart_parity_config(com,?USART_PM_NONE);
????usart_hardware_flow_rts_config(com,?USART_RTS_DISABLE);
????usart_hardware_flow_cts_config(com,?USART_CTS_DISABLE);
????usart_enable(com);

????usart_receive_config(com,?USART_RECEIVE_ENABLE);
????usart_transmit_config(com,?USART_TRANSMIT_ENABLE);
????
????usart_dma_receive_config(com,?USART_DENR_ENABLE);
????usart_dma_transmit_config(com,?USART_DENT_ENABLE);

????usart_flag_clear(com,?USART_FLAG_TC);
????
????usart_interrupt_enable(com,?USART_INT_IDLE);
????nvic_irq_enable(USART1_IRQn,?3,?3);
}

STM32代码:

void?uart2_init(uint32_t?baudrate)
{
????USART_InitTypeDef?USART_InitStructure;
????USART_TypeDef*?USART?=?USART2;

????USART_DeInit(USART);
????RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,?ENABLE);
????RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,?ENABLE);

????///<??USART1_TX???PA.2
????gpio_init(GPIOA,?GPIO_Mode_AF_PP,?GPIO_Speed_50MHz,?GPIO_Pin_2);??///<??USART2_TX???PA.2
????gpio_init(GPIOA,?GPIO_Mode_IN_FLOATING,?GPIO_Speed_50MHz,?GPIO_Pin_3);??///<??USART2_RX???PA.3

????USART_StructInit(&USART_InitStructure);
????USART_InitStructure.USART_BaudRate?=?baudrate;??///<??波特率
????USART_Init(USART,?&USART_InitStructure);????
????USART_Cmd(USART,?ENABLE);??///<??使能串口

????USART_DMACmd(USART,?USART_DMAReq_Tx,?ENABLE);
????USART_DMACmd(USART,?USART_DMAReq_Rx,?ENABLE);

????USART_ClearFlag(USART,?USART_FLAG_TC);
????
????USART_ITConfig(USART,?USART_IT_IDLE,?ENABLE);
????nvic_irq_enable(USART2_IRQn,?3,?3);
}

串口DMA发送代码

uart2_dma_tx_init:串口发送DMA初始化

uart2_dma_send_ascii:DMA发送数据,严格按照手册中流程

【注】GD32 DMA是DMA0的通道6,STM DMA是DMA1的通道7。

GD32代码:

void?uart2_dma_tx_init(uint32_t?addr,?uint32_t?number)
{
????dma_single_data_parameter_struct?dma_parameter;
????/*?enable?DMA1?*/
????rcu_periph_clock_enable(RCU_DMA0);

????/*?发送?dm0?channel6(USART1?tx)?*/
????dma_deinit(DMA0,?DMA_CH6);
????dma_parameter.direction?=?DMA_MEMORY_TO_PERIPH;
????dma_parameter.periph_addr?=?(uint32_t)(&USART_DATA(USART1));
????dma_parameter.periph_inc?=?DMA_PERIPH_INCREASE_DISABLE;
????dma_parameter.periph_memory_width?=?DMA_PERIPH_WIDTH_8BIT;
????dma_parameter.memory0_addr?=?addr;
????dma_parameter.memory_inc?=?DMA_MEMORY_INCREASE_ENABLE;
????dma_parameter.number?=?number;
????dma_parameter.priority?=?DMA_PRIORITY_ULTRA_HIGH;
????dma_parameter.circular_mode?=?DMA_CIRCULAR_MODE_DISABLE;
????dma_single_data_mode_init(DMA0,?DMA_CH6,?&dma_parameter);
????
????/*?configure?DMA?mode?*/
????dma_channel_subperipheral_select(DMA0,?DMA_CH6,?DMA_SUBPERI4);
}

void?uart2_dma_send_ascii(uint8_t?*buff,?uint32_t?count)
{
????usart_flag_clear(USART1,?USART_FLAG_TC);

????dma_channel_disable(DMA0,?DMA_CH6);
????//dma_flag_clear(DMA0,?DMA_CH6,?DMA_CHINTF_RESET_VALUE);
????dma_flag_clear(DMA0,?DMA_CH6,?DMA_FLAG_FTF);
????dma_memory_address_config(DMA0,?DMA_CH6,?DMA_MEMORY_0,?(uint32_t)buff);
????dma_transfer_number_config(DMA0,?DMA_CH6,?count);
????dma_channel_enable(DMA0,?DMA_CH6);
????while?(usart_flag_get(USART1,?USART_FLAG_TC)!=RESET);
}

void?uart2_dma_send_string(char?*str)
{
????uart2_dma_send_ascii((uint8_t?*)str,?strlen(str));
}

STM32代码:

void?uart2_dma_tx_init(uint32_t?addr,?uint32_t?number)
{
????DMA_InitTypeDef?DMA_InitStructure;

????RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,?ENABLE);
????DMA_DeInit(DMA1_Channel7);

????/*?发送?dma1?channel7(USART2?tx)?*/
????DMA_InitStructure.DMA_PeripheralBaseAddr?=?(uint32_t)(&USART2->DR);
????DMA_InitStructure.DMA_MemoryBaseAddr?=?addr;
????DMA_InitStructure.DMA_DIR?=?DMA_DIR_PeripheralDST;
????DMA_InitStructure.DMA_BufferSize?=?number;
????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_Normal;
????DMA_InitStructure.DMA_Priority?=?DMA_Priority_High;
????DMA_InitStructure.DMA_M2M?=?DMA_M2M_Disable;
????DMA_Init(DMA1_Channel7,?&DMA_InitStructure);

????DMA_Cmd(DMA1_Channel7,?ENABLE);
}

void?uart2_dma_send_ascii(uint8_t?*buff,?uint32_t?count)
{
????USART_ClearFlag(USART2,?USART_FLAG_TC);

????DMA_Cmd(DMA1_Channel7,?DISABLE);
????DMA_ClearFlag(DMA1_FLAG_GL7);
????DMA1_Channel7->CMAR?=?(uint32_t)buff;
????DMA1_Channel7->CNDTR?=?count;
????DMA_Cmd(DMA1_Channel7,?ENABLE);
????
????while?(USART_GetFlagStatus(USART2,?USART_FLAG_TC)!=RESET);
}

void?uart2_dma_send_string(char?*str)
{
????uart2_dma_send_ascii((uint8_t?*)str,?strlen(str));
}

串口DMA接收代码

uart2_dma_rx_init:串口接收DMA初始化

uart2_dma_rx_get_count:获取DMA接收数据长度,并置位DMA,在串口空闲中断中调用

【注】GD32 DMA是DMA0的通道5,STM DMA是DMA1的通道6。

GD32代码:

void?uart2_dma_rx_init(uint32_t?addr,?uint32_t?number)
{
????dma_single_data_parameter_struct?dma_parameter;
????/*?enable?DMA1?*/
????rcu_periph_clock_enable(RCU_DMA0);

????/*?接收?dm0?channel5(USART1?rx)?*/
????dma_deinit(DMA0,?DMA_CH5);
????dma_parameter.direction?=?DMA_PERIPH_TO_MEMORY;
????dma_parameter.periph_addr?=?(uint32_t)(&USART_DATA(USART1));
????dma_parameter.periph_inc?=?DMA_PERIPH_INCREASE_DISABLE;
????dma_parameter.periph_memory_width?=?DMA_PERIPH_WIDTH_8BIT;
????dma_parameter.memory0_addr?=?addr;
????dma_parameter.memory_inc?=?DMA_MEMORY_INCREASE_ENABLE;
????dma_parameter.number?=?number;
????dma_parameter.priority?=?DMA_PRIORITY_ULTRA_HIGH;
????dma_parameter.circular_mode?=?DMA_CIRCULAR_MODE_DISABLE;
????dma_single_data_mode_init(DMA0,?DMA_CH5,?&dma_parameter);

????/*?configure?DMA?mode?*/
????dma_channel_subperipheral_select(DMA0,?DMA_CH5,?DMA_SUBPERI4);
????dma_channel_enable(DMA0,?DMA_CH5);
}

uint32_t?uart2_dma_rx_get_count(uint32_t?dma_rx_count_max)
{
????uint32_t?receive_len?=?dma_rx_count_max?-?dma_transfer_number_get(DMA0,?DMA_CH5);
????if?(receive_len?!=?0)
????{
????????dma_channel_disable(DMA0,?DMA_CH5);
????????dma_transfer_number_config(DMA0,?DMA_CH5,?dma_rx_count_max);
????????dma_flag_clear(DMA0,?DMA_CH5,?DMA_FLAG_FTF);
????????dma_channel_enable(DMA0,?DMA_CH5);
????}
????return?receive_len;
}
void?USART1_IRQHandler(void)
{
????uint8_t?temp;
????if?(usart_interrupt_flag_get(USART1,?USART_INT_FLAG_IDLE)?!=?RESET)
????{
????????//?软件先读USART_STAT0,再读USART_DATA可清除该位。
????????temp?=?USART_STAT0(USART1);
????????temp?=?USART_DATA(USART1);
????????temp?=?temp;

????????process_uart2_dma_rx_data();
????}
}

STM32代码:

void?uart2_dma_rx_init(uint32_t?addr,?uint32_t?number)
{
????DMA_InitTypeDef?DMA_InitStructure;

????RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,?ENABLE);
????DMA_DeInit(DMA1_Channel6);

????/*?发送?dma1?channel6(USART2?rx)?*/
????DMA_InitStructure.DMA_PeripheralBaseAddr?=?(uint32_t)(&USART2->DR);
????DMA_InitStructure.DMA_MemoryBaseAddr?=?addr;
????DMA_InitStructure.DMA_DIR?=?DMA_DIR_PeripheralSRC;
????DMA_InitStructure.DMA_BufferSize?=?number;
????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_Normal;
????DMA_InitStructure.DMA_Priority?=?DMA_Priority_High;
????DMA_InitStructure.DMA_M2M?=?DMA_M2M_Disable;
????DMA_Init(DMA1_Channel6,?&DMA_InitStructure);

????DMA_Cmd(DMA1_Channel6,?ENABLE);
}

uint32_t?uart2_dma_rx_get_count(uint32_t?dma_rx_count_max)
{
????uint32_t?receive_len?=?dma_rx_count_max?-?DMA_GetCurrDataCounter(DMA1_Channel6);
????if?(receive_len?!=?0)
????{
????????DMA_Cmd(DMA1_Channel6,?DISABLE);
????????DMA_ClearFlag(DMA1_FLAG_GL6);
????????DMA_SetCurrDataCounter(DMA1_Channel6,?dma_rx_count_max);
????????DMA_Cmd(DMA1_Channel6,?ENABLE);
????}
????return?receive_len;
}
void?USART2_IRQHandler(void)
{
????uint8_t?temp;
????if?(USART_GetITStatus(USART2,?USART_IT_IDLE)?!=?RESET)
????{
????????//?软件先读USART_SR,再读USART_DR可清除该位。
????????temp?=?USART2->SR;
????????temp?=?USART2->DR;
????????temp?=?temp;

????????process_uart2_dma_rx_data();
????}
}

process_uart2_dma_rx_data:处理数据主函数

void?process_uart2_dma_rx_data(void)
{
????uint32_t?receive_len?=?uart2_dma_rx_get_count(UART_DATA_DMA_RX_COUNT);
????uint32_t?index;
????if?(receive_len?!=?0)
????{
????????printf("receivelen?=?%d\r\n",?receive_len);
????????for?(index?=?0;?index?<?receive_len;?index++)
????????{
????????????printf("%02X?",?uart_dma_rx[index]);
????????}
????????printf("\r\n");
????}
}

串口DMA发送例行结果展示

COM3接串口2

MCU发送数据为:Hello, My Name is Cortex M !\r\n

9bb795c263e42b61e63df59c05b69f3f.gif

串口DMA接收例行结果展示

COM9接串口1,COM3接串口2

COM3循环发数据给MCU,MCU通过串口1打印给电脑

b24f78b2988639882c367cd6dc80913b.gif

【注:STM32的串口DMA没找到串口头子,没测试】

--EOF--

例行求粉,谢谢!

7e291c131989fa0ac1472707492fa8ad.png
求粉
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-03 17:14:14  更:2021-10-03 17:15:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:39:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码