单片机
返回首页

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

2018-06-08 来源:eefocus

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、声明

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


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 简洁的过零调功器电路设计与分析

  • 永不缺相启动运行的电动机控制电路

  • IGBT模块通过控制门极阻断过电流

  • CCFL的工作原理及电子驱动电路解析

  • 基于M66T旋律发​​生器的电路图解析

  • 基于TDA1554的立体声放大器电路

    相关电子头条文章