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
Hi,
nothing at all? I'm still groping around in the dark, any help / advice is welcome.
Best regards,
Alessandro
Hi Alessandro Morniroli
I have check your code, I couldn't see any problem in the code.
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?
As long as you have mentioned:
(by the way, I also tried running @ 38400 baudrate but frame errors happen the same but with minor frequency).
It could be that the problem is related with the first comment of mark in the post Using Kinetis Low Power Stop Modes with unrestricted UART operation - a report
where he mentions that there is a problem with the wakeup time and the baud rate limit (where he mentioned that he doesn't see any problem with 57600).
If you reduce your baud rate, for example, 9600, do errors still happened?
Regardless of this, the problem is related only with the VLPS, as it is taking too much time to wakeup from VLPS to RUN mode, could you try with SMC_SetPowerModeStop (Stop mode), there should be too much difference.
Best Regards
Jorge Alcala
Hi,
thank you for the support.
It could be that the problem is related with the first comment of mark in the post Using Kinetis Low Power Stop Modes with unrestricted UART operation - a report
where he mentions that there is a problem with the wakeup time and the baud rate limit (where he mentioned that he doesn't see any problem with 57600).
If you reduce your baud rate, for example, 9600, do errors still happened?
I think that's correct: in fact the first byte @115200bps (in VLPS) is always lost because of frame error. I've tried also with 9600 and 19200, but errors still happen randomically.
Jorge Antonio Alcala Vazquez ha scritto:
Regardless of this, the problem is related only with the VLPS, as it is taking too much time to wakeup from VLPS to RUN mode, could you try with SMC_SetPowerModeStop (Stop mode), there should be too much difference.
Unfortunately, STOP mode is not allowed by our applications because of power consumption
Hi
On the K64 with external oscillator it is quite fast/repeatable to wake from VLPS mode and so 57600Baud is reliable.
However it is dependent on clocks and clock sources, as well as exact parts used and there can be a spread in the time taken.
If possible to control the data used on the UART (protocol) one could always send some dummy characters that are just there to wake up the processor and allow it enough time to get back to a stable state before the message is interpreted.
Regards
Mark