UART API not catching RX data

Hello, 

I am having some trouble with the UART API (Async and interrupt). I am connecting the development board (nrf9151 DK) to a 3rd party device (keypad / prox reader) and I cannot seem to "catch" the inbound data. To give a little more context on the sequence of events..

The reader initiates the communication with the development kit through an interrupt line. 

The development kit then sends a data packet to the reader (less than 48 bytes)

The reader then responds with the relevant data (less than 48 bytes). 

Communication is over. to send more data you must repeat the process.

I have tried this with two different readers, along with a development board and a custom board that I am making. All result in the same issue UART_RX_RDY is never triggered. If it is, only 1 byte comes through. All of this is happening over UART 2. This is leading me to think I my set up is incorrect. I have looked at both the lpuart example and the dev academy lesson as well as set the pins on the DK to both 1.8 v and 3.3 v.

Information about the reader:

There is no CTS or RTS line, only RX and TX. 

Baud is 9600

Uses OSDP for communication protocol. 

Quick note about the OSDP communication - The data for the tx line is formatted correctly (I've had this working in the past but needed to rework a few parts of the application). A valid response should return 43 bytes starting with a 0x53 and invalid should return with about 12 bytes starting with a 0x53 as well. 

Any guidance or suggestions would be appreciated. 

Thank you, 

Jack

Device Tree overlay: 

&uart2 {
    compatible = "nordic,nrf-uarte";
    current-speed = <9600>;
    parity = "none";
    stop-bits = "1";
    data-bits = <8>;
    pinctrl-0 = <&uart2_default>;
    pinctrl-1 = <&uart2_sleep>;
    pinctrl-names = "default","sleep";
    status = "okay";
};
 
&pinctrl {
    uart2_default: uart2_default {
      group1 {
        psels = <NRF_PSEL(UART_TX, 0, 22)>;
      };
      group2 {
        psels = <NRF_PSEL(UART_RX, 0, 23)>;
        bias-pull-up;
      };
    };
    uart2_sleep: uart2_sleep {
        group1 {
            psels = <NRF_PSEL(UART_TX, 0, 22)>,
                <NRF_PSEL(UART_RX, 0, 23)>;
            low-power-enable;
        };
      };
  

Project Config File:

CONFIG_GPIO=y
CONFIG_LOG=y
CONFIG_NRF_MODEM_LIB=y
CONFIG_UART_CONSOLE=y
CONFIG_CONSOLE=y

# General config
CONFIG_EVENTS=y
CONFIG_PICOLIBC_IO_FLOAT=y
CONFIG_RESET_ON_FATAL_ERROR=y
CONFIG_NCS_SAMPLES_DEFAULTS=y
CONFIG_DK_LIBRARY=y

# LED indication
CONFIG_LED=y

CONFIG_RING_BUFFER=y

# AT commands interface
CONFIG_UART_INTERRUPT_DRIVEN=n
CONFIG_AT_HOST_LIBRARY=y
CONFIG_UART_0_ASYNC=n
CONFIG_UART_0_INTERRUPT_DRIVEN=n

#OSDP library
CONFIG_UART_ASYNC_API=y
CONFIG_UART_2_INTERRUPT_DRIVEN=n
CONFIG_UART_2_ASYNC=y
CONFIG_UART_2_NRF_HW_ASYNC=y
CONFIG_UART_2_NRF_HW_ASYNC_TIMER=2

CONFIG_LOG_MODE_DEFERRED=y

Device Output:

[00:00:00.259,094] <inf> Reader_test: Reader provisioning started.
[00:00:00.259,429] <dbg> Reader_test: cmd_function: Full CRC: 3252
[00:00:00.259,460] <inf> Reader_test: Waiting for reader event...
[00:00:03.867,797] <inf> Reader_test: Pin ISR called
[00:00:03.867,858] <inf> Reader_test: Reader event triggered.
[00:00:03.867,889] <inf> Reader_test: Bytes written: 32
[00:00:03.867,889] <inf> Reader_test: Bytes Claimed (0+ = valid):32
[00:00:03.867,950] <inf> Reader_test: New Buf
[00:00:03.867,980] <inf> Reader_test: Looping...
[00:00:03.901,611] <inf> Reader_test: TX complete
[00:00:03.901,641] <inf> Reader_test: Bytes Claimed (0+ = valid):0
[00:00:04.906,311] <inf> Reader_test: RX stopped
[00:00:04.906,402] <inf> Reader_test: RX stopped
[00:00:04.907,409] <inf> Reader_test: RX stopped
[00:00:04.907,531] <inf> Reader_test: RX stopped
[00:00:04.908,508] <inf> Reader_test: RX stopped
[00:00:04.908,630] <inf> Reader_test: RX stopped
[00:00:04.909,637] <inf> Reader_test: RX stopped
[00:00:04.909,729] <inf> Reader_test: RX stopped
[00:00:04.910,736] <inf> Reader_test: RX stopped
[00:00:04.910,827] <inf> Reader_test: RX stopped
[00:00:04.911,834] <inf> Reader_test: RX stopped
[00:00:04.911,956] <inf> Reader_test: RX stopped
[00:00:04.912,231] <inf> Reader_test: DATA RECEIVED FROM RX... 1 Bytes
[00:00:04.912,261] <inf> Reader_test: RX released
[00:00:04.912,292] <inf> Reader_test: RX released
[00:00:04.912,322] <inf> Reader_test: RX Disabled

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log.h>
#include <string.h>
//#include <zephyr/mgmt/osdp.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>



LOG_MODULE_REGISTER(Reader_test, LOG_LEVEL_DBG);


#define UART_BUF_SIZE		256
#define UART_TX_TIMEOUT_MS	100
#define UART_RX_TIMEOUT_MS	1000000

#define UART_TX_BUF_SIZE  		32
#define UART_RX_MSG_QUEUE_SIZE	8

#define OSDP_UART               DT_NODELABEL(uart2)
#define READER_EVENT			BIT(1)

static const struct device *uart = DEVICE_DT_GET(OSDP_UART);  // Adjust alias as needed

static uint8_t tx_buf[UART_TX_BUF_SIZE];


K_SEM_DEFINE(tx_done, 1, 1);
K_SEM_DEFINE(rx_disabled, 0, 1);


struct uart_msg_queue_item {
	uint8_t bytes[UART_BUF_SIZE];
	uint32_t length;
};

// UART TX fifo
RING_BUF_DECLARE(app_tx_fifo, UART_TX_BUF_SIZE);
volatile int bytes_claimed;

// UART RX primary buffers
uint8_t uart_double_buffer[2][UART_BUF_SIZE];
uint8_t *uart_buf_next = uart_double_buffer[1];

// UART RX message queue
K_MSGQ_DEFINE(uart_rx_msgq, sizeof(struct uart_msg_queue_item), UART_RX_MSG_QUEUE_SIZE, 4);


static K_EVENT_DEFINE(reader_event);


/* Helper functions for pending on pendable events. */
bool await_reader_event(k_timeout_t timeout)
{
	return k_event_wait(&reader_event, READER_EVENT, true, timeout) != 0;
}


// All changes to pins needs to be reflected in device tree overlay. In DT they are all set to low. Here we will set them to the correct state.
 struct gpio_dt_spec tamper_pin;
 struct gpio_dt_spec tamper_pin =  {
     .port = DEVICE_DT_GET(DT_NODELABEL(gpio0)),
     .pin = 30, // 25 for DEVKIT - 30 of SB6
     .dt_flags = GPIO_OUTPUT_LOW
};

 struct gpio_dt_spec reset_pin;
 struct gpio_dt_spec reset_pin =  {
     .port = DEVICE_DT_GET(DT_NODELABEL(gpio0)),
     .pin = 29, // 28 for DEVKIT - 29 of SB6
     .dt_flags = GPIO_OUTPUT_HIGH 
};

 struct gpio_dt_spec interupt_pin;
 struct gpio_dt_spec interupt_pin =  {
     .port = DEVICE_DT_GET(DT_NODELABEL(gpio0)),
     .pin = 28, // 24 for DEVKIT - 28 of SB6
     .dt_flags = GPIO_INPUT | GPIO_INT_EDGE_RISING
};


void reader_init(void)
{

    
    if (!gpio_is_ready_dt(&tamper_pin)) {
        LOG_ERR("Reader pins are not ready");
	}

    if (!gpio_is_ready_dt(&reset_pin)) {
        LOG_ERR("Reader pins are not ready");
	}

    if (!gpio_is_ready_dt(&interupt_pin)) {
        LOG_ERR("Reader pins are not ready");
	}
}


/**
 * @brief This is the pin interrupt function that is tied to the interrupt pin. 
 * For the reader there are two key functions, this one highlight the interrupt pin. This allows commuincation to the reader.
 * There is a sequence to initiate the OSDP handshake(TX pin). 
 * If the device is reset the handshake will need to start again. 
 */

static struct gpio_callback interrupt_pin_callback;

void pin_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    // When the interrupt pin on the reader is triggered, this function will be called.
    // This function will send the data to the reader using the UART Interrupt API
    LOG_INF("Pin ISR called");

	k_event_post(&reader_event, READER_EVENT);
    // NOT HERE .. uart_rx_enable(uart, rx_buf, sizeof rx_buf, RECEIVE_TIMEOUT);

}



// Function to initiate the interrupt pin
void config_interupt(void) {

    int err;
    gpio_pin_interrupt_configure_dt(&interupt_pin,	GPIO_INT_EDGE_RISING);

    gpio_init_callback(&interrupt_pin_callback, pin_isr, BIT(interupt_pin.pin));

    err = gpio_add_callback(interupt_pin.port, &interrupt_pin_callback);
    if (err < 0){
        printk("Callback is not ready\n");
    return;
    }
}



static uint16_t nCrcTblValid = 0; // preset: CRC Table not initialized
static uint16_t cCrcTable[256]; // CRC table – working copy
/* Used for the CRC function in the cmd_function */
const uint16_t CrcTable[256] =
{ 
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};


// generate the table for POLY == 0x1012
int fCrcTblInit( uint16_t *pTbl)
{ 
    int ii, jj;
    uint16_t ww;

    for (ii = 0; ii < 256; ii++) 
    {
        ww = (uint16_t)(ii << 8);
        for (jj = 0; jj < 8; jj++) 
        {
            if ( ww & 0x8000 ) 
            {
            ww = (ww << 1) ^ 0x1021;
            } 
            else
            {
            ww = (ww << 1);
            } 
        }    
    pTbl[ii] = ww;
    } 
    return 1;
} 


// table based CRC – this is the "direct table" mode - 
uint16_t fCrcBlk( uint8_t *pData, uint16_t nLength)
{ 
    uint16_t nCrc;
    int ii;

    if ( nCrcTblValid == 0 ) 
    {
    nCrcTblValid = fCrcTblInit(&cCrcTable[0]);
        } 
        for ( ii = 0, nCrc = 0x1D0F; ii < nLength; ii++ ) 
        {
            nCrc = (nCrc<<8) ^ cCrcTable[ ((nCrc>>8) ^ pData[ii]) & 0xFF];
            } 
    return nCrc;
}


void  cmd_function(uint8_t *arr, size_t len)
{   
    arr[2] = len;

    memset(&tx_buf, 0, sizeof(tx_buf));

    uint16_t full_crc = fCrcBlk(arr,(len -2));

    uint8_t msb = full_crc >> 8;
    uint8_t lsb = (uint8_t) full_crc; 
    LOG_DBG("Full CRC: %04x", full_crc);
    
    arr[(len-2)] = lsb;
    arr[(len-1)] = msb;

    memcpy(tx_buf,arr,len);

}


void osdp_init_1(void)
{
    uint8_t SCS11_tx_array[19] = 
    {
    0x53, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x11, 0x00, 0x76, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0x00, 0x00
    };

    //k_busy_wait(1000);

    cmd_function(SCS11_tx_array,sizeof(SCS11_tx_array));
}

static int uart_tx_get_from_queue(void)
{
	uint8_t *data_ptr;
	// Try to claim any available bytes in the FIFO
	bytes_claimed = ring_buf_get_claim(&app_tx_fifo, &data_ptr, UART_TX_BUF_SIZE);

    LOG_INF("Bytes Claimed (0+ = valid):%d", bytes_claimed);

	if(bytes_claimed > 0) {
		// Start a UART transmission based on the number of available bytes
		uart_tx(uart, data_ptr, bytes_claimed, SYS_FOREVER_MS);
	}
	return bytes_claimed;
}




void app_uart_async_callback(const struct device *uart_dev, struct uart_event *evt, void *user_data)
{
    int ret;
	static struct uart_msg_queue_item new_message;

	switch (evt->type) {
		case UART_TX_DONE:
            LOG_INF("TX complete");

            //uart_rx_enable(uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);

			// Free up the written bytes in the TX FIFO
			ret = ring_buf_get_finish(&app_tx_fifo, bytes_claimed);

			// If there is more data in the TX fifo, start the transmission
			if(uart_tx_get_from_queue() == 0) {
				// Or release the semaphore if the TX fifo is empty
				k_sem_give(&tx_done);
			}

			break;
		
		case UART_RX_RDY:
            LOG_INF("DATA RECEIVED FROM RX... %d Bytes", evt->data.rx.len);

			memcpy(new_message.bytes, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len);
			new_message.length = evt->data.rx.len;
			if(k_msgq_put(&uart_rx_msgq, &new_message, K_NO_WAIT) != 0){
				printk("Error: Uart RX message queue full!\n");
			}
			break;
		
		case UART_RX_BUF_REQUEST:
            LOG_INF("New Buf");

			uart_rx_buf_rsp(uart, uart_buf_next, UART_BUF_SIZE);
            break;

		case UART_RX_BUF_RELEASED:
            LOG_INF("RX released");

			uart_buf_next = evt->data.rx_buf.buf;
			break;

		case UART_RX_DISABLED:
            LOG_INF("RX Disabled");

			k_sem_give(&rx_disabled);
			break;

		case UART_RX_STOPPED:
            LOG_INF("RX stopped");

			break;
		default:
			break;
	}
}

// Function to send UART data, by writing it to a ring buffer (FIFO) in the application
// WARNING: This function is not thread safe! If you want to call this function from multiple threads a semaphore should be used
static int app_uart_send(const uint8_t * data_ptr, uint32_t data_len)
{
	while(1) {
		// Try to move the data into the TX ring buffer
		uint32_t written_to_buf = ring_buf_put(&app_tx_fifo, data_ptr, data_len);
		data_len -= written_to_buf;
		
        LOG_INF("Bytes written: %d", written_to_buf);

		// In case the UART TX is idle, start transmission

		if(k_sem_take(&tx_done, K_NO_WAIT) == 0) {
			uart_tx_get_from_queue();
		}	
		
		// In case all the data was written, exit the loop
		if(data_len == 0) break;
        LOG_INF("Buffer overflow");

		// In case some data is still to be written, sleep for some time and run the loop one more time
		k_msleep(10);
		data_ptr += written_to_buf;
	}

	return 0;
}



static void app_uart_init(void)
{
	uart_callback_set(uart, app_uart_async_callback, NULL);
	//uart_rx_enable(uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);
}


int main(void)
{    
    LOG_INF("Reader provisioning started.");

    // Initialize reader pins
    reader_init();

    // Initialize interrupt pin - This is that call from the reader that always starts communication
    config_interupt();

    if (!device_is_ready(uart)) {
	    LOG_ERR("UART device not ready");
	}
    app_uart_init();

    osdp_init_1();

    struct uart_msg_queue_item incoming_message;
    
    //uart_rx_enable(uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);


    while(1)
    {
        LOG_INF("Waiting for reader event...");
            
        (void)await_reader_event(K_FOREVER);

        LOG_INF("Reader event triggered.");
        
        app_uart_send(tx_buf, sizeof(tx_buf));

        uart_rx_enable(uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);

            while (1) 
            {
                LOG_INF("Looping...");
        
                // This function will not return until a new message is ready
                k_msgq_get(&uart_rx_msgq, &incoming_message, K_FOREVER);
                
                // Process the message here.
                static uint8_t string_buffer[UART_BUF_SIZE + 1];
                memcpy(string_buffer, incoming_message.bytes, incoming_message.length);
                string_buffer[incoming_message.length] = 0;
                printk("RX %i: %s\n", incoming_message.length, string_buffer);

            }
    }
        
            
return 0;
}   


Related