在《STM32 Uart 接收变长数据》的结尾,我们觉得每次使用这样的形式来输出信息感觉好麻烦,也不方便调试。
HAL_UART_Transmit(huart, &RxLenHi, 1, 1000); // 发送长度高位
HAL_UART_Transmit(huart, &RxLenlo, 1, 1000); // 发送长度低位
HAL_UART_Transmit(huart, uart4Rx, uart4RxLength, 1000); // 发送接收到的数据
所以我们就想想办法,看可不可以像标准C语言一样,使用printf往控制台打印信息。
在这一篇,我们提供了三种方法。
方法1:简单粗暴,直接写三个函数,一个负责输出字符串,名myPrintfChar;一个负责输出十进制数,名myPrintfDec;一个输出十六进制数,名myPrintfHex;
void myPrintfChar(uint8_t* buf)
{
uint8_t * pDebBuf;
uint16_t debugLen = 0;
pDebBuf = debugBuffer;
if(buf == NULL)
return;
// Put string to debugBuffer;
while(1)
{
if(*buf=='\0'||debugLen>=MAX_DEBBUF)
{
break;
}
*pDebBuf = *buf;
pDebBuf++;
buf++;
debugLen++;
}
// Output the debugBuffer to uart;
if(debugLen)
{
HAL_UART_Transmit(printfUart, debugBuffer, debugLen, 1000);
}
}
void myPrintfHex(uint32_t num)
{
uint16_t debugLen = 0;
// Translate num to DEC string;
debugLen = itoa(num, debugBuffer, 16);
// Output the string to uart
if(debugLen)
{
HAL_UART_Transmit(printfUart, debugBuffer, debugLen, 1000);
}
}
void myPrintfDec(uint32_t num)
{
uint16_t debugLen = 0;
// Translate num to HEX string, ignore 0x;
debugLen = itoa(num, debugBuffer, 10);
// Output the string to uart
if(debugLen)
{
HAL_UART_Transmit(printfUart, debugBuffer, debugLen, 1000);
}
}
里面有个函数,itoa,作用是把数字转成字符串,参数有三个,1.数字;2.字符串地址;3.控制,10就是十进制,16就是16进制,返回值是字符串的长度。函数的实现如下:
static uint8_t itoa(uint32_t num, uint8_t *str, uint8_t format)
{
uint8_t i,j;
uint8_t strLen = 0;
switch (format){
case 10:{
i = 0;
do{
str[i] = num%10+'0';
i++;
num/=10;
}while(num>0);
strLen = i;
// Exchange Str abcdef... to ...fedcba;
if(strLen>=2){
j = 0; i--;
do{
str[i] = str[i]^str[j];
str[j] = str[i]^str[j];
str[i] = str[i]^str[j];
j++; i--;
}while(i/2);
}
}
break;
case 16:{
i = 0;
do{
str[i] = ((num&0xf)>=10)?((num&0xf)%10+'A'):((num&0xf)+'0');
i++;
num>>=4;
}while(num>0);
strLen = i;
// Exchange Str abcdef... to ...fedcba;
if(strLen>=2){
j = 0; i--;
do{
str[i] = str[i]^str[j];
str[j] = str[i]^str[j];
str[i] = str[i]^str[j];
j++; i--;
}while(i/2);
}
}
break;
default:
break;
}
str[strLen] = '\0';
return strLen;
}
方法2:写一个不定参数的函数,类似于printf(char *,...),名为 myPrintf(char* str, ...)。
如何实现不定参数的函数呢?
首先,在#include
va_start(ap, fmt):作用是指向参数中第一个可变参数,va_arg(ap, xxx):作用是返回这个参数并指向下一个可变参数。
myPrintf(char* str, ...)函数的实现如下:
void myPrintf(char* str, ...)
{
va_list ap;
uint8_t * pDebBuf;
uint16_t debugLen = 0;
uint16_t valLen = 0;
char *p, *sval;
uint32_t ival;
pDebBuf = debugBuffer;
va_start(ap, str);
for(p=str; *p; p++)
{
if(*p!='%'){
*pDebBuf = *p; pDebBuf++;
debugLen++;
continue;
}
switch(*++p){
case 'd':{
ival = va_arg(ap, uint32_t);
valLen = itoa(ival, pDebBuf, 10);
pDebBuf += valLen; debugLen += valLen;
}
break;
case 'x':{
ival = va_arg(ap, uint32_t);
valLen = itoa(ival, pDebBuf, 16);
pDebBuf += valLen; debugLen += valLen;
}
break;
case 's':{
for(sval = va_arg(ap, char *); *sval; sval++){
*pDebBuf = *sval;
pDebBuf++; debugLen++;
}
}
break;
default:{
*pDebBuf = *p; pDebBuf++;
debugLen++;
}
break;
}
}
va_end(ap);
if(debugLen)
{
HAL_UART_Transmit(printfUart, debugBuffer, debugLen, 1000);
}
}
方法3:printf重定向,只需要重写fputc便可,简单实用,以后,我们都用这种方式吧,对了,记得检查一下微库有没有钩上,请看图:
重写的fputc函数实现如下:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(printfUart, (uint8_t *)&ch, 1, 1000);
return ch;
}
有没有发现,不管哪种方式,调用的HAL_UART_Transmit(printfUart, ..........)函数,都带有printfUart这个参数,记得,把这个参数换成你板子上对应的Uart:
UART_HandleTypeDef *printfUart = &huart4;
最后,我们测试一下这三种方法,如《STM32 Uart 接收不定长数据》里所写,在idle中断的回调函数里打印字符串,以及收到的字符长度十进制显示和十六进制显示,测试代码如下:
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
#ifdef RCV_DMAIDLE_PROCESS
uart4RxLength = UART4_BUF_MAX-__HAL_DMA_GET_COUNTER(&hdma_uart4_rx);
myPrintfChar("Test myPrintfChar/myPrintfDec/myPrintfHex\r\n");
myPrintfChar("Receive ");
myPrintfDec(uart4RxLength);
myPrintfChar("(0x");
myPrintfHex(uart4RxLength);
myPrintfChar(") Bytes Wow\r\n");
myPrintfChar("Test myPrintf\r\n");
myPrintf("Receive %d(0x%x) %s Wow Wow. \r\n", uart4RxLength, uart4RxLength, "Bytes");
printf("Test printf\r\n");
printf("Receive %d(0x%x) %s Wow Wow Wow. \r\n", uart4RxLength, uart4RxLength, "Bytes");
__HAL_DMA_DISABLE(&hdma_uart4_rx);
#endif
}
其实,我们不建议在中断里面打印信息,在中断里面,最好只接收数据,做一些简单的判断,尽量让中断程序简短,执行时间短,一般情况下,我们会把处理数据放在主循环里面,下一篇的《STM32 Uart @调试命令的实现》,就把处理数据放在主循环里面处理,以后,我们都这么做吧。
老套路,编译,烧录,重启开发板,打开串口调试工具,发送一串数据看一看:
我们已经实现了打印,实现在从STM32串口发送信息到PC机的功能,但要调试还是不够的,我们还要从PC往STM32传送各种命令,各种参数啊,咋办?请看下一篇《STM32 Uart @调试命令的实现》。
整个工程及代码呢,请上百度网盘上下载:
链接:https://pan.baidu.com/s/19usUcgZPX8cCRTKt_NPcfg
密码:07on
文件夹:\Stm32CubeMx\Code\Uart_Printf.rar
上一篇:《STM32 Uart 接收不定长数据》
下一篇:《STM32 Uart @调试命令的实现》
回目录:《目录》