热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Mini2440裸机开发之LCD编程(GB2312、ASCII字库制作)

在上一节我们介绍了LCD的硬件基础只是、以及S3C2440LCD控制器相关的寄存器。这一节我们将会动手在LCD上显示一幅日落的图片。一、LCD初始化编程步骤1.1 初始化GPIO,

在上一节我们介绍了LCD的硬件基础只是、以及S3C2440 LCD控制器相关的寄存器。这一节我们将会动手在LCD上显示一幅日落的图片。


一、LCD初始化编程步骤


1.1 初始化GPIO,引脚复用

 

 

在上一节我们介绍了S3C2440这些引脚对应的LCD TFT上的引脚。这里就不在重复介绍了。我们需要配置Port C和Port D为LCD功能。

端口C相关寄存器的相关信息。








































寄存器地址R/W描述复位值
GPCCON0x56000020R/W配置端口C的引脚0x00
GPCDAT0x56000024R/W配置C的数据寄存器-
GPCUP0x56000028R/W端口C的上拉使能寄存器0x00
保留0x5600002C-保留-

(1) GPCCON











































































































GPCCON描述初始状态
GPC15[31:30]00 = 输入 01 = 输出 10 = VD[7] 11 = 保留0
GPC14[29:28]00 = 输入 01 = 输出 10 = VD[6] 11 = 保留0
GPC13[27:26]00 = 输入 01 = 输出 10 = VD[5] 11 = 保留0
GPC12[25:24]00 = 输入 01 = 输出 10 = VD[4] 11 = 保留0
GPC11[23:22]00 = 输入 01 = 输出 10 = VD[3] 11 = 保留0
GPC10[21:20]00 = 输入 01 = 输出 10 = VD[2] 11 = 保留0
GPC9[19:18]00 = 输入 01 = 输出 10 = VD[1] 11 = 保留0
GPC8[17:16]00 = 输入 01 = 输出 10 = VD[0] 11 = 保留0
GPC7[15:14]00 = 输入 01 = 输出 10 = LCD_LPCREVB 11 = 保留0
GPC6[13:12]00 = 输入 01 = 输出 10 = LCD_LPCREV 11 = 保留0
GPC5[11:10]00 = 输入 01 = 输出 10 = LCD_LPCOE 11 = 保留0
GPC4[9:8]00 = 输入 01 = 输出 10 = VM 11 = 保留0
GPC3[7:6]00 = 输入 01 = 输出 10 = VFRAME 11 = 保留0
GPC2[5:4]00 = 输入 01 = 输出 10 = VLINE 11 = 保留0
GPC1[3:2]00 = 输入 01 = 输出 10 = VCLK 11 = 保留0
GPC0[1:0]00 = 输入 01 = 输出 10 = LEND 11 = 保留0

由上表可知,B端口的控制寄存器可以将每个引脚配置为四种模式:



  • 00:输入模式

  • 01:输出模式

  • 10:功能扩展模式

  • 11:保留模式

配置端口C功能复用为LCD:

GPCCON=0xaaaaaaaa

(2) GPCDAT















GPCDAT描述
GPC[15:0][15:0]

当端口配置为输入端口时,相应位为引脚状态。当端口配置为输出端口时,引脚状态将与相应位相同。当端口配置为功能引脚,将读取到未定义值。


(3) GPCUP















GPCUP描述
GPC[15:0][15:0]

0:使能附加上拉功能到相应端口引脚

1:禁止附加上拉功能到相应端口引脚


禁止上拉:

GPCUP = 0xffffffff;

端口D相关寄存器的相关信息。








































寄存器地址R/W描述复位值
GPDCON0x56000030R/W配置端口D的引脚0x00
GPDDAT0x56000034R/W配置D的数据寄存器-
GPDUP0x56000038R/W端口D的上拉使能寄存器0xF000
保留0x5600003C-保留-

(1) GPDCON











































































































GPDCON描述初始状态
GPD15[31:30]00 = 输入 01 = 输出 10 = VD[23] 11 = 保留0
GPD14[29:28]00 = 输入 01 = 输出 10 = VD[22] 11 = 保留0
GPD13[27:26]00 = 输入 01 = 输出 10 = VD[21] 11 = 保留0
GPD12[25:24]00 = 输入 01 = 输出 10 = VD[20] 11 = 保留0
GPD11[23:22]00 = 输入 01 = 输出 10 = VD[19] 11 = 保留0
GPD10[21:20]00 = 输入 01 = 输出 10 = VD[18] 11 = 保留0
GPD9[19:18]00 = 输入 01 = 输出 10 = VD[17] 11 = 保留0
GPD8[17:16]00 = 输入 01 = 输出 10 = VD[16] 11 = 保留0
GPD7[15:14]00 = 输入 01 = 输出 10 = VD[15] 11 = 保留0
GPD6[13:12]00 = 输入 01 = 输出 10 = VD[14] 11 = 保留0
GPD5[11:10]00 = 输入 01 = 输出 10 = VD[13] 11 = 保留0
GPD4[9:8]00 = 输入 01 = 输出 10 = VD[12] 11 = 保留0
GPD3[7:6]00 = 输入 01 = 输出 10 =VD[11] 11 = 保留0
GPD2[5:4]00 = 输入 01 = 输出 10 = VD[10] 11 = 保留0
GPD1[3:2]00 = 输入 01 = 输出 10 = VD[9] 11 = 保留0
GPD0[1:0]00 = 输入 01 = 输出 10 = VD[8] 11 = 保留 0

由上表可知,B端口的控制寄存器可以将每个引脚配置为四种模式:



  • 00:输入模式

  • 01:输出模式

  • 10:功能扩展模式

  • 11:保留模式

配置端口D功能复用为LCD:

GPDCON=0xaaaaaaaa

(2) GPDDAT















GPCDAT描述
GPD[15:0][15:0]

当端口配置为输入端口时,相应位为引脚状态。当端口配置为输出端口时,引脚状态将与相应位相同。当端口配置为功能引脚,将读取到未定义值。


(3) GPDUP















GPCUP描述
GPD[15:0][15:0]

0:使能附加上拉功能到相应端口引脚

1:禁止附加上拉功能到相应端口引脚


禁止上拉:

GPDUP = 0xffffffff;

总结下来:

void _lcd_gpio_init()
{
GPCUP
= 0xffffffff; /* Disable Pull - up register */
GPCCON
= 0xaaaaaaaa; /* Initialize VD[7:0], VM, VFRAME, VLINE, VCLK */
GPDUP
= 0xffffffff; /* Disable Pull - up register */
GPDCON
= 0xaaaaaaaa; /* Initialize VD[15:8] */
}

1.2 打开LCD电源

LCD_PWR对应的引脚,所以设置GPG4就可以控制LED背光电源了。(GPGCON:9-8写入11),这时候LCD电源的打开/关闭可以通过LCDCON5位3来控制:

LCD_PWREN 输出信号使能/禁止:0 = 禁止PWREN 信号 1 = 允许PWREN信号:

void _lcd_power_on()
{
/* 打开LCD电源 */
GPGUP
|= (1 <<4); /* Pull - up disable */
GPGCON
|= (3 <<8); /* 设置GPG4引脚为LCD_PWREN模式 */
LCDCON5
|= (1 <<3); /* LCD_PWREN输出信号使能 */
}

1.3 设置其他信号线

其他信号线包括VD0-VD23和VFRAME、VLINE、VCLK等,分别在GPCCON,GPDCON中选择相应功能。这些在上一节已经介绍过。这里就稍微提一下

1) 设置VCLK、BPPMODE、PNRMODE

(VCLK)LCD的Datasheet上一般会写有一个推荐的频率,上一节已经介绍过我使用的屏幕推荐频率为6.4M,我们通过计算得到的CLKVAL=7比较合适。写入LCDCON1位17-8;

位4-1选择BPP(位每像素)模式    1100 = TFT 的16 BPP;

位6-5选择显示模式  11 = TFT LCD 面板

2) 设置其他相关参数

LCD相关的参数主要还有这几个:LINEVAL: LCD水平像素-1,如240-1 = 239;
HOZVAL:  LCD垂直像素-1,如320-1 = 319;
HFPD:    行开始前的VCLK时钟数(LCD屏幕的Datasheet一般有推荐值);
HBPD:    行结束后的VCLK时钟数(LCD屏幕的Datasheet一般有推荐值);
HSPW:    行之间水平同步的无效VCLK时钟数(LCD屏幕的Datasheet一般有推荐值);
VFPD:    帧数据开始前的空白行数(LCD屏幕的Datasheet一般有推荐值);
VBPD:    帧数据结束后的空白行数(LCD屏幕的Datasheet一般有推荐值);
VSPW:    帧之间垂直同步的无效行数(LCD屏幕的Datasheet一般有推荐值);
(相关寄存器LCDCON2, LCDCON3, LCDCON4);

3) 设置视频缓冲区的地址

S3C2440支持虚拟屏幕,可以通过改变LCD寄存器实现屏幕快速移动;
PAGEWIDTH:虚拟屏幕一行的字节数,如果不使用虚拟屏幕,设置为实际屏幕的行半字数,如16位宽320像素,设为320 ;
OFFSIZE:虚拟屏幕左侧偏移的字节数,如果不使用虚拟屏幕,设置为0;
LCDBANK: 视频帧缓冲区内存地址30-22位;
LCDBASEU: 视频帧缓冲区的开始地址21-1位;
LCDBASEL: 视频帧缓冲区的结束地址21-1位;
(相关寄存器LCDSADDR1,LCDSADDR2,LCDSADDR3);

4) 确定信号的极性

S3C2440的LCD控制器允许设置VCLK、VLINE、VFRAME等信号的极性(上升沿有效还是下降沿有效),需要对照LCD的Datasheet一一确认。
(相关寄存器LCDCON5)

5) 关中断

设置LCDINTMSK位1-0。

6) 禁止LPC3600/LCC3600模式

如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL位4、0)。

7)  关闭临时调色板

TPAL位24设置为0。

总体设置代码如下:

void _lcd_control_init()
{
/* [17:8] CLKVAL
* [6:5] PNRMODE;选择显示模式
* 00 = 4 位双扫描显示模式 01 = 4 位单扫描显示模式(STN)
* 10 = 8 位单扫描显示模式 11 = TFT LCD 面板
* [4:1] BPPMODE 选择BPP(位每像素)模式 1100 = TFT 的16 BPP
* [0] ENVID LCD 视频输出和逻辑使能/禁止。
* 0 = 禁止视频输出和LCD 控制信号 1 = 允许视频输出和LCD 控制信号
*/
LCDCON1
= (CLKVAL<<8)| (3<<5)|(0xC<<1); /* 16 bpp for TFT */
/* [31:24] VBPD:帧同步信号的后肩
* [23:14] LINEVAL:LCD面板的垂直尺寸
* [13:6] VFPD:帧同步信号的前肩
* [5:0] VSPW:同步信号的脉宽
*/
LCDCON2
= (VBPD<<24)|(LINEVAL<<14)|(VFPD<<6)|(VSPW);
/* [25:19] HBPD: 行同步信号的后肩
* [18:8] HOZVAL: LCD面板的水平尺寸
* [7:0] HFPD: 行同步信号的前肩
*/
LCDCON3
= (HBPD<<19)|(HOZVAL<<8)|(HFPD);
LCDCON4
= (HSPW);
/* [11] FRM565: 此位选择16 BPP 输出视频数据的格式 0 = 5:5:5:1 格式 1= 5:6:5 格式
* [10] STN/TFT: 此位控制VCLK 有效沿的极性
* [9] INVVLINE: STN/TFT:此位表明VLINE/HSYNC 脉冲极性 0 = 正常 1 = 反转
* [8] INVVFRAME: STN/TFT:此位表明VFRAME/VSYNC 脉冲极性 0 = 正常 1 = 反转
* VLINE/HSYNC 脉冲极性、VFRAME/VSYNC 脉冲极性反转(LCD型号决定)
* [0] HWSWP: STN/TFT:半字节交换控制位 0 = 交换禁止 1 = 交换使能
*/
LCDCON5
= ((1<<11) | (1<<10) | (1 <<9) | (1 <<8) | (1 <<0));
#define M5D(n) ((n)&0x1fffff)
#define LCD_ADDR ((u32)LCD_BUFFER) /* FrameBuffer地址 */
/* [29:21] LCDBANK:存放帧缓冲起始地址的[30:22]
* [20:0] LCDBASEU: 存放帧缓冲起始地址的[21:1]
*/
LCDSADDR1
= ((LCD_ADDR >> 22) <<21) | (M5D(LCD_ADDR >> 1)) ;
/* 存放帧结束地址[21:1] */
LCDSADDR2
= M5D((LCD_ADDR + (LCD_WIDTH * LCD_HEIGHT * 2 )) >> 1);
/* [21:11] OFFSIZE:表示虚拟屏偏移尺寸 即上一行最后像素点到下一行第一个像素点之间间隔多少个像素点
* [10:0] PAGEWIDTH:表示行的宽度(半字为单位 16bit)
*/
LCDSADDR3
= LCD_WIDTH;
LCDINTMSK
|= 3;
TCONSEL
&= ~((1 <<4) | 1);
TPAL
= 0x00; /* 关闭临时调色板 */
}

1.4 打开视频输出

ENVID设为1 (LCDCON1:0写入1)。

/* LCD使能 */
LCDCON1
|= 1;

到此LCD初始化步骤就介绍完成了,整体代码如下:

void lcd_init()
{
_lcd_gpio_init();
_lcd_control_init();
_lcd_power_on();
/* LCD使能 */
LCDCON1
|= 1;
}

二、显示BMP图片

如果想在LCD上显示一幅图片,只需要向LCD_BUFFER缓冲区写入图片数据即可。

void lcd_draw_bmp(u16 x0,u16 y0,u16 width,u16 height,const u8 *bmp)
{
u16 x,y;
u16 c;
u32 p
= 0;
/* 绘制每一行 */
for( y = 0 ; y )
{
/* 绘制每一点 */
for( x = 0 ; x )
{
c = bmp[p + 1] | (bmp[p] <<8);
/* bmp.c中的数组元素大小是8bit,屏幕像素设置16bit */
if (((x0 + x) /* LCD尺寸范围,根据实际LCD设置 */
{
LCD_BUFFER[y0
+ y][x0 + x] = c;
}
p
= p + 2 ; /* 屏幕像素设置16bit,所以要+2 */
}
}
}

在Ecllispe调试模式下,将代码直接下载SDRAM地址0x30000000运行,图片显示正常:

但是通过MiniTools下载到SDRAM地址0x30000000运行,图片却显示异常:

目前原因还未排查清楚。


三、显示字符


3.1 字符显示原理

既然我们能使用LCD去显示图片,那么去显示字符其实也是很简单的道理。我们可以把每个字符看成一个比较小的图片。

比如16*16的汉字显示原理就是在一个长宽16的正方形中绘制若干个点,这样就只有弄清楚哪些点是要显示的就行了。比如第一行要显示一个点我们就可以 xxxxxxxoxxxxxxxx, 我们只有把中间的圈显示,其余的点不显示就可以了。

这样我们就可以用一个数组来保存这些点,每一位表示是否要显示,当然这一位要显示什么颜色, 我们可以自行设置。


3.2 字符制作

对于字模数组的提取现在已经有很多好用的字模提取软件,字库软件的,到网上搜一个然后就很容易了,只要输入想要显示的汉字软件就帮你把字模的数组显示好了,你只有把这个字模数组放到程序当中去就可以了。

当然这里要注意字模提取的顺序,还有有些字模软件中可以设置要不要倒序的问题,这里我用的是 PCtoLCD2002.EXE 这个软件,我的LCD的取模方式是横向取模,字节不倒序,C51格式 。

结果为:

你(0)
DB 08H 80H 08H 80H 08H 80H 11H FEH 11H 02H 32H 04H 34H 20H 50H 20H;
DB 91H 28H 11H 24H 12H 24H 12H 22H 14H 22H 10H 20H 10H A0H 10H 40H;
"",0

可以看到一共生成32个字节,每两个字节对应图片中一行的16个点,我们以第一行对应的两个字节为例 0x08 0x80,转换成二进制就是0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0,也就是说将第一行16个像素中的第5个、9个像素点亮。

当然要是有很多汉字要显示的话一个一个字去提取就不容易了。现在已经有人或者有一些标准已经把汉字弄成了一些字库,像16*16的话就标准库GB2312 ,但是注意,这些库好像要么是没有后缀名要么是数据库形式,对于裸机还是比较不方便,这里的话可以去找一下有人把这些做成了C语言数组形式这样就比较好用 了,不过这样有点占内存,16*16 / 8 = 32 就是一个字占32个字节,常用汉字字符库的话一般有6 7千个,那么简单算一下应该就需要差不多200k的内存,对于单片机来说还是比较难以消化的。


3.3 字符原理

有了字库我们就只需要找个我们需要的汉字然后取出来显示就好了,但是怎么找这么汉字,当然这么问题别人早已经解决。

首先对于汉字在GB2312编码中使用两个字节表示,收录7445个图形字符,其中包括6763个汉字。,其中一级汉字3755个,二级汉字3008个;同时收录包括拉丁字母、希腊字母、日文平假名及片假名字母、俄罗斯语西里尔字母在内的682个全形字符。

GB2312对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。



  • 01-09区为特殊符号;

  • 10-15区没有编码;

  • 6-55区为一级汉字,按拼音排序,共3755个;

  • 56-87区为二级汉字,按部首/笔画排序,共3008个;

  • 88-94区没有编码;

  • GB2312只是编码表,在计算机中通常都是用"EUC-CN"表示法,即在每个区位加上0xA0来表示。区和位分别占用一个字节。

GB2312编码规则



  • 2字节编码,高位为0xA1-0xFE,低位为0xA1-0xFE;(0xFF并没有使用,所以高、低的范围均为0xFE-0xA1+1=94个)

  • 汉字区域,高位为0xB0-0xF7,低位为0xA1-0xFE

  • 特殊符号,高位为0xA1-0xA9,低位为0xA1-0xFE

举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601。

字节编码,通常采用EUC储存方法,以便兼容于ASCII。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。



  • “高位字节”使用了0xA1-0xFF(把01-94区的区号加上0xA0);

  • “低位字节”使用了0xA1-0xFF(把01-94加上0xA0)。

例如“啊”字在大多数程序中,会以0xB0A1储存(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。

有了区位码。这样我们查找汉字是就比较方便了,首先我们通过区号找个是属于哪个区,然后再通过位号找个属于哪个位就可以找到汉字了,

假设我们将我们的GB2312字库采用二位数组表示,数组长度为94*94=8836:

const u8 GB2312[][32] ;

我们将汉字采用GB2312编码,我们需要计算每个汉字在二位数组的索引,该索引为(高位字节-0xA1)*94 + (低位字节-0xA1)。

在GB2312编码表里面,区位码里有英文和数字,按道理说是不是也应该是双字节的呢。

而一般情况下,我们见到的英文和数字是单字节的,以ASCII编码,也就是说现代的GB2312编码是兼容ASCII编码的。比如一个数字2,对应的二进制是0x32,而不是 0xA3 0xB2。那么问题来了,0xA3 0xB2 又对应到什么呢?还是2(笑)。注意看了,这里的2跟2是不是有点不太一样?!确实是不一样的。这里的双字节2是全角的二,ASCII的2是半角的二,一般输入法里的切换全角半角就是这里不同。

同一个编码文件里,怎么区分ASCII和中文编码呢?从ASCII表我们知道标准ASCII只有128个字符,0~127即0x00~0x7F(0111 1111)。所以区分的方法就是,高字节的最高位为0则为ASCII,为1则为中文。


3.4 字库数组制作

我们如何将字库转为二位数组呢,下面这个程序读取一个字库二进制文件,并遍历整个字库文件,转换为二位数组,保存下来:

#include
// GB 2312标准共收录6763个汉字
#define MAX_COUNT 10000
void write(unsigned char gb2312[MAX_COUNT][32])
{
int a[3][3];
int i, j;
FILE
* fp;
if ((fp = fopen("./data.txt", "w")) == NULL) {
printf(
"the file can not open..");
exit(
0);
}
int m, n = 0;
//出错处理
for (m = 0; m )
{
fprintf(fp,"{");
for (n = 0; n <32; n++)
{
fprintf(fp,
"0x%02X", gb2312[m][n]);
if (n != 31) {
fprintf(fp,
",");
}
}
fprintf(fp,
"},\n");
}
//把每个数组元素以十进制的方式存入data.txt中
fclose(fp);
}
int main(void)
{
FILE
*fphzk = NULL;
unsigned
char gb2312[MAX_COUNT][32];
int i, j, k, offset=0 ;
int flag;
int count = 0;
unsigned
char buffer[32];
unsigned
char key[8] = {
0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01
};
// 读取字库
fphzk = fopen("./hzk16s", "rb");
if (fphzk == NULL) {
fprintf(stderr,
"error hzk16\n");
return 1;
}
while(fseek(fphzk, offset, SEEK_SET) != -1)
{
// 读取32位长度,返回0 读取完毕
if (fread(buffer, 1, 32, fphzk) == 0)
{
break;
}
// 显示读取到16进制
for (k = 0; k <32; k++) {
printf(
"%02X ", buffer[k]);
gb2312[count][k]
= buffer[k];
}
// 绘制图形
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(
"\n");
}
offset
+= 32;
count
+= 1;
}
write(gb2312);
printf(
"共有字符%d\n",count);
fclose(fphzk);
fphzk
= NULL;
return 0;
}

3.5 绘制16x16 GB2312编码字符

/*************************************************************
*
* Function : 显示单个汉字 16*16
* Input : (x,y) : 起始坐标
* color :汉字颜色
* ch[] : 字模数组 长度为32
* 横向取模 C51 取模顺向: 从第一行开始向右 ,每取8个点作为一个字节,如果最后不足8个点就补满8位。
取模顺序是从高到低,即第一个点作为最高位。 比如0x08,0x80对应第一行
0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0
*
*************************************************************
*/
void lcd_draw_text(u16 x, u16 y, u32 color,const u8 *ch)
{
u16 i, j;
u8 mask, tmp;
/* 绘制每一行 */
for (i = 0; i <16; i++)
{
mask
= 0x80;
tmp
= ch[i*2]; /* 2个字节一组16位,取第一个字节 */
for (j = 0; j <8; j++)
{
if (tmp & mask)
{
lcd_draw_point(x
+ j, y + i, color);
}
mask
= mask >> 1;
}
mask
= 0x80;
tmp
= ch[i*2+1];
for (j = 0; j <8; j++)
{
if (tmp & mask)
{
lcd_draw_point(x
+ j + 8, y + i, color);
}
mask
= mask >> 1;
}
}
}

3.6 绘制16x8 ASCII编码字符

/*************************************************************
*
* Function : 显示ASCII 16*8
* Input : (x,y) : 起始坐标
* color :ASCII颜色
* ch[] : 字模数组 长度为16
* 横向取模 C51 取模顺向
*
*************************************************************
*/
static void lcd_draw_ascii(u32 x, u32 y, u32 col,const u8 *ch)
{
u16 i, j;
u8 tmp, mask;
for (i = 0; i <16; i++)
{
mask
= 0x80;
tmp
= ch[i];
for (j = 0; j <8; j++)
{
if (mask & tmp)
{
lcd_draw_point(x
+ j, y + i, col);
}
mask
= mask >> 1;
}
}
}

3.7 利用GB2312字库显示汉字

/*************************************************************
*
* Function : 利用GB2312字库显示汉字 16*16
* Input : (x,y) : 起始坐标
* color :ASCII颜色
* str : 字符数组 比如:'你好' => 内存存放[c4,e3,ba,c3] 你 : c4e3 好 : bac3
* len : 字符串所占字节长度 比如:4
*
*************************************************************
*/
void lcd_draw_char_lib(u32 x, u32 y, u32 color, const u8 str[], unsigned int len)
{
u16 loc, i;
u8 qh, wh;
const u8 *pchlib;
for (loc = 0, i = 0; i )
{
/* 校验每一个字节编码是否大于0x80 GB2312 16*16 */
if (str[i] & 0x80)
{
/* 计算区号和位号 */
qh
= str[i] - 0xA0; /* 01-94 */
wh
= str[i + 1] - 0xA0; /* 01-94 */
/* 到字库数组查找对应的字模数组 */
pchlib
= GB2312[94 * (qh - 1) + (wh - 1)];
lcd_draw_text(x
+ loc, y, color, pchlib);
i
++;
loc
+= 16;
}
else /* ASCII 16*8 */
{
pchlib
= ASCII[str[i]];
lcd_draw_ascii(x
+ loc, y, color, pchlib);
loc
+= 8;
}
}
}

3.8 测试代码

/*************************************************************
*
* Function : 测试lcd显示,需要先初始化lcd_init()
*
*************************************************************
*/
void lcd_test()
{
/* 文件必须采用GBK编码 */
lcd_draw_bmp(
0,0,LCD_WIDTH,LCD_HEIGHT, sunflower_320x240);
char *data = "我爱你刘燕 I LOVE YOU ";
lcd_draw_char_lib(
100, 50, 0xFF00FF, data, strlen(data));
}

需要注意的是这段代码文件必须以GBK编码(兼容GB2312),这样这些字符写到开发板data执行的内存空间上是才是GBK编码,比如“我爱你刘燕 I LOVE YOU ”保存到内存的数据为:

0xCE 0xD2(我) 0xB0 0xAE(爱) 0xC4 0xE3(你) 0x C1 0xF5(刘) 0xD1 0xE0(燕) 0x20(空格) 0x49(I) 0x20(空格) 0x4C(L) 0x4F(O) 0x56(V) 0x45(E) 0x20(空格) 0x59(Y) 0x4F(O) 0x55(U) 0x20(空格) 字符串以0x00结尾。

执行测试代码,显示效果


四、源码下载

Young / s3c2440_project【7.lcd】

这个代码已经远超过4kb,并且没有将代码从NAND拷贝到SDRAM运行,只可以下载到SDRAM 0x30000000处运行。

参考文章

[1]GB2312简体中文编码表

[2]HZK16汉字16*16点阵字库的使用及示例程序

[3]裸机系列——s3c2440之LCD汉字显示

[4]彻底搞懂编码 GBK 和 UTF8


原文链接:https://www.cnblogs.com/zyly/p/15414659.html



推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
author-avatar
Ajax
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有