最近遇到一个小问题,感觉很有意思便记下来分享一下 ,顺便也为日后类似的问题提供点思路:

使用stm32串口发送数据 ,串口是单线半双工模式,要求数据发送前串口变成发送模式,发送完后立即变回接收模式,发送操作使用DMA来发送,程序开了串口中断和DMA中断,串口中断主要是用于接收与解析数据,DMA中断是想用于操作串口收发模式切换,发现DMA发送时最后一个数据老是发不出来。

经过分析定位发现是因为DMA将传输完成后,串口其实还没有将所有的问题发送出去(至少还有一个字节没发出去),然后修改操串口收发模式切换时间点将该问题解决,具体为:串口在DMA传输前由接收模式变成发送模式,在DMA传输完成中断中开启串口发送完成中断,在串口发送完成中断中将串口工作模式从发送模式变回接收模式,问题完解决,相关配置代码如下:

串口配置:

void Usart_Init( void )
{USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef  GPIO_InitStructure;//GPIO clock enableRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);     // USART1时钟使能//USART1_TXGPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;              // 复用推挽输出GPIO_Init(USART1_TXRX_PORT, &GPIO_InitStructure);//USART1_RXGPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      // 浮空输入GPIO_Init(USART1_TXRX_PORT, &GPIO_InitStructure);USART_InitStructure.USART_BaudRate = 115200;               // 串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 字长为8位数据格式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);                       // 初始化串口1USART_HalfDuplexCmd(USART1, ENABLE);                            // 使能变双工DMA_Usart_Init();NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;         // USART1中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ;// 抢占优先级2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          // 子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                  // IRQ通道使能NVIC_Init(&NVIC_InitStructure);                           // 根据指定的参数初始化NVIC寄存器USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);            // 开启串口1接收中断USART_Cmd(USART1, ENABLE);                                // 使能串口1USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);            // 开启串口USART1的DMA发送}

DMA配置:

void DMA_Usart_Init(void)
{DMA_InitTypeDef DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开启DMA1时钟DMA_Cmd(DMA1_Channel4, DISABLE);//关闭DMA1的通道4  Usart1_TX对应通道4DMA_DeInit(DMA1_Channel4);//恢复缺省值DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);//设置串口发送数据寄存器DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMATXBUFF1;//设置发送缓冲区首地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//设置外设位目标,内存缓冲区->外设寄存器DMA_InitStructure.DMA_BufferSize = 10;//需要发送的字节数,这里可以设置为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_Low;//优先级配置DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存的DMA模式DMA_Init(DMA1_Channel4, &DMA_InitStructure);//写入配置NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;//DMA1中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 ;//抢占优先级2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;      //子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化NVIC寄存器DMA_ClearFlag(DMA1_FLAG_GL4);//清除DMA所有标志 GL4:通道4全局标志 TC4:传输完成 HT4:传输过半 TE4:传输错误DMA_Cmd(DMA1_Channel4, DISABLE);//关闭DMA1的通道4DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);//开启发送DMA通道中断}

串口中断:

void USART1_IRQHandler(void)
{if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  // 接收数据寄存器非空标志{if(RxNum1 > (RXBUFFSIZE1 - 1)){RxNum1 = 0;}RXBUFF1[RxNum1++] = USART_ReceiveData(USART1);}else if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)// 发送完成标志{USART_ITConfig(USART1, USART_IT_TC, DISABLE);USART_ClearFlag(USART1,USART_FLAG_TC);USART1_RX_EN_TX_DIS();}else{}}

DMA中断:

void DMA1_Channel4_IRQHandler(void)
{if(DMA_GetITStatus(DMA1_FLAG_TC4) != RESET)     // DMA1通道4发送完成标志{Usart1TxBusy = 0;DMA_Cmd(DMA1_Channel4, DISABLE);            // 关闭DMA1的通道4DMA_ClearFlag(DMA1_FLAG_GL4);               // 清除DMA1的通道4的所有中断标志,因为只有发送完成标志,所以直接全部清除USART_ClearFlag(USART1,USART_FLAG_TC);      // 在关中断前先清中断标志,防止是串口上一个数据发送时的中断标志USART_ITConfig(USART1, USART_IT_TC, ENABLE);// DMA1传输完成后使能Usart1发送完成中断}
}

stm32 usart 单线半双工串口 DMA发送 最后一个字节发不出来问题相关推荐

  1. 【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)

    前言: 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1.芯片: STM32F407ZET6/ STM32F103ZET6 ...

  2. FPGA 串口中断_一个严谨的STM32串口DMA发送amp;接收(1.5Mbps波特率)机制

    昨天分享的<嵌入式大杂烩读者福利:第一期>大家有去抽奖吗,没抽的可参与抽奖,碰碰运气.我最喜欢抽奖了,还记得前几个月疫情严重时期连抽中了3包口罩,真刺激,哈哈.之后多多安排抽奖,敬请期待. ...

  3. 一个严谨的STM32串口DMA发送接收(1.5Mbps波特率)机制

    文章目录 1 前言 2 串口有必要使用DMA吗 3 实现方式 4 STM32串口使用DMA 5 串口DMA接收 5.1 基本流程 5.2 相关配置 5.3 接收处理 5.3 .1 接收数据大小 5.3 ...

  4. hal库串口dma卡死_STM32 HAL库 串口DMA发送完成中断

    近期使用STM32驱动MAX3485进行485通信,发现STM32F103C8并不自带硬件485首发功能,需要软件上控制IO高低来驱动MAX3485进行485接收.485发送. 根据MAX3485手册 ...

  5. STM32H7xx 串口DMA发送接收(LL库)

    文章目录 1 前言 2 STM32H7实现 2.1 关键步骤 2.2 注意事项 3 代码仓库 1 前言   关于串口DMA收发实现,不同CPU其套路都是类似的,不同之处在于寄存器配置.依赖BSP库等差 ...

  6. HC32F460 串口 DMA 发送 接收

    HC32F460 DMA TX RX 前言 发送/接收功能 比较 接收超时定时器通道选择 默认关系,无法任意选择通道,根据实际使用的串口确认定时器通道 串口初始化 DMA初始化 定时器初始化 串口DM ...

  7. 嵌入式之串口中断只能收到一个字节

    串口中断只能收到一个字节原因排查: 1.中断程序占用时间过长,导致后面的数据到达的时候无法接受. 2.中断清除标志位. 3.串口中断优先级太低,导致中断嵌套.将优先级设为最高.

  8. 【STM32】HAL库——串口DMA通信(三)

    前期准备: STM32CubeMX STM32RCT6核心板 IDE Keil(MDK-ARM) 关于DMA 1. 什么是DMA? DMA(Direct Memory Access,直接存储器访问) ...

  9. STM32 进阶教程 15 - 串口DMA收发

    前言 串口操作相信大家一定很熟悉,如果你已经会串口的收发数据,并可以灵活使用轮询及中断方式对串口进行数据收发,那么恭喜你,学完本节内容后,也将可以学会串口的更高级操作方式,DMA方式,DMA操作串口可 ...

最新文章

  1. 妙用 Intellij IDEA 创建临时文件,Git 跟踪不到的那种
  2. java反射比较两个model6_java反射机制
  3. 逻辑回归和线性回归的区别_[PRML]线性分类模型贝叶斯逻辑回归
  4. Nacos配置中心-加载多配置集
  5. leetcode 434. 字符串中的单词数(Java版)
  6. 【数据结构】——冒泡排序、插入排序、选择排序
  7. js substring和substr的区别实例,一目了然
  8. Unity DOTween插件和iTween插件使用(笔记)
  9. 一阶微分方程组c语言编程,一阶常微分方程数值解的C语言编程实现.doc
  10. linux投影手机,linux连接投影机方法介绍
  11. QQ伤感日志_你教会了我爱,让我懂得了爱
  12. exchange 2013 C盘空间不足清理
  13. [问题/解决]Could not chdir to home directory /home/zwj: Permission denied
  14. “云脑启智 开源赋能”2021年度AI盛会召开 PNEdit赋能人工智能产业发展
  15. 读书笔记: 当我谈跑步时,我谈些什么
  16. 【快速上手系列】使用Springboot集成Swagger2的简单使用测试
  17. Android基础教程(二)之五大布局对象---FrameLayout,LinearLayout ,AbsoluteLayout,RelativeLayout,TableLayout.
  18. 生命的计算和计算的生命
  19. CSS3 用动画实现无缝滚动图效果
  20. 【OpenCV】77 视频读写与处理

热门文章

  1. 离散化+unique()+二分查找
  2. 【HDOJ图论题集】【转】
  3. 其实吧,微信就是已经用电脑上线,第二天消息还是从手机上重新接收
  4. JavaScript 中的事件设计
  5. [comparator] 策略模式
  6. MakeFile 文件的作用
  7. [原创].NET 分布式架构开发实战之一 故事起源
  8. UA OPTI512R 傅立叶光学导论22 透镜成像与傅立叶变换
  9. UA MATH563 概率论的数学基础 中心极限定理10 Borel-Cantelli引理
  10. C++11 新增关键字decltype学习