Search code examples
esp32uartfreertosesp-idf

RTOS Basics with TWAI and UART on ESP32


I am working on a project which involves receiving TWAI messages which are updated every 1ms and send them through UART.

As I do not have access to TWAI Transmitter at the moment, I am simulating incoming messages with a GPTimer running at 1ms and updating a global shared twai_message_t object.

Somehow, it is working, but I am not getting my data in the correct order, and maybe even missing some data. I wanted some help in understanding what is going on with my RTOS Tasks.

Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include "string.h"
#include <inttypes.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <freertos/queue.h>
#include "freertos/event_groups.h"
#include <driver/uart.h>
#include "driver/gptimer.h"
#include "driver/twai.h"
#include "esp_log.h"

/*Configuration Settings*/
enum { BUF_LEN = 20 };

/*Pins*/
// #define TXD_PIN (GPIO_NUM_17)
// #define RXD_PIN (GPIO_NUM_16)

#define TXD_PIN (GPIO_NUM_1)
#define RXD_PIN (GPIO_NUM_3)

/*Globals*/

#define UART_NUM UART_NUM_1
#define BUF_SIZE (1024)
#define TWAI_RX_TASK_PRIORITY (configMAX_PRIORITIES - 1)
#define UART_TX_TASK_PRIORITY (configMAX_PRIORITIES - 2)

static const char *Timer_TAG = "Timer";
static const char *TX_TASK_TAG = "TX_TASK";

static TaskHandle_t processing_task = NULL;
static SemaphoreHandle_t sem_done_reading = NULL;
static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
static portMUX_TYPE uartlock = portMUX_INITIALIZER_UNLOCKED;

gptimer_handle_t gptimer = NULL;

QueueHandle_t uart_tx_queue[2];           // Two buffers for UART transmit
volatile int active_buffer = 0;
static volatile uint8_t buf_overrun = 0;      // Double buffer overrun flag

TaskHandle_t uart_tx_task_handle;

twai_message_t rx_message = { .identifier = 1,
                                .data_length_code = 3,
                                .data = {0, 0, 0, 0, 0, 0, 0, 0}
                              };

// Message struct to wrap strings for queue
typedef struct CAN_Serial_Frame {
  uint8_t sof;
  uint32_t timestamp;
  uint8_t dlc;
  uint32_t msg_id;
  uint8_t payload[8];
  uint8_t eof;
}CAN_Serial_Frame; 

CAN_Serial_Frame CSF = {0xAA,100,8,1,{0, 0, 0, 0, 0, 0, 0, 0},0xBB};

/*Timer ISR*/
uint8_t update_flag = 1;
void IRAM_ATTR onTimer() {
  xQueueSend(uart_tx_queue[active_buffer], &rx_message, 0.001/portTICK_PERIOD_MS);
  static uint16_t idx = 0;
  
  BaseType_t task_woken = pdFALSE;
  portENTER_CRITICAL(&spinlock);
  if(update_flag){
    rx_message.data[0]++;
  }
  
  if(rx_message.data[0]>84){
    rx_message.data[0] = 0;
    // update_flag = 0;
  }
  active_buffer = 1 - active_buffer;
  portEXIT_CRITICAL(&spinlock);
  xTaskNotifyFromISR(uart_tx_task_handle,0,eNoAction,&task_woken);
  xTaskNotifyFromISR(processing_task,0,eNoAction,&task_woken);

  if (task_woken == pdTRUE) {
        portYIELD_FROM_ISR(task_woken);
  }
}

void Data_Update_Task(void *parameters){

  // Start a timer to run ISR every 1 ms
  // %%% We move this here so it runs in core 0
  ESP_LOGI(Timer_TAG, "Create timer handle");
  
  gptimer_config_t timer_config = {
      .clk_src = GPTIMER_CLK_SRC_DEFAULT,
      .direction = GPTIMER_COUNT_UP,
      .resolution_hz = 1000000, // 1MHz, 1 tick=1us
  };
  ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));

  gptimer_event_callbacks_t cbs = {
      .on_alarm = onTimer,
  };
  ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));

  ESP_LOGI(Timer_TAG, "Enable timer");
  ESP_ERROR_CHECK(gptimer_enable(gptimer));

  ESP_LOGI(Timer_TAG, "Start timer, auto-reload at alarm event");
  gptimer_alarm_config_t alarm_config = {
      .reload_count = 0,
      .alarm_count = 1000, // period = 1ms
      .flags.auto_reload_on_alarm = true,
  };

  ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
  ESP_ERROR_CHECK(gptimer_start(gptimer));

  while(1){

  }
}

/*UART*/
void uart_init() {
    uart_config_t uart_config = {
        .baud_rate = 921600,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };

    uart_param_config(UART_NUM, &uart_config);
    ESP_ERROR_CHECK( uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) );
    
    // Install the UART driver with a buffer size of 1024 bytes
    ESP_ERROR_CHECK( uart_driver_install(UART_NUM, 1024, 1024*4, 0, NULL, 0) );
    ESP_LOGI(TX_TASK_TAG, "UART Driver Installed");
}

void uart_tx_task(void *parameters){
  uart_init();
  twai_message_t tx_message;

  while (1) {
    // Process all available TWAI messages in the active buffer
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    xQueueReceive(uart_tx_queue[active_buffer], &tx_message, portMAX_DELAY) ;

    // Process the TWAI message and send it over UART
    CSF.dlc = tx_message.data_length_code;
    CSF.msg_id = tx_message.identifier;
    for (int i = 0; i < tx_message.data_length_code && i < sizeof(CSF.payload); i++) {
        CSF.payload[i] = tx_message.data[i];
    }

    const int txBytes = uart_write_bytes(UART_NUM,(const char *)&CSF, sizeof(CSF));
    // ESP_LOGI(TX_TASK_TAG, "Wrote %d bytes", txBytes);
    ESP_LOGI(TX_TASK_TAG, "Data Sent: %d",CSF.payload[0]);
  }
}

void app_main(void){

  /*Create two queues for double buffering of UART transmit*/
  uart_tx_queue[0] = xQueueCreate(100, sizeof(twai_message_t));
  uart_tx_queue[1] = xQueueCreate(100, sizeof(twai_message_t));

  /*Initialize TWAI*/
  // twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TWAI_RX_PIN_CONFIG);
  // twai_init(&g_config);

  /*Create UART Transmit Task*/
  xTaskCreate(uart_tx_task, "uart_tx_task", 1024*5, NULL, UART_TX_TASK_PRIORITY, &uart_tx_task_handle);
  /*Create TWAI Receive Task*/
  xTaskCreate(Data_Update_Task, "twai_receive_task", 1024*5, NULL, TWAI_RX_TASK_PRIORITY, &processing_task);
  
}

Here is the output:

I (4854) TX_TASK: Data Sent: 74
I (4864) TX_TASK: Data Sent: 78
I (4864) TX_TASK: Data Sent: 82
I (4864) TX_TASK: Data Sent: 8
I (4874) TX_TASK: Data Sent: 12
I (4874) TX_TASK: Data Sent: 16
I (4874) TX_TASK: Data Sent: 20
I (4884) TX_TASK: Data Sent: 24
I (4884) TX_TASK: Data Sent: 1
I (4894) TX_TASK: Data Sent: 27
I (4894) TX_TASK: Data Sent: 31
I (4894) TX_TASK: Data Sent: 35
I (4904) TX_TASK: Data Sent: 39
I (4904) TX_TASK: Data Sent: 46
I (4904) TX_TASK: Data Sent: 50
I (4914) TX_TASK: Data Sent: 54
I (4914) TX_TASK: Data Sent: 58
I (4924) TX_TASK: Data Sent: 62
I (4924) TX_TASK: Data Sent: 43
I (4924) TX_TASK: Data Sent: 65
I (4934) TX_TASK: Data Sent: 69
I (4934) TX_TASK: Data Sent: 73
I (4944) TX_TASK: Data Sent: 77
I (4944) TX_TASK: Data Sent: 81
I (4944) TX_TASK: Data Sent: 3
I (4954) TX_TASK: Data Sent: 7
I (4954) TX_TASK: Data Sent: 11
I (4954) TX_TASK: Data Sent: 15
I (4964) TX_TASK: Data Sent: 0
I (4964) TX_TASK: Data Sent: 22
I (4974) TX_TASK: Data Sent: 26
I (4974) TX_TASK: Data Sent: 30
I (4974) TX_TASK: Data Sent: 34
I (4984) TX_TASK: Data Sent: 19
I (4984) TX_TASK: Data Sent: 41
I (4984) TX_TASK: Data Sent: 45
I (4994) TX_TASK: Data Sent: 49
I (4994) TX_TASK: Data Sent: 53
I (5004) TX_TASK: Data Sent: 57
I (5004) TX_TASK: Data Sent: 38
I (5004) TX_TASK: Data Sent: 64
I (5014) TX_TASK: Data Sent: 68
I (5014) TX_TASK: Data Sent: 72
I (5024) TX_TASK: Data Sent: 76
I (5024) TX_TASK: Data Sent: 80
I (5024) TX_TASK: Data Sent: 61
I (5034) TX_TASK: Data Sent: 83
I (5034) TX_TASK: Data Sent: 2
I (5034) TX_TASK: Data Sent: 6
I (5044) TX_TASK: Data Sent: 17
I (5044) TX_TASK: Data Sent: 21
I (5054) TX_TASK: Data Sent: 25
I (5054) TX_TASK: Data Sent: 29
I (5054) TX_TASK: Data Sent: 33
I (5064) TX_TASK: Data Sent: 55
I (5064) TX_TASK: Data Sent: 10
I (5064) TX_TASK: Data Sent: 14
I (5074) TX_TASK: Data Sent: 36
I (5074) TX_TASK: Data Sent: 40
I (5084) TX_TASK: Data Sent: 44
I (5084) TX_TASK: Data Sent: 59
I (5084) TX_TASK: Data Sent: 63
I (5094) TX_TASK: Data Sent: 67
I (5094) TX_TASK: Data Sent: 71
I (5104) TX_TASK: Data Sent: 8
I (5104) TX_TASK: Data Sent: 48
I (5104) TX_TASK: Data Sent: 52
I (5114) TX_TASK: Data Sent: 74
I (5114) TX_TASK: Data Sent: 78
I (5114) TX_TASK: Data Sent: 82
I (5124) TX_TASK: Data Sent: 1
I (5124) TX_TASK: Data Sent: 12
I (5134) TX_TASK: Data Sent: 16
I (5134) TX_TASK: Data Sent: 20
I (5134) TX_TASK: Data Sent: 24
I (5144) TX_TASK: Data Sent: 50
I (5144) TX_TASK: Data Sent: 5
I (5144) TX_TASK: Data Sent: 27
I (5154) TX_TASK: Data Sent: 31
I (5154) TX_TASK: Data Sent: 35
I (5164) TX_TASK: Data Sent: 39
I (5164) TX_TASK: Data Sent: 54
I (5164) TX_TASK: Data Sent: 58
I (5174) TX_TASK: Data Sent: 62
I (5174) TX_TASK: Data Sent: 66
I (5184) TX_TASK: Data Sent: 3
I (5184) TX_TASK: Data Sent: 43
I (5184) TX_TASK: Data Sent: 47
I (5194) TX_TASK: Data Sent: 69
I (5194) TX_TASK: Data Sent: 73
I (5194) TX_TASK: Data Sent: 77
I (5204) TX_TASK: Data Sent: 7
I (5204) TX_TASK: Data Sent: 11
I (5214) TX_TASK: Data Sent: 15
I (5214) TX_TASK: Data Sent: 19
I (5214) TX_TASK: Data Sent: 23
I (5224) TX_TASK: Data Sent: 45
I (5224) TX_TASK: Data Sent: 81
I (5224) TX_TASK: Data Sent: 0
I (5234) TX_TASK: Data Sent: 26
I (5234) TX_TASK: Data Sent: 30
I (5244) TX_TASK: Data Sent: 34
I (5244) TX_TASK: Data Sent: 49
I (5244) TX_TASK: Data Sent: 53
I (5254) TX_TASK: Data Sent: 57
I (5254) TX_TASK: Data Sent: 61
I (5264) TX_TASK: Data Sent: 65
I (5264) TX_TASK: Data Sent: 38
I (5264) TX_TASK: Data Sent: 42
I (5274) TX_TASK: Data Sent: 68
I (5274) TX_TASK: Data Sent: 72
I (5274) TX_TASK: Data Sent: 76
I (5284) TX_TASK: Data Sent: 80
I (5284) TX_TASK: Data Sent: 83
I (5294) TX_TASK: Data Sent: 2
I (5294) TX_TASK: Data Sent: 6
I (5294) TX_TASK: Data Sent: 10
I (5304) TX_TASK: Data Sent: 17
I (5304) TX_TASK: Data Sent: 21
I (5304) TX_TASK: Data Sent: 25
I (5314) TX_TASK: Data Sent: 29
I (5314) TX_TASK: Data Sent: 33
I (5324) TX_TASK: Data Sent: 37
I (5324) TX_TASK: Data Sent: 14
I (5324) TX_TASK: Data Sent: 40
I (5334) TX_TASK: Data Sent: 44
I (5334) TX_TASK: Data Sent: 48
I (5344) TX_TASK: Data Sent: 52
I (5344) TX_TASK:E (5344) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (5344) task_wdt:  - IDLE (CPU 0)
E (5344) task_wdt: Tasks currently running:
E (5344) task_wdt: CPU 0: twai_receive_ta
E (5344) task_wdt: CPU 1: uart_tx_task
E (5344) task_wdt: Print CPU 0 (current core) backtrace


Backtrace: 0x400D976B:0x3FFB0D50 0x400D98F2:0x3FFB0D70 0x40082AA9:0x3FFB0D90 0x400D5A51:0x3FFBAB70 0x40088F29:0x3FFBABC0
0x400d976b: task_wdt_timeout_handling at C:/Users/shauryachandra/esp/esp-idf/components/esp_system/task_wdt/task_wdt.c:461 (discriminator 3)

0x400d98f2: task_wdt_isr at C:/Users/shauryachandra/esp/esp-idf/components/esp_system/task_wdt/task_wdt.c:585

0x40082aa9: _xt_lowint1 at C:/Users/shauryachandra/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/xtensa_vectors.S:1240

0x400d5a51: Data_Update_Task at C:/Users/shauryachandra/Timer_UART_MultiCore/main/main.c:144 (discriminator 1)

0x40088f29: vPortTaskWrapper at C:/Users/shauryachandra/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162


 Data Sent: 59
I (5394) TX_TASK: Data Sent: 63
I (5394) TX_TASK: Data Sent: 67
I (5404) TX_TASK: Data Sent: 71
I (5404) TX_TASK: Data Sent: 75
I (5404) TX_TASK: Data Sent: 79
I (5414) TX_TASK: Data Sent: 56
I (5414) TX_TASK: Data Sent: 82
I (5424) TX_TASK: Data Sent: 1
I (5424) TX_TASK: Data Sent: 5
I (5424) TX_TASK: Data Sent: 9
I (5434) TX_TASK: Data Sent: 16
I (5434) TX_TASK: Data Sent: 20
I (5434) TX_TASK: Data Sent: 24
I (5444) TX_TASK: Data Sent: 28
I (5444) TX_TASK: Data Sent: 32
I (5454) TX_TASK: Data Sent: 13
I (5454) TX_TASK: Data Sent: 35
I (5454) TX_TASK: Data Sent: 39
I (5464) TX_TASK: Data Sent: 43
I (5464) TX_TASK: Data Sent: 47
I (5464) TX_TASK: Data Sent: 54
I (5474) TX_TASK: Data Sent: 58
I (5474) TX_TASK: Data Sent: 62
I (5484) TX_TASK: Data Sent: 66
I (5484) TX_TASK: Data Sent: 70
I (5484) TX_TASK: Data Sent: 74
I (5494) TX_TASK: Data Sent: 51
I (5494) TX_TASK: Data Sent: 77
I (5504) TX_TASK: Data Sent: 81
I (5504) TX_TASK: Data Sent: 0
I (5504) TX_TASK: Data Sent: 4
I (5514) TX_TASK: Data Sent: 11

After around every 5 seconds, the watchdog timer is also triggered as seen in the output log.

If someone can please explain what is happening, it will be really helpful.

Thank you!

I am expecting a sequential output of the messages from the queue and if not sequential then at least all data should be captured and sent through UART.


Solution

  • As HS2 and sawdust were pointing out Data_Update_Task() is probably causing the crashes.

    I (5344) TX_TASK:E (5344) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
    E (5344) task_wdt:  - IDLE (CPU 0)
    

    Your log clearly indicates that the Task Watch dog was triggered.

    This is by default set to 5s in your SDKconfig. You find this parameter under Component Configs->ESP System Settings->Task Watchdog timeoput period:

    enter image description here

    Here is how you should implement a task in FreeRTOS according to from the official FreeRTOS documentation)

    void vATaskFunction( void *pvParameters )
        {
            for( ;; )
            {
                /* Psudeo code showing a task waiting for an event 
                with a block time. If the event occurs, process it.  
                If the timeout expires before the event occurs, then 
                the system may be in an error state, so handle the
                error.  Here the pseudo code "WaitForEvent()" could 
                replaced with xQueueReceive(), ulTaskNotifyTake(), 
                xEventGroupWaitBits(), or any of the other FreeRTOS 
                communication and synchronisation primitives. */
                if( WaitForEvent( EventObject, TimeOut ) == pdPASS )
                {
                    -- Handle event here. --
                }
                else
                {
                    -- Clear errors, or take actions here. --
                }
            }
    
            /* As per the first code listing above. */
            vTaskDelete( NULL );
        }
     
    

    As HS2 suggested a reasonibly defined vTaskDelay() is a good start to solve your problem, allowing the Idle-Task to run, so that that the Task Watch Dog is not triggered.