#include "debug_driver.h"
#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/**
* @file debug_driver.c
* @brief 将底层 stdio 函数重定向到 USART2,用于调试收发。
* @author rocket
*/
#ifdef __GNUC__ // GCC
/**
* @brief 将 newlib 的 write 系统调用重定向到调试 USART。
* @param file C 库提供的文件描述符(未使用)。
* @param ptr 待发送数据缓冲区。
* @param len 待发送字节数。
* @return 实际写入的字节数。
*/
int _write(int file, char *ptr, int len)
{
(void)file;
HAL_UART_Transmit(&huart2, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
/**
* @brief 将 newlib 的 read 系统调用重定向到调试 USART。
* @param file C 库提供的文件描述符(未使用)。
* @param ptr 接收缓冲区。
* @param len 待读取字节数。
* @return 实际读取的字节数。
*/
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 在 Arm Compiler/Keil 下,将 fputc 重定向到调试 USART。
* @param ch 待发送字符。
* @param f 被忽略的文件句柄。
* @return 实际发送的字符。
*/
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 在 Arm Compiler/Keil 下,将 fgetc 重定向到调试 USART。
* @param f 被忽略的文件句柄。
* @return 接收到的字符。
*/
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 空的退出处理,避免 Keil 回退到半主机。
* @param x 退出码(忽略)。
*/
void _sys_exit(int x)
{
(void)x;
}
#endif
#else
#error "Unsupported compiler"
#endif
/* ========= Configuration section ========= */
#ifndef UART_LOG_INSTANCE
#define UART_LOG_INSTANCE huart2 // 可替换为你希望使用的 UART 句柄
#endif
#ifndef UART_LOG_TIMEOUT
#define UART_LOG_TIMEOUT 1000 // 发送超时(毫秒)
#endif
#ifndef UART_LOG_BUF_SIZE
#define UART_LOG_BUF_SIZE 256 // 格式化缓冲区大小
#endif
/* ======================================== */
/**
* @brief 阻塞式 UART 发送辅助(需在非中断上下文调用)。
*/
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 发送前将换行符规范化为 CRLF。
*/
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 输出以 0 结尾的字符串(自动做 CRLF 规范化)。
*/
void uart_puts(const char *s)
{
if (s == NULL) {
return;
}
uart_write_with_crlf(s, strlen(s));
}
/**
* @brief 基于调试 UART 的 printf 风格输出。
* @return 预期写入的字符数,错误返回负值。
*/
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 十六进制转储,便于快速检查二进制内容。
*/
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);
}
}