Skip to content

Instantly share code, notes, and snippets.

@davidthings
Last active November 24, 2020 19:36
Show Gist options
  • Save davidthings/e06081ce465a0dfbd0df52074b88806f to your computer and use it in GitHub Desktop.
Save davidthings/e06081ce465a0dfbd0df52074b88806f to your computer and use it in GitHub Desktop.
Test Code Demonstrating UART Error with SPIRAM Buffers
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
#include <esp_pthread.h>
#include <esp_task_wdt.h>
//
// CONFIG
//
// https://gist.github.com/davidthings/e06081ce465a0dfbd0df52074b88806f
// CONFIG_ESP32_SPIRAM_SUPPORT Y
// CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=512
//
// TESTS
//
// These tests are designed to show a sequence of possible causes for data errors
// and to also show that the underlying UART HW is perfect!
//
// Test 0 - UART TX and RX, with no TX Buffer, and with no interferring (NULL) task
// Test 1 - as above but adding in the NULL task doing busy work with static memory at lower priority as UART task
// Test 2 - as above but adding adding TX Buffer
// Test 3 - as above but adding another task to gather and print out task info
// Test 4 - as above but NULL task uses SPIRAM - lots of errors
// Test 5 - as above but NULL task at lower rasther than higher priority than the base task - lots of errors
#define MESSAGE_LENGTH 2047
#define Test 11
#if ( Test == 0 )
// Test 0 is the base line
// - no null task
// - no UART TX Buffer
// - no task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE 0
#define TX_RX_TASK_PRIORITY 10
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 0
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY+1)
#define DO_TASK_STATS 0
// Result
// - zero errors
#endif
#if ( Test == 1 )
// Test 1 adds in the null task
// - null task
// - null task use static memory
// - no UART TX Buffer
// - no task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE 0
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY+1)
#define DO_TASK_STATS 0
// Result
// - zero errors
#endif
#if ( Test == 2 )
// Test 2 adds TX buffer
// - null task
// - null task use static memory
// - using UART TX Buffer
// - no task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY+1)
#define DO_TASK_STATS 0
// Result
// - zero errors
#endif
#if ( Test == 3 )
// Test 3 adds task stats
// - null task
// - null task use static memory
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY+1)
#define DO_TASK_STATS 1
// Result
// - zero errors
#endif
#if ( Test == 4 )
// Test 4 makes null task use heap memory at higher priority than the TX RX task
// - null task
// - null task use heap memory
// - null task at higher priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY+1)
#define DO_TASK_STATS 1
// Result
// - zero errors
#endif
#if ( Test == 5 )
// Test 5 makes null task use lower priority
// - null task
// - null task use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 1
// Result
// - so many errors
#endif
#if ( Test == 6 )
// Test 6 don't use heap memory
// - null task
// - null task do not use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 1
// Result
// - couple of errors at startup
#endif
#if ( Test == 7 )
// Test 7 try using heap in tx rx task
// - null task
// - null task do not use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 1
// Result
// - couple of errors (from stats?)
#endif
#if ( Test == 8 )
// Test 8 try using smaller null task alloc (smaller than SPIRAM threshold)
// - null task
// - null task do not use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 500
#define NULL_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 1
// Result
// - couple of errors (from stats?)
#endif
#if ( Test == 9 )
// Test 9 no stats again
// - null task
// - null task do not use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - no task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 500
#define NULL_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 0
// Result
// - couple of errors at startup
#endif
#if ( Test == 10 )
// Test 10 - task 5 (lots of errors) except not using UART
// - null task
// - null task use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 4000000
#define TX_RX_TASK 0
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 1
// Result
// - no errors
#endif
#if ( Test == 11 )
// Test 11 - task 5 (lots of errors) except slower baud rate.. .and hence no errors
// - null task
// - null task use heap memory
// - null task at lower priority
// - using UART TX Buffer
// - task stats
#define TX_RX_BAUD_RATE 3000000
#define TX_RX_TASK 1
#define TX_BUF_SIZE ( MESSAGE_LENGTH * 2 )
#define TX_RX_TASK_PRIORITY 8
#define TX_RX_TASK_MEMORY_ON_HEAP 0
#define NULL_TASK 1
#define NULL_BUF_SIZE 100000
#define NULL_TASK_MEMORY_ON_HEAP 1
#define NULL_TASK_PRIORITY (TX_RX_TASK_PRIORITY - 1)
#define DO_TASK_STATS 1
// Result
// - no errors
#endif
#define RX_BUF_SIZE (MESSAGE_LENGTH * 4)
#define TXD_PIN (GPIO_NUM_5)
#define RXD_PIN (GPIO_NUM_18)
int txSequence;
int rxSequence;
void init(void) {
const uart_config_t uart_config = {
.baud_rate = TX_RX_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 0,
.source_clk = UART_SCLK_APB,
};
uart_driver_install(UART_NUM_1, RX_BUF_SIZE, TX_BUF_SIZE, 0, NULL, 0);
uart_param_config(UART_NUM_1, &uart_config);
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
int sent;
#if ( TX_RX_TASK_MEMORY_ON_HEAP )
uint8_t *txBuffer;
uint8_t *rxBuffer;
#else
uint8_t txBuffer[MESSAGE_LENGTH];
uint8_t rxBuffer[RX_BUF_SIZE+1];
#endif
static void txrx_task(void *arg)
{
txSequence = 0;
sent = 0;
static const char *TXRX_TASK_TAG = "TXRX_TASK";
esp_log_level_set(TXRX_TASK_TAG, ESP_LOG_DEBUG);
#if ( TX_RX_TASK_MEMORY_ON_HEAP )
txBuffer = (uint8_t*) malloc(MESSAGE_LENGTH);
rxBuffer = (uint8_t*) malloc(RX_BUF_SIZE+1);
#endif
int rxErrors = 0;
rxSequence = 0;
while (1) {
for ( int i = 0; i < MESSAGE_LENGTH; i++ ) {
txBuffer[ i ] = txSequence;
txSequence = ( txSequence + 1 ) & 0xFF;
}
uart_write_bytes(UART_NUM_1, txBuffer, MESSAGE_LENGTH);
sent++;
vTaskDelay(10 / portTICK_PERIOD_MS);
while (1) {
const int rxBytes = uart_read_bytes(UART_NUM_1, rxBuffer, RX_BUF_SIZE, 0);
if (rxBytes > 0) {
// printf( "Read %d bytes d0 %3d (Errors %d)\n", rxBytes, (int)data[ 0 ], rxErrors );
for ( int i = 0; i < rxBytes; i++ ) {
if ( rxBuffer[i] != rxSequence ) {
rxErrors++;
printf(" %d: %d != %d (Total Errors %d)\n", i, (int)rxBuffer[i], (int)rxSequence, rxErrors );
//printf( "." );
}
rxSequence = ( rxBuffer[i] + 1 ) & 0xFF;
}
} else
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
#if ( TX_RX_TASK_MEMORY_ON_HEAP )
free( txBuffer );
free( rxBuffer );
#endif
}
static void mx_task(void *arg)
{
txSequence = 0;
static const char *TXRX_TASK_TAG = "TXRX_TASK";
esp_log_level_set(TXRX_TASK_TAG, ESP_LOG_DEBUG);
// uint8_t* txBuffer = (uint8_t*) malloc(MESSAGE_LENGTH);
int rxErrors = 0;
rxSequence = 0;
// uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
#if ( TX_RX_TASK_MEMORY_ON_HEAP )
txBuffer = (uint8_t*) malloc(MESSAGE_LENGTH);
#endif
while (1) {
for ( int i = 0; i < MESSAGE_LENGTH; i++ ) {
txBuffer[ i ] = txSequence;
txSequence = ( txSequence + 1 ) & 0xFF;
}
sent++;
vTaskDelay(10 / portTICK_PERIOD_MS);
// printf( "Read %d bytes d0 %3d (Errors %d)\n", rxBytes, (int)data[ 0 ], rxErrors );
for ( int i = 0; i < MESSAGE_LENGTH; i++ ) {
if ( txBuffer[i] != rxSequence ) {
rxErrors++;
printf(" %d: %d != %d (Total Errors %d)\n", i, (int)txBuffer[i], (int)rxSequence, rxErrors );
//printf( "." );
}
rxSequence = ( txBuffer[i] + 1 ) & 0xFF;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
#if ( TX_RX_TASK_MEMORY_ON_HEAP )
free( txBuffer );
#endif
}
#if ( NULL_TASK_MEMORY_ON_HEAP )
uint8_t* null_data;
#else
uint8_t null_data[ NULL_BUF_SIZE ];
#endif
static void null_task(void *arg)
{
static const char *NULL_TASK_TAG = "NULL_TASK";
esp_log_level_set(NULL_TASK_TAG, ESP_LOG_DEBUG);
while (1) {
//printf( "Null Task %d Start\n", NULL_BUF_SIZE );
for ( int i = 0; i < NULL_BUF_SIZE; i++ ) {
null_data[ i ] = null_data[ i ] + 1;
//if ( ( i % 1000) == 0 )
// printf( "Null Task ----------------------------------------------- %d\n", i );
}
//printf( "Null Task -----------------------------------------------\n" );
//printf( "Null Task %d End\n", NULL_BUF_SIZE );
vTaskDelay(10 / portTICK_PERIOD_MS );
}
}
static void stats_task(void *arg);
void app_main(void)
{
init();
printf( "\nTesting UART RX TX with SPIRAM Conflict Tests\n\n" );
printf( " Test %d\n", Test );
printf( " TX RX Baud Rate %d\n", TX_RX_BAUD_RATE );
printf( " TX RX Task (or ~MX Task) %d\n", TX_RX_TASK );
printf( " TX RX Task Priority %d\n", TX_RX_TASK_PRIORITY );
printf( " TX RX Memory on Heap %d\n", TX_RX_TASK_MEMORY_ON_HEAP );
printf( " UART TX buffer size %d\n", TX_BUF_SIZE );
printf( " Using Null Task %d\n", NULL_TASK );
#if ( NULL_TASK)
printf( " Null Task Memory Size %d\n", NULL_BUF_SIZE );
printf( " Null Task Memory On Heap %d\n", NULL_TASK_MEMORY_ON_HEAP );
printf( " Null Task Priority %d\n", NULL_TASK_PRIORITY );
#endif
printf( " Do Task Monitoring %d\n", DO_TASK_STATS );
printf( "\n" );
xTaskCreatePinnedToCore(stats_task, "stats", 4096, NULL, 3, NULL, tskNO_AFFINITY);
#if ( TX_RX_TASK )
xTaskCreate(txrx_task, "uart_txrx_task", 1024*2, NULL, TX_RX_TASK_PRIORITY, NULL);
#else
xTaskCreate(mx_task, "mx_task", 1024*2, NULL, TX_RX_TASK_PRIORITY, NULL);
#endif
#if ( NULL_TASK_MEMORY_ON_HEAP )
null_data = (uint8_t*) malloc(NULL_BUF_SIZE+1);
#endif
#if ( NULL_TASK )
xTaskCreate(null_task, "null_task", 1024*2, NULL, NULL_TASK_PRIORITY, NULL);
// xTaskCreate(null_task, "null_task", 1024*2, NULL, 4, NULL);
// xTaskCreate(null_task, "null_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
// xTaskCreate(null_task, "null_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
#endif
// txrx_task(0);
}
//
// CODE BELOW IS BOILER-PLATE TASK STATS REPORT
//
#define DO_TASK_STATS_PRIO 3
#define STATS_TICKS pdMS_TO_TICKS(10000)
#define ARRAY_SIZE_OFFSET 5 //Increase this if print_real_time_stats returns ESP_ERR_INVALID_SIZE
/**
* @brief Function to print the CPU usage of tasks over a given duration.
*
* This function will measure and print the CPU usage of tasks over a specified
* number of ticks (i.e. real time stats). This is implemented by simply calling
* uxTaskGetSystemState() twice separated by a delay, then calculating the
* differences of task run times before and after the delay.
*
* @note If any tasks are added or removed during the delay, the stats of
* those tasks will not be printed.
* @note This function should be called from a high priority task to minimize
* inaccuracies with delays.
* @note When running in dual core mode, each core will correspond to 50% of
* the run time.
*
* @param xTicksToWait Period of stats measurement
*
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Insufficient memory to allocated internal arrays
* - ESP_ERR_INVALID_SIZE Insufficient array size for uxTaskGetSystemState. Trying increasing ARRAY_SIZE_OFFSET
* - ESP_ERR_INVALID_STATE Delay duration too short
*/
static esp_err_t print_real_time_stats(TickType_t xTicksToWait)
{
TaskStatus_t *start_array = NULL, *end_array = NULL;
UBaseType_t start_array_size, end_array_size;
uint32_t start_run_time, end_run_time;
// esp_err_t ret;
//Allocate array to store current task states
start_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;
start_array = (TaskStatus_t *)malloc(sizeof(TaskStatus_t) * start_array_size);
if (start_array == NULL) {
return ESP_ERR_NO_MEM;
}
//Get current task states
start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time);
if (start_array_size == 0) {
return ESP_ERR_INVALID_SIZE;
}
vTaskDelay(xTicksToWait);
//Allocate array to store tasks states post delay
end_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;
end_array = (TaskStatus_t *)malloc(sizeof(TaskStatus_t) * end_array_size);
if (end_array == NULL) {
free( start_array );
return ESP_ERR_NO_MEM;
}
//Get post delay task states
end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time);
if (end_array_size == 0) {
free( start_array );
free( end_array );
return ESP_ERR_INVALID_SIZE;
}
//Calculate total_elapsed_time in units of run time stats clock period.
uint32_t total_elapsed_time = (end_run_time - start_run_time);
if (total_elapsed_time == 0) {
free( start_array );
free( end_array );
return ESP_ERR_INVALID_STATE;
}
printf("| Task | Pri | Run Time | Percentage\n");
//Match each task in start_array to those in the end_array
for (int i = 0; i < start_array_size; i++) {
int k = -1;
for (int j = 0; j < end_array_size; j++) {
if (start_array[i].xHandle == end_array[j].xHandle) {
k = j;
//Mark that task have been matched by overwriting their handles
start_array[i].xHandle = NULL;
end_array[j].xHandle = NULL;
break;
}
}
//Check if matching task found
if (k >= 0) {
uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter;
uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * portNUM_PROCESSORS);
printf("| %20s | %3d | %10d | %2d%%\n", start_array[i].pcTaskName, start_array[i].uxCurrentPriority, task_elapsed_time, percentage_time);
}
}
//Print unmatched tasks
for (int i = 0; i < start_array_size; i++) {
if (start_array[i].xHandle != NULL) {
printf("| %20s | Deleted\n", start_array[i].pcTaskName);
}
}
for (int i = 0; i < end_array_size; i++) {
if (end_array[i].xHandle != NULL) {
printf("| %20s | Created\n", end_array[i].pcTaskName);
}
}
free( start_array );
free( end_array );
return ESP_OK;
}
static void stats_task(void *arg)
{
//Print real time stats periodically
while (1) {
printf("\nMessages %d\n", sent);
#if ( DO_TASK_STATS)
if (print_real_time_stats(STATS_TICKS) == ESP_OK) {
} else {
printf("Error getting real time stats\n");
}
#endif
vTaskDelay(pdMS_TO_TICKS(10000));
// call the dogs off
esp_task_wdt_reset();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment