diff --git a/srv/debug.c b/srv/debug.c index 44711ac..416880c 100644 --- a/srv/debug.c +++ b/srv/debug.c @@ -8,53 +8,75 @@ //--includes-------------------------------------------------------------------- #include "debug.h" +#include "dma_mbuf.h" +#include "format.h" + +#if DEBUG_TRACE || DEBUG_WARN || DEBUG_ERROR //--local definitions----------------------------------------------------------- -#define BUFFER_SIZE 83 //80 char line + \n + 2 bytes for index -#define BUFFER_NB 3 //1 buffer in write, one in transfer and one waiting +static uint32_t write_debug(uint8_t c, void* arg); + +#define BUFFER_SIZE 162 //(80 char line + \n) * 2 + //--local variables------------------------------------------------------------- -static enum UsartPeriph usart_periph; -static uint8_t tx_buffer1[BUFFER_SIZE]; -static uint8_t tx_buffer2[BUFFER_SIZE]; -static uint8_t tx_buffer3[BUFFER_SIZE]; -static uint8_t* tx_buffers[BUFFER_NB] = {tx_buffer1, tx_buffer2, tx_buffer3}; +static struct DmaMultiBuffer mbuf; +static uint8_t tx_buffer[BUFFER_SIZE]; //--public functions------------------------------------------------------------ -#if DEBUG_TRACE || DEBUG_WARN || DEBUG_ERROR - void _debug_init(enum UsartPeriph usart, enum GpioPort tx_port, enum GpioPin tx_pin) { - usart_periph = usart; gpio_configure(tx_port, tx_pin, GPIO_MODE_OUTPUT_FAST, GPIO_CONFIG_OUT_ALT_PUSH_PULL); - usart_configure(usart_periph, USART_CONFIG_8N1, 1152000); - - usart_set_tx_buffer(USART_PERIPH_3, tx_buffers, BUFFER_SIZE, BUFFER_NB, - DMA_CONFIG_PRIO_LOW); + usart_configure(usart, USART_CONFIG_8N1, 1000000); + dma_mbuf_configure(&mbuf,usart_configure_tx_dma(usart), + DMA_CONFIG_PRIO_LOW, tx_buffer, BUFFER_SIZE); debug_trace("\n"); - debug_trace("--------------------------------------------------------------------------------\n"); + debug_trace("------------------------------------------------------------------------------\n"); debug_trace("starting debug software\n"); debug_trace("compiled on " __DATE__ " at " __TIME__ "\n"); - debug_trace("--------------------------------------------------------------------------------\n"); + debug_trace("------------------------------------------------------------------------------\n"); } -void _debug_trace(char* format, ...) +void _debug_print(const char* restrict header, + const char* restrict format, ...) { - while(*format != '\0') { - while(usart_write_byte(usart_periph, *format)) {} - ++format; - } + va_list va; + va_start(va, format); + format_vfctprintf(write_debug, nullptr, header, va); + format_vfctprintf(write_debug, nullptr, format, va); + va_end(va); + write_debug('\n', nullptr); + + //ensure everything is written when done + while (dma_mbuf_switch(&mbuf)) {} } -#endif //--local functions------------------------------------------------------------- +/** + * Callback to use with format_vfctprintf() to print caracters to the debug port + */ +static uint32_t write_debug(uint8_t c, void* arg) +{ + (void)arg; //unused, required because of FormatCallback + + while (dma_mbuf_write_byte(&mbuf, c)) + { + //buffer is full, wait until transfer started + while (dma_mbuf_switch(&mbuf)) {} + } + + return 0; +} + +#endif //DEBUG_TRACE || DEBUG_WARN || DEBUG_ERROR + diff --git a/srv/debug.h b/srv/debug.h index cd511d6..523b117 100644 --- a/srv/debug.h +++ b/srv/debug.h @@ -13,11 +13,10 @@ #include "../drv/usart.h" #include "../drv/gpio.h" -#include "stdint.h" - #define DEBUG_TRACE 1 -#define DEBUG_WARN 0 -#define DEBUG_ERROR 0 +#define DEBUG_WARN 1 +#define DEBUG_ERROR 1 + //--type definitions------------------------------------------------------------ @@ -28,19 +27,40 @@ #endif #if DEBUG_TRACE -#define debug_trace(format, ...) _debug_trace(format __VA_OPT__(,) __VA_ARGS__) +/** + * Prints a trace message on the debug port. This function should return + * immediately, provided that the message's length doesn't exceed 80 caracters + * and that it isn't called in very quick succession. Otherwise, it will block + * until the internal buffering system is ready to accept the message + */ +#define debug_trace(format, ...) _debug_print("T:", \ + format __VA_OPT__(,) __VA_ARGS__) #else #define debug_trace(format, ...) do {} while (0) #endif #if DEBUG_WARN -#define debug_warn(format, ...) _debug_warn(format __VA_OPT__(,) __VA_ARGS__) +/** + * Prints a warning message on the debug port. This function should return + * immediately, provided that the message's length doesn't exceed 80 caracters + * and that it isn't called in very quick succession. Otherwise, it will block + * until the internal buffering system is ready to accept the message + */ +#define debug_warn(format, ...) _debug_print("W:", \ + format __VA_OPT__(,) __VA_ARGS__) #else #define debug_warn(format, ...) do {} while (0) #endif #if DEBUG_ERROR -#define debug_error(format, ...) _debug_error(format __VA_OPT__(,) __VA_ARGS__) +/** + * Prints an error message on the debug port. This function should return + * immediately, provided that the message's length doesn't exceed 80 caracters + * and that it isn't called in very quick succession. Otherwise, it will block + * until the internal buffering system is ready to accept the message + */ +#define debug_error(format, ...) _debug_print("E:", \ + format __VA_OPT__(,) __VA_ARGS__) #else #define debug_error(format, ...) do {} while (0) #endif @@ -48,12 +68,30 @@ //--functions------------------------------------------------------------------- +#if DEBUG_TRACE || DEBUG_WARN || DEBUG_ERROR + +/** + * Initializes the debug system to use the given usart peripheral on the given + * tx port, printing a banner if traces are enabled. + * The usart doesn't need to be configured, neither does the tx port. + * + * This function shouldn't be called directly. Instead, use the debug_init macro + */ void _debug_init(enum UsartPeriph usart, enum GpioPort tx_port, enum GpioPin tx_pin); -void _debug_trace(char* format, ...); -void _debug_warn(char* format, ...); -void _debug_error(char* format, ...); +/** + * Prints a debug message on the debug port with the given header. This function + * should return immediately, provided that the message's length doesn't exceed + * 80 caracters and that it isn't called in very quick succession. Otherwise, it + * will block until the internal buffering system is ready to accept the message + * + * This function shouldn't be called directly. Instead, use the debug macros + */ +void _debug_print(const char* restrict header, + const char* restrict format, ...); + +#endif #endif //_DEBUG_H_ diff --git a/srv/format.c b/srv/format.c new file mode 100644 index 0000000..ccf392e --- /dev/null +++ b/srv/format.c @@ -0,0 +1,265 @@ +/** @file format.c + * Module handling various formating functions + * + * The module provides a lightweight printf function and the attached tools + */ + +//--includes-------------------------------------------------------------------- + +#include "format.h" + + +//--local definitions----------------------------------------------------------- + +struct BufferArg { + char* restrict buffer; + uint32_t buffer_size; + uint32_t byte_index; +}; + +static uint32_t buffer_write(uint8_t byte, void* arg); +static uint32_t render_format(FormatCallback callback, void* arg, + const char* restrict format, va_list va); +static uint32_t render_signed(FormatCallback callback, void* arg, + int32_t number, uint8_t base, uint8_t width); +static uint32_t render_unsigned(FormatCallback callback, void* arg, + uint32_t number, uint8_t base, uint8_t width, bool zero_padding); + + +//--local variables------------------------------------------------------------- + +//--public functions------------------------------------------------------------ + +uint32_t format_snprintf(char* restrict buffer, uint32_t buffer_size, + const char* restrict format, ...) +{ + va_list va; + va_start(va, format); + struct BufferArg arg = { buffer, buffer_size, 0 }; + uint32_t ret = render_format(buffer_write, &arg, format, va); + va_end(va); + return ret; +} + +uint32_t format_fctprintf(FormatCallback callback, void* arg, + const char* restrict format, ...) +{ + va_list va; + va_start(va, format); + uint32_t ret = render_format(callback, arg, format, va); + va_end(va); + return ret; +} + +uint32_t format_vfctprintf(FormatCallback callback, void* arg, + const char* restrict format, va_list va) +{ + return render_format(callback, arg, format, va); +} + + +//--local functions------------------------------------------------------------- + +/** + * FormatCallback to write into a regular buffer. A pointer to BufferArg shall + * be passed as arg. + * + * Returns 0 if everything went as expected, 1 otherwise + */ +static uint32_t buffer_write(uint8_t byte, void* arg) +{ + struct BufferArg* buffer_arg = (struct BufferArg*)arg; + if (buffer_arg->byte_index >= buffer_arg->buffer_size) { + return 1; + } + + buffer_arg->buffer[buffer_arg->byte_index] = byte; + ++buffer_arg->byte_index; + return 0; +} + +/** + * Renders the given format to the output defined by the provided callback, + * formatting accordingly the provided arguments + * + * Returns 0 if everything went as expected, 1 otherwise + */ +static uint32_t render_format(FormatCallback callback, void* arg, + const char* restrict format, va_list va) +{ + uint8_t width = 0; + bool in_format = false; + bool zero_padding = false; + + for (const char* c = format; *c != '\0'; ++c) { + + if (in_format) { + switch (*c) { + case '%': + if (callback('%', arg)) return 1; + break; + case 'c': + if (callback((uint8_t)va_arg(va, uint32_t), arg)) { + return 1; + } + break; + case 's': + { + const char* str = va_arg(va, const char*); + for (const char* c2 = str; *c2 != '\0'; ++c2) { + if (callback(*c2, arg)) return 1; + } + } + break; + case 'i': + if (render_signed(callback, arg, va_arg(va, int32_t), + 10, width)) { + return 1; + } + break; + case 'u': + if (render_unsigned(callback, arg, va_arg(va, uint32_t), + 10, width, zero_padding)) { + return 1; + } + break; + case 'x': + if (render_unsigned(callback, arg, va_arg(va, uint32_t), + 16, width, zero_padding)) { + return 1; + } + break; + case '0': + if (width == 0) { + zero_padding = true; + continue; + } + // fall through + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + width *= 10; + width += (*c - '0'); + continue; + default: + //TODO manage error + break; + } + } else { + switch (*c) { + case '%': + in_format = true; + zero_padding = false; + width = 0; + continue; + default: + if (callback(*c, arg)) return 1; + break; + } + } + + in_format = false; + } + + return 0; +} + +/** + * Formats a signed integer and renders it to the output defined by the provided + * callback + * + * Returns 0 if everything went as expected, 1 otherwise + */ +static uint32_t render_signed(FormatCallback callback, void* arg, + int32_t number, uint8_t base, uint8_t width) +{ + if (width > 0 && number < 0) { + --width; + } + + if (width > 0) { + uint32_t abs_number; + if (number < 0) { + abs_number = -number; + } else { + abs_number = number; + } + + uint32_t base_power = base; + while (width > 0 && base_power <= abs_number) { + base_power *= base; + --width; + } + if (width > 0) { + --width; + } + + while (width > 0) { + --width; + if (callback(' ', arg)) return 1; + } + } + + if (number < 0) { + callback('-', arg); + number = -number; + } + + return render_unsigned(callback, arg, (uint32_t)number, base, 0, false); +} + +/** + * Formats an unsigned integer and renders it to the output defined by the + * provided callback + * + * Returns 0 if everything went as expected, 1 otherwise + */ +static uint32_t render_unsigned(FormatCallback callback, void* arg, + uint32_t number, uint8_t base, uint8_t width, bool zero_padding) +{ + uint32_t base_power = base; + + while (base_power <= number) { + base_power *= base; + if (width > 0) { + --width; + } + } + if (width > 0) { + --width; + } + + while (width > 0) { + --width; + if (zero_padding) { + if (callback('0', arg)) return 1; + } else { + if (callback(' ', arg)) return 1; + } + } + + while (base_power > 1) { + base_power /= base; + + uint8_t digit = 0; + while (number >= base_power) { + number -= base_power; + ++digit; + } + + if (digit < 10) { + if (callback('0' + digit, arg)) return 1; + } else { + if (callback('A' + digit - 10, arg)) return 1; + } + } + + return 0; +} + diff --git a/srv/format.h b/srv/format.h new file mode 100644 index 0000000..a735542 --- /dev/null +++ b/srv/format.h @@ -0,0 +1,57 @@ +/** @file format.h + * Module handling various formating functions + * + * The module provides a lightweight printf function and the attached tools + */ + +#ifndef _FORMAT_H_ +#define _FORMAT_H_ + +//--includes-------------------------------------------------------------------- + +#include +#include + + +//--type definitions------------------------------------------------------------ + +typedef uint32_t (*FormatCallback)(uint8_t byte, void* arg); + + +//--functions------------------------------------------------------------------- + +/** + * Custom implementation of snprintf. Print the given format into the given + * buffer with the attached arguments properly formated. + * + * This function only provided a minimal subset of the standard library's + * snprintf : %c, %s, %i, %d, %x as well as right-justified padding for %d and + * %x + * + * Returns 0 if everything went as expected, 1 otherwise + */ +uint32_t format_snprintf(char* restrict buffer, uint32_t buffer_size, + const char* restrict format, ...); + +/** + * Custom implementation of fprintf using a callback instead of a file pointer. + * Print the given format using the given callback with the attached arguments + * properly formated. + * + * This function only provided a minimal subset of the standard library's + * snprintf : %c, %s, %i, %d, %x as well as right-justified padding for %d and + * %x + * + * Returns 0 if everything went as expected, 1 otherwise + */ +uint32_t format_fctprintf(FormatCallback callback, void* arg, + const char* restrict format, ...); + +/** + * Variadic variation of format_fctprintf(), see the later for more information + */ +uint32_t format_vfctprintf(FormatCallback callback, void* arg, + const char* restrict format, va_list va); + +#endif //_FORMAT_H_ +