diff --git a/Makefile b/Makefile index b6ee4e3d..c92101bb 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,7 @@ $(wildcard $(FREERTOS_PATH)/*.c) \ $(FREERTOS_PATH)/portable/GCC/ARM_CM4F/port.c \ $(wildcard common/Src/*.c) \ $(wildcard driver/Src/*.c) \ +$(wildcard task/Src/*.c) \ $(filter-out $(addprefix bsp/Src/,$(addsuffix .c,$(BSP_DISABLE))),$(wildcard bsp/Src/*.c)) # ASM sources @@ -180,6 +181,7 @@ $(FREERTOS_PATH)/include \ $(FREERTOS_PATH)/portable/GCC/ARM_CM4F \ common/Inc \ driver/Inc \ +task/Inc \ bsp/Inc C_INCLUDES := $(addprefix -I,$(C_INCLUDES)) @@ -187,7 +189,17 @@ C_INCLUDES := $(addprefix -I,$(C_INCLUDES)) # compile gcc flags ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -Werror -Wfatal-errors -fdata-sections -ffunction-sections -CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -Werror -Wfatal-errors -fdata-sections -ffunction-sections +CFLAGS += \ +$(MCU) \ +$(C_DEFS) \ +$(C_INCLUDES) \ +$(OPT) \ +-Wall \ +-Werror \ +-Wfatal-errors \ +-fdata-sections \ +-ffunction-sections \ +-ffile-prefix-map=$(MAKEFILE_DIR)= ifeq ($(DEBUG), 1) CFLAGS += -g -gdwarf-2 diff --git a/bsp/Src/UART.c b/bsp/Src/UART.c index 0b96acf2..7dddda31 100644 --- a/bsp/Src/UART.c +++ b/bsp/Src/UART.c @@ -1,6 +1,9 @@ #include "UART.h" #include +static int a = 0; +static int b = 0; + // Define the size of the data to be transmitted // Currently not used, as we send uint8_t directly // may need to be configured for support for packets less more than 8 bits @@ -227,7 +230,7 @@ __weak void HAL_UART_MspGPIOInit(UART_HandleTypeDef *huart){ init.Pin = GPIO_PIN_2; - HAL_GPIO_Init(GPIOD, &init); + HAL_GPIO_Init(GPIOD, &init); } #endif /* UART5 */ @@ -502,7 +505,7 @@ uart_status_t uart_init(UART_HandleTypeDef* handle) { if (HAL_UART_Init(handle) != HAL_OK || !IS_UART_INSTANCE(handle->Instance) || !IS_USART_INSTANCE(handle->Instance)){ - return UART_ERR; + return UART_ERR; } // Start reception @@ -531,36 +534,36 @@ uart_status_t uart_deinit(UART_HandleTypeDef* handle) { // Deinitialize Handler #ifdef UART4 if (handle->Instance == UART4) { - uart4_tx_queue = NULL; - uart4_rx_queue = NULL; + uart4_tx_queue = NULL; + uart4_rx_queue = NULL; } #endif /* UART4 */ #ifdef UART5 if (handle->Instance == UART5) { - uart5_tx_queue = NULL; - uart5_rx_queue = NULL; + uart5_tx_queue = NULL; + uart5_rx_queue = NULL; } #endif /* UART5 */ #ifdef USART1 if (handle->Instance == USART1) { - usart1_tx_queue = NULL; - usart1_rx_queue = NULL; + usart1_tx_queue = NULL; + usart1_rx_queue = NULL; } #endif /* USART1 */ #ifdef USART2 if (handle->Instance == USART2) { - usart2_tx_queue = NULL; - usart2_rx_queue = NULL; + usart2_tx_queue = NULL; + usart2_rx_queue = NULL; } #endif /* USART2 */ #ifdef USART3 if (handle->Instance == USART3) { - usart3_tx_queue = NULL; - usart3_rx_queue = NULL; + usart3_tx_queue = NULL; + usart3_rx_queue = NULL; } #endif /* USART3 */ @@ -650,6 +653,7 @@ uart_status_t uart_send(UART_HandleTypeDef* handle, const uint8_t* data, uint8_t portENTER_CRITICAL(); if ((HAL_UART_GetState(handle) & HAL_UART_STATE_BUSY_TX) != HAL_UART_STATE_BUSY_TX && tx_queue != NULL && uxQueueMessagesWaiting (*tx_queue) == 0 ) { // check if UART is ready and queue is empty + a++; // Copy data to static buffer memcpy(tx_buffer, data, length); if (HAL_UART_Transmit_IT(handle, tx_buffer, length) != HAL_OK) { @@ -660,26 +664,27 @@ uart_status_t uart_send(UART_HandleTypeDef* handle, const uint8_t* data, uint8_t } portEXIT_CRITICAL(); + b++; // Send data in chunks based on DATA_SIZE - for (uint8_t i = 0; i < length; i+=DATA_SIZE) { - tx_payload_t payload; + for (size_t i = 0; i < length; i+=DATA_SIZE) { + tx_payload_t payload; - // Ensure we only copy DATA_SIZE bytes at a time - uint8_t chunk_size = (length - i < DATA_SIZE) ? (length - i) : DATA_SIZE; - // EX: i=4, length=6, DataSize=4, then chunk_size = 2, instead of usual 4 since we've reached end of length + // Ensure we only copy DATA_SIZE bytes at a time + size_t chunk_size = (length - i < DATA_SIZE) ? (length - i) : DATA_SIZE; + // EX: i=4, length=6, DataSize=4, then chunk_size = 2, instead of usual 4 since we've reached end of length - // Copy the appropriate number of bytes to the payload data - memcpy(payload.data, &data[i], chunk_size); // Usually chunk_size = DATA_SIZE until end of data length + // Copy the appropriate number of bytes to the payload data + memcpy(payload.data, &data[i], chunk_size); // Usually chunk_size = DATA_SIZE until end of data length - // If data size is smaller than DATA_SIZE, fill the rest of the payload - if (chunk_size < DATA_SIZE) { - memset(&payload.data[chunk_size], 0, DATA_SIZE - chunk_size); // Fill the rest with 0 (or other padding if needed) - } + // If data size is smaller than DATA_SIZE, fill the rest of the payload + if (chunk_size < DATA_SIZE) { + memset(&payload.data[chunk_size], 0, DATA_SIZE - chunk_size); // Fill the rest with 0 (or other padding if needed) + } - // Enqueue the payload to be transmitted - if (xQueueSend(*tx_queue, &payload, delay_ticks) != pdTRUE) { - return UART_ERR; - } //delay_ticks: 0 = no wait, portMAX_DELAY = wait until space is available + // Enqueue the payload to be transmitted + if (xQueueSend(*tx_queue, &payload, delay_ticks) != pdTRUE) { + return UART_ERR; + } //delay_ticks: 0 = no wait, portMAX_DELAY = wait until space is available } exit: diff --git a/common/Src/syscalls.c b/common/Src/syscalls.c index bc00267b..9f7a20b0 100644 --- a/common/Src/syscalls.c +++ b/common/Src/syscalls.c @@ -30,11 +30,7 @@ #include #include -#include "UART.h" -#include "portmacro.h" - -extern int __io_putchar(int ch) __attribute__((weak)); -extern int __io_getchar(void) __attribute__((weak)); +#include "stm32xx_hal.h" char *__env[1] = {0}; char **environ = __env; @@ -56,22 +52,12 @@ void _exit(int status) { __weak int _read(int file, char *ptr, int len) { (void)file; - int DataIdx; - - for (DataIdx = 0; DataIdx < len; DataIdx++) { - *ptr++ = __io_getchar(); - } - + (void)ptr; return len; } __weak int _write(int file, char *ptr, int len) { (void)file; - int DataIdx; - - for (DataIdx = 0; DataIdx < len; DataIdx++) { - __io_putchar(*ptr++); - } return len; } diff --git a/docs/FlashAndTheBug.md b/docs/FlashAndTheBug.md index 01e9143f..74ba357a 100644 --- a/docs/FlashAndTheBug.md +++ b/docs/FlashAndTheBug.md @@ -174,3 +174,20 @@ To view the output, open up an application like [PuTTY](https://www.chiark.green - For PuTTY, click Serial and enter your desired COM port. This should show up on your device manager (for Mac or Linux, run `lsusb`). Set the baud rate to what you configured the UART for. Hit the big open button at the bottom. - For picocom, type in `picocom -b ` and you should be set. +#### Logging Library + +A convenient integration we've made for easier debugging is this logging header file. Credit to Clark Poon. + +To use the logging library, `#define LOGGING_ENABLE 1` and `#define LOGGING_LEVEL x`, where x is a number between 1 and 5. Then, `#include "log.h"`. + +Now, use `log(LEVEL, ...)` to log to the console. It's the exact same variadic arguments as `printf`, but with a LEVEL argument beforehand. The way this works is that any LOGGING_LEVEL equal to or below x will be what is recorded to the console. + +The logging levels are as follows: +- L_FATAL: 1 +- L_ERROR: 2 +- L_WARN: 3 +- L_INFO: 4 +- L_DEBUG: 5 +- L_TRACE: 6 + +So, for example, if logging level 5 is selected, any logs of L_DEBUG or below will be printed (L_INFO, L_WARN, L_ERROR, L_FATAL). diff --git a/driver/Inc/log.h b/driver/Inc/log.h new file mode 100644 index 00000000..ac762e73 --- /dev/null +++ b/driver/Inc/log.h @@ -0,0 +1,86 @@ +#include +#include +#include "stm32xx_hal.h" + +#ifndef LOGGING_ENABLE +#define LOGGING_ENABLE 0 +#endif + +#ifndef LOGGING_LEVEL +#define LOGGING_LEVEL 6 +#endif + +#define LOG_MAX_BEFORE_SIZE 32 +#define LOG_MAX_LOG_SIZE 128 + +#define CONVERT_TO_STRING(arg) #arg +#define AS_STRING(arg) CONVERT_TO_STRING(arg) + +static char log_buf[LOG_MAX_LOG_SIZE]; +static char log_before_buf[LOG_MAX_BEFORE_SIZE]; + +typedef enum { + L_NONE = 0, + L_FATAL = 1, + L_ERROR = 2, + L_WARN = 3, + L_INFO = 4, + L_DEBUG = 5, + L_TRACE = 6, +} LoggingLevel; + +static inline const char * +log_level_name(LoggingLevel level) +{ + switch (level) { + case L_TRACE: return "TRACE"; + case L_DEBUG: return "DEBUG"; + case L_INFO: return "INFO"; + case L_WARN: return "WARN"; + case L_ERROR: return "ERROR"; + case L_FATAL: return "FATAL"; + case L_NONE: return "NONE"; + } + /* Should be unreachable */ + return NULL; +} + +static inline const char * +log_level_color(LoggingLevel level) +{ + switch (level) { + case L_TRACE: return "\x1b[00;34m"; + case L_DEBUG: return "\x1b[00;35m"; + case L_INFO: return "\x1b[00;36m"; + case L_WARN: return "\x1b[00;33m"; + case L_ERROR: return "\x1b[00;31m"; + case L_FATAL: return "\x1b[37;41m"; + case L_NONE: return "\x1b[0m"; + } + /* Should be unreachable */ + return NULL; +} + +static inline void +log_printf(void) +{ + printf("%-" AS_STRING(LOG_MAX_BEFORE_SIZE) "s | %s%s\n\r\r", + (char *)&log_before_buf, (char *)&log_buf, log_level_color(L_NONE)); +} + +#if LOGGING_ENABLE == 0 || LOGGING_LEVEL == 0 +#define log(LEVEL, fmt ...) +#else +#define log(LEVEL, fmt ...) \ + do { \ + LoggingLevel level = LEVEL; \ + if (LOGGING_LEVEL >= level) { \ + snprintf(log_before_buf, LOG_MAX_BEFORE_SIZE, \ + "%s%-5s | %s() ", \ + log_level_color(level), log_level_name(level), \ + pcTaskGetTaskName(NULL)); \ + snprintf((char *)&log_buf, LOG_MAX_LOG_SIZE, fmt); \ + log_printf(); \ + } \ + } while (0) +#endif diff --git a/driver/Src/printf.c b/driver/Src/printf.c deleted file mode 100644 index 524d7acd..00000000 --- a/driver/Src/printf.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "printf.h" - -UART_HandleTypeDef *printf_huart = NULL; - -/** - * @brief Initializes printf - * @param huart pointer to the UART handle - * @return bool true if success - */ -bool printf_init(UART_HandleTypeDef *huart) { - printf_huart = huart; - return uart_init(huart) == UART_OK; -} - -// Called by _read in syscalls.c -int __io_getchar() { - if (printf_huart != NULL) { - uint8_t data; - uart_status_t ret = uart_recv(printf_huart, &data, 1, portMAX_DELAY); - if(ret != UART_OK) return -1; - return data; - } - return -1; -} - -// Called by _write in syscalls.c -int __io_putchar(int data) { - if (printf_huart != NULL) { - uart_status_t ret = uart_send(printf_huart, (const uint8_t *)&data, 1, portMAX_DELAY); - if(ret != UART_OK) return -1; - return data; - } - return -1; -} - diff --git a/middleware/FreeRTOS-Kernel/queue.c b/middleware/FreeRTOS-Kernel/queue.c index f91841f9..6a87bbd9 100644 --- a/middleware/FreeRTOS-Kernel/queue.c +++ b/middleware/FreeRTOS-Kernel/queue.c @@ -29,6 +29,8 @@ #include #include +static int number_of_yields = 0; + /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining * all the API functions to use the MPU wrappers. That should only be done when * task.h is included from an application file. */ @@ -1129,6 +1131,7 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, * is also a higher priority task in the pending ready list. */ if( xTaskResumeAll() == pdFALSE ) { + number_of_yields++; taskYIELD_WITHIN_API(); } } diff --git a/driver/Inc/printf.h b/task/Inc/task_print.h similarity index 51% rename from driver/Inc/printf.h rename to task/Inc/task_print.h index 3b94aecd..c56d3fcf 100644 --- a/driver/Inc/printf.h +++ b/task/Inc/task_print.h @@ -1,9 +1,10 @@ #pragma once #include +#include #include "UART.h" extern int printf ( const char * format, ... ); -extern int scanf ( const char * format, ... ); -bool printf_init(UART_HandleTypeDef *huart); +void task_print_init(UART_HandleTypeDef *huart); +void task_print(void *huart); diff --git a/task/Src/task_print.c b/task/Src/task_print.c new file mode 100644 index 00000000..57a8d8c0 --- /dev/null +++ b/task/Src/task_print.c @@ -0,0 +1,44 @@ +#include "UART.h" +#include "stm32xx_hal.h" +#include + +#define MAX_PRINTF_SIZE (256) +#define MAX_PRINTF_ITEMS (4) + +#ifndef TASK_PRINT_DELAY +#define TASK_PRINT_DELAY (pdMS_TO_TICKS(1)) +#endif + +static QueueHandle_t print_tx_queue = NULL; +static StaticQueue_t print_tx_queue_buffer; +static uint8_t print_tx_queue_storage[MAX_PRINTF_SIZE * MAX_PRINTF_ITEMS]; + +static UART_HandleTypeDef *print_huart = NULL; + +void task_print_init(UART_HandleTypeDef *huart){ + print_huart = huart; + uart_init(print_huart); + print_tx_queue = xQueueCreateStatic(MAX_PRINTF_ITEMS, MAX_PRINTF_SIZE, print_tx_queue_storage, &print_tx_queue_buffer); +} + +void task_print(void *huart){ + static char print_item_buffer[MAX_PRINTF_SIZE]; + + while(1) { + if(xQueueReceive(print_tx_queue, (uint8_t *)&print_item_buffer[0], portMAX_DELAY) == pdPASS){ + uart_send(print_huart, (uint8_t *)print_item_buffer, strlen(print_item_buffer)+1, portMAX_DELAY); + } + taskYIELD(); + } +} + +int _write(int file, char *ptr, int len) { + (void)file; + + static char write_item_buffer[MAX_PRINTF_SIZE] = {0}; + memcpy(write_item_buffer, ptr, len); + + if(xQueueSend(print_tx_queue, (const uint8_t *)write_item_buffer, portMAX_DELAY) != pdPASS) return -1; + + return len; +} diff --git a/test/tests/log_test.c b/test/tests/log_test.c new file mode 100644 index 00000000..e7a4eaea --- /dev/null +++ b/test/tests/log_test.c @@ -0,0 +1,146 @@ +// A simple echo application to test input and output over serial +#include "UART.h" +#include "task_print.h" +#include "stm32xx_hal.h" +#include + +// Enables logging +#define LOGGING_ENABLE 1 + +// MUST BE DEFINED AS AN INTEGER +// Logging levels: +// L_FATAL: 1 +// L_ERROR: 2 +// L_WARN: 3 +// L_INFO: 4 +// L_DEBUG: 5 +// L_TRACE: 6 +#define LOGGING_LEVEL 2 +#include "log.h" + +StaticTask_t initTaskBuffer; +StackType_t initTaskStack[configMINIMAL_STACK_SIZE]; + +StaticTask_t txTaskBuffer1; +StackType_t txTaskStack1[configMINIMAL_STACK_SIZE * 4]; + +StaticTask_t txTaskBuffer2; +StackType_t txTaskStack2[configMINIMAL_STACK_SIZE * 4]; + +StaticTask_t txTaskBuffer3; +StackType_t txTaskStack3[configMINIMAL_STACK_SIZE * 4]; + +StaticTask_t txTaskBuffer4; +StackType_t txTaskStack4[configMINIMAL_STACK_SIZE * 4]; + +StaticTask_t txTaskBuffer5; +StackType_t txTaskStack5[configMINIMAL_STACK_SIZE * 4]; + +StaticTask_t printTaskBuffer; +StackType_t printTaskStack[configMINIMAL_STACK_SIZE * 4]; + +void HAL_UART_MspGPIOInit(UART_HandleTypeDef *huart){ + GPIO_InitTypeDef init = {0}; + __HAL_RCC_GPIOA_CLK_ENABLE(); + + /* enable port A USART2 gpio + PA2 -> USART2_TX + PA3 -> USART2_RX + */ + init.Pin = GPIO_PIN_2|GPIO_PIN_3; + init.Mode = GPIO_MODE_AF_PP; + init.Pull = GPIO_NOPULL; + init.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + init.Alternate = GPIO_AF7_USART2; + HAL_GPIO_Init(GPIOA, &init); +} + +void TxTask(void *argument){ + while(1){ + log(L_FATAL, "%s", "FATAL"); + log(L_ERROR, "%s", "ERROR"); + log(L_WARN, "%s", "WARN"); + log(L_INFO, "%s", "INFO"); + log(L_DEBUG, "%s", "DEBUG"); + log(L_TRACE, "%s", "TRACE"); + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +void InitTask(void *argument){ + husart2->Init.BaudRate = 115200; + husart2->Init.WordLength = UART_WORDLENGTH_8B; + husart2->Init.StopBits = UART_STOPBITS_1; + husart2->Init.Parity = UART_PARITY_NONE; + husart2->Init.Mode = UART_MODE_TX_RX; + husart2->Init.HwFlowCtl = UART_HWCONTROL_NONE; + husart2->Init.OverSampling = UART_OVERSAMPLING_16; + + task_print_init(husart2); + + xTaskCreateStatic(task_print, + "Print Task", + configMINIMAL_STACK_SIZE, + husart2, + tskIDLE_PRIORITY + 2, + printTaskStack, + &printTaskBuffer); + + xTaskCreateStatic(TxTask, + "TX1", + configMINIMAL_STACK_SIZE*4, + NULL, + tskIDLE_PRIORITY + 2, + txTaskStack1, + &txTaskBuffer1); + xTaskCreateStatic(TxTask, + "TX2", + configMINIMAL_STACK_SIZE*4, + NULL, + tskIDLE_PRIORITY + 2, + txTaskStack2, + &txTaskBuffer2); + xTaskCreateStatic(TxTask, + "TX3", + configMINIMAL_STACK_SIZE*4, + NULL, + tskIDLE_PRIORITY + 2, + txTaskStack3, + &txTaskBuffer3); + xTaskCreateStatic(TxTask, + "TX4", + configMINIMAL_STACK_SIZE*4, + NULL, + tskIDLE_PRIORITY + 2, + txTaskStack4, + &txTaskBuffer4); +// xTaskCreateStatic(TxTask, +// "TX5", +// configMINIMAL_STACK_SIZE*4, +// NULL, +// tskIDLE_PRIORITY + 2, +// txTaskStack5, +// &txTaskBuffer5); + + vTaskDelete(NULL); + + while(1); +} + +int main(void) { + HAL_Init(); + SystemClock_Config(); + + xTaskCreateStatic(InitTask, + "Init", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 3, + initTaskStack, + &initTaskBuffer); + + vTaskStartScheduler(); + + while (1) { + } +} diff --git a/test/tests/printf_test.c b/test/tests/printf_test.c index 0c5f6993..dfa2a24c 100644 --- a/test/tests/printf_test.c +++ b/test/tests/printf_test.c @@ -1,11 +1,17 @@ -// A simple echo application to test input and output over serial #include "UART.h" -#include "projdefs.h" #include "stm32xx_hal.h" -#include "printf.h" + +#include +#include "task_print.h" + +StaticTask_t initTaskBuffer; +StackType_t initTaskStack[configMINIMAL_STACK_SIZE]; StaticTask_t txTaskBuffer; -StackType_t txTaskStack[configMINIMAL_STACK_SIZE]; +StackType_t txTaskStack[configMINIMAL_STACK_SIZE * 4]; + +StaticTask_t printTaskBuffer; +StackType_t printTaskStack[configMINIMAL_STACK_SIZE * 4]; void HAL_UART_MspGPIOInit(UART_HandleTypeDef *huart){ GPIO_InitTypeDef init = {0}; @@ -24,6 +30,13 @@ void HAL_UART_MspGPIOInit(UART_HandleTypeDef *huart){ } void TxTask(void *argument){ + while(1){ + printf("Hello World! %s %d %f\n\r", "Test String", 5, 4.4); + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +void InitTask(void *argument){ husart2->Init.BaudRate = 115200; husart2->Init.WordLength = UART_WORDLENGTH_8B; husart2->Init.StopBits = UART_STOPBITS_1; @@ -31,26 +44,42 @@ void TxTask(void *argument){ husart2->Init.Mode = UART_MODE_TX_RX; husart2->Init.HwFlowCtl = UART_HWCONTROL_NONE; husart2->Init.OverSampling = UART_OVERSAMPLING_16; - - printf_init(husart2); - while(1){ - printf("Hello World! %s %d %f\n\r", "Test String", 5, 4.4); - vTaskDelay(pdMS_TO_TICKS(100)); - } + task_print_init(husart2); + + xTaskCreateStatic(task_print, + "Print Task", + configMINIMAL_STACK_SIZE, + husart2, + tskIDLE_PRIORITY + 2, + printTaskStack, + &printTaskBuffer); + + xTaskCreateStatic(TxTask, + "TX", + configMINIMAL_STACK_SIZE*4, + NULL, + tskIDLE_PRIORITY + 2, + txTaskStack, + &txTaskBuffer); + + vTaskDelete(NULL); + + while(1); } int main(void) { HAL_Init(); SystemClock_Config(); - xTaskCreateStatic(TxTask, - "TX", - configMINIMAL_STACK_SIZE, - NULL, - tskIDLE_PRIORITY + 2, - txTaskStack, - &txTaskBuffer); + xTaskCreateStatic(InitTask, + "Init", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 3, + initTaskStack, + &initTaskBuffer); + vTaskStartScheduler();