#include "debug_driver.h"
#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/**
* @file debug_driver.c
* @brief Retarget low-level stdio functions to USART2 for debugging output/input.
* @author rocket
* @copyright Copyright (c) 2025 rocket. Authorized use only.
*/
#ifdef __GNUC__ // GCC
/**
* @brief Retarget newlib write syscall to the USART debug port.
* @param file Logical file descriptor supplied by the C library (unused).
* @param ptr Buffer containing the data to be transmitted.
* @param len Number of bytes to be transmitted.
* @return Number of bytes written.
*/
int _write(int file, char *ptr, int len)
{
(void)file;
HAL_UART_Transmit(&huart2, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
/**
* @brief Retarget newlib read syscall to the USART debug port.
* @param file Logical file descriptor supplied by the C library (unused).
* @param ptr Destination buffer.
* @param len Number of bytes to be read.
* @return Number of bytes read.
*/
int _read(int file, char *ptr, int len)
{
(void)file;
HAL_UART_Receive(&huart2, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
#elif defined(__ARMCC_VERSION) // Keil
/**
* @brief Retarget fputc to the USART debug port for Arm Compiler/Keil.
* @param ch Character to transmit.
* @param f Ignored file handle.
* @return The transmitted character.
*/
int fputc(int ch, FILE *f)
{
(void)f;
uint8_t data = (uint8_t)ch;
HAL_UART_Transmit(&huart2, &data, 1U, HAL_MAX_DELAY);
return ch;
}
/**
* @brief Retarget fgetc to the USART debug port for Arm Compiler/Keil.
* @param f Ignored file handle.
* @return The received character.
*/
int fgetc(FILE *f)
{
(void)f;
uint8_t ch;
HAL_UART_Receive(&huart2, &ch, 1U, HAL_MAX_DELAY);
return (int)ch;
}
#ifndef __MICROLIB
/* Disable semihosting when using the standard C library (non-Microlib). */
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
FILE __stdin;
/**
* @brief Stub exit handler so Keil does not fall back to semihosting.
* @param x Exit code (ignored).
*/
void _sys_exit(int x)
{
(void)x;
}
#endif
#else
#error "Unsupported compiler"
#endif
/* ========= Configuration section ========= */
#ifndef UART_LOG_INSTANCE
#define UART_LOG_INSTANCE huart2 // Replace with the UART handle you want to use
#endif
#ifndef UART_LOG_TIMEOUT
#define UART_LOG_TIMEOUT 1000 // Send timeout (ms)
#endif
#ifndef UART_LOG_BUF_SIZE
#define UART_LOG_BUF_SIZE 256 // Size of the formatting buffer
#endif
/* ======================================== */
/**
* @brief Blocking UART transmit helper (call outside of ISR context).
*/
static inline void uart_write_blocking(const uint8_t *data, size_t len)
{
HAL_UART_Transmit(&UART_LOG_INSTANCE, (uint8_t *)data, (uint16_t)len, UART_LOG_TIMEOUT);
}
/**
* @brief Normalize newlines to CRLF before sending to the terminal.
*/
static void uart_write_with_crlf(const char *s, size_t len)
{
for (size_t i = 0; i < len; ++i) {
char c = s[i];
if (c == '\n') {
const char crlf[2] = {'\r','\n'};
uart_write_blocking((const uint8_t*)crlf, 2);
} else {
uart_write_blocking((const uint8_t*)&c, 1);
}
}
}
/**
* @brief Output a zero-terminated string (with automatic CRLF normalization).
*/
void uart_puts(const char *s)
{
if (s == NULL) {
return;
}
uart_write_with_crlf(s, strlen(s));
}
/**
* @brief printf-style helper built on top of the debug UART.
* @return Number of characters that would have been written, or a negative error.
*/
int uart_printf(const char *fmt, ...)
{
char buf[UART_LOG_BUF_SIZE];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (n < 0) return n;
size_t out_len = (n < (int)sizeof(buf)) ? (size_t)n : (size_t)sizeof(buf) - 1;
uart_write_with_crlf(buf, out_len);
/*
if (n >= (int)sizeof(buf)) {
uart_puts("...[truncated]\n");
}
*/
return n;
}
/**
* @brief Hex dump helper for quick binary inspection.
*/
void uart_hexdump(const void *data, size_t len, const char *title)
{
const uint8_t *p = (const uint8_t*)data;
if (title) uart_printf("%s (len=%u):\n", title, (unsigned)len);
char line[80];
for (size_t i = 0; i < len; i += 16) {
int pos = 0;
pos += snprintf(line + pos, sizeof(line) - pos, "%08X ", (unsigned)i);
/* hex */
for (size_t j = 0; j < 16; ++j) {
if (i + j < len) pos += snprintf(line + pos, sizeof(line) - pos, "%02X ", p[i + j]);
else pos += snprintf(line + pos, sizeof(line) - pos, " ");
if (j == 7) pos += snprintf(line + pos, sizeof(line) - pos, " ");
}
/* ascii */
pos += snprintf(line + pos, sizeof(line) - pos, " |");
for (size_t j = 0; j < 16 && i + j < len; ++j) {
uint8_t c = p[i + j];
pos += snprintf(line + pos, sizeof(line) - pos, "%c", (c >= 32 && c <= 126) ? c : '.');
}
pos += snprintf(line + pos, sizeof(line) - pos, "|\n");
uart_puts(line);
}
}