I connected STM32F407VE to an external CAN device(TCAN4550). The MCU receives command from PC and transmits received CAN data to PC via USART. With cyclic CAN messages active, MCU loses a byte here and there. A client service told me that sending and receiving of USART shared the same DR and normally the 'ask and answer' mode was used. I guess my problem happens when both sending and receiving occurs in one interrupt. Here are part of my codes in Keil5:
#define sizeUSART1TX 4096
#define sizeUSART1RX 256
static unsigned char USART1TxBuf[sizeUSART1TX];
static char USART1RxBuf[sizeUSART1RX];
static unsigned short USART1TxIdx0=0, USART1TxIdx1=0, USART1RxIdx=0;
static void USART1SendChar(char *buf, unsigned short cnt)
{
if(USART1Act==0) return;
for (unsigned short idx1 = 0; idx1 < cnt; ++idx1)
{
USART1TxBuf[USART1TxIdx0++] = buf[idx1];
if (USART1TxIdx0 == sizeUSART1TX)
USART1TxIdx0 = 0;
}
if ((USART1->CR1 & 1 << 7) == 0) //TXEIE
USART1->CR1 |= 1 << 7;
}
void USART1_IRQHandler(void)
{
if (USART1->SR & (1 << 5)) //RXNE, reset by reading DR
{
USART1RxBuf[USART1RxIdx++] = USART1->DR;
if (USART1RxBuf[USART1RxIdx-1] == 10) //command end
{
...... //actions
USART1SendChar(USART1RxBuf, USART1RxIdx); //confirm the command
memset(USART1RxBuf, 0, sizeUSART1RX);
USART1RxIdx = 0;
}
}
if ((USART1->SR & 1<<7) && (USART1->CR1 & 1<<7)) //TXE and TXEIE
{
USART1->DR = USART1TxBuf[USART1TxIdx1++]; //writing DR reset interrupts
if (USART1TxIdx1 == sizeUSART1TX)
USART1TxIdx1 = 0;
if (USART1TxIdx1 == USART1TxIdx0)
USART1->CR1 &= ~(1<<7); //turning off TXEIE
}
}
int main()
{
//USART
USART_InitTypeDef USART_InitStructure;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2Periph_SYSCFG;
//PA9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
USART1->SR = ~(0x00F0); //clear interrupts
while(1){}
}
Is it possible to realize true full duplex for USART?
With cyclic CAN messages active, MCU loses a byte here and there.
If the CAN uses lengthy interrupts (longer than Rx character duration plus any spacing between characters) and those have the same or higher priority than the UART ISR, then some of the Rx characters may get simply lost due to not being picked from UART_DR before next character arrives.
Check UART Rx Overrun in the ISR.