title: 点阵显示
date: 2019/2/27 19:32:07
toc: true
点阵显示
linux内核中存在着ASCII的字符点阵文件,这里我们挑选8*16的,也就是8列16行来测试显示英文.
汉字的话我们需要自己寻找点阵,这里选取了hzk16
,也就是16*16
的,这也是为什么我们英文选取8*16的原因,一个中文占据两个英文空间
具体关于汉字库的使用,参考这里
gbk转uft8
linux下默认都是utf-8编码的,终端的输入都是utf-8的,所以这里我们需要将输入的utf8转换为gbk才能使用hzk这个点阵
PC显示点阵
这里直接都是使用utf8
编码的
#include <stdio.h>
#include <stdlib.h>
#include <iconv.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
int code_convert(char *from_charset, char *to_charset, char *inbuf, size_t inlen,
char *outbuf, size_t outlen) {
iconv_t cd;
char **pin = &inbuf;
char **pout = &outbuf;
cd = iconv_open(to_charset, from_charset);
if (cd == 0)
return -1;
memset(outbuf, 0, outlen);
if (iconv(cd, pin, &inlen, pout, &outlen) == -1)
return -1;
iconv_close(cd);
*pout = ' ';
return 0;
}
int u2g(char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
return code_convert("utf-8", "gb2312", inbuf, inlen, outbuf, outlen);
}
int g2u(char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
return code_convert("gb2312", "utf-8", inbuf, inlen, outbuf, outlen);
}
int main(void)
{
FILE* fphzk = NULL;
int i, j, k, offset;
int flag;
unsigned char buffer[32];
unsigned char word[5];
unsigned char utf[5];
unsigned char key[8] = {
0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01
};
fphzk = fopen("hzk16", "rb");
if(fphzk == NULL){
fprintf(stderr, "error hzk16
");
return 1;
}
while(1){
printf("输入要生成字模的汉字(多个):");
for(;;){
fgets((char*)utf, 4, stdin);//这里是读取3个字符,然后补上一个结束
printf("raw char[] is :");
for(i=0;i<10;i++)
printf("%x ",(*(utf+i)&0xFF));
u2g(utf, 3, word, sizeof(word));
printf("
change char[] is :");
for(i=0;i<3;i++)
printf("%x ",(*(word+i)&0xFF));
if(*word == '
')
break;
offset = (94*(unsigned int)(word[0]-0xa0-1)+(word[1]-0xa0-1))*32;
fseek(fphzk, offset, SEEK_SET);
fread(buffer, 1, 32, fphzk);
for(k=0; k<16; k++){
for(j=0; j<2; j++){
for(i=0; i<8; i++){
flag = buffer[k*2+j]&key[i];
printf("%s", flag?".":" ");
}
}
printf("
");
}
printf("uchar code key[32] = {");
for(k=0; k<31; k++){
printf("0x%02X,", buffer[k]);
}
printf("0x%02X};
", buffer[31]);
printf("
");
}
}
fclose(fphzk);
fphzk = NULL;
return 0;
}
关键函数
LCD操作相关
ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)
获取到lcd可变信息:xy分辨率,像素位数ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)
,获取固定信息,缓存地址,每行字节数
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
文件大小查看
int fstat(int fd, struct stat *buf);
怎么查看函数的使用,直接man xxx
,然后使用/ fun_name
即可
编译错误排查
find -name "fb.h" #寻找头文件路径,在交叉工具链中搜索
open 函数中找不到 O_RDWR的宏定义,使用 man 2 open 找到对应头文件
详细代码
具体代码如下,没什么好分析的了
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#define FONTDATAMAX 4096
// 这里是英文点阵
int fd_fb;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
int fd_hzk16;
struct stat hzk_stat;
unsigned char *hzkmem;
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* 565 */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;
break;
}
case 32:
{
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp
", var.bits_per_pixel);
break;
}
}
}
void lcd_put_ascii(int x, int y, unsigned char c)
{
unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
int i, b;
unsigned char byte;
for (i = 0; i < 16; i++)
{
byte = dots[i];
for (b = 7; b >= 0; b--)
{
if (byte & (1<<b))
{
/* show */
lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
}
}
}
}
void lcd_put_chinese(int x, int y, unsigned char *str)
{
unsigned int area = str[0] - 0xA1;
unsigned int where = str[1] - 0xA1;
unsigned char *dots = hzkmem + (area * 94 + where)*32;
unsigned char byte;
int i, j, b;
for (i = 0; i < 16; i++)
for (j = 0; j < 2; j++)
{
byte = dots[i*2 + j];
for (b = 7; b >=0; b--)
{
if (byte & (1<<b))
{
/* show */
lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 */
}
}
}
}
int main(int argc, char **argv)
{
unsigned char str[] = "中";
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0
");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var
");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix
");
return -1;
}
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
");
return -1;
}
fd_hzk16 = open("HZK16", O_RDONLY);
if (fd_hzk16 < 0)
{
printf("can't open HZK16
");
return -1;
}
if(fstat(fd_hzk16, &hzk_stat))
{
printf("can't get fstat
");
return -1;
}
hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1)
{
printf("can't mmap for hzk16
");
return -1;
}
/* 清屏: 全部设为黑色 */
memset(fbmem, 0, screen_size);
lcd_put_ascii(var.xres/2, var.yres/2, 'A');
printf("chinese code: %02x %02x
", str[0], str[1]);
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);
return 0;
}
内核设置
内核需要加入对lcd的驱动支持,具体在
│ Symbol: FB_S3C2410 [=y] │
│ Type : tristate │
│ Prompt: S3C2410 LCD framebuffer support │
│ Defined at drivers/video/Kconfig:2062 │
│ Depends on: HAS_IOMEM [=y] && FB [=y] && ARCH_S3C24XX [=y] │
│ Location: │
│ -> Device Drivers │
│ -> Graphics support │
│ -> Support for frame buffer devices (FB [=y]) │
│ Selects: FB_CFB_FILLRECT [=y] && FB_CFB_COPYAREA [=y] && FB_CFB_IMAGEBLIT [=y]
后续
测试发现,从终端输入到串口,linux读取到的编码是utf-8的,参考这里写个程序输入字符,然后显示到液晶,这里还需要做一个utf8转gbk的函数,实际上这个函数在板子上不行,iconv_open
返回成功,下面的转换就返回失败
#include <stdio.h>
#include <stdlib.h>
#include <iconv.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
int code_convert(char *from_charset, char *to_charset, char *inbuf, size_t inlen,
char *outbuf, size_t outlen) {
iconv_t cd;
char **pin = &inbuf;
char **pout = &outbuf;
cd = iconv_open(to_charset, from_charset);
if (cd == 0)
return -1;
memset(outbuf, 0, outlen);
if (iconv(cd, pin, &inlen, pout, &outlen) == -1) //这里失败了
return -1;
iconv_close(cd);
*pout = ' ';
return 0;
}
int u2g(char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
return code_convert("utf-8", "gb2312", inbuf, inlen, outbuf, outlen);
}
int g2u(char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
return code_convert("gb2312", "utf-8", inbuf, inlen, outbuf, outlen);
}
int main(void)
{
FILE* fphzk = NULL;
int i, j, k, offset;
int flag;
unsigned char buffer[32];
unsigned char word[5];
unsigned char utf[5];
unsigned char key[8] = {
0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01
};
fphzk = fopen("hzk16", "rb");
if(fphzk == NULL){
fprintf(stderr, "error hzk16
");
return 1;
}
while(1){
printf("输入要生成字模的汉字(多个):");
for(;;){
fgets((char*)utf, 4, stdin);//这里是读取3个字符,然后补上一个结束
printf("raw char[] is :");
for(i=0;i<10;i++)
printf("%x ",(*(utf+i)&0xFF));
u2g(utf, 3, word, sizeof(word));
printf("
change char[] is :");
for(i=0;i<3;i++)
printf("%x ",(*(word+i)&0xFF));
if(*word == '
')
break;
offset = (94*(unsigned int)(word[0]-0xa0-1)+(word[1]-0xa0-1))*32;
fseek(fphzk, offset, SEEK_SET);
fread(buffer, 1, 32, fphzk);
for(k=0; k<16; k++){
for(j=0; j<2; j++){
for(i=0; i<8; i++){
flag = buffer[k*2+j]&key[i];
printf("%s", flag?".":" ");
}
}
printf("
");
}
printf("uchar code key[32] = {");
for(k=0; k<31; k++){
printf("0x%02X,", buffer[k]);
}
printf("0x%02X};
", buffer[31]);
printf("
");
}
}
fclose(fphzk);
fphzk = NULL;
return 0;
}