完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
学习stm32已经有一段时间了。以前纠结过一个问题,(USART)串口的可变参数问题,查找C语言的书终于还是解决了,自己编写了一个USART_printf()函数,功能模仿C语言的printf,实现可变参数处理。有点小成就感。
我也因此发表了一下C语言可变参数的博文 最近几天在玩LCD显示屏,基本驱动写好了,并写了一个函数支持中文英文混合打印,但是函数功能还是不够强大啊!串口的时候可以使用printf重定向,这么说开printf也可以重定向到LCD? 基于这个问题,本人昨天着手编写fputc()函数,目的是实现printf重定向到LCD。经过漫长的几个小时,调试成功了!!!调用printf(“hello,%sn”,str)其中str内容是“world“;实现了在LCD上打印hello,world了。嘿嘿,后续的学习就方便很多了。 首先还是同样先复习一下printf重定向到USART,原理是修改fputc()函数,将传递进来的参数转发到USART(串口),具体fputc()函数内容编写可以参考以下例子: int fputc(int ch, FILE* f) { if(ch == ‘n’) { USART_SendData(USART1,’r’); USART_SendData(USART1,’n’); } USART_SendData(USART1, (uint8_t)ch); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); return ch; } 看起来很熟悉吧。但是我们现在是要把它重定向到LCD,怎么实现呢? 同样的方法,把传递进来的参数转发到LCD就行了!嘿嘿,首先要有LCD驱动,也就是有LCD打印一个字符的函数(中文是两个字节),举一个例子。假设已经有一个打印一个字符的函数 LCD_Ascii_one(); 函数参数先不理,来到这一步就已经成功了一半了。像串口一样,只不过把 USART_SendData(USART1, (uint8_t)ch); 修改成 LCD_Ascii_One(…);//其中参数…中包含ch信息 这样, 整个框架就形成了, 下面是具体实现过程 先说明一下我的LCD_Ascii_One函数: LAC_Ascii_One(uint8_t X, uint16_t Y, uint8_t *Chinese,uint16_t Color); 四个参数的含义分别为: uint8_t X 打印字符的横坐标 uint16_t Y 打印字符的纵坐标 uint8_t *Chinese 打印字符的字模首地址 //字模提取软件得到的,字模放在sdcard, 使用时需要打开文件 uint16_t Color 打印字符的颜色 获取字符的字模不是这里的重点, 同学们另找资料. string = Get_Ascii(‘a’); //获取字符a的字模 调用LCD_Ascii_One(0, 10, string, WHITE);//打印 就实现了在LCD上的位置(0, 10)打印字符’a’, 颜色为白色 所以fputc函数里面会有这么一句 LCD_Ascii_One(x, y, ch, color); 不过还是不够, 我们需要打印一个字符后跳到下一个位置, 不然下次打印就覆盖了本次字符了 所以还必须封装x, y, 让他们带有记忆功能, 呵呵,关键字static派上用场了 fputc函数需要声明 static uint16_t x = 0, y = 0; 为什么是uint16_t 应为我的LCD显示屏是240 * 320的 uint8_t 无法表示320, 所以是uint16_t, x也就顺便uint16_t了哈 调用玩LCD_Ascii_one之后 x += 8; //因为我提取字模是8 * 16的Ascii字模 还有一部是fputc需要的 return ch; 这样功能就完善一点了,加把劲, 继续换行功能 在调用LCD_Ascii_One()之前, 先判断x是否越界, 也就是当前这一行已经无法打印一个字符了 if(x > X_MAX - 8) //X_MAX是宏定义,该值为240 { x = 0; //x置零 y += 16; //y移至下一行 } 但是y也可能越界, 也就是说已经到了LCD的底部了, 我这里处理是越界直接退出, 不打印字符了 if(y > Y_MAX - 16) //Y_MAX是宏定义,该值是320 { return ch; //直接退出 } 换行功能还有调用printf()时候的’n’ 所以还是老样子, 跟串口一样: if(ch == ‘n’) { x = 0; y += 16; } 是否越界是上面的函数在判断. 来到这里, 我们重新再这里一下fputc的内容 需要打印一个字符自动跳到下一个位置, x越界换行, y越界退出 当然这里只是一个框架, 还有其他需要封装的, 例如中文, Ascii区分, 接下来会讨论 只是fputc至少必须包含语句: int fputc(int ch, FILE* f) { static uint16_t x = 0, y = 0; uint8_t string[16]; if(ch == ‘n’) //换行 { x = 0; y += 16; return ch; } if(x > X_MAX - 8) //X_MAX是宏定义, 该值为240 { x = 0; //x置零 y += 16; //y移至下一行 } if(y > Y_MAX - 16) //Y_MAX是宏定义, 该值是320 { return ch; //直接退出 } string = Get_Ascii(ch); //获取ch的字模 LCD_Ascii_One(x, y, string, color);//打印字符ch x += 8; //跳转到下一个位置, 是否越界有上面函数判断 return ch; } (能够理解的同学接下来可以测试自己封装一下中英文了, 接下来我们继续讨论实现中英文混输.) 嘿嘿, 我的大概框架就是这样, 当然这样的功能还是不够强大的, 对, 我们需要的是再强大一点的printf, 接下来是,实现中英文混输, 可变参数C语言已经帮我们实现了, 也就是说我们调用printf(“hello %s”,str);已经能打印出hello world并且换行了, 但是如果printf(“你好n”);会怎么样? 乱码? 没错,我们现在只是实现了Ascii打印, 并没有中文, 这是与串口所不同的, 因为串口打印到电脑的超级终端,超级终端已经帮我们实现了这个功能了, 所以printf重定向到LCD还有再封装, 对, 要更强大的函数,实现完美的打印功能!! 中英文混输先要有一点预备知识. 1. 一个中文占两个字节, 一个Ascii占一个字节. 2. Ascii最高字节为0, 中文的两个字节最高字节都是1 上面的第二点再说明一下 如果将Ascii看成是无符号数字, 它的数值小于128, 也就是0~127 如果将中文的两个字节分别看成无符号数字, 第一个字节范围为128~255, 第二个字节也是128~255 嘿嘿, 判断中英文就有办法了~~~~ if(ch & 0x80) //判断最高位是否为1, 最高位为1表达式ch& 0x80为真 { 中文字节; } else { Ascii字节; } 中文有两个字节, 所以要进入fputc两个才能打印出一个中文, 所以还必须把先进来的ch保存起来, 等下一个字节进来在打印,当然Ascii如果进来就直接打印. 这样我们又要有记忆功能的tmp[2]分别保存中文的两个字节了.并且需要记录是计入的是第一个中文字节还是第二个中文字节 static uint8_t flag; static uint8_t tmp[2]; 当然, 要打印中文, 还必须有一个打印中文的函数, 我这里的函数是 LCD_Chinese_One(uint8_t X, uint16_t Y, uint8_t *Chinese,uint16_t Color); uint8_t X 打印中文的横坐标 uint16_t Y 打印中文的纵坐标 uint8_t *Chinese 打印中文的字模 uint16_t Color 打印中文的颜色 string = Get_Chinese(“好”); //获取’好’的字模 例如调用LCD_Chinese_One(5, 20, string, WHITE); //打印 就在LCD的位置(5, 20)打印"好", 颜色为白色 来到这里, 相信同学们都懂了吧, 啰嗦一下, 我们再来看看fputc应该怎么写 int fputc(int ch, FILE* f) { static uint16_t x = 0, y = 0; //坐标 uint8_t string[32]; //读取字模数组 static uint8_t flag = 0; //汉字第一第二字节标志 static uint8_t tmp[2]; //保存汉字的两个字节 if(ch == ‘n’) //换行 { x = 0; y += 16; return ch; } if(ch & 0x80) //先判断是否为中文 { if(flag == 0) //中文的第一个字节还是第二个字节 { flag= 1; //接下来是第二个字节 tmp[0] = ch; //记录该字节 return ch; //按照C语言的fputc需要返回ch } else { flag= 0; tmp[1] = ch; if(x> X_MAX - 16) //判断是否越界 { x = 0; //换行处理 y += 16; //字模大小为16*16 } if(y> Y_MAX - 16) //行越界 { return ch; //直接退出处理 } string = Get_Chinese(tmp); LCD_Chinese_One(x, y, string, color); x +=16; return ch; } } else { if(x > X_MAX - 8) //X_MAX是宏定义, 该值为240 { x =0; //x置零 y +=16; //y移至下一行 } if(y > Y_MAX - 16) //Y_MAX是宏定义, 该值是320 { return ch; //直接退出 } string = Get_Ascii(ch); //获取ch的字模LCD_Ascii_One(x, y,string, color); //打印字符chx += 8; //跳转到下一个位置, 是否越界有上面函数判断return ch; } } 至此, 函数封装完成了. 测试调用一下 uint8_t str = “hello world”; printf(“你好, 这是一个测试程序n%sn”, str); 在LCD上打印的是 你好, 这是一个测试程序 hello world 嘿嘿, 重定向成功了!!! 相信已经能满足广大开发者的需求了, 有待完善的地方是控制符’t”r’等, 不过都是相当简单的. 下面给出我的封装, 不过我是把字模放到sdcard的, 所以需要打开文件, 读取文件等操作, 但是原理还是一样的,嘿嘿~~ ifdef STDOUT_LCD int fputc(int ch, FILE* f) { static uint32_t Offset, Read_Count; static uint8_t Read_Font[32]; static FRESULT File_Status; static FIL File; static uint16_t x = 0, y = 0; static uint8_t flag = 0; static uint8_t tmp[2]; File_Status = f_open(&File, CHINESE_FONT, FA_OPEN_EXISTING| FA_READ); //中文字库 if(ch == ‘n’) //换行处理 { x = 0; //… y += 18; //… return ch; } if(ch & 0x80) //中文处理 { if(flag == 0) //中文的两个字节判断, flag == 0 为第一个字节 { tmp[0] = (uint8_t)(ch); //把第一个字节保存进来 flag = 1; //接下来是第二个字节 return ch; //按照C语言官方库, 返回ch } else //是中文的第二个字节 { tmp[1] = (uint8_t)(ch); //保存该字节 flag = 0; //下一次是第一个字节 if(x > X_MAX - 16) //判断是否需要换行 { x = 0; //换行操作… y += 18; //… } if(y > Y_MAX - 16) //判断是否越界 { return ch; //越界退出 } File_Status = f_open(&File, CHINESE_FONT, FA_OPEN_EXISTING| FA_READ); if(Check_FileStatus(File_Status)) //判断文件操作状态 { Offset = Chinese_Offset(tmp); //根据中文字库字模提取软件计算出汉字偏移位置 f_lseek(&File, Offset); //文件指针移至偏移位置 } if(Check_FileStatus(File_Status)) //判断文件操作状态 { File_Status = f_read(&File, Read_Font, 32,&Read_Count); } if(Check_FileStatus(File_Status)) //判断文件操作状态 { LCD_Chinese_One(x, y, Read_Font, WHITE);//调用中文打印函数打印汉字 x += 16; //液晶横坐标+16, 中文为16*16的 } File_Status = f_close(&File); //关闭文件 } } else { if(x > X_MAX - 8) //判断是否需要换行 { x = 0; //换行操作… y += 18; //… } if(y > Y_MAX - 16) //判断是否越界 { return ch; //越界退出 } File_Status = f_open(&File, ASCII_FONT, FA_OPEN_EXISTING |FA_READ); if(Check_FileStatus(File_Status)) //判断文件操作状态 { Offset = ((uint8_t)(ch) - 0x20) << 4; //计算出Ascii偏移地址,要先跳过Ascii控制字符 f_lseek(&File, Offset); //文件指针移至偏移位置 } if(Check_FileStatus(File_Status))//判断文件操作状态 { f_read(&File, Read_Font, 16,&Read_Count);//读取字模信息 } if(Check_FileStatus(File_Status))//判断文件操作状态 { LCD_Ascii_One(x, y, Read_Font, WHITE); //调用Ascii打印函数打印字符 x += 8;//Ascii像素为8*16, 横坐标+8 } File_Status = f_close(&File); //关闭文件 } return ch; } endif //#ifdef STDOUT_LCD |
|
|
|
只有小组成员才能发言,加入小组>>
调试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?各有什么优势啊?
645浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
516浏览 3评论
532浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
505浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 11:23 , Processed in 0.584861 second(s), Total 44, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号