USART 串口 DMA 发送和接收

发布者:Enchanted2023最新更新时间:2019-09-30 来源: eefocus关键字:USART  串口  DMA  发送和接收 手机看文章 扫描二维码
随时随地手机看文章

串口DMA发送:


发送数据的流程:


前台程序中有数据要发送,则需要做如下几件事


1. 在数据发送缓冲区内放好要发送的数据,说明:此数据缓冲区的首地址必须要在DMA初始化的时候写入到DMA配置中去。


2. 将数据缓冲区内要发送的数据字节数赋值给发送DMA通道,(串口发送DMA和串口接收DAM不是同一个DMA通道)


3. 开启DMA,一旦开启,则DMA开始发送数据,说明一下:在KEIL调试好的时候,DMA和调试是不同步的,即不管Keil 是什么状态,DMA总是发送数据。


4. 等待发送完成标志位,即下面的终端服务函数中的第3点设置的标志位。或者根据自己的实际情况来定,是否要一直等待这个标志位,也可以通过状态机的方式来循环查询也可以。或者其他方式。


判断数据发送完成:


启动DMA并发送完后,产生DMA发送完成中断,在中断函数中做如下几件事:


1. 清DMA发送完成中断标志位


2. 关闭串口发送DMA通道


3. 给前台程序设置一个软件标志位,说明数据已经发送完毕


串口DMA接收:


接收数据的流程:


串口接收DMA在初始化的时候就处于开启状态,一直等待数据的到来,在软件上无需做任何事情,只要在初始化配置的时候设置好配置就可以了。


判断数据数据接收完成:


       这里判断接收完成是通过串口空闲中断的方式实现,即当串口数据流停止后,就会产生IDLE中断。这个中断里面做如下几件事:


1. 关闭串口接收DMA通道,2点原因:1.防止后面又有数据接收到,产生干扰。2.便于DMA的重新配置赋值,下面第4点。


2. 清除DMA 所有标志位


3.从DMA寄存器中获取接收到的数据字节数


4.重新设置DMA下次要接收的数据字节数,注意,这里是给DMA寄存器重新设置接收的计数值,这个数量只能大于或者等于可能接收的字节数,否则当DMA接收计数器递减到0的时候,又会重载这个计数值,重新循环递减计数,所以接收缓冲区的数据则会被覆盖丢失。


5. 开启DMA通道,等待下一次的数据接收,注意,对DMA的相关寄存器配置写入,如第4条的写入计数值,必须要在关闭DMA的条件进行,否则操作无效。


说明一下,STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。


USART 和 DMA 硬件初始化配置


/*--- LumModule Usart Config ---*/


#define LUMMOD_UART                      USART3


#define LUMMOD_UART_GPIO                 GPIOC


#define LUMMOD_UART_CLK                  RCC_APB1Periph_USART3


#define LUMMOD_UART_GPIO_CLK        RCC_APB2Periph_GPIOC


#define LUMMOD_UART_RxPin               GPIO_Pin_11


#define LUMMOD_UART_TxPin               GPIO_Pin_10


#define LUMMOD_UART_IRQn                USART3_IRQn


#define LUMMOD_UART_DR_Base                  (USART3_BASE + 0x4)  //0x40013804


 


#define LUMMOD_UART_Tx_DMA_Channel      DMA1_Channel2


#define LUMMOD_UART_Tx_DMA_FLAG         DMA1_FLAG_GL2//DMA1_FLAG_TC2 | DMA1_FLAG_TE2 


#define LUMMOD_UART_Tx_DMA_IRQ          DMA1_Channel2_IRQn


 


#define LUMMOD_UART_Rx_DMA_Channel      DMA1_Channel3


#define LUMMOD_UART_Rx_DMA_FLAG         DMA1_FLAG_GL3//DMA1_FLAG_TC3 | DMA1_FLAG_TE3 


#define LUMMOD_UART_Rx_DMA_IRQ      DMA1_Channel3_IRQn


 


 


void Uart_Init(void)


{


    NVIC_InitTypeDef NVIC_InitStructure;


    GPIO_InitTypeDef GPIO_InitStructure;


    USART_InitTypeDef USART_InitStructure;


 


    /* System Clocks Configuration */


//= System Clocks Configuration ====================================================================//


   


    /* Enable GPIO clock */


    RCC_APB2PeriphClockCmd(LUMMOD_UART_GPIO_CLK ,  ENABLE ); // 开启串口所在IO端口的时钟


    /* Enable USART Clock */


    RCC_APB1PeriphClockCmd(LUMMOD_UART_CLK, ENABLE); // 开始串口时钟


   


   


//=NVIC_Configuration==============================================================================//


 


    /* Configure the NVIC Preemption Priority Bits */


    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);


 


    /* Enable the DMA Interrupt */


    NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_Tx_DMA_IRQ;   // 发送DMA通道的中断配置


    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     // 优先级设置


    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;


    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


    NVIC_Init(&NVIC_InitStructure);


 


    /* Enable the USART Interrupt */


    NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_IRQn;     // 串口中断配置


    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;


    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;


    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


    NVIC_Init(&NVIC_InitStructure);


   


//=GPIO_Configuration==============================================================================//


 


    GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);  // 我这里没有用默认IO口,所以进行了重新映射,这个可以根据自己的硬件情况配置选择


   


    /* Configure USART3 Rx as input floating */


    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 串口接收IO口的设置


    GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_RxPin;


    GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);


 


    /* Configure USART3 Tx as alternate function push-pull */


    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   // 串口发送IO口的设置


    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 这里设置成复用形式的推挽输出   


    GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_TxPin;


    GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);


 


    DMA_Uart_Init();   // 串口 DMA 配置


 


    /* USART Format configuration ------------------------------------------------------*/


 


    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;


 


    /* Configure USART3 */


    USART_InitStructure.USART_BaudRate = 115200;  //  波特率设置


    USART_Init(LUMMOD_UART, &USART_InitStructure);


 


    /* Enable USART3 Receive and Transmit interrupts */


    USART_ITConfig(LUMMOD_UART, USART_IT_IDLE, ENABLE);  // 开启 串口空闲IDEL 中断


   


    /* Enable the USART3 */


    USART_Cmd(LUMMOD_UART, ENABLE);  // 开启串口


    /* Enable USARTy DMA TX request */


    USART_DMACmd(LUMMOD_UART, USART_DMAReq_Tx, ENABLE);  // 开启串口DMA发送


    USART_DMACmd(LUMMOD_UART, USART_DMAReq_Rx, ENABLE); // 开启串口DMA接收


}


 


 


void DMA_Uart_Init(void)


{


    DMA_InitTypeDef DMA_InitStructure;


   


    /* DMA clock enable */


    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 开启DMA1时钟


   


   


//=DMA_Configuration==============================================================================//


 


/*--- LUMMOD_UART_Tx_DMA_Channel DMA Config ---*/


 


    DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE);                           // 关DMA通道


    DMA_DeInit(LUMMOD_UART_Tx_DMA_Channel);                                 // 恢复缺省值


    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口发送数据寄存器


    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Tx_Buf;         // 设置发送缓冲区首地址


    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 设置外设位目标,内存缓冲区 ->外设寄存器


    DMA_InitStructure.DMA_BufferSize = LUMMOD_TX_BSIZE;                     // 需要发送的字节数,这里其实可以设置为0,因为在实际要发送的时候,会重新设置次值


    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的


    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整


    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节


    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节


    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式


    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置


    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式


    DMA_Init(LUMMOD_UART_Tx_DMA_Channel, &DMA_InitStructure);               // 写入配置


    DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG);                                 // 清除DMA所有标志


DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // 关闭DMA


    DMA_ITConfig(LUMMOD_UART_Tx_DMA_Channel, DMA_IT_TC, ENABLE);            // 开启发送DMA通道中断


   


/*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/


 


    DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE);                           // 关DMA通道


    DMA_DeInit(LUMMOD_UART_Rx_DMA_Channel);                                 // 恢复缺省值


    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口接收数据寄存器


    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Rx_Buf;         // 设置接收缓冲区首地址


    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                      // 设置外设为数据源,外设寄存器 -> 内存缓冲区


    DMA_InitStructure.DMA_BufferSize = LUMMOD_RX_BSIZE;                     // 需要最大可能接收到的字节数


    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的

[1] [2]
关键字:USART  串口  DMA  发送和接收 引用地址:USART 串口 DMA 发送和接收

上一篇:CAN通信标准帧和扩展帧
下一篇:STM32F105 CAN总线数据收发调试

推荐阅读最新更新时间:2024-11-13 11:25

YTM32的DMA控制器要点简析
引言 考虑到DMA是一个AHB Master设备,可以同处理器内核一样,主动向总线发起传输请求,将“小脏手”伸向各总线上挂载的各外设模块,因此,我更愿意把DMA看做是处理器核心服务的一部分,甚至把它当成一个小核或者协处理器都不为过。 DMA可以在CPU之外,捕获到触发信号后,自行搬运数据从指定地址到另一个指定地址,并且还可以根据预先的配置自动计算下一次搬运的地址。使用DMA可以有效地节约CPU处理海量数据传输的负载。可以想见,如果使用中断方式处理通过外设发送或者接收数据,CPU将会在频繁切换中断服务之间花费大量的时间。 另外,DMA从DMAMUX获取的触发源,能够实现的自动读写的操作,若是某些读操作或者写操作能够产生额外的
[嵌入式]
YTM32的<font color='red'>DMA</font>控制器要点简析
STM32学习记录8:DMA
官方的例子:STM32F10x_StdPeriph_Lib_V3.1.2\Project\STM32F10x_StdPeriph_Examples\DMA\FLASH_RAM /* DMA1 channel6 configuration */ DMA_DeInit(DMA1_Channel6); //peripheral base address 外设地址是自己的一个定义 //例如#define SPI1_DR_Addr ( (u32)0x4001300C ) DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer; //memory base
[单片机]
STM32学习记录8:<font color='red'>DMA</font>
stm32 freertos 之串口中断
一、中断处理函数 void USART1_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; u8 cChar; if(USART_GetITStatus (USART1,USART_IT_RXNE)!=RESET) { cChar=USART_ReceiveData(USART1); xQueueSendToBackFromISR (xQueueRx,&cChar,&xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPri
[单片机]
个人总结之MSP430F5510串口通讯(485)
1.首先先介绍一下USCI模块 通用串行通信接口(USCI)模块支持多种异步通信模式。不同的 USCI 模块支持不同的模式。 每一个 USCI 模块以不同的字母命名。例如,USCI_A 不同于 USCI_B 等等。如果不止一个相同的USCI 模块被安装在一个设备上,这些模块以不同的数字命名。例如,一个设备有两个 USCI_A模块,它们可以命名 USCI_A0 和 USCI_A1。参见设备明细表去确定各种 USCI 模块,如果需要,它们可以安放在任何设备上。 USCI_Ax 模块支持: UART 模式 脉冲调整的 IRDA 通信 自动波特率检测的 LIN 通信 SPI 模式 USCI_Bx 模
[单片机]
个人总结之MSP430F5510<font color='red'>串口</font>通讯(485)
深藏不露的'NUL'(51单片机串口通讯是遇到的错误之一)
NUl的定义: C语言中规定以字符 \0'作为字符串结束标志。 \0'是一个ASCII码为0的字符,从ASCII码表中可以看到ASCII码为0的字符是 空操作字符 , 它不引起任何控制动作,也不是一个可显示的字符 这是本人在用单片机做串口通讯时遇到的小问题,因为那个电子设备比较特殊,发送的数据也是比较的特殊,所以我便遇到了这个问题。一开始按照预定的指令和电子设备通讯,总是不行,后来通过串口调试器才发现,发送的数据只能发送一半。于是我返回去看代码,这才发现了症结所在。 以下便是我的程序出错的原因: 出错的程序代码段: void Send_data(char p ) { a=0; while(
[单片机]
51单片机串口通信编程
51单片机是对所有兼容Intel 8031指令系统的单片机的统称。该系列单片机的始祖是Intel的8004单片机,后来随着Flash rom技术的发展,8004单片机取得了长足的进展,成为应用最广泛的8位单片机之一,其代表型号是ATMEL公司的AT89系列,它广泛应用于工业测控系统之中。很多公司都有51系列的兼容机型推出,今后很长的一段时间内将占有大量市场。51单片机是基础入门的一个单片机,还是应用最广泛的一种。需要注意的是51系列的单片机一般不具备自编程能力。 一、 pc机上的 串口通信编程过程如下: 1.建立项目 打开VC++6.0,建立一个基于对话框的MFC应用程序SCommTest(与我源代码一致,等会你会方便一点);
[单片机]
51单片机<font color='red'>串口</font>通信编程
51单片机实验11:串口通信
目的:通过电脑向单片机发送字符,单片机收到字符后,向电脑回应相同的字符。 开发板时钟频率为12MHz。程序设置波特率为4800,加倍。 #include reg52.h #define uc unsigned char void usartinit() { TMOD=0X20; TH1=0XF3; TL1=0XF3; PCON=0X80; SCON=0X50; TR1=1; ES=1; EA=1; } void usart() interrupt 4 { uc receive_data; receive_data=SBUF; RI=0; SBUF=receive_data
[单片机]
异步串口uart的c语言编程
#include io8535v.h #define uchar unsigned char #define uint unsigned int uchar Table ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; uchar Send_Da ta ={'a','b','c','d'}; uchar Receive_Da ta ={0,0,0,0}; void DelayMs(uint i) {uint j; for(;
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved