完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
常规打印方法
在STM32的应用中,我们常常对printf进行重定向的方式来把打印信息printf到我们的串口助手。 在MDK环境中,我们常常使用MicroLIB+fputc的方式实现串口打印功能,即: 要实现fputc函数的原因是:printf函数依赖于fputc函数,重新实现fputc内部从串口发送数据即可间接地实现printf打印输出数据到串口。 不知道大家有没有看过正点原子裸机串口相关的例程,他们的串口例程里不使用MicroLIB,而是使用标准库+fputc的方式。相关代码如: #if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle; }; FILE __stdout; /** * @brief 定义_sys_exit()以避免使用半主机模式 * @param void * @return void */ void _sys_exit(int x) { x = x; } int fputc(int ch, FILE *f) { while((USART1->ISR & 0X40) == 0); //循环发送,直到发送完毕 USART1->TDR = (u8) ch; return ch; } #endif 关于这两种方法的一些说明可以查看Mculover666兄的《重定向printf函数到串口输出的多种方法》这篇文章。这篇文章中不仅包含上面的两种方法,而且也包含着在GCC中使用标准库重定向printf的方法。 自己实现一个打印函数 以上的几种方法基本上是改造C库的printf函数来实现串口打印的功能。其实我们也可以自己实现一个串口打印的功能。 printf本身就是一个变参函数,其原型为: int printf (const char *__format, ...);所以,我们要重新封装的一个串口打印函数自然也应该是一个变参函数。具体实现如下: 1、基于STM32的HAL库 左右滑动查看全部代码>>> #define TX_BUF_LEN 256 /* 发送缓冲区容量,根据需要进行调整 */uint8_t TxBuf[TX_BUF_LEN]; /* 发送缓冲区 */void MyPrintf(const char *__format, ...){ va_list ap; va_start(ap, __format); /* 清空发送缓冲区 */ memset(TxBuf, 0x0, TX_BUF_LEN); /* 填充发送缓冲区 */ vsnprintf((char*)TxBuf, TX_BUF_LEN, (const char *)__format, ap); va_end(ap); int len = strlen((const char*)TxBuf); /* 往串口发送数据 */ HAL_UART_Transmit(&huart1, (uint8_t*)&TxBuf, len, 0xFFFF);}因为我们使用printf函数基本不使用其返回值,所以这里直接用void类型了。 自定义变参函数需要用到va_start、va_end等宏,需要包含头文件stdarg.h。关于变参函数的一些学习可以查看网上的一些博文,如: https://www.cnblogs.com/wulei0630/p/9444062.html这里我们使用的是STM32的HAL库,其给我们提供HAL_UART_Transmit接口可以直接把整个发送缓冲区的内容给一次性发出去。 2、基于STM32标准库 若是基于STM32的标准库,就需要一字节一字节的循环发送出去,具体代码如: 左右滑动查看全部代码>>> #define TX_BUF_LEN 256 /* 发送缓冲区容量,根据需要进行调整 */uint8_t TxBuf[TX_BUF_LEN]; /* 发送缓冲区 */void MyPrintf(const char *__format, ...){ va_list ap; va_start(ap, __format); /* 清空发送缓冲区 */ memset(TxBuf, 0x0, TX_BUF_LEN); /* 填充发送缓冲区 */ vsnprintf((char*)TxBuf, TX_BUF_LEN, (const char *)__format, ap); va_end(ap); int len = strlen((const char*)TxBuf); /* 往串口发送数据 */ for (int i = 0; i < len; i++) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); USART_SendData(USART1, TxBuf); }}测试结果: 我们也可以使用我们的MyPrintf函数按照上一篇文章:《C语言、嵌入式中几个非常实用的宏技巧》的方式封装一个宏打印函数: 以上就是我们自定义方式实现的一种串口打印函数。 但是,我想说:对于串口打印的使用,我们没必要自己创建一个打印函数。 看到这,是不是有人想要打我了。。。。看了半天,你却跟我说没必要用。。。 哈哈,别急,我们不应用在串口打印调试方面,那可以用在其它方面呀。 (1)应用一: 比如最近我在实际应用中:我们的MCU跑的是我们老大自己写的一个小的操作系统+我们公司自己开发的上位机。 我们MCU端与上位机使用的是串口通讯,MCU往上位机发送的数据有两种类型,一种是HEX格式数据,一种是字符串数据。 但是我们下位机的这两种数据,在通过串口发送之前都得统一把数据封包交给那个系统通信任务,然后再由通信任务发出去。 在这里,就不能用printf了。老大也针对他的这个系统实现了一个deb_printf函数用于打印调试。 但是,那个函数既复杂又很鸡肋,稍微复杂一点的数据就打印不出来了。 因此我利用上面的思路给它新封装了一个打印调试函数,很好用,完美地兼容了老大的那个系统。具体代码就不分享了,大体代码、思路如上。 (2)应用二: 我们在使用串口与ESP8266模块通讯时,可利用类似这样的方式封装一个发送数据的函数,这个函数的使用可以像printf一样简单。 可以以很简单的方式把数据透传至服务端,比如我以前的毕设中就有这么应用: 最后 以上就是本次的分享。如有错误,欢迎指出!谢谢 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1617 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1543 浏览 1 评论
977 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
683 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1595 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 02:15 , Processed in 0.754282 second(s), Total 78, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号