GNUC的printf必须使用一个\n,或者使用fflush函数才能将缓冲输出至串口,抑或是等到缓冲区满,一次性将缓冲区内数据全部输出。对于并不习惯Linux编程的人来说,这也是不方便的。因此可以基于sprintf自定义一个“printf”函数,这里我们命名为APP_PRINT,不习惯GNUC的printf的话,可以选择使用这个带参宏。
APP_PRINT的使用方法与printf完全一致。
注意,如果需要打印浮点数的话,需要修改项目环境,默认情况下不支持浮点。
这里以Renesas RA系列单片机为例。stm32的话没差太多。
头文件
#include "hal_data.h"
#include "stdio.h"
#define DEBUG_STR_LEN 0x1000 //缓冲区长度
#if defined __GNUC__ && !defined __clang__ //GNUC环境使用自定义printf
#define APP_PRINT(fn, ...) Debug_UART_print(debug_str,sprintf((char *)debug_str, fn , ##__VA_ARGS__));
extern uint8_t debug_str[DEBUG_STR_LEN];
#else //标准库环境直接使用printf
#define APP_PRINT(fn, ...) printf(fn, ##__VA_ARGS__);
#endif
void Debug_UART_print(uint8_t *str, int bytes); //自定义的串口打印,配合宏APP_PRINT使用或者直接用都行
void Debug_UART_Init(void); //调用APP_PRINT前记得初始化串口
源文件
//使用GNUC则需要此全局变量,自定义的print需要使用到
#if defined __GNUC__ && !defined __clang__
uint8_t debug_str[DEBUG_STR_LEN];
#endif
static volatile uint8_t uart_send_complete_flag = false; //必须加上volatile,否则可能被编译器优化
/**
* @brief 串口初始化并开始接收
* @param 无
* @return 无
*/
void Debug_UART_Init(void)
{
R_SCI_UART_Open (&Debug_UART_ctrl, &Debug_UART_cfg); //打开串口
}
/**
* @brief 串口中断回调函数
* @param p_args
*/
void Debug_UART_Callback(uart_callback_args_t * p_args)
{
switch(p_args->event)
{
case UART_EVENT_RX_CHAR: //收到数据后发给上位机
R_SCI_UART_Write(Debug_UART.p_ctrl, (uint8_t *)&p_args->data, 1);
break;
case UART_EVENT_TX_COMPLETE:
/*发送完成后设置此标志位置1,即可开始下一次printf打印*/
uart_send_complete_flag = true;
break;
default:
break;
}
}
/**
* @brief 因为直接使用printf需要修改C开发环境,需要分配堆,且需要/n或者手动fflush或者等缓冲区满了才会打印,不方便移植,故这里写了个自定义的print
* @param str 字符串
* @param bytes 字符串长度
*/
void Debug_UART_print(uint8_t *str, int bytes)
{
uart_send_complete_flag = false;
R_SCI_UART_Write(Debug_UART.p_ctrl, str, (uint32_t)bytes);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
}