Custom UART0 and UART1 interrupt implementation with working terminal and programming?
Posted: Fri Jun 02, 2023 10:12 am
Hi,
I'm trying to switch my application UART0 implementation to a custom interrupt implementation and implement a custom UART1 interrupt soluation - the terminal and programming via UART should be kept working.
I currently tried following:
The UART0/UART1 custom Interrupt implementation:
The interrupt handling seemed to work, but the UART0 console only prints "?????????" and the UART0-Programming via Command line doesnt work anymore.
The UART0/UART1 data Queue handling:
I'm not sure what to do, so that the console prints actual text in Visual Studio Code, and the MCU resetting to bootloader works on UART0 Programming, like normal when you output to stdout with
vprintf_DRIVER
that you give to:
esp_log_set_vprintf(&vprintf_DRIVER);
I need the custom interrupt implementation for different reasons. the UART1 implementation of the eBus-Bus has timing restrictions, and needs to send certain responses on UART input Bytes very fast;
The UART0 Terminal implementation I want to extend with a second switchable usage case of something else (a custom Visualization interface that is not the standard terminal)
I could just switch back to normal Usage over specific handshakes to use normal Terminal and Programming again.
I'm trying to switch my application UART0 implementation to a custom interrupt implementation and implement a custom UART1 interrupt soluation - the terminal and programming via UART should be kept working.
I currently tried following:
The UART0/UART1 custom Interrupt implementation:
Code: Select all
#include "uartTransfer.h"
#include "main.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_intr_alloc.h"
#include "driver/uart_select.h"
#include <stdint.h>
#include "hal/uart_ll.h"
#include "hal/uart_hal.h"
#include "esp_private/critical_section.h"
#include "FreeRTOSConfig.h"
#include "include_all.h"
#include "../APPL/usercode.h"
#include "uarts.h"
#include "o_uart_isrHandler.h"
#include "stdarg.h"
#include "stdio.h"
// from uart.c ESP-IDF
#define UART_EMPTY_THRESH_MY (2)
#define UART_FULL_THRESH_MY (30)
#define UART_TOUT_THRESH_MY (2)
#define UART_CLKDIV_FRAG_BIT_WIDTH (3)
#define UART_TX_IDLE_NUM_DEFAULT (0)
#define UART_PATTERN_DET_QLEN_DEFAULT (1)
#define UART_MIN_WAKEUP_THRESH (UART_LL_MIN_WAKEUP_THRESH)
#define UART_INTR_CONFIG_FLAG ((UART_INTR_RXFIFO_FULL) \
| (UART_INTR_RXFIFO_TOUT) \
| (UART_INTR_RXFIFO_OVF) \
| (UART_INTR_BRK_DET) \
| (UART_INTR_PARITY_ERR))
// _from uart.c ESP-IDF
#define BUF_SIZE (SIZE_OF_TRANSMIT_UART_BUFFER)
static QueueHandle_t uart0_queue;
void IRAM_ATTR uart_intr_handle(void *arg);
vprintf_like_t esp_log_set_vprintf(vprintf_like_t func);
void MyLog(char* toLog)
{
short length = strlen(toLog);
UART_WriteDataToBuffer((unsigned char*)toLog, length, (short)UART_NUM_0);
}
int _log_vprintf(const char* format, ...)
{
char* result = NULL;
size_t maxBufSize = 300;
char* buf[301];
if (format != NULL)
{
va_list arg_list;
int length;
va_start(arg_list, format);
length = vsnprintf(buf, maxBufSize, format, arg_list);
va_end(arg_list);
if (length > 0)
{
result = (char*)malloc(length+1);
if (result != NULL)
{
va_start(arg_list, format);
if (vsnprintf(result, length+1, format, arg_list) < 0)
{
free(result);
result = NULL;
MyLog("Failure: vsnprintf formatting failed.");
}
va_end(arg_list);
}
else
{
free(result);
result = NULL;
MyLog("Failure: allocation sprintf value failed.");
}
}
else if (length == 0)
{
result = NULL;
}
else
{
result = NULL;
MyLog("Failure: vsnprintf return 0 length");
}
}
else
{
MyLog("Failure: invalid argument.");
result = NULL;
}
if(result!=NULL)
{
MyLog(result);
}
return 0;
}
void uartESPInitUARTAndInterrupt_int(void);
void uartEBUSInitUARTAndInterrupt(void);
void IRAM_ATTR uartESPInitUARTAndInterrupt(void)
{
uartESPInitUARTAndInterrupt_int();
esp_log_set_vprintf(&_log_vprintf);
uartEBUSInitUARTAndInterrupt();
}
void IRAM_ATTR uartESPInitUARTAndInterrupt_int(void)
{
const uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
uart_param_config(UART_NUM_0, &uart_config);
//Set UART log level
esp_log_level_set(TAG_Main, ESP_LOG_INFO);
//Set UART pins (using UART0 default pins ie no changes.)
//uart_set_pin(UART_NUM_0, GPIO_NUM_1 , GPIO_NUM_3 , UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
//Install UART driver, and get the queue.
uart_driver_install(UART_NUM_0, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
InitUARTStruct(UART_NUM_0);
// changed uart.c from ESP-IDF - see line 1628 in esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int event_queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags)
/*
ret = esp_intr_alloc(uart_periph_signal[uart_num].irq, intr_alloc_flags,
uart_rx_intr_handler_default, p_uart_obj[uart_num],
&p_uart_obj[uart_num]->intr_handle);
ESP_GOTO_ON_ERROR(ret, err, UART_TAG, "Could not allocate an interrupt for UART");
ret = uart_intr_config(uart_num, &uart_intr);
ESP_GOTO_ON_ERROR(ret, err, UART_TAG, "Could not configure the interrupt for UART");
*/
// OWN ISR
// release the pre registered UART handler/subroutine
static unsigned char uartnum = UART_NUM_0;
// register new UART subroutine
ESP_ERROR_CHECK(esp_intr_alloc(ETS_UART0_INTR_SOURCE, NULL, uart_intr_handle, (void*)(&uartnum), NULL));
uart_intr_config_t uart_intr = {
.intr_enable_mask = UART_INTR_CONFIG_FLAG,
.rxfifo_full_thresh = UART_FULL_THRESH_MY,
.rx_timeout_thresh = UART_TOUT_THRESH_MY, // the timeout Threshold after which TOUT Int arrives - std 10 TBD
.txfifo_empty_intr_thresh = UART_EMPTY_THRESH_MY,
};
ESP_ERROR_CHECK(uart_intr_config(UART_NUM_0, &uart_intr));
// enable RX interrupt
ESP_ERROR_CHECK(uart_enable_rx_intr(UART_NUM_0));
}
// ____The Interrupt initialisation_____
void IRAM_ATTR uartEBUSInitUARTAndInterrupt(void)
{
const uart_config_t uart_config = {
.baud_rate = 2400,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
uart_param_config(EX_UART_NUM, &uart_config);
uart_set_line_inverse(EX_UART_NUM, UART_SIGNAL_IRDA_TX_INV /*| UART_SIGNAL_RXD_INV*/ );
//Set UART log level
esp_log_level_set(TAG_Main, ESP_LOG_INFO);
//Set UART pins (using UART0 default pins ie no changes.)
uart_set_pin(EX_UART_NUM, GPIO_NUM_33 , GPIO_NUM_2 , UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
//Install UART driver, and get the queue.
uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
InitUARTStruct(EX_UART_NUM);
UART_InitEWBus(eBusUART, 2400, 400L); // eBus UART1
static unsigned char uartnum = EX_UART_NUM;
// register new UART subroutine
ESP_ERROR_CHECK(esp_intr_alloc(ETS_UART1_INTR_SOURCE, NULL, uart_intr_handle, (void*)(&uartnum), NULL));
uart_intr_config_t uart_intr = {
.intr_enable_mask = UART_INTR_CONFIG_FLAG,
.rxfifo_full_thresh = UART_FULL_THRESH_MY,
.rx_timeout_thresh = UART_TOUT_THRESH_MY, // the timeout Threshold after which TOUT Int arrives - std 10 TBD
.txfifo_empty_intr_thresh = UART_EMPTY_THRESH_MY,
};
ESP_ERROR_CHECK(uart_intr_config(EX_UART_NUM, &uart_intr));
// enable RX interrupt
ESP_ERROR_CHECK(uart_enable_rx_intr(EX_UART_NUM));
}
// from uart.c ESP-IDF
#define UART_ENTER_CRITICAL_SAFE(spinlock) esp_os_enter_critical_safe(spinlock)
#define UART_EXIT_CRITICAL_SAFE(spinlock) esp_os_exit_critical_safe(spinlock)
#define UART_ENTER_CRITICAL_ISR(spinlock) esp_os_enter_critical_isr(spinlock)
#define UART_EXIT_CRITICAL_ISR(spinlock) esp_os_exit_critical_isr(spinlock)
#define UART_ENTER_CRITICAL(spinlock) esp_os_enter_critical(spinlock)
#define UART_EXIT_CRITICAL(spinlock) esp_os_exit_critical(spinlock)
#define UART_CONTEX_INIT_DEF(uart_num) {\
.hal.dev = UART_LL_GET_HW(uart_num),\
INIT_CRIT_SECTION_LOCK_IN_STRUCT(spinlock)\
.hw_enabled = false,\
}
typedef struct {
uart_hal_context_t hal; // UART hal context
DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(spinlock)
bool hw_enabled;
} uart_context_t;
static uart_context_t uart_context[UART_NUM_MAX] = {
UART_CONTEX_INIT_DEF(UART_NUM_0),
UART_CONTEX_INIT_DEF(UART_NUM_1),
#if UART_NUM_MAX > 2
UART_CONTEX_INIT_DEF(UART_NUM_2),
#endif
};
#define UART_IS_MODE_SET(uart_number, mode) (1)
// _from uart.c ESP-IDF
#define UART_ISR_ATTR
#define BREAK_LENGTH 1
static uint32_t UART_ISR_ATTR uart_tx_write_fifo(uart_port_t uart_num, const uint8_t *pbuf, uint32_t len)
{
uint32_t sent_len = 0;
UART_ENTER_CRITICAL_SAFE(&(uart_context[uart_num].spinlock));
uart_hal_write_txfifo(&(uart_context[uart_num].hal), pbuf, len, &sent_len);
UART_EXIT_CRITICAL_SAFE(&(uart_context[uart_num].spinlock));
return sent_len;
}
static unsigned char uart_get_tx_write_fifo_empty(uart_port_t uart_num)
{
unsigned char result = 0;
UART_ENTER_CRITICAL_SAFE(&(uart_context[uart_num].spinlock));
if(uart_ll_get_txfifo_len(&(uart_context[uart_num].hal)) == UART_LL_FIFO_DEF_LEN)
result = 1;
UART_EXIT_CRITICAL_SAFE(&(uart_context[uart_num].spinlock));
return result;
}
// The actual Interrupt handling
unsigned char curRX_Char[D_UART_NUMBER_OF_MODULES] = {0};
short curTX_Char[D_UART_NUMBER_OF_MODULES] = {0};
void IRAM_ATTR putuart(short md, unsigned char b) // in o_uart_isrHandler.c
{
curTX_Char[md] = b;
while( uart_get_tx_write_fifo_empty(md)==0 ){}
}
uint32_t send_len;
uint32_t uart_intr_status = 0;
//unsigned char break_flag = 0;
/*
* Define UART interrupt subroutine to ackowledge interrupt
*/
// uart.c static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param)
void IRAM_ATTR uart_intr_MyTXHandling(short uart_num)
{
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_disable_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY);
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY);
//SENDING
if (UART_GetTransmitDataSize(uart_num)!=0)
{
UART_ProceedDataFromBuffer(uart_num);
// To fill the TX FIFO.
if(curTX_Char[uart_num]!=-1)
{
send_len = uart_tx_write_fifo(uart_num, (const uint8_t *) &curTX_Char[uart_num], 1);
}
if(send_len)
{
curTX_Char[uart_num] = -1;
}
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY);
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY);
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
}
else
{
UART_ProceedDataFromBuffer(uart_num);
}
}
void IRAM_ATTR uart_intr_handle(void *arg)
{
unsigned char* p_uart = (unsigned char*) arg;
uart_port_t uart_num = (uart_port_t)(*p_uart);
if((uart_num==UART_NUM_0)||(uart_num==EX_UART_NUM))
{
// The `continue statement` may cause the interrupt to loop infinitely
// we exit the interrupt here
uart_intr_status = uart_hal_get_intsts_mask(&(uart_context[uart_num].hal));
if ((uart_intr_status & UART_INTR_RXFIFO_TOUT)
|| (uart_intr_status & UART_INTR_RXFIFO_FULL)
)
{
// RECEIVING
uint32_t rx_fifo_len = uart_hal_get_rxfifo_len(&(uart_context[uart_num].hal));
if(rx_fifo_len>1)
rx_fifo_len = 1;
uart_hal_read_rxfifo(&(uart_context[uart_num].hal), (unsigned char*)&curRX_Char[uart_num], &rx_fifo_len);
UART_PutReceivedDataIntoBuffer(curRX_Char[uart_num], uart_num);
uart_clear_intr_status(uart_num, UART_INTR_RXFIFO_FULL|UART_INTR_RXFIFO_TOUT );
}
else if (uart_intr_status & UART_INTR_TXFIFO_EMPTY)
{
uart_intr_MyTXHandling(uart_num);
}
else if ( uart_intr_status & UART_INTR_CMD_CHAR_DET
)
{
uart_clear_intr_status(uart_num, UART_INTR_CMD_CHAR_DET );
}
else if (uart_intr_status & UART_INTR_RXFIFO_OVF)
{
// When fifo overflows, we reset the fifo.
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_RXFIFO_OVF);
}
else if (uart_intr_status & UART_INTR_BRK_DET)
{
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_BRK_DET);
}
else if (uart_intr_status & UART_INTR_FRAM_ERR)
{
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_FRAM_ERR);
}
else if (uart_intr_status & UART_INTR_PARITY_ERR)
{
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_PARITY_ERR);
}
else if (uart_intr_status & UART_INTR_TX_BRK_DONE)
{
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_disable_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_DONE);
/*
if (break_flag == 1) {
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY); // TBD prüfen
break_flag = 0;
}
*/
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_DONE);
}
else if (uart_intr_status & UART_INTR_TX_BRK_IDLE)
{
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_disable_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_IDLE);
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_IDLE);
}
else if (uart_intr_status & UART_INTR_CMD_CHAR_DET)
{
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_CMD_CHAR_DET);
}
else if (uart_intr_status & UART_INTR_TX_DONE)
{
// Workaround for RS485: If the RS485 half duplex mode is active
// and transmitter is in idle state then reset received buffer and reset RTS pin
// skip this behavior for other UART modes
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_disable_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
}
else
{
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), uart_intr_status); /*simply clear all other intr status*/
}
}
}
// The UART TX Interrupt enabler
void IRAM_ATTR enable_disable_UART_TX_Int(uart_port_t uart_num, unsigned char enable)
{
if(enable)
{
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY);
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TXFIFO_EMPTY);
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
}
else // disable
{
}
}
The interrupt handling seemed to work, but the UART0 console only prints "?????????" and the UART0-Programming via Command line doesnt work anymore.
The UART0/UART1 data Queue handling:
Code: Select all
unsigned char IRAM_ATTR UART_ProceedDataFromBuffer(short md)
{
UART_DATA_STRUCT *p = &g_uart[md];
if (p->TransmitReadIndex != p->TransmitWriteIndex)
{
if (p->TransmitReadIndex >= SIZE_OF_TRANSMIT_UART_BUFFER) {
p->TransmitReadIndex = 0;
}
putuart(md, p->Transmit[ p->TransmitReadIndex ]);
p->TransmitReadIndex++;
if (p->TransmitReadIndex >= SIZE_OF_TRANSMIT_UART_BUFFER) {
p->TransmitReadIndex = 0;
}
}
else
{
p->TransmitProceedFlag = FALSE;
enable_disable_UART_TX_Int(md,g_uart[md].TransmitProceedFlag);
}
return 0;
}
void IRAM_ATTR UART_PutReceivedDataIntoBuffer(unsigned char data, short md)
{
register UART_DATA_STRUCT *p = &g_uart[md];
...
p->Receive[p->ReceiveWriteIndex++] = data;
if (p->ReceiveWriteIndex >= SIZE_OF_RECEIVE_UART_BUFFER)
p->ReceiveWriteIndex = 0;
....
}
void IRAM_ATTR UART_WriteDataToBuffer(unsigned char *puc, short size, short md)
{
unsigned char c;
for (; size; size--)
{
g_uart[md].TransmitProceedFlag = TRUE;
c = *puc++;
g_uart[md].Transmit[ g_uart[md].TransmitWriteIndex++ ] = c;
if (g_uart[md].TransmitWriteIndex >= SIZE_OF_TRANSMIT_UART_BUFFER)
{
g_uart[md].TransmitWriteIndex = 0;
}
}
enable_disable_UART_TX_Int(md,g_uart[md].TransmitProceedFlag);
}
vprintf_DRIVER
that you give to:
esp_log_set_vprintf(&vprintf_DRIVER);
I need the custom interrupt implementation for different reasons. the UART1 implementation of the eBus-Bus has timing restrictions, and needs to send certain responses on UART input Bytes very fast;
The UART0 Terminal implementation I want to extend with a second switchable usage case of something else (a custom Visualization interface that is not the standard terminal)
I could just switch back to normal Usage over specific handshakes to use normal Terminal and Programming again.