stm32interruptkeilusart

STM32 randomly loses bytes during simultaneous USART sending and receiving


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?


Solution

  • 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.