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

news/2024/11/8 14:49:32/

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

    文字在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_operationsfb_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开发板运行,运行结果如下:
在这里插入图片描述


http://www.ppmy.cn/news/588353.html

相关文章

特权的DIY数码相框总结

本文是对DIY数码相框一章的总结&#xff0c;重在对整个数据通路的理解。 具体的需求参见该书的相关章节。整个数据流是&#xff1a; sd卡控制模块&#xff0c;通过spi接口读sd卡中的数据&#xff0c;给wrfifo进行缓存&#xff0c;满了256byte后wrfifo通知sdram读取fifo中的数…

11、数码相框编写程序之MainPage显存管理与页面规划

文章目录 1、显存的分配和获取1.1、构造显存链表的结构体1.2、显存分配函数1、将我们自己分配的显存加入显存链表2、 将LCD 实际设备FB 显存加入链表 1.3、显存获取函数 2、显示页面图标函数框架构思3、编写MainPage页面3.1、获得显存3.2、描画数据1、判断或得到的显存是否已经…

18.数码相框总结

框架 整体框架如上&#xff0c;我对他们的阅读顺序是按照博客发布的顺序来的&#xff0c;感觉还行&#xff0c;读起来不是特别困难 page文件夹下的代码太多&#xff0c;就不一个一个拿出来粘代码了&#xff0c;关于page文件夹的阅读顺序 **page_manager -> main_page ->…

数码相框笔记

1. 程序框架 1.1 触摸屏: 主按线程,通过socket发给显示进程 --------------------------- 封装事件&#xff1a;ts线程 按键线程 --------------------------- 操作系统 封装的数据有: 时间 类型(点击、上下左右移动) 位置 速度 幅度 1.2 显示…

数码相框项目学习笔记(一)

之前看了韦东山老师的数码相框项目&#xff0c;断断续续学完了&#xff0c;现在再整理回顾&#xff0c;做个笔记记录一下。 项目需求&#xff1a; 实现在开发板上显示、浏览图片文件&#xff0c;并能进行图片的放大、缩小、移动、连播等操作 项目的主体框架&#xff1a; 项目…

数码相框(一、系统框架)

注&#xff1a;本人已购买韦东山第三期项目视频&#xff0c;内容来源《数码相框项目视频》&#xff0c;只用于学习记录&#xff0c;如有侵权&#xff0c;请联系删除。 1.项目流程 ① 弄清需求 (弄清产品需要实现的功能)&#xff1b; ② 设计框架 (怎么实现需求)&#xff1b; ③…

vue3-element-admin 项目说明文档

vue3-element-admin官方文档 | 在线预览 项目介绍 vue3-element-admin 是基于 Vue3 Vite4 TypeScript5 Element-Plus Pinia 等最新主流技术栈构建的后台管理前端模板&#xff08;配套后端源码&#xff09;。 项目有以下特性&#xff1a; 基于 vue-element-admin 升级到…

12、数码相框编写程序之效果演示与代码讲解

文章目录 1、编译方法2、运行3、完整代码讲解1、main函数先进行DebugInit()注册调试模块2、注册调试模块之后初始化调试通道3、注册显示设备4、为显示页面预先分配缓存5、初始化输入设备6、注册编码模块7、注册字库模块8、注册图片解析模块9、注册页面10、运行1、显示页面2、获…