一、实验目的
1.熟悉点阵字库的使用
2.熟悉Linux操作系统的使用
3.熟悉ARM嵌入式系统开发的过程
二、实验内容
1.pc端编写用×和空格显示自己名字的c程序
2.安装交叉编译工具,修改程序,交叉编译
3.制作新的文件系统镜像,烧写。
三、实验设备及工具
硬件:MX1ADS嵌入式实验平台、PC机
软件:Linux操作系统、交叉编译工具
四、实验原理
1.16×16点阵字库HZK16的使用
GB2312规定对收录的每个字符采用两个字节表示,第一个字节为“高字节”,对应94个区;第二个字节为“低字节”,对应94个位。
每个区记录94个汉字,位号为该字在该区中的位置。
区码:汉字的第一个字节 - 0xA0 (因为汉字编码是从0xA1区开始的,要算出相对区码)位码:汉字的第二个字节 - 0xA0
汉字在HZK16中的绝对偏移位置:offset =[94×(区号-1)+(位号-1)]×一个字模占用的字节数。
2.MX1 ADS液晶显示的硬件结构
MX1 ADS附带的是一块Sharp的TFT显示屏,分辨率320*240, 16位真彩色。每个像素点对应于一个16位色彩描述存储器,颜色描述方式为R5G6B5。
3.BSP-Linux的显示结构
在MX1 ADS上运行的操作系统是BSP-Linux0.3.6(Kernel 2.4.18)。该系统使用的显示驱动是FrameBuffer。
在FrameBuffer驱动模式下,系统将显示设备映射成文件描述符(/dev/fb0)。应用程序只需打开这个文件,调用mmap()将显示存储器映射为用户空间的内存后,即可在用户空间对现存直接进行读/写
五、实验步骤
1.Linux系统上编写输出自己名字的c程序
代码如下
#include
#include
void LCD_ShowChar(unsigned char *word)
{
FILE* fphzk = NULL;
int i, j, k, p, offset;
int flag;
unsigned char buffer[32];
fphzk = fopen("HZK16", "rb");
if(fphzk == NULL){
fprintf(stderr, "error hzk16\n");
}
offset = (94*(unsigned int)(*word-0xa0-1)+(*(word+1)-0xa0-1))*32;
fseek(fphzk, offset, SEEK_SET);//SEEK_SET(对应 0):文件开头
fread(buffer, 1, 32, fphzk);
//const void *buffer 为指向二进制数据来源的指针;size_t size 为每个数据单元所占的字节数;
//size_t num 为需要读取的数据单元的个数;FILE *stream 为目的文件指针
for(j=0; j<16; j++){
printf("\n");
for(i=0; i<2; i++){
for(k=0; k<8; k++){
flag = buffer[j*2+i]&(0x80>>k);
printf("%s", flag?"* ":" ");}
}
}
fclose(fphzk);
fphzk = NULL;
}
void LCD_ShowString(unsigned char *p)
{
while(*p>0)
{
LCD_ShowChar(p);
p=p+2;
}
}
int main(void)
{
LCD_ShowString((unsigned char*)"张三李四");//此处写自己的名字或者任意汉字组合
system("pause");
return 0;
}
2.在Linux系统中建立交叉编译环境并修改程序
(1)解压交叉编译工具
①将armLinuxXToolChain.tar.gz(arm-linux 交叉编译工具)复制到 /usr/local 目录(注意一定要用老师的低版本交叉编译器,不要自己在网上下载)
②终端"cd" 到 /usr/local (意思是所有的命令都要在对应的路径输入,否则会提示找不到文件)
③执行 “tar -xzf armLinuxXToolChain.tar.gz” 命令解压缩
(2)配置环境变量
①打开环境变量文件:sudo gedit /etc/profile
②增加路径设置,在末尾添加如下:
export PATH=$PATH:/usr/local/bin
export PATH=$PATH:/usr/local/arm/bin
③注册环境变量:source /etc/profile
④测试是否安装成功:在终端里输入arm-linux-gcc -v
(3)修改代码(先编译执行之前的c代码之后再进行这一步,编译执行的命令请看下一步)
要在LCD上显示需要将printf语句改成在对应地址写寄存器值。首先打开FrameBuffer驱动:fd_fb = open("/dev/fb0", O_RDWR),然后使用mmap()函数映射显示寄存器,返回首地址。
具体代码如下:
#include
#include
#include
#include
#include
#include
#include
//画笔颜色
#define BLACK 0xFFFF
#define WHITE 0x0000
unsigned short *fb=0;
void LCD_ShowChar(int x,int y,unsigned char *word)
{
FILE* fphzk = NULL;
int i, j, k, p, offset;
int flag;
unsigned char buffer[32];
fphzk = fopen("HZK16", "rb");
if(fphzk == NULL){
fprintf(stderr, "error hzk16\n");
}
offset = (94*(unsigned int)(*word-0xa0-1)+(*(word+1)-0xa0-1))*32;
fseek(fphzk, offset, SEEK_SET);//SEEK_SET(对应 0):文件开头
fread(buffer, 1, 32, fphzk);
for(j=0; j<16; j++){
printf("\n");
for(i=0; i<2; i++){
for(k=0; k<8; k++){
flag = buffer[j*2+i]&(0x80>>k);
printf("%s",flag?"* ":" ");
if(flag==1){
*(fb+(j+y)*240+x+i*8+k) = WHITE;}
}
}
}
fclose(fphzk);
fphzk = NULL;
}
void LCD_ShowString(int x,int y,unsigned char *p)
{
while(*p>0)
{
LCD_ShowChar(x,y,p);
p=p+2;
x=x+20;
}
}
int main(void)
{
int fd_fb=0;
int x,y;
fd_fb=open("/dev/fb0",O_RDWR);
fb=(unsigned short*)mmap(0,240*320*2,PROT_READ|PROT_WRITE,MAP_SHARED,fd_fb,0);
for(y=0;y<320;y++){
for(x=0;x<240;x++)
{*(fb+y*240+x) = BLACK;}
}
LCD_ShowString(30,30,(unsigned char*)"张三李四");}
(4)交叉编译
(所有命令行均在该文件夹路径下执行)
对于修改前的代码:
①使用gcc编译:gcc -o display display.c 报错则修改,不报错则继续执行下一步
②执行编译文件:./display
对于修改后的代码:
①使用gcc编译:gcc -o display1 display1.c 报错则修改,不报错则继续执行下一步
②使用arm-linux-gcc编译:arm-linux-gcc -o display1 display1.c
3.制作新的文件系统镜像并烧写
(1)生成root文件系统
①将root文件系统镜像Mount(挂载)到一个空目录(disk),使用如下命令:mount –t cramfs –o loop root.cramfs disk
②将disk目录内的整个root文件系统复制到一个新文件夹newdisk:将挂载后的文件夹在终端打开,输入sudo nautilus获得最高管理员权限,在弹出的文件窗口中操作即可(否则将没有复制权限)
③确定完整无误地复制了root文件系统后,Unmount(卸载)disk 内的文件系统,使用命令如下:
umount disk
④修改newdisk文件夹下所有文件的权限:
chmod -R 777 newdisk/
⑤将经过交叉编译的目标文件display及HZK16字库文件复制到newdisk的bin文件夹中(确保复制后两文件权限为可读写)
⑥使用如下命令生成一个新的 root 文件系统镜像:
mkcramfs newdisk newroot.cramfs
⑦修改newroot的权限:
chmod 777 newroot.cramfs
(2)烧写
将newroot.cramfs文件拷贝至老师电脑某文件夹下,按照第一个实验的第三步(文件系统镜像烧写)进行烧写
①新建超级终端,恢复默认值,码率选择115200,上电
②输入2(文件系统镜像烧写)
③插拔usb激活
④将文件系统镜像文件拷贝到u盘里
⑤弹出u盘(文件拷贝结束的标志)
⑥按任意键开始烧写
⑦烧写结束后按复位键进入文件系统
最终结果
并没有显示成功,显示的位置和大小都对,但是没有显示全,不知道问题出在哪里
还有一个我写的比较高大上的代码,但是也出错了(段错误 访问的内存超出了系统所给这个程序的内存空间)
#include
#include
#include
#include
#include
#include
#include
//画笔颜色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
//画笔颜色
unsigned short POINT_COLOR = 0x0000;
//首地址
unsigned short *start_addr;
void LCD_DrawPoint(unsigned short x,unsigned short y,unsigned short color)
{
*(start_addr+x+y*240) = color;
}
//清屏函数
//color:要清屏的填充色
void LCD_Clear(unsigned short color)
{
unsigned short x,y;
for(y=0;y<320;y++)
{
for(x=0;x<240;x++)
{
LCD_DrawPoint(x,y,color);
}
}
}
void LCD_ShowChar(unsigned short x,unsigned short y,unsigned char *word,unsigned char mode)
{
FILE* fphzk = NULL;
int i, j, k, p, offset;
int flag;
unsigned char buffer[32];
fphzk = fopen("HZK16", "rb");
if(fphzk == NULL){
fprintf(stderr, "error hzk16\n");
}
offset = (94*(unsigned int)(*word-0xa0-1)+(*(word+1)-0xa0-1))*32;
fseek(fphzk, offset, SEEK_SET);//SEEK_SET(对应 0):文件开头
fread(buffer, 1, 32, fphzk);//const void *buffer 为指向二进制数据来源的指针;size_t size 为每个数据单元所占的字节数;size_t num 为需要读取的数据单元的个数;FILE *stream 为目的文件指针
if(mode == 0)
{
for(j=0; j<16; j++){
y++;
for(i=0; i<2; i++){
for(k=0; k<8; k++){
x++;
flag = buffer[j*2+i]&(0x80>>k);
if(flag == 1)
{
LCD_DrawPoint(x,y,POINT_COLOR);
}
}
}
}
}
else if(mode == 1)
{
for(j=0; j<16; j++){
x--;
for(i=0; i<2; i++){
for(k=0; k<8; k++){
y++;
flag = buffer[j*2+i]&(0x80>>k);
if(flag == 1)
{
LCD_DrawPoint(x,y,POINT_COLOR);
}
}
}
}
}
fclose(fphzk);
fphzk = NULL;
}
void LCD_ShowString(unsigned short x,unsigned short y,unsigned char *p,unsigned char mode)
{
if(mode == 0)
{
while(*p>0)
{
LCD_ShowChar(x,y,p,mode);
p=p+2;
x=x+20;
}
}
if(mode == 1)
{
while(*p>0)
{
LCD_ShowChar(x,y,p,mode);
p=p+2;
y=y+20;
}
}
}
int main(void)
{
//init();
unsigned char x = 0;
int fd;
int i,a;
int delay = 100000000;//100M
//使用mmap函数返回首地址
fd = open("dev/fb0",O_RDWR);
start_addr = (unsigned short*)mmap(0,240*320*2,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
while(1)
{
switch(x)
{
case 0:LCD_Clear(WHITE);break;
case 1:LCD_Clear(BLACK);break;
case 2:LCD_Clear(BLUE);break;
case 3:LCD_Clear(RED);break;
case 4:LCD_Clear(GREEN);break;
case 5:LCD_Clear(YELLOW);break;
case 6:LCD_Clear(GRAY);break;
case 7:LCD_Clear(BROWN);break;
}
x++;
if(x==8)x=0;
LCD_ShowString(30,30,(unsigned char*)"张三李四",0);//横屏
LCD_ShowString(50,50,(unsigned char*)"张三李四",1);//竖屏
//延时函数
for(i=0;i
几点注意的地方
有问题可以随时给我留言