RS485通讯中使用STM32串口以DMA方式发送数据丢失字节的问题

发布者:chwwdch最新更新时间:2018-06-08 来源: eefocus关键字:RS485通讯  STM32串口  DMA方式  发送数据  丢失字节 手机看文章 扫描二维码
随时随地手机看文章

1、开发平台

计算机操作系统:WIN7 64位;

开发环境:Keil MDK 5.14;

MCU:STM32F407ZET6;

STM32F4xx固件库:STM32F4xx_DSP_StdPeriph_Lib_V1.4.0;

串口调试助手;

2、问题描述

    在测试用STM32F4xx芯片的串口USART1以DMA方式进行RS485收发通讯时,出现数据字节丢失的现象,一般丢失1~2个字节。

    出现问题时测试的简单收发机制:使能串口USART1的DMA收发功能,开启了DMA发送完成中断和USART1空闲中断。通过串口调试助手发送N个字节给MCU,当MCU产生USART1空闲中断时,在USART1空闲中断服务程序中将DMA接收到的N个字节数据从接收缓存拷贝到发送缓存,准备好数据后,RS485切换为发送模式,通过启动一次DMA发送,将N个字节数据原样回送到串口调试助手。最后,在DMA发送完成中断服务程序中判断到有DMA发送完成标志TCIF7置位时,立即将RS485再次切换为接收模式。

3、原因分析

        在STM32F4xx英文参考手册(RM0090)中,USART章节的使用DMA发送小节给出了如下时序图:

        由图可见,当DMA将第3个字节Frame 3写到USART数据寄存器USART_DR时,TX线上才刚准备出现第2个字节Frame 2的时序,并且DMA发送完成中断标志在TX线还未出现第2个字节Frame 2时序时就由硬件置1了,所以,如果软件中在DMA发送完成中断服务程序中检测到DMA TCIF标志置1后马上将RS485切换为接收模式,则后面的字节数据将会丢失。

        所以,需要让数据字节不丢失的话,必须让所有字节(包括字节的停止位)在TX线上稳定发送完成后,才能将RS485切换为接收模式。

4、解决方法

        如上图所示,有一个关键点是:当所有字节(包括字节的停止位)在TX线上稳定发送完成后,串口发送完成标志(TC flag)置1。所以,有两个解决方法:

      方法一:用DMA发送完成中断,不用USART1发送完成中断。在DMA发送完成中断服务程序中检测到有TCIF7置1时,再等待USART1发送完成标志TC置1,直到USART1发送完成标志TC置1后,清零USART1发送完成标志TC,然后再将RS485切换为接收模式。

      方法二:用USART1发送完成中断,不用DMA发送完成中断。在USART1中断服务程序USART1_IRQHandler()中,检测到有USART1发送完成标志TC置1时,清零USART1发送完成标志TC,并且要清零DMA发送完成标志DMA_FLAG_TCIF7,最好同时清零DMA_FLAG_FEIF7、DMA_FLAG_DMEIF7、DMA_FLAG_TEIF7 、DMA_FLAG_HTIF7,然后再将RS485切换为接收模式。

5、参考源代码

Usart.h头文件


  1. /*---------------------------------------------------------------------------------------------------- 

  2. *Copyright: SXD Tech. Co., Ltd.  

  3. *开发 环境: Keil MDK 5.14 && STM32F407ZET6 

  4. *文件 名称: USART串行通信驱动头文件                       

  5. *作     者: 顺信德 

  6. *版     本: V1.0 

  7. *日     期: 2018-2-6 

  8. *说     明:           

  9. *修改 日志: (1)  

  10. ----------------------------------------------------------------------------------------------------*/  

  11. #ifndef _USART_H_  

  12. #define _USART_H_  

  13.   

  14. #include "Global.h"   

  15.   

  16. /*---------------------------------------------宏定义(S)---------------------------------------------*/  

  17. #define RS485_Recv();   {PFout(11)=0;}  //SP485接收模式,低电平有效  

  18. #define RS485_Send();   {PFout(11)=1;}  //SP485发送模式,高电平有效  

  19.   

  20. #define USART1_SEND_MAXLEN  512 /*串口1最大发送字节长度*/  

  21. #define USART1_RECV_MAXLEN  512 /*串口1最大接收字节长度*/  

  22. /*---------------------------------------------宏定义(E)---------------------------------------------*/  

  23.   

  24.   

  25. /*--------------------------------------------端口定义(S)--------------------------------------------*/  

  26.   

  27. /*--------------------------------------------端口定义(E)--------------------------------------------*/  

  28.   

  29.   

  30. /*--------------------------------------------变量声明(S)--------------------------------------------*/  

  31. extern u32 G_u32RS485BaudRate;              //RS485通讯波特率  

  32. extern u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN];    //发送数据缓冲区  

  33. extern u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN];    //接收数据缓冲区     

  34. extern u16 G_u16CommRecvLen;                        //通讯接收的一帧数据长度  

  35. /*--------------------------------------------变量声明(E)--------------------------------------------*/  

  36.   

  37.   

  38. /*--------------------------------------------函数声明(S)--------------------------------------------*/  

  39. extern void USART1_Init(u32 baud);                                                  //USART1串口初始化  

  40. extern void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //串口USART1启动一次DMA传输  

  41. extern void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt);         //串口USART1以DMA方式发送多字节  

  42. /*--------------------------------------------函数声明(E)--------------------------------------------*/  

  43.   

  44. #endif  

Usart.c源文件

方法一:用DMA发送完成中断


  1. /*---------------------------------------------------------------------------------------------------- 

  2. *Copyright: SXD Tech. Co., Ltd.  

  3. *开发 环境: Keil MDK 5.14 && STM32F407ZET6 

  4. *文件 名称: USART串行通信驱动源文件                       

  5. *作     者: 顺信德 

  6. *版     本: V1.0 

  7. *日     期:   2018-2-6 

  8. *说     明:           

  9. *修改 日志: (1)  

  10. ----------------------------------------------------------------------------------------------------*/  

  11. #include "Usart.h"     

  12.   

  13. /*--------------------------------------------变量定义(S)--------------------------------------------*/  

  14. u32 G_u32RS485BaudRate = 9600;                  //RS485通讯波特率  

  15. u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]={0,};  //发送数据缓冲区  

  16. u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]={0,};  //接收数据缓冲区  

  17. u16 G_u16CommRecvLen=0;         //通讯接收的一帧数据长度  

  18. /*--------------------------------------------变量定义(E)--------------------------------------------*/  

  19.   

  20.   

  21. /*--------------------------------------------函数声明(S)--------------------------------------------*/  

  22. void USART1_Init(u32 baud);                                                     //USART1串口初始化  

  23. void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt);    //串口USART1启动一次DMA传输  

  24. void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt);                    //串口USART1以DMA方式发送多字节  

  25. /*--------------------------------------------函数声明(E)--------------------------------------------*/  

  26.   

  27. /*----------------------------------------------------------------------------------------------------  

  28. *函数名称:void USART1_Init(u32 baud) 

  29. *函数功能:USART1串口初始化函数   

  30. *入口参数:u32 baud - 波特率(单位bps) 

  31. *出口参数:无 

  32. *说    明:用于RS485通信; 

  33. ----------------------------------------------------------------------------------------------------*/  

  34. void USART1_Init(u32 baud)  

  35. {  

  36.     GPIO_InitTypeDef GPIO_InitStructure;  

  37.     USART_InitTypeDef USART_InitStructure;  

  38.     NVIC_InitTypeDef NVIC_InitStructure;  

  39.     DMA_InitTypeDef  DMA_InitStructure;  

  40.     u16 mid_u16RetryCnt = 0;  

  41.       

  42.     USART_DeInit(USART1);  

  43.       

  44.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);       //使能GPIOA时钟  

  45.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);      //使能USART1时钟  

  46.    

  47.     //USART1对应引脚复用映射  

  48.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);   //GPIOA9复用为USART1_TX  

  49.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);  //GPIOA10复用为USART1_RX  

  50.       

  51.     //USART1端口配置  

  52.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;     //GPIOA9与GPIOA10  

  53.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                //复用功能  

  54.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;           //速度25MHz  

  55.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;              //推挽输出  

  56.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                //上拉  

  57.     GPIO_Init(GPIOA, &GPIO_InitStructure);                      //初始化PA9,PA10  

  58.   

  59.     //USART1初始化设置  

  60.     USART_InitStructure.USART_BaudRate = baud;                  //波特率设置  

  61.     USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式  

  62.     USART_InitStructure.USART_StopBits = USART_StopBits_1;      //一个停止位  

  63.     USART_InitStructure.USART_Parity = USART_Parity_No;         //无奇偶校验位  

  64.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制  

  65.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 //收发模式  

  66.     USART_Init(USART1, &USART_InitStructure);                   //初始化USART1  

  67.       

  68.     USART_Cmd(USART1, ENABLE);                                  //使能USART1   

  69.       

  70.     USART_ClearFlag(USART1, USART_FLAG_TC); //清除发送完成标志    

  71.     while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //等待空闲帧发送完成后再清零发送完成标志  

  72.     USART_ClearFlag(USART1, USART_FLAG_TC); //清除发送完成标志  

  73.        

  74.     USART_ITConfig(USART1, USART_IT_TC, DISABLE);               //禁止USART1传输完成中断  

  75.     USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);             //禁止USART1接收不为空中断  

  76.     USART_ITConfig(USART1, USART_IT_TXE, DISABLE);              //禁止USART1发送空中断  

  77.     USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);              //开启USART1空闲中断   

  78.            

  79.     //USART1 NVIC配置    

  80.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;           //串口1中断通道    

  81.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;     //抢占优先级3    

  82.     NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;           //子优先级3    

  83.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //IRQ通道使能    

  84.     NVIC_Init(&NVIC_InitStructure);   

  85.     

  86.     USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);              //使能串口1的DMA发送       

  87.     USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);              //使能串口1的DMA接收    

  88.       

  89.     // - USART1发送DMA配置  

  90.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);         //DMA2时钟使能  

  91.       

  92.     DMA_DeInit(DMA2_Stream7);    

  93.     while ((DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) && (mid_u16RetryCnt++ < 500));    //等待DMA可配置     

  94.     //配置DMA2_Stream7   

  95.     DMA_InitStructure.DMA_Channel = DMA_Channel_4;                          //通道选择    

  96.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;         //DMA外设地址    

  97.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1SendBuf;         //DMA 存储器0地址    

  98.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                 //存储器到外设模式    

  99.     DMA_InitStructure.DMA_BufferSize = USART1_SEND_MAXLEN;                  //数据传输量     

  100.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        //外设非增量模式    

  101.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //存储器增量模式    

  102.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位    

  103.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         //存储器数据长度:8位    

  104.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           //使用普通模式     

  105.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //中等优先级    

  106.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  107.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  108.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //存储器突发单次传输    

  109.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     //外设突发单次传输    

  110.     DMA_Init(DMA2_Stream7, &DMA_InitStructure);                             //初始化DMA Stream    

  111.   

  112.     //DMA2_Stream7的NVIC配置      

  113.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;      

  114.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;      

  115.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;      

  116.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      

  117.     NVIC_Init(&NVIC_InitStructure);  

  118.       

  119.     DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);  //清除DMA发送完成中断标志  

  120.     DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);      //使能DMA发送完成中断  

  121.   

  122.     DMA_Cmd(DMA2_Stream7, ENABLE);  //使能DMA2_Stream7  

  123.     

  124.     // - USART1接收DMA配置   

  125.     mid_u16RetryCnt = 0;  

  126.     DMA_DeInit(DMA2_Stream5);         

  127.     while ((DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) && (mid_u16RetryCnt++ < 500));    //等待DMA可配置     

  128.     //配置DMA2_Stream5    

  129.     DMA_InitStructure.DMA_Channel = DMA_Channel_4;                          //通道选择    

  130.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;         //DMA外设地址    

  131.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1RecvBuf;         //DMA 存储器0地址    

  132.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                 //外设到存储器模式    

  133.     DMA_InitStructure.DMA_BufferSize = USART1_RECV_MAXLEN;                  //数据传输量     

  134.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        //外设非增量模式    

  135.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //存储器增量模式    

  136.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位    

  137.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         //存储器数据长度:8位    

  138.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           //使用普通模式     

  139.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //中等优先级    

  140.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  141.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  142.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //存储器突发单次传输    

  143.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     //外设突发单次传输    

  144.     DMA_Init(DMA2_Stream5, &DMA_InitStructure);                             //初始化DMA Stream    

  145.       

  146.     DMA_Cmd(DMA2_Stream5, ENABLE);  //使能DMA2_Stream5        

  147. }  

  148.   

  149. /*--------------------------------------------------------------------------------------  

  150. 函数名称:void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt) 

  151. 函数功能:串口USART1启动一次DMA传输函数   

  152. 入口参数:DMA_Stream_TypeDef DMA_Streamx - DMA数据流(DMA1_Stream0~7/DMA2_Stream0~7); 

  153.          u16 m_u16SendCnt - 待传输数据字节数 

  154. 出口参数:无 

  155. 说    明:无 

  156. ---------------------------------------------------------------------------------------*/  

  157. void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt)    

  158. {      

  159.     u16 l_u16RetryCnt = 0;  

  160.       

  161.     DMA_Cmd(DMA_Streamx, DISABLE);                      //关闭DMA传输             

  162.     while ((DMA_GetCmdStatus(DMA_Streamx) != DISABLE) && (l_u16RetryCnt++ < 500));   //等待DMA可配置    

  163.     DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt);  //数据传输量          

  164.     DMA_Cmd(DMA_Streamx, ENABLE);                       //开启DMA传输     

  165. }          

  166.   

  167. /*--------------------------------------------------------------------------------------  

  168. 函数名称:void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)  

  169. 函数功能:串口USART1以DMA方式发送多字节函数   

  170. 入口参数:u8 *m_pSendBuf - 待发送数据缓存, u16 m_u16SendCnt - 待发送数据个数 

  171. 出口参数:无 

  172. 说    明:无 

  173. ---------------------------------------------------------------------------------------*/    

  174. void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)    

  175. {      

  176.     memcpy(G_u8Usart1SendBuf, m_pSendBuf, m_u16SendCnt);        

  177.     USART_DMA_SendStart(DMA2_Stream7, m_u16SendCnt); //启动一次DMA传输        

  178. }    

  179.     

  180. /*--------------------------------------------------------------------------------------  

  181. 函数名称:void DMA2_Stream7_IRQHandler(void)  

  182. 函数功能:串口USART1以DMA方式发送完成中断服务程序   

  183. 入口参数:无 

  184. 出口参数:无 

  185. 说    明:无 

  186. ---------------------------------------------------------------------------------------*/  

  187. void DMA2_Stream7_IRQHandler(void)    

  188. {      

  189.     if(DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) != RESET)    //DMA发送完成?    

  190.     {     

  191.         DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7 | DMA_FLAG_FEIF7 |   

  192.                       DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7);   //清除标志位           

  193.           

  194.         while(!USART_GetFlagStatus(USART1, USART_FLAG_TC)); //等待USART1发送完成标志TC置1  

  195.         USART_ClearFlag(USART1, USART_FLAG_TC);     //清除发送完成标志  

  196.           

  197.         RS485_Recv();       //切换为RS485接收模式        

  198.     }    

  199. }  

  200.   

  201. /*--------------------------------------------------------------------------------------  

  202. 函数名称:void USART1_IRQHandler(void) 

  203. 函数功能:USART串口1中断服务程序   

  204. 入口参数:无 

  205. 出口参数:无 

  206. 说    明:无 

  207. ---------------------------------------------------------------------------------------*/  

  208. void USART1_IRQHandler(void)    

  209. {    

  210.     u16 l_u16Temp = 0;  

  211.       

  212.     if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)   //若有空闲中断    

  213.     {    

  214.         DMA_Cmd(DMA2_Stream5, DISABLE); //关闭DMA2_Stream5,防止处理期间有数据   

  215.           

  216.         DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 |   

  217.                       DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);   //清除标志位       

  218.     

  219.         //清除USART总线空闲中断标志(只要读USART1->SR和USART1->DR即可)  

  220.         l_u16Temp = USART1->SR;        

  221.         l_u16Temp = USART1->DR;  

  222.             

  223.         G_u16CommRecvLen = USART1_RECV_MAXLEN - DMA_GetCurrDataCounter(DMA2_Stream5);   //求出接收到数据的字节数   

  224.         if(G_u16CommRecvLen <= USART1_RECV_MAXLEN)  

  225.         {  

  226.             RS485_Send();       //RS485发送模式  

  227.             USART1_DMA_SendNByte(G_u8Usart1RecvBuf, G_u16CommRecvLen);  //回送接收到的数据  

  228.         }  

  229.             

  230.         DMA_SetCurrDataCounter(DMA2_Stream5, USART1_RECV_MAXLEN);  //设置传输数据长度  

  231.         DMA_Cmd(DMA2_Stream5, ENABLE);     //使能DMA2_Stream5    

  232.     }     

  233. }  

方法二:用USART1发送完成中断


  1. /*---------------------------------------------------------------------------------------------------- 

  2. *Copyright: SXD Tech. Co., Ltd.  

  3. *开发 环境: Keil MDK 5.14 && STM32F407ZET6 

  4. *文件 名称: USART串行通信驱动源文件                       

  5. *作     者: 顺信德 

  6. *版     本: V1.0 

  7. *日     期:   2018-2-6 

  8. *说     明:           

  9. *修改 日志: (1)  

  10. ----------------------------------------------------------------------------------------------------*/  

  11. #include "Usart.h"     

  12.   

  13. /*--------------------------------------------变量定义(S)--------------------------------------------*/  

  14. u32 G_u32RS485BaudRate = 9600;                  //RS485通讯波特率  

  15. u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]={0,};  //发送数据缓冲区  

  16. u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]={0,};  //接收数据缓冲区  

  17. u16 G_u16CommRecvLen=0;                         //通讯接收的一帧数据长度  

  18. /*--------------------------------------------变量定义(E)--------------------------------------------*/  

  19.   

  20.   

  21. /*--------------------------------------------函数声明(S)--------------------------------------------*/  

  22. void USART1_Init(u32 baud);                                                     //USART1串口初始化  

  23. void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt);    //串口USART1启动一次DMA传输  

  24. void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt);                    //串口USART1以DMA方式发送多字节  

  25. /*--------------------------------------------函数声明(E)--------------------------------------------*/  

  26.   

  27. /*----------------------------------------------------------------------------------------------------  

  28. *函数名称:void USART1_Init(u32 baud) 

  29. *函数功能:USART1串口初始化函数   

  30. *入口参数:u32 baud - 波特率(单位bps) 

  31. *出口参数:无 

  32. *说    明:用于RS485通信; 

  33. ----------------------------------------------------------------------------------------------------*/  

  34. void USART1_Init(u32 baud)  

  35. {  

  36.     GPIO_InitTypeDef GPIO_InitStructure;  

  37.     USART_InitTypeDef USART_InitStructure;  

  38.     NVIC_InitTypeDef NVIC_InitStructure;  

  39.     DMA_InitTypeDef  DMA_InitStructure;  

  40.     u16 mid_u16RetryCnt = 0;  

  41.       

  42.     USART_DeInit(USART1);  

  43.       

  44.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);       //使能GPIOA时钟  

  45.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);      //使能USART1时钟  

  46.    

  47.     //USART1对应引脚复用映射  

  48.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);   //GPIOA9复用为USART1_TX  

  49.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);  //GPIOA10复用为USART1_RX  

  50.       

  51.     //USART1端口配置  

  52.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;     //GPIOA9与GPIOA10  

  53.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                //复用功能  

  54.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;           //速度25MHz  

  55.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;              //推挽输出  

  56.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                //上拉  

  57.     GPIO_Init(GPIOA, &GPIO_InitStructure);                      //初始化PA9,PA10  

  58.   

  59.     //USART1初始化设置  

  60.     USART_InitStructure.USART_BaudRate = baud;                  //波特率设置  

  61.     USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式  

  62.     USART_InitStructure.USART_StopBits = USART_StopBits_1;      //一个停止位  

  63.     USART_InitStructure.USART_Parity = USART_Parity_No;         //无奇偶校验位  

  64.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制  

  65.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 //收发模式  

  66.     USART_Init(USART1, &USART_InitStructure);                   //初始化USART1  

  67.       

  68.     USART_Cmd(USART1, ENABLE);                                  //使能USART1   

  69.       

  70.     USART_ClearFlag(USART1, USART_FLAG_TC); //清除发送完成标志    

  71.     while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //等待空闲帧发送完成后再清零发送完成标志  

  72.     USART_ClearFlag(USART1, USART_FLAG_TC); //清除发送完成标志  

  73.       

  74.     USART_ITConfig(USART1, USART_IT_TC, ENABLE);                //使能USART1传输完成中断   

  75.     USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);             //禁止USART1接收不为空中断  

  76.     USART_ITConfig(USART1, USART_IT_TXE, DISABLE);              //禁止USART1发送空中断  

  77.     USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);              //开启USART1空闲中断   

  78.            

  79.     //USART1 NVIC配置    

  80.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;           //串口1中断通道    

  81.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;     //抢占优先级3    

  82.     NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;           //子优先级3    

  83.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //IRQ通道使能    

  84.     NVIC_Init(&NVIC_InitStructure);   

  85.     

  86.     USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);              //使能串口1的DMA发送       

  87.     USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);              //使能串口1的DMA接收    

  88.       

  89.     // - USART1发送DMA配置  

  90.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);         //DMA2时钟使能  

  91.       

  92.     DMA_DeInit(DMA2_Stream7);    

  93.     while ((DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) && (mid_u16RetryCnt++ < 500));            //等待DMA可配置     

  94.     //配置DMA2_Stream7   

  95.     DMA_InitStructure.DMA_Channel = DMA_Channel_4;                          //通道选择    

  96.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;         //DMA外设地址    

  97.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1SendBuf;         //DMA 存储器0地址    

  98.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                 //存储器到外设模式    

  99.     DMA_InitStructure.DMA_BufferSize = USART1_SEND_MAXLEN;                  //数据传输量     

  100.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        //外设非增量模式    

  101.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //存储器增量模式    

  102.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位    

  103.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         //存储器数据长度:8位    

  104.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           //使用普通模式     

  105.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //中等优先级    

  106.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  107.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  108.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //存储器突发单次传输    

  109.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     //外设突发单次传输    

  110.     DMA_Init(DMA2_Stream7, &DMA_InitStructure);                             //初始化DMA Stream    

  111.   

  112.     DMA_Cmd(DMA2_Stream7, ENABLE);  //使能DMA2_Stream7  

  113.     

  114.     // - USART1接收DMA配置   

  115.     mid_u16RetryCnt = 0;  

  116.     DMA_DeInit(DMA2_Stream5);         

  117.     while ((DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) && (mid_u16RetryCnt++ < 500));    //等待DMA可配置     

  118.     //配置DMA2_Stream5    

  119.     DMA_InitStructure.DMA_Channel = DMA_Channel_4;                          //通道选择    

  120.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;         //DMA外设地址    

  121.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1RecvBuf;         //DMA 存储器0地址    

  122.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                 //外设到存储器模式    

  123.     DMA_InitStructure.DMA_BufferSize = USART1_RECV_MAXLEN;                  //数据传输量     

  124.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        //外设非增量模式    

  125.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //存储器增量模式    

  126.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位    

  127.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         //存储器数据长度:8位    

  128.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           //使用普通模式     

  129.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //中等优先级    

  130.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  131.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  132.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //存储器突发单次传输    

  133.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     //外设突发单次传输    

  134.     DMA_Init(DMA2_Stream5, &DMA_InitStructure);                             //初始化DMA Stream    

  135.       

  136.     DMA_Cmd(DMA2_Stream5, ENABLE);  //使能DMA2_Stream5        

  137. }  

  138.   

  139. /*--------------------------------------------------------------------------------------  

  140. 函数名称:void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt) 

  141. 函数功能:串口USART1启动一次DMA传输函数   

  142. 入口参数:DMA_Stream_TypeDef DMA_Streamx - DMA数据流(DMA1_Stream0~7/DMA2_Stream0~7); 

  143.          u16 m_u16SendCnt - 待传输数据字节数 

  144. 出口参数:无 

  145. 说    明:无 

  146. ---------------------------------------------------------------------------------------*/  

  147. void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt)    

  148. {      

  149.     u16 l_u16RetryCnt = 0;  

  150.       

  151.     DMA_Cmd(DMA_Streamx, DISABLE);                      //关闭DMA传输             

  152.     while ((DMA_GetCmdStatus(DMA_Streamx) != DISABLE) && (l_u16RetryCnt++ < 500));   //等待DMA可配置    

  153.     DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt);  //数据传输量          

  154.     DMA_Cmd(DMA_Streamx, ENABLE);                       //开启DMA传输     

  155. }          

  156.   

  157. /*--------------------------------------------------------------------------------------  

  158. 函数名称:void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)  

  159. 函数功能:串口USART1以DMA方式发送多字节函数   

  160. 入口参数:u8 *m_pSendBuf - 待发送数据缓存, u16 m_u16SendCnt - 待发送数据个数 

  161. 出口参数:无 

  162. 说    明:无 

  163. ---------------------------------------------------------------------------------------*/    

  164. void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)    

  165. {      

  166.     memcpy(G_u8Usart1SendBuf, m_pSendBuf, m_u16SendCnt);        

  167.     USART_DMA_SendStart(DMA2_Stream7, m_u16SendCnt); //启动一次DMA传输        

  168. }  

  169.   

  170. /*--------------------------------------------------------------------------------------  

  171. 函数名称:void USART1_IRQHandler(void) 

  172. 函数功能:USART串口1中断服务程序   

  173. 入口参数:无 

  174. 出口参数:无 

  175. 说    明:无 

  176. ---------------------------------------------------------------------------------------*/  

  177. void USART1_IRQHandler(void)    

  178. {    

  179.     u16 l_u16Temp = 0;  

  180.       

  181.     if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)   //若有空闲中断    

  182.     {    

  183.         DMA_Cmd(DMA2_Stream5, DISABLE); //关闭DMA2_Stream5,防止处理期间有数据       

  184.         DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 |   

  185.                       DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清零标志位          

  186.     

  187.         //清除USART总线空闲中断标志(只要读USART1->SR和USART1->DR即可)  

  188.         l_u16Temp = USART1->SR;        

  189.         l_u16Temp = USART1->DR;  

  190.             

  191.         G_u16CommRecvLen = USART1_RECV_MAXLEN - DMA_GetCurrDataCounter(DMA2_Stream5);   //求出接收到数据的字节数   

  192.         if(G_u16CommRecvLen <= USART1_RECV_MAXLEN)  

  193.         {  

  194.             RS485_Send();       //RS485发送模式  

  195.             USART1_DMA_SendNByte(G_u8Usart1RecvBuf, G_u16CommRecvLen);  

  196.         }  

  197.             

  198.         DMA_SetCurrDataCounter(DMA2_Stream5, USART1_RECV_MAXLEN);  //设置传输数据长度  

  199.         DMA_Cmd(DMA2_Stream5, ENABLE);     //使能DMA2_Stream5    

  200.     }   

  201.   

  202.     if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) //若有发送完成中断    

  203.     {    

  204.         USART_ClearITPendingBit(USART1, USART_IT_TC);   //清除USART1发送完成中断标志  

  205.         DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7 | DMA_FLAG_FEIF7 |   

  206.                       DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7);//清零标志位  

  207.               

  208.         RS485_Recv();       //切换为RS485接收模式  

  209.     }     

  210. }  

6、声明

    本程序的收发机制只是简单的处理机制,只是为了说明解决数据丢失字节问题的方法,对进行快速大数据通讯时会出现乱码。所以,用于实际项目中,需对此程序的收发处理机制进行重新设计。两种方法中,个人认为方法二更好,因为方法一在中断里面等待白白耗费了时间。


关键字:RS485通讯  STM32串口  DMA方式  发送数据  丢失字节 引用地址:RS485通讯中使用STM32串口以DMA方式发送数据丢失字节的问题

上一篇:STM32串口接收使用DMA双缓冲
下一篇:STM32串口通信USART(二)---DMA方式

推荐阅读最新更新时间:2024-03-16 16:05

STM32学习总结之------串口通信USART
学习内容: 1、利用串口可以帮助我们调试程序,本节介绍的为串口最基本、最常用的方法,全双工、异步通讯方式。 2、要配置串口通讯,至少要设置以下几个参数:字长(一次传送的数据长度)、波特率(每秒传输的数据位数)、奇偶校验位、还有停止位。 在初始化串口的时候,必然有一个串口初始化结构体,这个结构体的几个成员就是有来存储这些控制参数的。 3、串口线主要分两种,直通线(平行线)和交叉线。 假如PC与板子之间要实现全双工串口通讯,必然是PC的Tx针脚要连接到板子的Rx针脚,而PC的Rx针脚则要连接至板子的Tx针脚了。由于板子和pc的串口接法是相同的,就要使用交叉线来连接了。 直通线接法:开发板Tx连接至DB9的第2针脚,而Rx连
[单片机]
<font color='red'>STM32</font>学习总结之------<font color='red'>串口</font>通信USART
STM32 HAL CubeMX 串口IDLE接收空闲中断+DMA
历程详解 详解包括: 中断原理讲解 例程流程详解 库函数分析详解 对应寄存器介绍 对应函数介绍 对应注释详解 本篇文章提供两种方法: 一种是 :IDLE 接收空闲中断+DMA 一种是: IDLE 接收空闲中断+RXNE接收数据中断 都可完成串口数据的收发 知识点介绍: STM32 IDLE 接收空闲中断 功能: 在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。 接受完一帧数据,触发中断 STM32的IDLE的中断产生条件: 在串口无数据接收的情况下,不会产生,
[单片机]
<font color='red'>STM32</font> HAL CubeMX <font color='red'>串口</font>IDLE接收空闲中断+<font color='red'>DMA</font>
STM32串口知识及其配置
1.串口知识 串口的应用就是芯片给电脑发数据,电脑下载程序到芯片上,芯片把芯片存储器中的数据发回电脑,比如当你测量角度或者速度时,芯片测量好后,你要看到,必须让芯片把数据发回电脑的串口,大致就是这样。 对于STM32中容量的芯片有3个串口,我们一般使用串口1,说到串口,那么芯片怎么通过串口发送数据到电脑上的,当然是通过引脚的端口,如果芯片串口的输出引脚和外部的RX和TX已经集成,那么厂家已经把STM32的端口连接到RX和TX上了,把串口看成外部设备,简称外设,厂家已经把串口输出端口内部连接到RX和TX上,所以我们不用手动连接,如果没有,你还需要用杜邦线连接。 如果你还想知道内部的硬件情况的话,下图就是: 我们一般使用USART
[单片机]
STM32 | 串口打印知多少?
常规打印方法 在STM32的应用中,我们常常对printf进行重定向的方式来把打印信息printf到我们的串口助手。 在MDK环境中,我们常常使用MicroLIB+fputc的方式实现串口打印功能,即: 要实现fputc函数的原因是:printf函数依赖于fputc函数,重新实现fputc内部从串口发送数据即可间接地实现printf打印输出数据到串口。 不知道大家有没有看过正点原子裸机串口相关的例程,他们的串口例程里不使用MicroLIB,而是使用标准库+fputc的方式。相关代码如: #if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct _
[单片机]
<font color='red'>STM32</font> | <font color='red'>串口</font>打印知多少?
STM32学习笔记之-串口中断接收不定数据buff
今天说一下STM32单片机的接收不定长度字节数据的方法。由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的ARM单片机。 IDLE中断什么时候发生? IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。 如何判断一帧数据结束,就是我们今天讨论的问题。因为很多项目中都要用到这个,因为只有接收到一帧数据以后,你才可以判断这次收了几个字节和每个字节的内容是否符合协议要求。 看了前面IDLE中断的定义,你就会明白了,一帧数据结束后
[单片机]
<font color='red'>STM32</font>学习笔记之-<font color='red'>串口</font>中断接收不定<font color='red'>数据</font>buff
STM32 IO模拟实现软件串口
最近项目中STM32的串口资源紧张,于是使用IO口进行模拟串口,现进行整理记录。 实现思路 IO口模拟串口的思路也比较简单,一切按照串口协议进行操作即可。 对于发送,计算好不同波特率对应的延时时间进行数据发送。 对于接收,稍微复杂。通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。 测试Demo说明 TXD : PC13 RXD : PB14 波特率:9600 ,1-8-N Demo功能 接收11个数据,然后把接收到的数据发送出去。 程序实现 #define OI_TXD PCout(13) #define
[单片机]
STM32 基于串口RS485双机通信原理浅析
RS485通信想必大家都知道,在学习RS232时,都会拿485(RS485下文就用485代替)和其作对比。485优缺点不说,网上有 我用的是STM32库函数学的485通信,所以接下来就讲讲STM32串口实现485双机通信的原理: 485和232都是基于串口的通讯接口,在数据的收发操作上都是一致的。但是他两的通讯模式却大不相同~!232是全双工(例:A- B的同时B- A,瞬时同步)工作模式,而485是半双工(发时不能收,收时不能发)工作模式。在232通信中,主机在发送数据的同时可以收到从机发过来的数据;但在485通信中,收发要经过模式位的切换来进行,譬如,发送数据时,会把模式为置‘1’,表示为发送模式,此时不能接收;当接收数据时
[单片机]
<font color='red'>STM32</font> 基于<font color='red'>串口</font><font color='red'>RS485</font>双机通信原理浅析
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • ARM裸机篇--按键中断
    先看看GPOI的输入实验:按键电路图:GPF1管教的功能:EINT1要使用GPF1作为EINT1的功能时,只要将GPFCON的3:2位配置成10就可以了!GPF1先配 ...
  • 网上下的--ARM入门笔记
    简单的介绍打今天起菜鸟的ARM笔记算是开张了,也算给我的这些笔记找个存的地方。为什么要发布出来?也许是大家感兴趣的,其实这些笔记之所 ...
  • 学习ARM开发(23)
    三个任务准备与运行结果下来看看创建任务和任运的栈空间怎么样的,以及运行输出。Made in china by UCSDN(caijunsheng)Lichee 1 0 0 ...
  • 学习ARM开发(22)
    关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个 ...
  • 学习ARM开发(21)
    先要声明任务指针,因为后面需要使用。 任务指针 volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • 学习ARM开发(20)
  • 学习ARM开发(19)
  • 学习ARM开发(14)
  • 学习ARM开发(15)
何立民专栏 单片机及嵌入式宝典

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

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