• 在LCD上显示字母、汉字
  • 从字母和汉字的点阵取出数据,在LCD上显示
  • LCD就可以理解为一个非常大的点阵,我们对每个点进行操作得出我们想要的文字或图片
  • 在内存中划出一块空间,称为framebuffer或显存
  • LCD控制器会从framebuffer取出若干字节数据(代表一个像素),发给LCD
  • LCD控制器会循环不断的从framebuffer取数据发给LCD
  • 所以说,我们只要修改frame buffer中的数据,就可以在LCD上显示了

image.png

  • 想在某个位置显示一个字符,那么就要在frame buffer里找到那个位置,根据点阵设置frame buffer里的数据
  • 终端上输入:echo hello > /dev/tty1 这样就可以在LCD上显示”hello”
    • 显然,在LCD上显示一个字符,肯定要有它的点阵
    • 所以内核中肯定有英文字母的点阵
  • 内核中的font字体文件:image.png
  • 对于英文字母,点阵就是一个数组
  • 816个像素点阵,每个像素要么显示要么不显示,则大小为816bit=16字节;其中的每一位表示一个像素点
  • 例如字母A:image.png 每行占一个字节,一共16行 ```c //dot_font

int fd_fb; struct fb_var_screeninfo var; / current var / struct fb_fix_screeninfo fix; / current fix / int screen_size; unsigned char fbmem = null; unsigned int line_width; unsigned int pixel_width; int fd_hzk16; unsigned char hzkmem; struct stat hzk_stat;

//点阵数组

define FONTDATAMAX 4096

static const unsigned char fontdata_8x16[FONTDATAMAX] = {…};

/ color: 0x00RRGGBB / void lcd_put_pixel(int x, ing y, unsigned int color) { / 在屏幕上怎么显示一个点? / unsigned char pen_8 = fbmem + yline_width + xpixel_width; unsigned short pen_16; unsigned int pen_32; / 对于不同的像素宽度,用不同的变量表示 / pen_16 = (unsigned short )pen_8; pen_32 = (unsigned int *)pen_8;

  1. unsigned int red, green, blue;
  2. switch(var.bits_per_pixel)
  3. {
  4. case 8:
  5. {
  6. *pen_8 = color;
  7. break;
  8. }
  9. case 16:
  10. {
  11. /* RGB:565bit */
  12. red = (color >> 16) & 0xff;
  13. green = (color >> 8) & 0xff;
  14. blue = color & 0xff;
  15. *pen_16 = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
  16. break;
  17. }
  18. case 32: //大多数RGB的LCD都会用到32位来表示一个像素,方便取整
  19. {
  20. *pen_32 = color;
  21. break;
  22. }
  23. default:
  24. {
  25. printf("can't surport %dbpp\n",var.bits_per_pixel);
  26. }
  27. }

}

void lcd_put_ascii(int x, int y, unsigned char c) { unsigned char dots = &fontdata_8x16[c16]; unsigned char byte; int i,b;

  1. for(i=0; i<16; i++)
  2. {
  3. byte = dots[i]; 取出每一行的数据
  4. for(b=7; b>=0; b--)
  5. {
  6. if(byte & (1<<b)) //一位一位地判断数据是否为1
  7. {
  8. /* show */
  9. lcd_put_pixel(x-b+7, y+i, 0xffffff); /* 白 */
  10. }
  11. else
  12. {
  13. /* hide */
  14. lcd_put_pixel(x-b+7, y+i, 0x000000); /* 黑 */
  15. }
  16. }
  17. }

}

void lcd_put_chinese(int x, int y, unsigned char str_ch) { //显示汉字需要有所谓的汉字库,如:HZK16 //需要先打开HZK16文件才能使用 unsigned int area = str_ch[0] - 0xA1; unsigned int where = str_ch[1] - 0xA1; unsigned int dots = = hzkmem + (area 94 + where) 32; unsigned char byte;

  1. int i, j, b;
  2. for(i=0; i<16; i++)
  3. {
  4. for(j=0; j<2; j++)
  5. {
  6. byte = dots[2*i + j];
  7. for(b=7; b>=0; b++)
  8. {
  9. if(byte & (1<<b)) //一位一位地判断数据是否为1
  10. {
  11. /* show */
  12. lcd_put_pixel(x + j*8 + 7-b, y+i, 0xffffff); /* 白 */
  13. }
  14. else
  15. {
  16. /* hide */
  17. lcd_put_pixel(x + j*8 + 7-b, y+i, 0x000000); /* 黑 */
  18. }
  19. }
  20. }
  21. }

}

int main(int argc, char **argv) {
unsigned char str_ch[] = “中”;

  1. fd_fb = open("/dev/fb0",O_RDWR);
  2. if(fd_fb < 0)
  3. {
  4. printf("can't open /dev/fb0\n");
  5. }
  6. /* 获得可变信息 */
  7. if( ioctl(fd_fb, FBIOGET_VSCREENINFO, &var) )
  8. {
  9. printf("can't get var\n");
  10. return -1;
  11. }
  12. /* 获得固定信息 */
  13. if( ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix) )
  14. {
  15. printf("can't get fix\n");
  16. return -1;
  17. }
  18. line_width = var.xres * var.bits_per_pixel /8;
  19. pixel_width = var.bits_per_pixel / 8;
  20. screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //屏幕大小
  21. fbmem = (unsigned char *)mmap(NULL, screen_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_fb, 0);
  22. if((unsigned char*)-1 == fbmem)
  23. {
  24. printf("can't mmap\n");
  25. }
  26. fd_hzk16 = open("HZK16", O_RDONLY);
  27. if(fd_hzk16 < 0)
  28. {
  29. printf("cant't open HZK16\n");
  30. }
  31. if( fstat(fd_hzk16, &hzk_stat) )
  32. {
  33. printf("can't get stat");
  34. }
  35. /* 进行内存映射 */
  36. hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
  37. if((unsigned char*)-1 == hzkmem)
  38. {
  39. printf("can't mmap\n");
  40. }
  41. /* 清屏 */
  42. memset(fbmem, 0, screen_size);
  43. lcd_put_ascii(var.xres/2, var.yres/2, 'A'); //在屏幕的中间显示一个字母
  44. printf("chinese code:%02x %02x\n",str_ch[0], str_ch[1]); //打印出中文的编码值
  45. lcd_put_chinese(var.res/2 + 8, var.yres/2, str_ch); //显示中文

} ```

  • 首先要获得LCD的参数
    • 每个像素用多少位来表示
    • LCD屏幕的x,y分辨率是多少
  • 内核fbmem.c中有很多ioctl,我们获取要LCD的可变信息
  • image.png
  • 从可变信息中,我们就可以知道x、y分辨率;每个像素用多少位表示
  • LCD提供了mmap函数,那么就可以直接映射内存到程序用户空间(直接使用frame buffer)
  • mmap用法image.png
  • 一个字体点阵,坐标从左上角开始
    • image.png
  • GBK的汉字,是用”两个字节”来表示的
    • 在显示汉字之前,需要得到其对应的点阵
    • HZK16这样一个汉字字体文件中包含了汉字的编码和对应的点阵
  • 在对一个文件进行mmap之前需要知道文件有多大,可以使用fstat函数获取
    • image.png
    • image.png
  • 汉字在HZK16文件中是怎样存储的?
    • 按区号和位号来
    • GBK中用两个字节来存储一个汉字,第一个字节就用来表示区号,第二个字节用来表示位号
    • 每个区由94个汉字组成
    • 因为是1616的点阵,所以每个汉字在文件中都占1616bit=32字节
    • image.png
    • image.png区码和位码都是从A1开始的
  • 交叉编译工具链:/usr/local/arm/4.3.2/
  • 查找一个文件:find -name “fb.h”
  • image.png启动了console和tty1两个控制台
    • tty1::askfirst: ~/bin/sh 把LCD也当作控制台输出
  • 将程序拷贝到nfs网络文件系统目录下
    • image.png
  • 要先配置Linux内核(因为原有内核没有加入LCD的支持,即无相关驱动,原先LCD驱动是作为模块在启动完后通过脚本配置的),要把lcd.c编译进内核
  • image.png
  • 要修改drivers/video/Makefile使得编译内核时能包含lcd.c
    • image.png
  • 执行make menuconfig配置内核编译选项,把lcd包含进来
    • image.png
    • image.png
    • image.png
    • image.png
  • 执行make uImage生成新的内核镜像
  • 拷贝生成的内核到网络文件系统nfs上
    • image.png
  • 使用编译了lcd的内核启动
    • 在uboot中,加载新的内核镜像启动
    • image.png
    • 启动完成后,直接执行程序
    • image.png