#stm32调试使用printf向OLED打印信息(printf重定向),并简单实现转义字符“\n”和“\t”#
printf 是 C 语言中用于格式化输出的函数,可以向标准设备输出各种类型的数据。使用printf在调试过程输出打印数据,可以大大简化调试过程。而且向OLED输出可以直观的观察数据的变化,但是我是用的是0.96寸小屏,只能显示4*16的ascii码能表示的字符;
1.使用Keil需要打开MicroLIB
使用Keil的话需要打开MicroLIB,MicroLIB是ARM-MDK官网提供的一个为ARM基础的嵌入式应用程序编写的高度优化的库。MicroLIB专为深度嵌入式应用设计,优化了代码和数据存储器的使用。如果使用Stm32CubeIDE或者Stm32CubeMX直接生成的MDK-ARM工程可以直接使用printf();不用打开MicreLIB,但是依旧需要对printf进行重定向打印位置。
2.printf重定向
printf重定向,主要是通过对MicroLIB自带的库函数fputc()进行重定义,如果编译器检测到用户定义了与库函数同名的函数,会优先使用用户定义的函数。使用printf前需要先#include<stdio.h>
int fputc(int ch, FILE *f) //fputc函数原型
这个函数是printf对用户的想要达到的输出结果字符串,通过fputc一个字符一个字符的发送到标准输出设备上,但是对与嵌入式系统,通常没有标准输出设备,因此用户可以通过自定义fputc函数指定输出设备;
3.实现fputc
fputc函数代码实现;
static int gOled_PrintPosRow = 0; //输出光标行位置
static int gOled_PrintPosCow = 0; //输出光标列位置(列位置是128列)
int fputc(int ch, FILE *f){
if( ch >= ' ' && ch <= '~') //其他不在字库中的ascii码禁止显示
{
gOled_PrintPosRow %= OLED_HEIGHT / 8; //实现光标的自动换行
gOled_PrintPosCow %= OLED_WIDTH;
if( 0 == gOled_PrintPosCow && ' ' == ch) //默认第一列不显示空格
return ch;
draw_Char(gOled_PrintPosRow, gOled_PrintPosCow, (uint8_t)ch);
//字符发送函数,可以替换成自己的字符发送函数
gOled_PrintPosCow += 8;
if(gOled_PrintPosCow > 127) gOled_PrintPosRow += 2;
}
return ch;
}
注意到 fputc函数接收的ch是int类型,32位数据,所以ch接收UTF-8类型的编码,因此可以输出中文,但是对于OLED,中文字库占的内存太大,所以只显示ASCII码;
4.使用printf的局限性
printf只能向一个输出设备,但是很多情况下往往不会只想要在一个输出设备上打印信息,最好能在多个设备上打印信息,最好能自己指定在那个设备上打印信息;
5.为每个输出设备创建自己的输出函数
#include <stdarg.h> //需要包含此头文件
//OLED打印函数
void OLED_printf(char *format, ...)
{
static char StringBuff[STRINGBUFFSIZE] ; //创建OLED打印BUFF
va_list arg;
va_start(arg, format);
vsprintf(StringBuff, format, arg); //将打印字符串写入到StringBuff中
va_end(arg);
OLED_SendString(StringBuff); //调用自己的字符串发送函数,发送格式化输出字符串
}
static void OLED_SendString(char * String )//这里应该放在OLED_printf前面,为了说明暂时放在这里
{
//发送 StringBuff
int i, DataNum = 0;
int RestDataNum;
const char* DataArray = String;
while(*DataArray != '\0')
{
DataArray ++;
DataNum ++;
}
for(i = 0; i < DataNum; i ++)
{
if(String[i] == '\n' )//实现转义字符\n
{
if(gOled_PrintPosCow == OLED_WIDTH)
{
continue;
}
gOled_PrintPosRow += 2;
gOled_PrintPosCow = 0;
continue;
}
if(String[i] == '\t' )//实现转义字符\t(两个空格)
{
if(gOled_PrintPosCow == OLED_WIDTH)
{
continue;
}
else if(gOled_PrintPosCow == OLED_WIDTH - 8)
{
gOled_PrintPosCow = OLED_WIDTH;
continue;
}
else
{
gOled_PrintPosCow +=16;
continue;
}
}
gOled_PrintPosRow %= OLED_HEIGHT / 8;
gOled_PrintPosCow %= OLED_WIDTH;
draw_Char(gOled_PrintPosRow, gOled_PrintPosCow, String[i]);//字符发送函数
//字符发送函数,可以替换成自己的字符发送函数
gOled_PrintPosCow += 8;
if(gOled_PrintPosCow >127) gOled_PrintPosRow += 2;
}
}
6.效果展示
测试代码:
printf("Time:\t%d:%d:%d\n",Hour , Minute, Second); //显示时间
printf("The ascii of 'A' is %#X\n ", 'A'); //%X显示十六进制数,%#X加上0x前缀
OLED_printf("Time:\t%d:%d:%d\n",Hour , Minute, Second); //OLED_printf显示时间
显示效果: