stm32实现printf重定向到LCD显示屏

发布者:甜美瞬间最新更新时间:2016-08-03 来源: eefocus关键字:stm32  printf重定向  LCD显示屏 手机看文章 扫描二维码
随时随地手机看文章
  嘿嘿,学习stm32已经有一段时间了。以前纠结过一个问题,(USART)串口的可变参数问题,查找C语言的书终于还是解决了,自己编写了一个USART_printf()函数,功能模仿C语言的printf,实现可变参数处理。有点小成就感。
  我也因此发表了一下C语言可变参数的博文, 同学们有兴趣可以参考一下:
  最近几天在玩LCD显示屏,基本驱动写好了,并写了一个函数支持中文英文混合打印,但是函数功能还是不够强大啊!串口的时候可以使用printf重定向,这么说开printf也可以重定向到LCD?
  基于这个问题,本人昨天着手编写fputc()函数,目的是实现printf重定向到LCD。经过漫长的几个小时,调试成功了!!!调用printf("hello,%s\n", 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); //打印字符ch
    x += 8;                   //跳转到下一个位置, 是否越界有上面函数判断
 
    return ch;
  }
}
至此, 函数封装完成了.
测试调用一下
uint8_t str = "hello world";
printf("你好, 这是一个测试程序\n%s\n", 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

关键字:stm32  printf重定向  LCD显示屏 引用地址:stm32实现printf重定向到LCD显示屏

上一篇:arm汇编实现的跑马灯实验
下一篇:STM32F4之FPU性能的充分发挥-设置要点

推荐阅读最新更新时间:2024-03-16 15:03

STM32的SWD烧录模式No Target Connected 错误的一种情况
一、问题 最近做项目第一次用到STM32F405RGT6这款单片机,之前用的是STM32F407;SMT贴片焊接,拿到板子准备烧录程序进行测试,使用的是STlink的SWD烧录模式,但是MDK弹出“No target connected”,也就是说检测不到单片机! 二、问题分析和尝试 查看了魔法棒的Debug选项中stlink的“setting”,结果如图1所示。 图1.错误状态下的SWD设备检测状态 既然显示检测不到单片机,那么首先考虑是否电路板的电源供给状态会否有错?接地是否可靠?测试结果显示正常;既然供电正常,那么是否是接线错误?检查结果显示正常;那么单片机的BOOT引脚配置呢?资料显示BOOT引脚的配置
[单片机]
<font color='red'>STM32</font>的SWD烧录模式No Target Connected 错误的一种情况
STM32的精确延时
/*---------------------------------------------------------- 文件名称:systick.c 文件描述:sysTick 系统滴答时钟1us中断函数库,中断时间可自由配置 备注:程序默认使用72M时钟,无分频 -----------------------------------------------------------------*/ #include delay.h static __IO u32 TimingDelay; /*---------------------------------------------------------------------
[单片机]
STM32掌机教程9,完成掌机
  这是教程的最后一篇了,完成之前的任务,到这里一切都应当水到渠成,没什么好讲的了。结尾可能略显仓储,未尽之处,自己看代码吧,看不懂的地方可以在下边评论。 修改BGM   之前已经实现了根据不同的按键切换不同的BGM的功能。接下来把切换BGM的代码放到加命、减命、加分、升级的函数中。 //main.c //加命并显示 void add_life(void) { BGM = LIFE_BGM; BGM_change_flg = 1;//修改BGM add_life_cnt++; life++; showNumber(56,2,life,DEC,8,FONT_16_EN); } //难度提升并显示 void leve
[单片机]
STM32驱动OV7670摄像头寻迹(直线)初步调试成功
#include sys.h #include usart.h #include delay.h #include led.h #include usmart.h #include lcd.h #include ov7670.h #include exti.h #include timer.h //ALIENTEK Mini STM32开发板扩展实验9 extern u8 ov_sta; //在exit.c里面定义 extern u8 ov_frame; //在timer.c里面定义 //更新LCD显示 void camera_refresh(void) { u32
[单片机]
STM32的GPIO的寄存器配置学习1
本篇文章主要是学习以M3内核的STM32的GPIO的寄存器的配置,为什么要学习寄存器,而不利用库函数呢?我只能说为了让学的知识更加牢固吧!当然,你可以直接去利用库函数,但是如果你能认真读完本篇博客,你会对知识豁然开朗!加油吧! STM32 的每个 IO 端口都有 7 个寄存器来控制。他们分别是:配置模式的 2 个 32 位的端口配置寄存器 CRL 和 CRH;2 个 32 位的数据寄存器 IDR 和 ODR;1 个 32 位的置位/复位寄存器BSRR;一个 16 位的复位寄存器 BRR;1 个 32 位的锁存寄存器 LCKR;这里我们仅介绍常用的几个寄存器,我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、OD
[单片机]
<font color='red'>STM32</font>的GPIO的寄存器配置学习1
CMSIS-RTOS是什么?
CMSIS:Cortex Microcontroller Software Interface Standard,Cortex微控制器软件接口标准。它包含的内容比较多: CMSIS-RTOS:主要用于RTOS的API,可与中间件和库组件实现一致的软件层。 CMSIS-DSP:Arm针对各种Cortex-M处理器内核进行了优化的丰富DSP功能的集合。 CMSIS-Driver:接口可用于许多微控制器系列。 CMSIS-Pack:定义了包含软件组件的软件包。 CMSIS-SVD:可通过当前寄存器状态显示设备外设的详细视图。 CMSIS-DAP:Cortex调试访问端口(DAP)的标准化接口。 CMSIS-NN:高效的神经网络内核的
[单片机]
CMSIS-RTOS是什么?
STM32之DAC君
先来张比如花漂亮的照片、大家请尽情欣赏:因为其够美丽了、所以我就不展现我美丽而销魂的涂鸦了、 鉴赏过之后、我们来看看STM32之DAC的Resume(简历简介): 2个DAC转换器:每个转换器对应1个输出通道 ● 8位或者12位单调输出 ● 12位模式下数据左对齐或者右对齐 ● 同步更新功能 ● 噪声波形生成 ● 三角波形生成 ● 双DAC通道同时或者分别转换 ● 每个通道都有DMA功能 ● 外部触发转换 ● 输入参考电压VREF+ 哇、、哇、、哇、、好多特征呀、、还记得上篇博客中ADC也有很多功能吗?在这里,我觉得,因为其功能多、所以其复杂、、这也没什么奇怪的哈、、 那我
[单片机]
<font color='red'>STM32</font>之DAC君
基于STM32单片机的电子称设计
摘要 电子秤是将检测与转换技术、计算机技术、信息处理、数字技术等技术综合一体的现代新型称重仪器。它与我们日常生活紧密结合息息相关。 电子称主要以单片机作为中心控制单元,通过称重传感器进行模数转换单元,在配以键盘、显示电路及强大软件来组成。电子称不但计量准确、快速方便,更重要的自动称重、数字显示,对人们生活的影响越来越大,广受欢迎。 本系统的设计主要从硬件电路设计,软件编程调试,实物焊接调试三部分进行详细阐述。硬件电路主要是基于单片机为核心的控制单元实现数据的处理,采用压力传感器对数据进行采集,电子秤专用24位AD转换芯片HX711对传感器采集到的模拟量进行AD转换,转换后的数据送到单片机进行处理显示,数据显示由LCD160
[单片机]
基于<font color='red'>STM32</font>单片机的电子称设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved