AnsweredAssumed Answered

UART frame error receving from VLPS

Question asked by alessandro morniroli on Jul 13, 2017
Latest reply on Aug 1, 2017 by Mark Butcher

Hello,

 

I'm trying to using VLPS power mode with unrestricted UART operations

 

I've already read some topics on the community:

 

Using Kinetis Low Power Stop Modes with unrestricted UART operation - a report 

Deep Sleep and VLPS on MK64FN1M0VLL12 

 

I understand that the main problem is to stay "alive" (RUN/WAIT or similar) while RX/TX transmissions are active, going to VLPS only when UART peripheral is in "idle" state, waiting for new RX/TX (enabling wakeup from VLPS using RX active edge interrupt).

 

I also understand that the baudrate limit is 57600, since transition from VLPS to RUN on the RX active edge interrupt takes some time (~5uS for my MCU).


Detecting TX idle status can be done easily, simply checking UARTx_S1[TC].

 

Detecting RX idle status is a bit more difficult (not so difficult indeed).


Briefly, it should be possible to use UART with unrestricted RX operations acting as follows:


- prior to enter VLPS mode, enable UARTx_BDH[RXEDGIE];
- remain "active" (e.g. RUN or WAIT mode) for RX to be completed;

- detect RX idle status prior to enter VLPS again;

 

I implemented the following solution, which seems to do the job very well, minimizing the time that MCU stay in RUN/WAIT for allowing RX/TX to complete correctly.

 

/* Common include. */
#include "fsl_common.h"

#include "fsl_clock.h"
#include "fsl_gpio.h"
#include "fsl_lptmr.h"
#include "fsl_port.h"
#include "fsl_pit.h"
#include "fsl_ftm.h"
#include "fsl_smc.h"
#include "fsl_uart.h"

/**
* Struct used for saving 'is busy' flag about RX/TX channels.
*/

static union
{
     uint8_t raw; /**< Raw data. */

     struct
     {
          uint8_t rx       : 1; /**< RX channel flag. */
          uint8_t tx       : 1; /**< TX channel flag. */
          uint8_t spare : 6; /**< Not used.           */
     } bit;
} tsgIsBusy;

/**
* Variable used for storing received input.
*/

static uint8_t u8sgInput;

/**
* UART2 ISR handler.
*/

void
UART2_RX_TX_IRQHandler (void)
{
     uint32_t u32lSr = 0;

     /* Get UART status register. */
     u32lSr = UART_GetStatusFlags (UART2);

     /* TX completed? */
     if (u32lSr & UART_S1_TC_MASK)
     {
          /* Clear TX 'is busy' flag. */
          tsgIsBusy.bit.tx = 0;
     }

     /* RX active edge interrupt? */
     if (u32lSr & kUART_RxActiveEdgeFlag)
     {
          /* Set RX 'is busy' flag. */
          tsgIsBusy.bit.rx = 1;

          /* Disable RX active edge interrupt (for minimizing ISR overhead). */
          UART_DisableInterrupts (UART2, kUART_RxActiveEdgeInterruptEnable);

          /* Clear RXEDGIF flag. */
          UART2->S2 |= UART_S2_RXEDGIF_MASK;
     }

     /* RX data received? */
     if (u32lSr & kUART_RxDataRegFullFlag)
     {
          /* Just read the byte received from UART_D register. */
          u8sgInput = UART_ReadByte (UART2);

          /* Clear RXEDGIF flag (just in case). */
          UART2->S2 |= UART_S2_RXEDGIF_MASK;
     }

     /* RX idle? */
     if (u32lSr & kUART_IdleLineFlag)
     {
          /* Clear RX 'is busy' flag. */
          tsgIsBusy.bit.rx = 0;

          /*
           * UART_D still not read?
           *
           * Please note that interrupt may be delayed and executed when IDLE
           * interrupt has been already triggered.
           */

          if (!(u32lSr & kUART_RxDataRegFullFlag))
          {
               /* Read data from UART_D for enabling IDLE interrupt again. */
               UART_ReadByte (UART2);
          }

          /* Enable RX active edge interrupt again. */
          UART_EnableInterrupts  (UART2, kUART_RxActiveEdgeInterruptEnable);
     }
}

/**
* UART2 error ISR handler.
*/

void
UART2_ERR_IRQHandler (void)
{
     uint32_t u32lSr = 0;

     /* Get UART status register. */
     u32lSr = UART_GetStatusFlags (UART2);

     /* Clear UART status register. */
     UART_ClearStatusFlags (UART2, u32lSr);
}


/**
* Main routine.
*
* @return Never.
*/

int
main (void)
{
     const mcg_config_t tlConfig =
     {
          .mcgMode           = kMCG_ModeFEI,
          .irclkEnableMode = 0,
          .ircs                = kMCG_IrcSlow,
          .fcrdiv                = 0,
          .frdiv                = 0,
          .dmx32                = kMCG_Dmx32Fine,
          .pll0Config           = { 0 },
          .drs                = kMCG_DrsMidHigh
     };

     const gpio_pin_config_t tlLedPinConfig =
     {
          .pinDirection = kGPIO_DigitalOutput,
          .outputLogic  = 1
     };

     const uart_config_t tlUartConfig =
     {
          .baudRate_Bps      = 57600,
          .parityMode           = kUART_ParityDisabled,
          .txFifoWatermark = 1,
          .rxFifoWatermark = 1,
          .enableTx           = 1,
          .enableRx           = 1
     };

     uint32_t u32lMask;

     SMC_SetPowerModeProtection (SMC, kSMC_AllowPowerModeVlp);

     CLOCK_SetSimSafeDivs ();

     CLOCK_SetMcgConfig (&tlConfig);

     SystemCoreClockUpdate ();

     CLOCK_EnableClock (kCLOCK_PortA);
     CLOCK_EnableClock (kCLOCK_PortD);

     PORT_SetPinMux (PORTA, 1, kPORT_MuxAsGpio);
     PORT_SetPinMux (PORTD, 6, kPORT_MuxAlt3);
     PORT_SetPinMux (PORTD, 7, kPORT_MuxAlt3);

     GPIO_PinInit (GPIOA, 1, &tlLedPinConfig);

     UART_Init (UART2, &tlUartConfig, CLOCK_GetBusClkFreq ());

     UART2->PFIFO &= ~UART_PFIFO_RXFE_MASK & ~UART_PFIFO_TXFE_MASK;

     /* Idle character bit count starts after stop bit. */
     UART2->C1 |= UART_C1_ILT_MASK;

     UART2->PFIFO &= ~UART_PFIFO_RXFE_MASK & ~UART_PFIFO_TXFE_MASK;

     UART_EnableInterrupts (UART2, kUART_RxDataRegFullInterruptEnable |
                                          kUART_RxOverrunInterruptEnable      |
                                          kUART_NoiseErrorInterruptEnable      |
                                          kUART_FramingErrorInterruptEnable      |
                                          kUART_ParityErrorInterruptEnable      |
                                          kUART_IdleLineInterruptEnable           |
                                          kUART_RxActiveEdgeInterruptEnable);

     NVIC_SetPriority (UART2_ERR_IRQn,     8);
     NVIC_SetPriority (UART2_RX_TX_IRQn, 10);

     NVIC_EnableIRQ (UART2_ERR_IRQn);
     NVIC_EnableIRQ (UART2_RX_TX_IRQn);

     while (true)
     {
          u32lMask = DisableGlobalIRQ ();

          /* RX/TX active? */
          if (tsgIsBusy.raw)
          {
               GPIO_SetPinsOutput (GPIOA, (1 << 1));

               /* Stay in wait power mode. */
               SMC_SetPowerModeWait (SMC);
          }
          else
          {
               GPIO_ClearPinsOutput (GPIOA, (1 << 1));

               /* RX/TX idle: just enter VLPS. */
               SMC_SetPowerModeVlps (SMC);
          }

          EnableGlobalIRQ (u32lMask);
     }

     return 0;
}


So, each time a byte is received, the UART ISR should trigger three times:

 

- the first one, because of RxD Pin Active Edge Interrupt Flag (incoming RX -> start bit);
- the second one, because of Receive Data Register Full Flag interrupt (byte received);

- the third one, because of Idle Line Flag (10 consecutive 1's recognized after the previous stop bit);

 

 

So far all right: PTA1 stay 'high' in wait mode and 'low' in vlps mode. I checked with an oscilloscope and PTA1 stay high synchronously with RX traffic (10bit transmission + 1 bit for IDLE detection).

 


Let's come to problems.

 

 

I ported the implementation in my project. The same project may be executed on two different boards, which mount two different MCU:

 

- MK22FN512VLH12 (mask 0N50M)
- MK22FX512VMC12 (mask 3N03G)

 

My project, after system initialization, goes to SLEEP using VLPS as power mode.

 

While it's in sleep, it'is receving asynchronously data from a BT controller (TI CC2564) @ 57600 baudrate.

 

All seems to work properly, except that randomly UART RX receive frame errors.

 

I exclude a problem with TI CC2564 because using WAIT power mode instead of VLPS frame errors are never received.

 


There is also a big difference between the two MCU. For example, in 12h runtime the following frame errors were reported:

 

MK22FN512VLH12  -> 3638
- MK22FX512VMC12 -> 293


The main difference is that FN is configured for run @ 72MHz (because of maximum core clock speed is 80MHz from datasheet), while FX @ 98MHz. Source clock for UARTs on both FN/FX boards is the bus clock (core clock divide by 2, so FN @ 36Mhz and FX @ 49MHz). Both boards operate using FLL Engaged External (FEE): external crystal is 32KHz.


From datasheet, the recovery time from VLPS -> RUN is:

MK22FN512VLH12  -> 5.7us
- MK22FX512VMC12 -> 4.4us

Both of them are below the half of baud period, so receiving data @ 57600 should not be a problem (by the way, I also tried running @ 38400 baudrate but frame errors happen the same but with minor frequency).



My questions are:

1) what could cause these frame errors?
2) why there is a so big difference with frame errors received between FN/FX?
3) are there any cases where recovery time from VLPS to RUN is above the max value reported from datasheet, de facto losing the first RX bit?
4) are there any cases where RX active edge interrupt may not be recognized if it happens synchronously with the execution of WFI instruction?


Any help / advice is welcome: these frame errors sometime force my system to re-initialize the whole BT stack because it stucks.

Best regards,

Alessandro

Outcomes