注:本人已购买韦东山第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。

    文字在LCD上的显示其实就是LCD上的一些点的显示与不显示,这些显示的点就像我们的笔画一样,有笔画经过的地方就显示,没有笔画经过的地方就不显示,这些显示与不显示的点组合在一起就构成了LCD上显示的文字。所以,在LCD上显示文字,需要知道文字的点阵数据。这些点阵数据也称为字模,字库就是字模的集合。

1. ASCII码字库文件使用

在linux-4.15内核目录下搜索font(输入命令:find -name "font*"),可以找到font_8x16.c文件,如下图所示:

我们可以在.font_8x16.c文件中找到8*16的点阵存在fontdata_8x16[]数组里,如下图所示:

在fontdata_8x16[]数组里我们可以找到A(对应ASCII码0x41)的点阵数据,如下图所示,从图中可知,这些点阵数组组成了一个“A”字。

从“A”字的点阵数据可知,一个ASCII字符的点阵数据占据了16字节,所以“A”字的点阵数据位于0x41*16~0x41*16+15之间。在后面的文字显示实现中,我们可以直接将fontdata_8x16[]数组拷贝到应用程序里,用来显示ASCII。

2. HZK16汉字库文件使用

    HZK16 字库是符合GB2312标准的16×16点阵字库,该字库里的16*16汉字需要256个点来显示,所以每个16*16汉字点阵所占的内存为16*16/8 = 32 字节。由数码相框(二、字符的编码方式)的GB2312编码可知,一个汉字的编码使用两个字节表示,其中高字节表示汉字的区号,低字节表示汉字的位号,区号和位号的范围都是0xA1-0xFE(一共有94个区号、94个位号)。要在字库里找到对应汉字的点阵数据,必须知道汉字的区码和位码(其实汉字的区位码就是汉字点阵数据的索引)。

区码: 区号(汉字的第一个字节)- 0xA0
位码: 位号(汉字的第二个字节)- 0xA0
(注:因为汉字编码是从0xA0区开始的,所以文件最前面就是从0xA0区开始,要算出相对区码)
因此,汉字在HZK16汉字库的绝对偏移地址为:
offset = (94 * (区码 - 1) + (位码 - 1)) * 32
注:① 区码减1是因为数组是以0为开始而区号位号是以1为开始的;
    ②最后乘以32是因为HZK16中每个汉字的点阵数据占据32字节。

以“中”为例,“中”的GBK编码为D6 D0,所以:
区码 = 0xD6 - 0xA0 = 0x36;
位码 = 0xD0 - 0xA0 = 0x30;
offset = (94 * (0x36 - 1) + (0x30 -1)) * 32 = (94 * 0x35 + 0x2F) * 32
因此:“中”的点阵数据位于 (94 * 0x35 + 0x2F) * 32 ~ (94 * 0x35 + 0x2F) * 32 + 31。

3. LCD 显示文字

(1) 以读写方式打开LCD设备fb0;
(2) 利用ioctl函数直接获取LCD的 var 和 fix 相关参数:
对于 LCD 设备fb0,它的file_operations是fb_fops,其中ioctl函数对应fb_fops的结构体成员函数unlocked_ioctl(.unlocked_ioctl = fb_ioctl),最终通过fb_ioctl函数调用do_fb_ioctl函数获取LCD的var 和 fix 相关参数,内核函数fb_ioctl、do_fb_ioctl的代码如下:

static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{struct fb_info *info = file_fb_info(file);if (!info)return -ENODEV;return do_fb_ioctl(info, cmd, arg);
}static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
{struct fb_ops *fb;struct fb_var_screeninfo var;struct fb_fix_screeninfo fix;struct fb_con2fbmap con2fb;struct fb_cmap cmap_from;struct fb_cmap_user cmap;struct fb_event event;void __user *argp = (void __user *)arg;long ret = 0;switch (cmd) {case FBIOGET_VSCREENINFO:if (!lock_fb_info(info))return -ENODEV;var = info->var;unlock_fb_info(info);ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;break;case FBIOPUT_VSCREENINFO:if (copy_from_user(&var, argp, sizeof(var)))return -EFAULT;console_lock();if (!lock_fb_info(info)) {console_unlock();return -ENODEV;}info->flags |= FBINFO_MISC_USEREVENT;ret = fb_set_var(info, &var);info->flags &= ~FBINFO_MISC_USEREVENT;unlock_fb_info(info);console_unlock();if (!ret && copy_to_user(argp, &var, sizeof(var)))ret = -EFAULT;break;case FBIOGET_FSCREENINFO:if (!lock_fb_info(info))return -ENODEV;fix = info->fix;unlock_fb_info(info);ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;break;case FBIOPUTCMAP:if (copy_from_user(&cmap, argp, sizeof(cmap)))return -EFAULT;ret = fb_set_user_cmap(&cmap, info);break;case FBIOGETCMAP:if (copy_from_user(&cmap, argp, sizeof(cmap)))return -EFAULT;if (!lock_fb_info(info))return -ENODEV;cmap_from = info->cmap;unlock_fb_info(info);ret = fb_cmap_to_user(&cmap_from, &cmap);break;case FBIOPAN_DISPLAY:if (copy_from_user(&var, argp, sizeof(var)))return -EFAULT;console_lock();if (!lock_fb_info(info)) {console_unlock();return -ENODEV;}ret = fb_pan_display(info, &var);unlock_fb_info(info);console_unlock();if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))return -EFAULT;break;case FBIO_CURSOR:ret = -EINVAL;break;case FBIOGET_CON2FBMAP:if (copy_from_user(&con2fb, argp, sizeof(con2fb)))return -EFAULT;if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)return -EINVAL;con2fb.framebuffer = -1;event.data = &con2fb;if (!lock_fb_info(info))return -ENODEV;event.info = info;fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);unlock_fb_info(info);ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;break;case FBIOPUT_CON2FBMAP:if (copy_from_user(&con2fb, argp, sizeof(con2fb)))return -EFAULT;if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)return -EINVAL;if (con2fb.framebuffer >= FB_MAX)return -EINVAL;if (!registered_fb[con2fb.framebuffer])request_module("fb%d", con2fb.framebuffer);if (!registered_fb[con2fb.framebuffer]) {ret = -EINVAL;break;}event.data = &con2fb;console_lock();if (!lock_fb_info(info)) {console_unlock();return -ENODEV;}event.info = info;ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);unlock_fb_info(info);console_unlock();break;case FBIOBLANK:console_lock();if (!lock_fb_info(info)) {console_unlock();return -ENODEV;}info->flags |= FBINFO_MISC_USEREVENT;ret = fb_blank(info, arg);info->flags &= ~FBINFO_MISC_USEREVENT;unlock_fb_info(info);console_unlock();break;default:if (!lock_fb_info(info))return -ENODEV;fb = info->fbops;if (fb->fb_ioctl)ret = fb->fb_ioctl(info, cmd, arg);elseret = -ENOTTY;unlock_fb_info(info);}return ret;
}

从上面的代码可知,在用户空间调用以下函数可以获取LCD的 var 和 fix 驱动数据:

ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)  /*FBIOGET_VSCREENINFO:获取fb_info-> var成员(可变信息:xy分辨率,像素位数等)*/
ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)  /*FBIOGET_FSCREENINFO:获取fb_info-> fix成员(固定信息:缓存地址,每行字节数)*/

(3) 利用mamp()函数申请一段用户空间的内存区域,并映射到内核空间某个内存区域;
mamp() 函数原型如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

返回值: 失败返回-1,并设置errno值.成功,返回映射的地址指针.若指定start则返回0;
addr: 需要映射的内存起始地址,通常填NULL,表示让系统自动映射,映射成功后返回该地址;
length: 映射地址的大小,填LCD显存字节数即可,因为2440一个地址存8位;
prot: 对映射地址的保护(protect)方式,常用组合如下:
       PROT_EXEC 映射区域可被执行
       PROT_READ 映射区域可被读取
       PROT_WRITE 映射区域可被写入
       PROT_NONE 映射区域不可访问

flag: 填MAP_SHARED即可,表示共享此映射,对其它进程可见.
fd: 需要将内存映射到哪个文件描述符(以后便可以直接通过内存来直接操作该文件)
offset: 映射偏移值,填0即可.

int munmap(void *addr, size_t length);

返回值: 成功返回0,失败返回-1,并设置errno值;
addr: 要取消映射的内存起始地址;
length: 映射地址的大小;

LCD显示文字的应用程序代码如下:(文件名为show_a.c)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
#include <unistd.h>
#include "fb.h" /*把8x16 ASCII码字库文件拷贝到fb.h, 成一个头文件*/unsigned char *fbmem;      /*framebuffer mem*/
unsigned int  line_width;  /*每一行数据的大小(字节)*/
unsigned int  pixel_width; /*每一个像素的大小(字节)*/struct fb_var_screeninfo var; /*LCD 显示屏的可变参数结构 var*/
struct fb_fix_screeninfo fix; /*LCD 显示屏的固定参数结构 fix*/
int screen_size;unsigned char *hzkmem;void lcd_put_pixel(unsigned int x, unsigned int y, unsigned int color)
{unsigned char  *pen_8  = fbmem + y * line_width + x * pixel_width;unsigned short *pen_16 = (unsigned short *)pen_8;unsigned int   *pen_32 = (unsigned int   *)pen_8;unsigned int red, green, blue;switch (var.bits_per_pixel){case 8:{*pen_8 = (unsigned char)color;break;}case 16:{/*RGB565 格式* 对于 16BPP: color 的格式为 0xAARRGGBB (AA = 透明度,此处为 0),需要转换为 5:6:5 格式*/red   = (color >> 16) & 0xff;green = (color >> 8)  & 0xff;blue  = (color >> 0)  & 0xff;  *pen_16 = (unsigned short)(((red >> 3) << 11) | ((green >> 2) << 5) | ((blue >> 3)<< 0)) & 0xffff;break;}case 32:{   *pen_32 = color;break;}default:{printf("can't surport %dbpp\n", var.bits_per_pixel);break;}}
}void lcd_put_ascii(unsigned int x, unsigned int y, unsigned char c)
{unsigned char *dots = (unsigned char *)&fontdata_8x16[c * 16];unsigned char byte;unsigned int i,j;for(i = 0; i < 16; i++){byte = dots[i];for(j = 0; j < 8; j++){if(byte & (1 << (7-j))){lcd_put_pixel(x + j, y + i, 0xffffff); /*显示白色*/}else{lcd_put_pixel(x + j, y + i, 0); /*显示黑色*/}}}
}void lcd_put_chinese(unsigned int x, unsigned int y, unsigned char *str)
{   /*GB2312 编码*/unsigned int area  = str[0] - 0xA1;unsigned int where = str[1] - 0xA1;unsigned char *dots =  hzkmem + (area * 94 + where) * 32;/*16*16的汉字,每一个汉字的位图所占的内存为 16*16/8 = 32 字节*/unsigned char byte;unsigned int i, j, k;for(i = 0; i < 16; i++) /*总共16行*/{for(j = 0; j < 2; j++) /*一行占据两个字节*/{byte = dots[i*2 + j];for(k = 0; k < 8; k++){if(byte & (1 << (7-k))){lcd_put_pixel(x + j*8 + k, y + i, 0xffffff); /*显示白色*/}else{lcd_put_pixel(x + j*8 + k, y + i, 0); /*显示黑色*/}}}}
}int main(int argc, char **argv)
{int fd_fb, fd_hzk;struct stat hzk_stat;int errno;/*打开framebuffer*/fd_fb = open("/dev/fb0", O_RDWR);if(fd_fb < 0){fprintf(stderr, "Can't open /dev/fb0: %s\n", strerror(errno));return -1;}/*利用ioctl直接获取lcd 的var 和 fix 相关参数*/if(ioctl(fd_fb,  FBIOGET_VSCREENINFO, &var)){printf("can't get var\n");return -1;}if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)){printf("can't get fix\n");}line_width   = var.xres * var.bits_per_pixel / 8;pixel_width = var.bits_per_pixel / 8;screen_size = var.xres * var.yres * var.bits_per_pixel / 8;fbmem = (unsigned char *)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if(fbmem == (unsigned char *)-1){printf("can't mmap\n");return -1;}fd_hzk  =  open("HZK16", O_RDONLY);if(fd_hzk< 0){fprintf(stderr, "Can't open HZK16: %s\n", strerror(errno));return -1;}if(fstat(fd_hzk, &hzk_stat)){printf("can't get fstat\n");return -1;}hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk, 0);if(hzkmem == (unsigned char *)-1){printf("can't mmap for hzk\n");return -1;}/*清屏,将屏幕全部写 0,置黑 */memset(fbmem, 0, screen_size);/* 利用 lcd_put_ascii 函数,向 LCD 写入字符'A'*/lcd_put_ascii(var.xres/2, var.yres/2, 'A');lcd_put_chinese(var.xres/2 + 8, var.yres/2,"中");munmap(hzkmem,hzk_start.st_size);munmap(fbmem,screensize);return 0;
}

执行以下命令编译应用程序:

arm-linux-gcc -o show_a show_a.c -fexec-charset=GBK

把编译好的应用程序拷贝到Jz2440开发板运行,运行结果如下:

数码相框(三、LCD显示文字)相关推荐

  1. Arduino TFT_eSPI库来驱动SPI接口的LCD显示文字详解

    Arduino TFT_eSPI库来驱动SPI接口的LCD显示详解 相关库github地址:https://github.com/Bodmer/TFT_eSPI 文字显示过程详解 TFT eSPI显示 ...

  2. python arduino i2c1602_Arduino基础入门—3.连接 IIC 1602 LCD显示文字

    1. IIC转接板介绍 Arduino Uno R3开发板的外部IO口是非常有限的.在驱动LCD1602时,尽管我们的数据线使用了4线,相对于8线方式减少一半,但是在需要外接多种传感器的应用中,4线驱 ...

  3. 数码相框(五、使用freetype库在LCD显示几行文字)

    注:本人已购买韦东山第三期项目视频,内容来源<数码相框项目视频>,只用于学习记录,如有侵权,请联系删除. 1.在LCD显示几行文字 (1) 在LCD显示几行文字,我们分为两种显示方法: ① ...

  4. 数码相框 在LCD上显示多行文字(6)

    数码相框 在LCD上显示多行文字(6) 目的: 1.从左边起显示几行文字 2.居中显示几行文字 在LCD上显示下列两行文字: 我是程序员gif Hello World 分析: 1.从左边起显示几行文字 ...

  5. 【正点原子FPGA连载】 第三十章双目OV5640摄像头LCD显示实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

    1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670 3)全套实验源码+手册+视频下载地址: h ...

  6. python+OpenCV图像处理(三)绘制简单的几何图形、显示文字

    绘制简单的几何图形.显示文字 (一)绘制直线和矩形 img = np.zeros([512, 512, 3]) # line函数用来画直线,第一个参数可以理解为画布矩阵, # 第二个参数pt1是直线的 ...

  7. 【龙芯1B】:LCD显示图片文字背景色前景色、小创语音控制lcd显示、数码管倒计时

    项目场景:     闲来无事,写了几个关于嵌入式技能大赛的任务.希望对大家有所帮助.本文开发板由百科荣创的龙芯1b开发板支持,关于嵌入式技能大赛的开发板.  LCD显示图片&文字&背景 ...

  8. 【正点原子FPGA连载】第三十二章RTC实时时钟LCD显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  9. 嵌入式应用-详解移植并使用freetype显示文字

    目录 前言 1. freetype和相关概念简介 2.freetype显示文字流程和主要函数 2.1 包含头文件及API头文件:ft2build.h 2.2 初始化: FT_InitFreetype ...

最新文章

  1. 批量获取成员机管理员组用户信息
  2. 技术面试时该反问面试官什么问题?小伙整理了灵魂50问,GitHub日入2500星
  3. faster rcnn源码解读(五)之layer(网络里的input-data)
  4. 根据BAPI_PO_CREATE1创建采购订单
  5. 算法题题目集合一,欢迎评论留言
  6. ArcGIS实验教程——实验十八:叠置分析(Overlay Analysis)
  7. java中n次方怎么表示_java如何计算一个数的n次方
  8. Android camera(4)---Android Camera开发之基础知识篇
  9. pthread_join来接收线程的返回参数
  10. Java-static概述
  11. linux fdisk ntfs,2014.1.2 学习记录(fdisk、ntfs)
  12. TCP/UDP压力测试工具
  13. postgresql 修改表字段的长度
  14. python实现阿里云盘同步功能(sync_folder)
  15. librosa实现音频格式转换(单曲批量) | MP3转wav
  16. 笔记本重装系统后office没了
  17. 2020 年最具潜力 44 个顶级开源项目,涵盖 11 类 AI 学习框架、平台(值得收藏)...
  18. loadstring的用法
  19. win10如何开启电源高性能模式
  20. python requests cookie处理

热门文章

  1. 排序-JAVA实现【四】堆排序
  2. 数据库日常之修改MySQL数据库密码的方法
  3. Docker创建容器时指定IP
  4. nginx的下载与安装
  5. 前端和后端的JSON数据交互
  6. 大计基笔记(1)数学运算
  7. Django使用旧有的数据库
  8. 风投 红杉资本 Sequoia Capital
  9. OSCP-Pelican(Exhibitor、gcore提权)
  10. 离线数仓搭建_15_ADS层数据构建