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
/*------------------------------------------------ -------------------------------------------------- --
*Copyright: SXD Tech. Co., Ltd.
* Development environment: Keil MDK 5.14 && STM32F407ZET6
*File name: USART serial communication driver header file
*Author: Shun Xinde
*Version: V1.0
*Date: 2018-2-6
*illustrate:
*Modification log: (1)
-------------------------------------------------- -------------------------------------------------- */
#ifndef _USART_H_
#define _USART_H_
#include "Global.h"
/*---------------------------------------------Macro definition (S)---------------------------------------------*/
#define RS485_Recv(); {PFout(11)=0;} //SP485 receiving mode, low level is valid
#define RS485_Send(); {PFout(11)=1;} //SP485 sending mode, high level is valid
#define USART1_SEND_MAXLEN 512 /*Maximum send byte length of serial port 1*/
#define USART1_RECV_MAXLEN 512 /*Maximum receive byte length of serial port 1*/
/*---------------------------------------------Macro definition (E)---------------------------------------------*/
/*--------------------------------------------Port Definition(S)--------------------------------------------*/
/*--------------------------------------------Port Definition (E)--------------------------------------------*/
/*--------------------------------------------Variable Declaration(S)--------------------------------------------*/
extern u32 G_u32RS485BaudRate; //RS485 communication baud rate
extern u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]; //Send data buffer
extern u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]; //Receive data buffer
extern u16 G_u16CommRecvLen; //The length of a frame of data received by communication
/*--------------------------------------------Variable Declaration (E)--------------------------------------------*/
/*--------------------------------------------Function Declaration(S)--------------------------------------------*/
extern void USART1_Init(u32 baud); //USART1 serial port initialization
extern void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //Serial port USART1 starts a DMA transfer
extern void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt); //Serial port USART1 sends multiple bytes in DMA mode
/*--------------------------------------------Function declaration (E)--------------------------------------------*/
#endif
Usart.c source file
Method 1: Use DMA to send completion interrupt
/*------------------------------------------------ -------------------------------------------------- --
*Copyright: SXD Tech. Co., Ltd.
* Development environment: Keil MDK 5.14 && STM32F407ZET6
*File name: USART serial communication driver source file
*Author: Shun Xinde
*Version: V1.0
*Date: 2018-2-6
*illustrate:
*Modification log: (1)
-------------------------------------------------- -------------------------------------------------- */
#include "Usart.h"
/*--------------------------------------------Variable definition (S)--------------------------------------------*/
u32 G_u32RS485BaudRate = 9600; //RS485 communication baud rate
u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]={0,}; //Send data buffer
u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]={0,}; //Receive data buffer
u16 G_u16CommRecvLen=0; //The length of a frame of data received by communication
/*--------------------------------------------Variable definition (E)--------------------------------------------*/
/*--------------------------------------------Function Declaration(S)--------------------------------------------*/
void USART1_Init(u32 baud); //USART1 serial port initialization
void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //Serial port USART1 starts a DMA transfer
void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt); //Serial port USART1 sends multiple bytes in DMA mode
/*--------------------------------------------Function declaration (E)--------------------------------------------*/
/*------------------------------------------------ -------------------------------------------------- --
*Function name: void USART1_Init(u32 baud)
*Function: USART1 serial port initialization function
* Input parameters: u32 baud - baud rate (in bps)
*Export parameters: None
*Description: Used for RS485 communication;
-------------------------------------------------- -------------------------------------------------- */
void USART1_Init(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
u16 mid_u16RetryCnt = 0;
USART_DeInit(USART1);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //Enable GPIOA clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //Enable USART1 clock
//USART1 corresponding pin multiplexing mapping
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9 is reused as USART1_TX
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10 is reused as USART1_RX
//USART1 port configuration
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9 and GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //Multiplexing function
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; //Speed 25MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull output
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Pull-up
GPIO_Init(GPIOA, &GPIO_InitStructure); //Initialize PA9, PA10
//USART1 initialization settings
USART_InitStructure.USART_BaudRate = baud; //Baud rate setting
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //Word length is 8-bit data format
USART_InitStructure.USART_StopBits = USART_StopBits_1; //One stop bit
USART_InitStructure.USART_Parity = USART_Parity_No; //No parity bit
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //No hardware data flow control
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //Transmit and receive mode
USART_Init(USART1, &USART_InitStructure); //Initialize USART1
USART_Cmd(USART1, ENABLE); //Enable USART1
USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //Wait for the idle frame to be sent and then clear the send completion flag
USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag
USART_ITConfig(USART1, USART_IT_TC, DISABLE); //Disable USART1 transfer completion interrupt
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); //Disable USART1 receive not empty interrupt
USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //Disable USART1 to send empty interrupt
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //Enable USART1 idle interrupt
//USART1 NVIC configuration
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //Serial port 1 interrupt channel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //Preemption priority 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //Subpriority 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enable
NVIC_Init(&NVIC_InitStructure);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //Enable DMA transmission of serial port 1
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //Enable DMA reception of serial port 1
// - USART1 transmit DMA configuration
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //DMA2 clock enable
DMA_DeInit(DMA2_Stream7);
while ((DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable
//Configure DMA2_Stream7
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1SendBuf; //DMA memory 0 address
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //Memory to peripheral mode
DMA_InitStructure.DMA_BufferSize = USART1_SEND_MAXLEN; //Data transfer amount
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer
DMA_Init(DMA2_Stream7, &DMA_InitStructure); //Initialize DMA Stream
//NVIC configuration of DMA2_Stream7
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7); //Clear DMA send completion interrupt flag
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE); //Enable DMA send completion interrupt
DMA_Cmd(DMA2_Stream7, ENABLE); //Enable DMA2_Stream7
// - USART1 receive DMA configuration
mid_u16RetryCnt = 0;
DMA_DeInit(DMA2_Stream5);
while ((DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable
//Configure DMA2_Stream5
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1RecvBuf; //DMA memory 0 address
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //Peripheral to memory mode
DMA_InitStructure.DMA_BufferSize = USART1_RECV_MAXLEN; //Data transfer amount
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer
DMA_Init(DMA2_Stream5, &DMA_InitStructure); //Initialize DMA Stream
DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5
}
/*------------------------------------------------ -----------------------------------------------
Function name: void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt)
Function: Serial port USART1 starts a DMA transfer function
Entry parameter: DMA_Stream_TypeDef DMA_Streamx - DMA data stream (DMA1_Stream0~7/DMA2_Stream0~7);
u16 m_u16SendCnt - Number of bytes of data to be transmitted
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt)
{
u16 l_u16RetryCnt = 0;
DMA_Cmd(DMA_Streamx, DISABLE); //Disable DMA transmission
while ((DMA_GetCmdStatus(DMA_Streamx) != DISABLE) && (l_u16RetryCnt++ < 500)); //Wait for DMA to be configurable
DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt); //Data transfer volume
DMA_Cmd(DMA_Streamx, ENABLE); // Enable DMA transfer
}
/*------------------------------------------------ -----------------------------------------------
Function name: void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)
Function: Serial port USART1 sends multi-byte function in DMA mode
Entry parameters: u8 *m_pSendBuf - buffer of data to be sent, u16 m_u16SendCnt - number of data to be sent
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)
{
memcpy(G_u8Usart1SendBuf, m_pSendBuf, m_u16SendCnt);
USART_DMA_SendStart(DMA2_Stream7, m_u16SendCnt); //Start a DMA transfer
}
/*------------------------------------------------ -----------------------------------------------
Function name: void DMA2_Stream7_IRQHandler(void)
Function: Serial port USART1 sends the interrupt service routine in DMA mode
Entry parameters: None
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void DMA2_Stream7_IRQHandler(void)
{
if(DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) != RESET) //DMA sending completed?
{
DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7 | DMA_FLAG_FEIF7 |
DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7); //Clear flag
while(!USART_GetFlagStatus(USART1, USART_FLAG_TC)); //Wait for USART1 to send the completion flag TC to 1
USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag
RS485_Recv(); //Switch to RS485 receiving mode
}
}
/*------------------------------------------------ -----------------------------------------------
Function name: void USART1_IRQHandler(void)
Function: USART serial port 1 interrupt service routine
Entry parameters: None
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void USART1_IRQHandler(void)
{
u16 l_u16Temp = 0;
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //If there is an idle interrupt
{
DMA_Cmd(DMA2_Stream5, DISABLE); //Disable DMA2_Stream5 to prevent data from being processed
DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 |
DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5); //Clear flag
// Clear the USART bus idle interrupt flag (just read USART1->SR and USART1->DR)
l_u16Temp = USART1->SR;
l_u16Temp = USART1->DR;
G_u16CommRecvLen = USART1_RECV_MAXLEN - DMA_GetCurrDataCounter(DMA2_Stream5); //Get the number of bytes of received data
if(G_u16CommRecvLen <= USART1_RECV_MAXLEN)
{
RS485_Send(); //RS485 sending mode
USART1_DMA_SendNByte(G_u8Usart1RecvBuf, G_u16CommRecvLen); //Send back the received data
}
DMA_SetCurrDataCounter(DMA2_Stream5, USART1_RECV_MAXLEN); //Set the transmission data length
DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5
}
}
Method 2: Use USART1 to send completion interrupt
/*------------------------------------------------ -------------------------------------------------- --
*Copyright: SXD Tech. Co., Ltd.
* Development environment: Keil MDK 5.14 && STM32F407ZET6
*File name: USART serial communication driver source file
*Author: Shun Xinde
*Version: V1.0
*Date: 2018-2-6
*illustrate:
*Modification log: (1)
-------------------------------------------------- -------------------------------------------------- */
#include "Usart.h"
/*--------------------------------------------Variable definition (S)--------------------------------------------*/
u32 G_u32RS485BaudRate = 9600; //RS485 communication baud rate
u8 G_u8Usart1SendBuf[USART1_SEND_MAXLEN]={0,}; //Send data buffer
u8 G_u8Usart1RecvBuf[USART1_RECV_MAXLEN]={0,}; //Receive data buffer
u16 G_u16CommRecvLen=0; //The length of a frame of data received by communication
/*--------------------------------------------Variable definition (E)--------------------------------------------*/
/*--------------------------------------------Function Declaration(S)--------------------------------------------*/
void USART1_Init(u32 baud); //USART1 serial port initialization
void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt); //Serial port USART1 starts a DMA transfer
void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt); //Serial port USART1 sends multiple bytes in DMA mode
/*--------------------------------------------Function declaration (E)--------------------------------------------*/
/*------------------------------------------------ -------------------------------------------------- --
*Function name: void USART1_Init(u32 baud)
*Function: USART1 serial port initialization function
* Input parameters: u32 baud - baud rate (in bps)
*Export parameters: None
*Description: Used for RS485 communication;
-------------------------------------------------- -------------------------------------------------- */
void USART1_Init(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
u16 mid_u16RetryCnt = 0;
USART_DeInit(USART1);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //Enable GPIOA clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //Enable USART1 clock
//USART1 corresponding pin multiplexing mapping
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9 is reused as USART1_TX
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10 is reused as USART1_RX
//USART1 port configuration
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9 and GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //Multiplexing function
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; //Speed 25MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull output
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Pull-up
GPIO_Init(GPIOA, &GPIO_InitStructure); //Initialize PA9, PA10
//USART1 initialization settings
USART_InitStructure.USART_BaudRate = baud; //Baud rate setting
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //Word length is 8-bit data format
USART_InitStructure.USART_StopBits = USART_StopBits_1; //One stop bit
USART_InitStructure.USART_Parity = USART_Parity_No; //No parity bit
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //No hardware data flow control
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //Transmit and receive mode
USART_Init(USART1, &USART_InitStructure); //Initialize USART1
USART_Cmd(USART1, ENABLE); //Enable USART1
USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //Wait for the idle frame to be sent and then clear the send completion flag
USART_ClearFlag(USART1, USART_FLAG_TC); //Clear the send completion flag
USART_ITConfig(USART1, USART_IT_TC, ENABLE); //Enable USART1 transfer completion interrupt
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); //Disable USART1 receive not empty interrupt
USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //Disable USART1 to send empty interrupt
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //Enable USART1 idle interrupt
//USART1 NVIC configuration
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //Serial port 1 interrupt channel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //Preemption priority 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //Subpriority 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enable
NVIC_Init(&NVIC_InitStructure);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //Enable DMA transmission of serial port 1
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //Enable DMA reception of serial port 1
// - USART1 transmit DMA configuration
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //DMA2 clock enable
DMA_DeInit(DMA2_Stream7);
while ((DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable
//Configure DMA2_Stream7
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1SendBuf; //DMA memory 0 address
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //Memory to peripheral mode
DMA_InitStructure.DMA_BufferSize = USART1_SEND_MAXLEN; //Data transfer amount
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer
DMA_Init(DMA2_Stream7, &DMA_InitStructure); //Initialize DMA Stream
DMA_Cmd(DMA2_Stream7, ENABLE); //Enable DMA2_Stream7
// - USART1 receive DMA configuration
mid_u16RetryCnt = 0;
DMA_DeInit(DMA2_Stream5);
while ((DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) && (mid_u16RetryCnt++ < 500)); //Wait for DMA to be configurable
//Configure DMA2_Stream5
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //Channel selection
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA peripheral address
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)G_u8Usart1RecvBuf; //DMA memory 0 address
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //Peripheral to memory mode
DMA_InitStructure.DMA_BufferSize = USART1_RECV_MAXLEN; //Data transfer amount
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral non-incremental mode
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory increment mode
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data length: 8 bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data length: 8 bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Use normal mode
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //Medium priority
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Memory burst single transfer
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Peripheral burst single transfer
DMA_Init(DMA2_Stream5, &DMA_InitStructure); //Initialize DMA Stream
DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5
}
/*------------------------------------------------ -----------------------------------------------
Function name: void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt)
Function: Serial port USART1 starts a DMA transfer function
Entry parameter: DMA_Stream_TypeDef DMA_Streamx - DMA data stream (DMA1_Stream0~7/DMA2_Stream0~7);
u16 m_u16SendCnt - Number of bytes of data to be transmitted
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void USART_DMA_SendStart(DMA_Stream_TypeDef *DMA_Streamx, u16 m_u16SendCnt)
{
u16 l_u16RetryCnt = 0;
DMA_Cmd(DMA_Streamx, DISABLE); //Disable DMA transmission
while ((DMA_GetCmdStatus(DMA_Streamx) != DISABLE) && (l_u16RetryCnt++ < 500)); //Wait for DMA to be configurable
DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt); //Data transfer volume
DMA_Cmd(DMA_Streamx, ENABLE); // Enable DMA transfer
}
/*------------------------------------------------ -----------------------------------------------
Function name: void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)
Function: Serial port USART1 sends multi-byte function in DMA mode
Entry parameters: u8 *m_pSendBuf - buffer of data to be sent, u16 m_u16SendCnt - number of data to be sent
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void USART1_DMA_SendNByte(u8 *m_pSendBuf, u16 m_u16SendCnt)
{
memcpy(G_u8Usart1SendBuf, m_pSendBuf, m_u16SendCnt);
USART_DMA_SendStart(DMA2_Stream7, m_u16SendCnt); //Start a DMA transfer
}
/*------------------------------------------------ -----------------------------------------------
Function name: void USART1_IRQHandler(void)
Function: USART serial port 1 interrupt service routine
Entry parameters: None
Export parameters: None
Description: None
-------------------------------------------------- ----------------------------------------*/
void USART1_IRQHandler(void)
{
u16 l_u16Temp = 0;
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //If there is an idle interrupt
{
DMA_Cmd(DMA2_Stream5, DISABLE); //Disable DMA2_Stream5 to prevent data from being processed
DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 |
DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5); // clear flag
// Clear the USART bus idle interrupt flag (just read USART1->SR and USART1->DR)
l_u16Temp = USART1->SR;
l_u16Temp = USART1->DR;
G_u16CommRecvLen = USART1_RECV_MAXLEN - DMA_GetCurrDataCounter(DMA2_Stream5); //Get the number of bytes of received data
if(G_u16CommRecvLen <= USART1_RECV_MAXLEN)
{
RS485_Send(); //RS485 sending mode
USART1_DMA_SendNByte(G_u8Usart1RecvBuf, G_u16CommRecvLen);
}
DMA_SetCurrDataCounter(DMA2_Stream5, USART1_RECV_MAXLEN); //Set the transmission data length
DMA_Cmd(DMA2_Stream5, ENABLE); //Enable DMA2_Stream5
}
if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) //If there is a send completion interrupt
{
USART_ClearITPendingBit(USART1, USART_IT_TC); //Clear USART1 send completion interrupt flag
DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7 | DMA_FLAG_FEIF7 |
DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7); // clear flag
RS485_Recv(); //Switch to RS485 receiving mode
}
}
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.
Previous article:STM32 serial port reception uses DMA double buffering
Next article:STM32 serial communication USART (II) --- DMA mode
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- CH340 circuit to prevent current backflow
- Bluetooth module responds incorrectly
- 【RT-Thread Reading Notes】Reflections on RT-Thread Learning Chapter 6
- 【ST NUCLEO-H743ZI Review】——by supermiao123
- How to Reduce the Temperature in Your Car's Fuse Box
- STM32 supports VCP+MSC+HID mode
- How does AD20 perform multi-person collaboration? (Multiple picture warning
- 13. [Learning LPC1768 library functions] ADC experiment
- Application of RPA in the customer service industry
- Efficiency Programming of Single Chip Microcomputer Active Buzzer Driver