Problem of missing bytes when using STM32 serial port to send data in DMA mode in RS485 communication

Publisher:chwwdchLatest update time:2018-06-08 Source: eefocus Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

1. Development Platform

Computer operating system: WIN7 64-bit;

Development environment: Keil MDK 5.14;

MCU: STM32F407ZET6;

STM32F4xx firmware library: STM32F4xx_DSP_StdPeriph_Lib_V1.4.0;

Serial debugging assistant;

2. Problem description

    When testing the serial port USART1 of the STM32F4xx chip for RS485 transceiver communication in DMA mode, data bytes are lost, usually 1 to 2 bytes.

    A simple transceiver mechanism tested when problems occur: Enable the DMA transceiver function of the serial port USART1, and turn on the DMA send completion interrupt and USART1 idle interrupt. Send N bytes to the MCU through the serial port debugging assistant. When the MCU generates a USART1 idle interrupt, copy the N bytes of data received by DMA from the receive buffer to the send buffer in the USART1 idle interrupt service program. After the data is prepared, RS485 switches to the send mode, and by starting a DMA send, the N bytes of data are sent back to the serial port debugging assistant as is. Finally, when it is determined in the DMA send completion interrupt service program that the DMA send completion flag TCIF7 is set, RS485 is immediately switched to the receive mode again.

3. Cause Analysis

        In the STM32F4xx English reference manual (RM0090), the USART chapter's Use DMA to send section gives the following timing diagram:

        As can be seen from the figure, when DMA writes the third byte Frame 3 to the USART data register USART_DR, the timing of the second byte Frame 2 is just about to appear on the TX line, and the DMA send completion interrupt flag is set to 1 by hardware before the timing of the second byte Frame 2 appears on the TX line. Therefore, if the software detects that the DMA TCIF flag is set to 1 in the DMA send completion interrupt service routine and immediately switches RS485 to receive mode, the subsequent byte data will be lost.

        Therefore, if you want to prevent data bytes from being lost, you must ensure that all bytes (including the stop bit of the byte) are sent stably on the TX line before switching the RS485 to receive mode.

4. Solution

        As shown in the figure above, there is a key point: when all bytes (including the stop bit of the byte) are stably sent on the TX line, the serial port transmission completion flag (TC flag) is set to 1. Therefore, there are two solutions:

      Method 1: Use DMA send completion interrupt instead of USART1 send completion interrupt. When TCIF7 is set to 1 in the DMA send completion interrupt service routine, wait for USART1 send completion flag TC to be set to 1. After USART1 send completion flag TC is set to 1, clear USART1 send completion flag TC, and then switch RS485 to receive mode.

      Method 2: Use USART1 send completion interrupt instead of DMA send completion interrupt. In USART1 interrupt service routine USART1_IRQHandler(), when it is detected that USART1 send completion flag TC is set to 1, clear USART1 send completion flag TC, and clear DMA send completion flag DMA_FLAG_TCIF7. It is best to clear DMA_FLAG_FEIF7, DMA_FLAG_DMEIF7, DMA_FLAG_TEIF7, and DMA_FLAG_HTIF7 at the same time, and then switch RS485 to receive mode.

5. Reference source code

Usart.h header file


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

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

  3. * Development environment: Keil MDK 5.14 && STM32F407ZET6 

  4. *File name: USART serial communication driver header file                       

  5. *Author: Shun Xinde 

  6. *Version: V1.0 

  7. *Date: 2018-2-6 

  8. *illustrate:           

  9. *Modification log: (1)  

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

  11. #ifndef _USART_H_  

  12. #define _USART_H_  

  13.   

  14. #include "Global.h"   

  15.   

  16. /*---------------------------------------------Macro definition (S)---------------------------------------------*/  

  17. #define RS485_Recv(); {PFout(11)=0;} //SP485 receiving mode, low level is valid  

  18. #define RS485_Send(); {PFout(11)=1;} //SP485 sending mode, high level is valid  

  19.   

  20. #define USART1_SEND_MAXLEN 512 /*Maximum send byte length of serial port 1*/  

  21. #define USART1_RECV_MAXLEN 512 /*Maximum receive byte length of serial port 1*/  

  22. /*---------------------------------------------Macro definition (E)---------------------------------------------*/  

  23.   

  24.   

  25. /*--------------------------------------------Port Definition(S)--------------------------------------------*/  

  26.   

  27. /*--------------------------------------------Port Definition (E)--------------------------------------------*/  

  28.   

  29.   

  30. /*--------------------------------------------Variable Declaration(S)--------------------------------------------*/  

  31. extern u32 G_u32RS485BaudRate; //RS485 communication baud rate  

  32. extern u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]; //Send data buffer  

  33. extern u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]; //Receive data buffer     

  34. extern u16 G_u16CommRecvLen; //The length of a frame of data received by communication  

  35. /*--------------------------------------------Variable Declaration (E)--------------------------------------------*/  

  36.   

  37.   

  38. /*--------------------------------------------Function Declaration(S)--------------------------------------------*/  

  39. extern void USART1_Init(u32 baud); //USART1 serial port initialization  

  40. extern void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //Serial port USART1 starts a DMA transfer  

  41. extern void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt); //Serial port USART1 sends multiple bytes in DMA mode  

  42. /*--------------------------------------------Function declaration (E)--------------------------------------------*/  

  43.   

  44. #endif  

Usart.c source file

Method 1: Use DMA to send completion interrupt


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

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

  3. * Development environment: Keil MDK 5.14 && STM32F407ZET6 

  4. *File name: USART serial communication driver source file                       

  5. *Author: Shun Xinde 

  6. *Version: V1.0 

  7. *Date: 2018-2-6 

  8. *illustrate:           

  9. *Modification log: (1)  

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

  11. #include "Usart.h"     

  12.   

  13. /*--------------------------------------------Variable definition (S)--------------------------------------------*/  

  14. u32 G_u32RS485BaudRate = 9600; //RS485 communication baud rate  

  15. u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]={0,}; //Send data buffer  

  16. u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]={0,}; //Receive data buffer  

  17. u16 G_u16CommRecvLen=0; //The length of a frame of data received by communication  

  18. /*--------------------------------------------Variable definition (E)--------------------------------------------*/  

  19.   

  20.   

  21. /*--------------------------------------------Function Declaration(S)--------------------------------------------*/  

  22. void USART1_Init(u32 baud); //USART1 serial port initialization  

  23. void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //Serial port USART1 starts a DMA transfer  

  24. void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt); //Serial port USART1 sends multiple bytes in DMA mode  

  25. /*--------------------------------------------Function declaration (E)--------------------------------------------*/  

  26.   

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

  28. *Function name: void USART1_Init(u32 baud) 

  29. *Function: USART1 serial port initialization function   

  30. * Input parameters: u32 baud - baud rate (in bps) 

  31. *Export parameters: None 

  32. *Description: Used for RS485 communication; 

  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); //Enable GPIOA clock  

  45.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //Enable USART1 clock  

  46.    

  47.     //USART1 corresponding pin multiplexing mapping  

  48.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9 is reused as USART1_TX  

  49.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10 is reused as USART1_RX  

  50.       

  51.     //USART1 port configuration  

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

  53.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //Multiplexing function  

  54.     GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_25MHz; //Speed ​​25MHz  

  55.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull output  

  56.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Pull-up  

  57.     GPIO_Init(GPIOA, &GPIO_InitStructure); //Initialize PA9, PA10  

  58.   

  59.     //USART1 initialization settings  

  60.     USART_InitStructure.USART_BaudRate = baud; //Baud rate setting  

  61.     USART_InitStructure.USART_WordLength = USART_WordLength_8b; //Word length is 8-bit data format  

  62.     USART_InitStructure.USART_StopBits = USART_StopBits_1; //One stop bit  

  63.     USART_InitStructure.USART_Parity = USART_Parity_No; //No parity bit  

  64.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //No hardware data flow control  

  65.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //Transmit and receive mode  

  66.     USART_Init(USART1, &USART_InitStructure); //Initialize USART1  

  67.       

  68.     USART_Cmd(USART1, ENABLE); //Enable USART1   

  69.       

  70.     USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag    

  71.     while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //Wait for the idle frame to be sent and then clear the send completion flag  

  72.     USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag  

  73.        

  74.     USART_ITConfig(USART1, USART_IT_TC, DISABLE); //Disable USART1 transfer completion interrupt  

  75.     USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); //Disable USART1 receive not empty interrupt  

  76.     USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //Disable USART1 to send empty interrupt  

  77.     USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //Enable USART1 idle interrupt   

  78.            

  79.     //USART1 NVIC configuration    

  80.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //Serial port 1 interrupt channel    

  81.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //Preemption priority 3    

  82.     NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //Subpriority 3    

  83.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enable    

  84.     NVIC_Init(&NVIC_InitStructure);   

  85.     

  86.     USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //Enable DMA transmission of serial port 1       

  87.     USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //Enable DMA reception of serial port 1    

  88.       

  89.     // - USART1 transmit DMA configuration  

  90.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //DMA2 clock enable  

  91.       

  92.     DMA_DeInit(DMA2_Stream7);    

  93.     while ((DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable     

  94.     //Configure DMA2_Stream7   

  95.     DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection    

  96.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address    

  97.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1SendBuf; //DMA memory 0 address    

  98.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //Memory to peripheral mode    

  99.     DMA_InitStructure.DMA_BufferSize = USART1_SEND_MAXLEN; //Data transfer amount     

  100.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode    

  101.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode    

  102.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits    

  103.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits    

  104.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode     

  105.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority    

  106.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  107.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  108.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer    

  109.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer    

  110.     DMA_Init(DMA2_Stream7, &DMA_InitStructure); //Initialize DMA Stream    

  111.   

  112.     //NVIC configuration of DMA2_Stream7      

  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); //Clear DMA send completion interrupt flag  

  120.     DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE); //Enable DMA send completion interrupt  

  121.   

  122.     DMA_Cmd(DMA2_Stream7, ENABLE); //Enable DMA2_Stream7  

  123.     

  124.     // - USART1 receive DMA configuration   

  125.     mid_u16RetryCnt = 0;  

  126.     DMA_DeInit(DMA2_Stream5);         

  127.     while ((DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable     

  128.     //Configure DMA2_Stream5    

  129.     DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection    

  130.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address    

  131.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1RecvBuf; //DMA memory 0 address    

  132.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //Peripheral to memory mode    

  133.     DMA_InitStructure.DMA_BufferSize = USART1_RECV_MAXLEN; //Data transfer amount     

  134.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode    

  135.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode    

  136.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits    

  137.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits    

  138.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode     

  139.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority    

  140.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  141.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  142.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer    

  143.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer    

  144.     DMA_Init(DMA2_Stream5, &DMA_InitStructure); //Initialize DMA Stream    

  145.       

  146.     DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5        

  147. }  

  148.   

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

  150. Function name: void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt) 

  151. Function: Serial port USART1 starts a DMA transfer function   

  152. Entry parameter: DMA_Stream_TypeDef DMA_Streamx - DMA data stream (DMA1_Stream0~7/DMA2_Stream0~7); 

  153.          u16 m_u16SendCnt - Number of bytes of data to be transmitted 

  154. Export parameters: None 

  155. Description: None 

  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); //Disable DMA transmission             

  162.     while ((DMA_GetCmdStatus(DMA_Streamx) != DISABLE) && (l_u16RetryCnt++ < 500)); //Wait for DMA to be configurable    

  163.     DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt); //Data transfer volume          

  164.     DMA_Cmd(DMA_Streamx, ENABLE); // Enable DMA transfer     

  165. }          

  166.   

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

  168. Function name: void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)  

  169. Function: Serial port USART1 sends multi-byte function in DMA mode   

  170. Entry parameters: u8 *m_pSendBuf - buffer of data to be sent, u16 m_u16SendCnt - number of data to be sent 

  171. Export parameters: None 

  172. Description: None 

  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); //Start a DMA transfer        

  178. }    

  179.     

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

  181. Function name: void DMA2_Stream7_IRQHandler(void)  

  182. Function: Serial port USART1 sends the interrupt service routine in DMA mode   

  183. Entry parameters: None 

  184. Export parameters: None 

  185. Description: None 

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

  187. void DMA2_Stream7_IRQHandler(void)    

  188. {      

  189.     if(DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) != RESET) //DMA sending completed?    

  190.     {     

  191.         DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7 | DMA_FLAG_FEIF7 |   

  192.                       DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7); //Clear flag           

  193.           

  194.         while(!USART_GetFlagStatus(USART1, USART_FLAG_TC)); //Wait for USART1 to send the completion flag TC to 1  

  195.         USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag  

  196.           

  197.         RS485_Recv(); //Switch to RS485 receiving mode        

  198.     }    

  199. }  

  200.   

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

  202. Function name: void USART1_IRQHandler(void) 

  203. Function: USART serial port 1 interrupt service routine   

  204. Entry parameters: None 

  205. Export parameters: None 

  206. Description: None 

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

  208. void USART1_IRQHandler(void)    

  209. {    

  210.     u16 l_u16Temp = 0;  

  211.       

  212.     if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //If there is an idle interrupt    

  213.     {    

  214.         DMA_Cmd(DMA2_Stream5, DISABLE); //Disable DMA2_Stream5 to prevent data from being processed   

  215.           

  216.         DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 |   

  217.                       DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5); //Clear flag       

  218.     

  219.         // Clear the USART bus idle interrupt flag (just read USART1->SR and USART1->DR)  

  220.         l_u16Temp = USART1->SR;        

  221.         l_u16Temp = USART1->DR;  

  222.             

  223.         G_u16CommRecvLen = USART1_RECV_MAXLEN - DMA_GetCurrDataCounter(DMA2_Stream5); //Get the number of bytes of received data   

  224.         if(G_u16CommRecvLen <= USART1_RECV_MAXLEN)  

  225.         {  

  226.             RS485_Send(); //RS485 sending mode  

  227.             USART1_DMA_SendNByte(G_u8Usart1RecvBuf, G_u16CommRecvLen); //Send back the received data  

  228.         }  

  229.             

  230.         DMA_SetCurrDataCounter(DMA2_Stream5, USART1_RECV_MAXLEN); //Set the transmission data length  

  231.         DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5    

  232.     }     

  233. }  

Method 2: Use USART1 to send completion interrupt


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

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

  3. * Development environment: Keil MDK 5.14 && STM32F407ZET6 

  4. *File name: USART serial communication driver source file                       

  5. *Author: Shun Xinde 

  6. *Version: V1.0 

  7. *Date: 2018-2-6 

  8. *illustrate:           

  9. *Modification log: (1)  

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

  11. #include "Usart.h"     

  12.   

  13. /*--------------------------------------------Variable definition (S)--------------------------------------------*/  

  14. u32 G_u32RS485BaudRate = 9600; //RS485 communication baud rate  

  15. u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]={0,}; //Send data buffer  

  16. u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]={0,}; //Receive data buffer  

  17. u16 G_u16CommRecvLen=0; //The length of a frame of data received by communication  

  18. /*--------------------------------------------Variable definition (E)--------------------------------------------*/  

  19.   

  20.   

  21. /*--------------------------------------------Function Declaration(S)--------------------------------------------*/  

  22. void USART1_Init(u32 baud); //USART1 serial port initialization  

  23. void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //Serial port USART1 starts a DMA transfer  

  24. void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt); //Serial port USART1 sends multiple bytes in DMA mode  

  25. /*--------------------------------------------Function declaration (E)--------------------------------------------*/  

  26.   

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

  28. *Function name: void USART1_Init(u32 baud) 

  29. *Function: USART1 serial port initialization function   

  30. * Input parameters: u32 baud - baud rate (in bps) 

  31. *Export parameters: None 

  32. *Description: Used for RS485 communication; 

  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); //Enable GPIOA clock  

  45.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //Enable USART1 clock  

  46.    

  47.     //USART1 corresponding pin multiplexing mapping  

  48.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9 is reused as USART1_TX  

  49.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10 is reused as USART1_RX  

  50.       

  51.     //USART1 port configuration  

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

  53.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //Multiplexing function  

  54.     GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_25MHz; //Speed ​​25MHz  

  55.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull output  

  56.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Pull-up  

  57.     GPIO_Init(GPIOA, &GPIO_InitStructure); //Initialize PA9, PA10  

  58.   

  59.     //USART1 initialization settings  

  60.     USART_InitStructure.USART_BaudRate = baud; //Baud rate setting  

  61.     USART_InitStructure.USART_WordLength = USART_WordLength_8b; //Word length is 8-bit data format  

  62.     USART_InitStructure.USART_StopBits = USART_StopBits_1; //One stop bit  

  63.     USART_InitStructure.USART_Parity = USART_Parity_No; //No parity bit  

  64.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //No hardware data flow control  

  65.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //Transmit and receive mode  

  66.     USART_Init(USART1, &USART_InitStructure); //Initialize USART1  

  67.       

  68.     USART_Cmd(USART1, ENABLE); //Enable USART1   

  69.       

  70.     USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag    

  71.     while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //Wait for the idle frame to be sent and then clear the send completion flag  

  72.     USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag  

  73.       

  74.     USART_ITConfig(USART1, USART_IT_TC, ENABLE); //Enable USART1 transfer completion interrupt   

  75.     USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); //Disable USART1 receive not empty interrupt  

  76.     USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //Disable USART1 to send empty interrupt  

  77.     USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //Enable USART1 idle interrupt   

  78.            

  79.     //USART1 NVIC configuration    

  80.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //Serial port 1 interrupt channel    

  81.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //Preemption priority 3    

  82.     NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //Subpriority 3    

  83.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enable    

  84.     NVIC_Init(&NVIC_InitStructure);   

  85.     

  86.     USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //Enable DMA transmission of serial port 1       

  87.     USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //Enable DMA reception of serial port 1    

  88.       

  89.     // - USART1 transmit DMA configuration  

  90.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //DMA2 clock enable  

  91.       

  92.     DMA_DeInit(DMA2_Stream7);    

  93.     while ((DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable     

  94.     //Configure DMA2_Stream7   

  95.     DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection    

  96.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address    

  97.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1SendBuf; //DMA memory 0 address    

  98.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //Memory to peripheral mode    

  99.     DMA_InitStructure.DMA_BufferSize = USART1_SEND_MAXLEN; //Data transfer amount     

  100.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode    

  101.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode    

  102.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits    

  103.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits    

  104.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode     

  105.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority    

  106.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  107.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  108.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer    

  109.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer    

  110.     DMA_Init(DMA2_Stream7, &DMA_InitStructure); //Initialize DMA Stream    

  111.   

  112.     DMA_Cmd(DMA2_Stream7, ENABLE); //Enable DMA2_Stream7  

  113.     

  114.     // - USART1 receive DMA configuration   

  115.     mid_u16RetryCnt = 0;  

  116.     DMA_DeInit(DMA2_Stream5);         

  117.     while ((DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable     

  118.     //Configure DMA2_Stream5    

  119.     DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection    

  120.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address    

  121.     DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1RecvBuf; //DMA memory 0 address    

  122.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //Peripheral to memory mode    

  123.     DMA_InitStructure.DMA_BufferSize = USART1_RECV_MAXLEN; //Data transfer amount     

  124.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode    

  125.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode    

  126.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits    

  127.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits    

  128.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode     

  129.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority    

  130.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;             

  131.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  132.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer    

  133.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer    

  134.     DMA_Init(DMA2_Stream5, &DMA_InitStructure); //Initialize DMA Stream    

  135.       

  136.     DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5        

  137. }  

  138.   

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

  140. Function name: void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt) 

  141. Function: Serial port USART1 starts a DMA transfer function   

  142. Entry parameter: DMA_Stream_TypeDef DMA_Streamx - DMA data stream (DMA1_Stream0~7/DMA2_Stream0~7); 

  143.          u16 m_u16SendCnt - Number of bytes of data to be transmitted 

  144. Export parameters: None 

  145. Description: None 

  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); //Disable DMA transmission             

  152.     while ((DMA_GetCmdStatus(DMA_Streamx) != DISABLE) && (l_u16RetryCnt++ < 500)); //Wait for DMA to be configurable    

  153.     DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt); //Data transfer volume          

  154.     DMA_Cmd(DMA_Streamx, ENABLE); // Enable DMA transfer     

  155. }          

  156.   

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

  158. Function name: void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)  

  159. Function: Serial port USART1 sends multi-byte function in DMA mode   

  160. Entry parameters: u8 *m_pSendBuf - buffer of data to be sent, u16 m_u16SendCnt - number of data to be sent 

  161. Export parameters: None 

  162. Description: None 

  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); //Start a DMA transfer        

  168. }  

  169.   

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

  171. Function name: void USART1_IRQHandler(void) 

  172. Function: USART serial port 1 interrupt service routine   

  173. Entry parameters: None 

  174. Export parameters: None 

  175. Description: None 

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

  177. void USART1_IRQHandler(void)    

  178. {    

  179.     u16 l_u16Temp = 0;  

  180.       

  181.     if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //If there is an idle interrupt    

  182.     {    

  183.         DMA_Cmd(DMA2_Stream5, DISABLE); //Disable DMA2_Stream5 to prevent data from being processed       

  184.         DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 |   

  185.                       DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5); // clear flag          

  186.     

  187.         // Clear the USART bus idle interrupt flag (just read USART1->SR and USART1->DR)  

  188.         l_u16Temp = USART1->SR;        

  189.         l_u16Temp = USART1->DR;  

  190.             

  191.         G_u16CommRecvLen = USART1_RECV_MAXLEN - DMA_GetCurrDataCounter(DMA2_Stream5); //Get the number of bytes of received data   

  192.         if(G_u16CommRecvLen <= USART1_RECV_MAXLEN)  

  193.         {  

  194.             RS485_Send(); //RS485 sending mode  

  195.             USART1_DMA_SendNByte(G_u8Usart1RecvBuf, G_u16CommRecvLen);  

  196.         }  

  197.             

  198.         DMA_SetCurrDataCounter(DMA2_Stream5, USART1_RECV_MAXLEN); //Set the transmission data length  

  199.         DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5    

  200.     }   

  201.   

  202.     if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) //If there is a send completion interrupt    

  203.     {    

  204.         USART_ClearITPendingBit(USART1, USART_IT_TC); //Clear USART1 send completion interrupt flag  

  205.         DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7 | DMA_FLAG_FEIF7 |   

  206.                       DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7); // clear flag  

  207.               

  208.         RS485_Recv(); //Switch to RS485 receiving mode  

  209.     }     

  210. }  

6. Statement

    The sending and receiving mechanism of this program is just a simple processing mechanism, just to illustrate the method of solving the problem of data loss bytes. Garbled characters will appear when performing fast large data communication. Therefore, for actual projects, the sending and receiving processing mechanism of this program needs to be redesigned. Of the two methods, I personally think that method 2 is better, because method 1 wastes time waiting in the interrupt.


Reference address:Problem of missing bytes when using STM32 serial port to send data in DMA mode in RS485 communication

Previous article:STM32 serial port reception uses DMA double buffering
Next article:STM32 serial communication USART (II) --- DMA mode

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号