文件指针的学习
第一天中液晶屏显示颜色 成功了,但在我实验的时候有个问题。就是液晶屏只有一个颜色,不会循环播放。
后面百度后,原因是:当使用open()打开一个文件的时候,文件指针指向文件最开始的位置,每向文件写入一个字节,文件指针向文件的结尾移动一个位置。当液晶屏写入满屏红色数据的时候,文件指针执行文件的结束,此时再写入数据,数据就写到显存外面了,液晶屏显示的颜色就不会变化了。
指针超出解决方案
1.写入满屏颜色数据后,关闭文件。下次写入数据再打开。
void main()
{
while(1)
{
1.open()//打开液晶屏
2.write()//写入红色
3.close()//关闭液晶屏
1.open()//打开液晶屏
2.write()//写入蓝色
3.close()//关闭液晶屏
}
}
2.使用lseek()函数移动文件指针
头文件有:#include <sys/types.h> 和 #include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数说明:
int fd ---- file descriptor文件描述符,也就是需要移动文件指针的文件,open()的返回值
off_t offset ---- 移动文件指针的字节数
int whence ---- 移动文件指针的基准位置
SEEK_SET---从文件头开始移动文件指针
The offset is set to offset bytes.
SEEK_CUR----从文件当前位置开始移动文件指针
The offset is set to its current location plus offset bytes.
SEEK_END----从文件结束位置开始移动文件指针
The offset is set to the size of the file plus offset bytes.
将lcd文件指针移动到文件最开始
lseek(fd_lcd, 0, SEEK_SET);
单色显示会出现花屏
原因和解决方法
原因:当使用 write() 系统调用函数向液晶屏的设备文件写入数据时,整个过程涉及到应用程序和Linux内核之间的数据传递。write() 函数本质上是一个系统调用,系统调用的开销较大,尤其是在频繁写入数据时,效率会非常低。因此,这种方式在传输大量数据时可能会导致显示效果不佳,如出现花屏现象。
方法:使用Linux系统中的内存映射函数,向Linux内核中的显存映射到应用程序中,得到应用程序中显存的首地址,通过该地址访问显存,这样提高数据传输效率。
内存映射
如果使用内存映射就需要将800*480的像素点全部填满。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
参数说明:
void addr ---- 需要映射的内存中显示的首地址,如果使用NULL,系统自动获取
size_t length ---- 需要映射的内存的大小,显存的大小800480*4
int prot ----- 映射后内存的访问属性,一般为PROT_READ|PROT_WRITE
int flags ---- 映射后显存的标志,MAP_SHARED—映射后的显存多个进程都可以访问
off_t offset ---- 映射内存的偏移量,一般为0;
返回值
void *----在应用程序中,得到映射后的显存的首地址,通过改地址就可以访问显存了。
显示各类国旗
int lcd_flag(int country){
int fd_lcd;
int *lcd_buf;
int color[] = {black,red, yellow, blue, green, white};
size_t len = SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(int);
fd_lcd = open("/dev/fb0", O_RDWR);
if (fd_lcd == -1)
{
perror("open lcd ");
return -1;
}
printf("fd_lcd = %d\n", fd_lcd);
lcd_buf = (int *)mmap(0, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd_lcd, 0);
switch(country){
case 1:
for (int j = 0; j <3 ; j++)
{
for (int i = 160 *800*(j); i < 160 *800*(j+1) ; i++)
{
lcd_buf[i] = color[j];
}
sleep(1);
}
break;
case 2:
for(int i = 0;i<800*480;i++){
lcd_buf[i] = color[5];
}
break;
case 3:
for (int i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) {
lcd_buf[i] = color[5];
}
int xx=400,yy=240;
for(int i=0;i<480;i++){
for(int j=0;j<800;j++){
if((xx-j)*(xx-j)+(yy-i)*(yy-i)<=144*144){
lcd_buf[i*800+j]= color[1];
}
}
}
break;
}
munmap(lcd_buf, len);
close(fd_lcd);
return 0;
}
LCD显示全屏bmp
1bmp图片
bmp图片是位图,是没有经过压缩的,该图片经过简单的处理,可以直接写入显存,并在液晶屏上显示。
注意bmp图片的格式,有两部分组成:
1)文件的头信息:54B
头信息的内容:可以查找资料,bmp图片的标准格式,里面包含图片的分辨率(像素点)、图片的色位(每个像素点使用多少位的数据来描述,bmp图片通常是24bits的,其中RGB各占8位)。
2)文件的颜色数据
800 * 480的bmp图:800 * 480 * 3
200 * 100的bmp图:200 * 100 * 3
图片显示翻转问题
问题:液晶屏显示的bmp图片是上下翻转的,
原因:液晶屏显示扫描的顺序:从左到右,从上到下,相当于液晶屏的原点是左上角。
bmp图片存放颜色顺序:从左到右,从下到上,相当于bmp图片的原点在左下角。
解决:
如果将液晶屏看成一个二维坐标,x是横轴,y是纵轴。如果需要图片上下翻转,则将y改成479-y。
代码如下:
int lcd_bmp(char *bmp_name)
{
int fd_bmp = open(bmp_name, O_RDONLY);
if (fd_bmp == -1)
{
perror("open bmp");
return -1;
}
// BMP文件头54字节,跳过文件头读取颜色数据
lseek(fd_bmp, 54, SEEK_SET);
char bmp_buf[800 * 480 * 3]; // BMP颜色数据
read(fd_bmp, bmp_buf, sizeof(bmp_buf));
close(fd_bmp);
int fd_lcd = open("/dev/fb0", O_RDWR);
if (fd_lcd == -1)
{
perror("open lcd");
return -1;
}
int *lcd_base = NULL; // 每个像素点占用内存是4B
lcd_base = (int *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd_lcd, 0);
if (lcd_base == MAP_FAILED)
{
perror("mmap");
close(fd_lcd);
return -2;
}
int n=0;
for(int y=0;y<480;y++)
for(int x=0;x<800;x++,n++)
{
*(lcd_base+800*(479-y)+x) = (bmp_buf[3*n+2]<<16) | (bmp_buf[3*n+1] <<8) | (bmp_buf[3*n]<<0);
}
munmap(lcd_base, 800 * 480 * 4);
close(fd_lcd);
return 0;
}