MKL26Z256 UART RX callback get ring buffer overflow, but not RX idle

cancel
Showing results for 
Search instead for 
Did you mean: 

MKL26Z256 UART RX callback get ring buffer overflow, but not RX idle

462 Views
bgraham
Contributor IV

MCUXpresso IDE v11.0.0 [Build 2516] [2019-06-05]

Device: MKL26Z256xxx4

SDK_2.x_MKL26Z256xxx4 2.4.1

 

I started with the interrupt_rb_transfer project.

My custom board has a MKL26Z256 that is connected to a modem at 115200.

Using a Logic Analyzer, I can see my TX strings ("AT\n") getting to a modem, and I see the RX strings being returned ("OK\r\n").

The UART_UserCallback() is being called when the TX is idle: kStatus_UART_TxIdle

But UART_UserCallback() is not getting called when the RX is idle: kStatus_UART_RxIdle

Since the kStatus_UART_RxIdle event does not occur, the UART_UserCallback() is being called with the events: kStatus_UART_RxRingBufferOverrun and then kStatus_UART_RxHardwareOverrun.

I have not modified the code that sets up the UART, so I can't explain why I am not getting the expected kStatus_UART_RxIdle event

Can somebody suggest what might explain why the kStatus_UART_RxIdle event is not occurring?

Thanks,

Bruce Graham
Senior Software Engineer
TechnoSoft Innovations, Inc.
bgraham@technosofteng.com

4 Replies

269 Views
bgraham
Contributor IV

I started this time with the freertos_uart example.

I modified the callback events. I used a seperate event for RX and TX Complete.

I used 2 separate events so that my RX and TX threads can wait for their own event.

I needed an independent RX thread so that it can parse modem status messages that the modem sends without a command being sent to it.

The TX thread is working as expected.

The RX thread has the same problem I ran into with the preveous app.

After the modem sends the string "\r\nRDY\r\n" to the NXP, UART_TransferReceiveNonBlocking():

gets the first char '\r',

gets a kStatus_UART_RxHardwareOverrun, and misses the second char '\n',

gets the third char 'R',

then gets kStatus_UART_RxHardwareOverrun forever.

The ring buffer is set up.

The UART's C2 is TIE:0 TCIE:0 RIE:1 ILIE:1 TE:1 RE:1 RWU:0 SBK:0

The callback only sets an event.

With the core running at 48 MHz, setting an event can't be taking so long that the UART RX hardware over runs.

The only way I can explain this is that the fsl_uart.c code is somehow halting the UART RX.

I put assert(UART1->C2 && UART_C2_ILIE_MASK) before the call to UART_TransferReceiveNonBlocking(), so the IDLE interrupt is OK.

So, what else could cause this loss of RX chars, and the RX overrun?

Thanks,

Bruce

0 Kudos

269 Views
bgraham
Contributor IV

One more detail.

When the kStatus_UART_RxRingBufferOverrun  event occurs, I call UART_TransferReceiveNonBlocking() and get all of the RX chars that the modem sent to the MKL26Z256.

So, the UART is working, expect that the kStatus_UART_RxIdle event is not being sent by the fsl_uart.c code.

I need the uart driver to send the kStatus_UART_RxIdle event.

What would cause the uart driver to fail to send the kStatus_UART_RxIdle event?

Thanks,

Bruce

0 Kudos

269 Views
Robin_Shen
NXP TechSupport
NXP TechSupport

Hi Bruce,

Please check the Idle Line Type UARTx_C1[ILT] and UARTx_C2[ILIE].

UARTx_C1[ILT].png

UARTx_C2[ILIE].png

I am not sure if you enable the kUART_IdleLineInterruptEnable?

UART_EnableInterrupts kUART_IdleLineInterruptEnable.png

The IDLE status flag includes logic that prevents it from getting set repeatedly when the RxD line remains idle for an extended period of time. IDLE is cleared by reading UART_S1 while UART_S1[IDLE] is set and then reading UART_D. After UART_S1[IDLE] has been cleared, it cannot become set again until the receiver has received at least one new character and has set UART_S1[RDRF].

UARTx_S1[IDLE].png

Best Regards,

Robin

 

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

269 Views
bgraham
Contributor IV

I added the line UART_EnableInterrupts(DEMO_UART, kUART_IdleLineInterruptEnable); before the call to UART_TransferStartRingBuffer().

After UART_UserCallback() kStatus_UART_RxHardwareOverrun, I checked the registers.

UART1 C2.ILIE is 1

UART1 S1.IDLE is 0

See registers below.

pastedImage_2.png

See code below.

Since C2.ILIE is set, why would UART_UserCallback() never got called with kStatus_UART_RxIdle?

Thanks,

Bruce

// File: uart_interrupt_rb_transfer.c

#include "board.h"
#include "fsl_uart.h"

#include "pin_mux.h"
#include "clock_config.h"

#include <stdio.h>
#include <ctype.h>

/*******************************************************************************
* Definitions
******************************************************************************/
/* UART instance and clock */
#define DEMO_UART UART1
#define DEMO_UART_CLKSRC BUS_CLK
#define DEMO_UART_CLK_FREQ CLOCK_GetFreq(BUS_CLK)

#define RX_RING_BUFFER_SIZE 20U
#define ECHO_BUFFER_SIZE 8U

/*******************************************************************************
* Prototypes
******************************************************************************/

/* UART user callback */
void UART_UserCallback(UART_Type *base, uart_handle_t *handle, status_t status, void *userData);

/*******************************************************************************
* Variables
******************************************************************************/
uart_handle_t g_uartHandle;

uint8_t g_rxRingBuffer[RX_RING_BUFFER_SIZE] = {0}; /* RX ring buffer. */

uint8_t g_rxBuffer[ECHO_BUFFER_SIZE] = {0};
uint8_t g_txBuffer[] = "ATE0\n";

volatile bool bProcessRx = false;
volatile bool bGotTxIdle = false;
volatile bool bRxMessageReceived = false;

char szDummyObj[] = "Hello";

/*******************************************************************************
* Code
******************************************************************************/
/* UART user callback */
void UART_UserCallback(UART_Type *base, uart_handle_t *handle, status_t status, void *userData)
{
assert(0 == strcmp(szDummyObj, (char*) userData));

switch(status)
{
case kStatus_UART_TxIdle:
printf("\r\nISR: Tx Idle\r\n");
bGotTxIdle = true;
break;

case kStatus_UART_RxIdle:
printf("\r\nISR: Rx Idle\r\n");
bProcessRx = true;
break;

case kStatus_UART_RxRingBufferOverrun:
printf("\r\nISR: Rx Ring Buffer Overrun\r\n");
bProcessRx = true;
break;

case kStatus_UART_RxHardwareOverrun:
printf("\r\nISR: Rx Hardware Overrun\r\n");
bProcessRx = true;
break;

case kStatus_UART_NoiseError:
printf("\r\nISR: Noise Error\r\n");
break;

case kStatus_UART_FramingError:
printf("\r\nISR: Framing Error\r\n");
bProcessRx = true;
break;

default:
printf("\r\nISR: status: %d\r\n", status);
break;
}

if (bProcessRx)
{
bProcessRx = false;
}
}


// output
#define BOARD_CELL_PWRKEY_GPIO GPIOE // PTE20
#define BOARD_CELL_PWRKEY_PORT PORTE
#define BOARD_CELL_PWRKEY_GPIO_PIN 20U

void delay(int iMillisec)
{
int iLoops = iMillisec * 4400;

//printf("delay(%d)\r\n", iMillisec);

for (int i = 0; i < iLoops; ++i)
{

}
}

/*
* Purpose: Cycling PWRKEY will turn the modem on or off.
*/
void cycleModemPWRKEY(void)
{
// modem did not respond, so wake it up
printf("\tEC21 Pulse PWRKEY low for at least 500ms\r\n");
printf("\tEC21 PWRKEY - High\r\n");
GPIO_WritePinOutput(BOARD_CELL_PWRKEY_GPIO, BOARD_CELL_PWRKEY_GPIO_PIN, 1);
printf("\tEC21 Delay 100 ms\r\n");
delay(100);
printf("\tEC21 PWRKEY - Low\r\n");
GPIO_WritePinOutput(BOARD_CELL_PWRKEY_GPIO, BOARD_CELL_PWRKEY_GPIO_PIN, 0);
printf("\tEC21 Delay 800 ms - minimum pulse width is 500ms\r\n");
delay(800);
printf("\tEC21 PWRKEY - High\r\n");
GPIO_WritePinOutput(BOARD_CELL_PWRKEY_GPIO, BOARD_CELL_PWRKEY_GPIO_PIN, 1);
}


/*!
* @brief Main function
*/
int main(void)
{
uart_config_t config;
uart_transfer_t sendXfer;

printf("starting\r\n");

BOARD_InitPins();
BOARD_BootClockRUN();

cycleModemPWRKEY();

/*
* config.baudRate_Bps = 115200U;
* config.parityMode = kUART_ParityDisabled;
* config.stopBitCount = kUART_OneStopBit;
* config.txFifoWatermark = 0;
* config.rxFifoWatermark = 1;
* config.enableTx = false;
* config.enableRx = false;
*/
UART_GetDefaultConfig(&config);
config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;
config.enableTx = true;
config.enableRx = true;

UART_Init(DEMO_UART, &config, DEMO_UART_CLK_FREQ);
UART_TransferCreateHandle(DEMO_UART, &g_uartHandle, UART_UserCallback, szDummyObj);
UART_EnableInterrupts(DEMO_UART, kUART_IdleLineInterruptEnable); // ADDED PER NXP COMMUNITY SUGGESTION
UART_TransferStartRingBuffer(DEMO_UART, &g_uartHandle, g_rxRingBuffer, RX_RING_BUFFER_SIZE);

sendXfer.data = g_txBuffer;
sendXfer.dataSize = strlen((char*)g_txBuffer);

// call to UART_TransferReceiveNonBlocking() enables kStatus_UART_RxIdle event
char szRxTemp[100] = {0};
int iCount = 0;

char szLastMessage[100];

size_t receivedCount;
uart_transfer_t receiveXfer;

while(1)
{
printf("UART_TransferReceiveNonBlocking()\r\n");

bRxMessageReceived = false;
receiveXfer.data = (uint8_t*)szRxTemp;
receiveXfer.dataSize = sizeof(szRxTemp);
do
{
UART_TransferReceiveNonBlocking(DEMO_UART, &g_uartHandle, &receiveXfer, &receivedCount);
if (receivedCount)
{
// done when \n (end of string) is received
if (NULL != strstr(szRxTemp, "\n"))
{
strcpy(szLastMessage, szRxTemp);
bRxMessageReceived = true;

memset(szRxTemp, 0, sizeof(szRxTemp));
iCount = 0;
}
else
{
iCount += receivedCount;
assert(iCount < sizeof(szRxTemp));
receiveXfer.data = (uint8_t*)&szRxTemp[iCount];
receiveXfer.dataSize = sizeof(szRxTemp) - iCount;
}
}
} while (!bRxMessageReceived);

// print to debug console, with hex for not printable chars
char szTemp[100] = {0};

for(int i = 0; i < strlen(szLastMessage); ++i)
{
int iLength = strlen(szTemp);

if (isprint(szLastMessage[i]))
sprintf(&szTemp[iLength], "%c", szLastMessage[i]);
else
sprintf(&szTemp[iLength], "<0x%02X>", szLastMessage[i]);
}

printf("LastMessage: %s\r\n", szTemp);
}

while (1)
{
while(!bRxMessageReceived)
{
}

printf("TX: %s", g_txBuffer);
bGotTxIdle = false;
UART_TransferSendNonBlocking(DEMO_UART, &g_uartHandle, &sendXfer);
while (!bGotTxIdle)
{
}
printf("\r\n");
}
}

0 Kudos