编写之前需要了解一些概念
1、linux一些皆文件的思想
2、帧缓存的概念
3、什么是虚拟像素什么是逻辑像素(为什么要刷逻辑像素)
4、什么是点阵图
5、BMP图片的格式
6、汉字库的使用
7、摄像头成像原理与bmp的关系(图像颠倒现象)
下面是我早期写的一段测试代码,在ubuntu10 gun上编译通过,在开发板1024*600的屏上测试ok,注意编译的时候需要连接线程库,另外如果不是1024*600的屏幕需要对应修改帧缓存的for循环的参数,否则直接段错误
bmp图像位深为32
欢迎读者对该代码进行优化,并重新发表,但请注明出处。
//----------------------------------------------------------------------------------------
// author by baiguilin
// Date 2015
//----------------------------------------------------------------------------------------
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include "lcd.h"
//----------------------------------------------------------------------------------------
// BMP文件信息
//----------------------------------------------------------------------------------------
typedef struct //14byte文件头
{
char cfType[2]; //文件类型,"BM"(0x4D42)
long cfSize; //文件大小(字节)
long cfReserved; //保留,值为0
long cfoffBits; //数据区相对于文件头的偏移量(字节)
}__attribute__((packed)) BITMAPFILEHEADER; //__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐
typedef struct //40byte信息头
{
char ciSize[4]; //BITMAPFILEHEADER所占的字节数
long ciWidth; //宽度
long ciHeight; //高度
char ciPlanes[2]; //目标设备的位平面数,值为1
int ciBitCount; //每个像素的位数
char ciCompress[4]; //压缩说明
char ciSizeImage[4]; //用字节表示的图像大小,该数据必须是4的倍数
char ciXPelsPerMeter[4]; //目标设备的水平像素数/米
char ciYPelsPerMeter[4]; //目标设备的垂直像素数/米
char ciClrUsed[4]; //位图使用调色板的颜色数
char ciClrImportant[4]; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要
}__attribute__((packed)) BITMAPINFOHEADER;
typedef struct //16位域
{
unsigned short red:5;
unsigned short green:6;
unsigned short blue:5;
}__attribute__((packed)) PIXEL; //颜色模式,RGB565
//----------------------------------------------------------------------------------------
// 变量定义区
//----------------------------------------------------------------------------------------
BITMAPFILEHEADER FileHead;
BITMAPINFOHEADER InfoHead;
static char *fbp = 0;
static int xres = 0;
static int yres = 0;
static int bits_per_pixel = 0;
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
//----------------------------------------------------------------------------------------
// 函数申明
//----------------------------------------------------------------------------------------
int show_bmp();
void clear_sector(int x0, int y0, int x, int y, char *fbp);//清除屏幕使用
void get_time(char *p_time);//获取现行时间
void show_words(int x0,int y0,const unsigned char *incode, char *fbp, unsigned color,int fsize);//显示一个汉字
void show_time(int x0,int y0,const unsigned char *incode, char *fbp, unsigned color,int fsize);//显示时间
void show_asc(int x0,int y0,const unsigned char *incode, char *fbp, unsigned color,int fsize);//显示ascll
void setmsg(int tmp,int sd,int tv_state);//设置温度湿度函数
void setcmr();//打开或关闭视频
void setcmr_addr(char *addr,long int size);
//----------------------------------------------------------------------------------------
// main 如果这里的main函数和其他文件main冲突,那么请调用nomain()
//----------------------------------------------------------------------------------------
#if 1
int main ( int argc, char *argv[] )
{
fbfd = open("/dev/fb0", O_RDWR); //打开显示设备
if (!fbfd)
{
//printf("Error: cannot open framebuffer device.\n");
exit(1);
}
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
{
//printf("Error:reading fixed information.\n");
exit(2);
}
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
{
//printf("Error: reading variable information.\n");
exit(3);
}
//printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );
xres = vinfo.xres;
yres = vinfo.yres;
bits_per_pixel = vinfo.bits_per_pixel;
//计算屏幕的总大小(字节)
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
//printf("screensize=%d\n",screensize);
//内存映射
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if ((int)fbp == -1)
{
//printf("Error: failed to map framebuffer device to memory.\n");
exit(4);
}
//5s的开机动画时间
clear_sector(0, 0, 1024, 600,fbp);
show_words(300,300,"这是测试",fbp, 0xffffffff,8);
sleep(5);
clear_sector(0, 0, 1024, 600,fbp);
//show_bmp();
//-----------------静态图像不重复刷新----------------
char p_time[60];
show_words(670,0,"这是测试",fbp, 0xffffffff,4);
show_words(650,50, "这是测试",fbp, 0xffffffff,4);
show_words(650,90,"这是测试",fbp, 0xffffffff,4);
show_words(650,130,"这是测试",fbp, 0xffffffff,4);
show_words(650,170,"这是测试",fbp, 0xffffffff,4);
show_words(650,210,"这是测试",fbp, 0xffffffff,4);
show_words(650,250,"这是测试",fbp, 0xffffffff,4);
show_words(650,290,"这是测试",fbp, 0xffffffff,4);
show_words(650,370,"这是测试",fbp, 0xffffffff,4);
//show_asc(800,370,"65",fbp, 255, 4);
show_words(650,410,"这是测试",fbp, 0xffffffff,4);
//show_asc(800,410,"65",fbp, 255, 4);
show_words(650,450,"这是测试",fbp, 0xffffffff,4);
show_words(650,330, "一一一一一一一一一一一",fbp, 0xffffffff,4);
//-------慢----------动态图像重复刷新----------------
pthread_t pth;
if(pthread_create(&pth,NULL,thread_func,NULL)<0)
{
//perror("pthread");
return 0;
}
while(1)//视屏刷新 重要!!
{
//模拟温湿度,到时需屏蔽
setmsg(55,21,1);
setcmr();//开始视屏
}
munmap(fbp, screensize);
close(fbfd);
return 0;
}
#endif
//子线程函数用于刷新更新不快的数据到屏幕上
void *thread_func(void *arg)
{
while(1)
{
char p_time[64];
get_time(p_time);
show_time(0,510,p_time, fbp,0xffffffff,4);
usleep(500000);
//printf("pthread\n");
}
}
int show_bmp()
{
FILE *fp;
int rc;
int line_x=0, line_y=0;
long int location = 0, BytesPerLine = 0;
char tmp[1024*600*4];
fp = fopen( "./21.bmp", "rb" );
if (fp == NULL)
{
return( -1 );
}
rc = fread( &FileHead, sizeof(BITMAPFILEHEADER),1, fp );
if ( rc != 1)
{
//printf("read header error!\n");
fclose( fp );
return( -2 );
}
if (memcmp(FileHead.cfType, "BM", 2) != 0)
{
//printf("it's not a BMP file\n");
fclose( fp );
return( -3 );
}
rc = fread( (char *)&InfoHead, sizeof(BITMAPINFOHEADER),1, fp );
if ( rc != 1)
{
//printf("read infoheader error!\n");
fclose( fp );
return( -4 );
}
//跳转的数据区
fseek(fp, FileHead.cfoffBits, SEEK_SET);
//每行字节数
while(!feof(fp))
{
PIXEL pix;
unsigned short int tmp;
// unsigned int t[1024 * 600];
// fread(t, 4, 1024*600*4, fp);
// unsigned int *p=(unsigned int *)fbp;
// for(line_y=0;line_y<600;line_y++)
// for(line_x=1023;line_x>-1;line_x--)
// {
// *(p+(599-line_y) * 1024 + line_x) = t[line_y*1024 + (line_x)];
// }
//640*480
int weight=640;
int hight=480;
unsigned int t[weight * hight];
fread(t, 4, weight*hight*4, fp);
unsigned int *p=(unsigned int *)fbp;
for(line_y=0;line_y<hight;line_y++)
for(line_x=weight-1;line_x>-1;line_x--)
{
*(p+((hight-1)-line_y) * 1024 + line_x) = t[line_y*1024 + (line_x)];
}
}
fclose( fp );
return( 0 );
}
//----------------------------------------------------------------------------------------
// 清除屏幕中的一块
//----------------------------------------------------------------------------------------
void clear_sector(int x0, int y0, int x, int y, char *fbp)
{
unsigned int location=(unsigned int)fbp+vinfo.xres*y0*4+x0*4;
//printf("---%ld---",location);
int i;
for(i = 0; i < (y-y0); i++)
{
memset((long int *)location, 0,(x-x0)*4);
location = location + vinfo.xres*4;
}
}
//----------------------------------------------------------------------------------------
// 获取时间
//----------------------------------------------------------------------------------------
void get_time(char *p_time)
{
time_t t;
static time_t t1;
t = time(NULL);
struct tm *tm1 = NULL;
tm1 = localtime(&t);
strftime(p_time, 60, "%Y-%m-%d %H:%M:%S WD:%A", tm1);
if(t1 !=t)
{
clear_sector(0, 500, 1024, 600,fbp);
clear_sector(800, 370, 900, 500,fbp);
t1=t;
}
//printf("curr_buff_time=%s\n", p_time);
}
//----------------------------------------------------------------------------------------
// 显示汉字
//----------------------------------------------------------------------------------------
void show_words(int x0,int y0,const unsigned char *incode, char *fbp, unsigned color,int fsize)
{
unsigned char qh, wh;
unsigned long offset;
unsigned long location = 0;
location=(x0+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y0+vinfo.yoffset) * finfo.line_length;
int tmp = location;
int n;
//循环显示所有汉字
for(n = 0; incode[n] != 0; n = n+2)
{
int bytes = 128;
qh = incode[n]-0xa0; //获得区码
wh = incode[n+1]-0xa0; //获得位码
offset = (94*(qh-1)+(wh-1))*bytes;//得到HZK16中的偏移位置
FILE *fp = NULL;
unsigned char buf[bytes];
bzero(buf,bytes);
//GB2312字库文件hzk16
if((fp=fopen("hzk32", "rb")) == NULL)
{
//perror("fopen");
exit(0);
}
fseek(fp, offset, SEEK_SET);
int ret = fread(buf, bytes, 1, fp);//buf存放一个汉字,有32个字节
if(0 > ret)
exit(0);
//显示一个汉字
int i = 0, j = 0;
while(i < bytes)//一行两次扫描,每循环一次扫描8位,共扫描16行,即是16×16
{
for(j = 0; j <8; j++)
{
if(buf[i] & (0x80>>j))
{
*((unsigned *)(fbp + location)) = color;
location+=fsize;
}
else
{
location+=fsize;
}
}
if((++i%4) == 0)//扫描换行
{
location+=(1024-32)*fsize;
//location = location-48+1024*8;
}
}
bzero(buf,bytes);
tmp += 32*fsize;
location=tmp;
fclose(fp);
}
}
void show_time(int x0,int y0,const unsigned char *incode, char *fbp, unsigned color,int fsize)
{
unsigned char qh, wh;
unsigned long offset;
unsigned long location = 0;
location=(x0+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y0+vinfo.yoffset) * finfo.line_length;
int tmp = location;
int n;
//循环显示所有汉字
for(n = 0; incode[n] != 0; n = n+1)
{
int bytes = 144;
offset = (incode[n]-32)*bytes;//得到HZK16中的偏移位置
FILE *fp = NULL;
unsigned char buf[bytes];
bzero(buf,bytes);
//GB2312字库文件hzk16
if((fp=fopen("ASC48", "rb")) == NULL)
{
//perror("fopen");
exit(0);
}
fseek(fp, offset, SEEK_SET);
int ret = fread(buf, bytes, 1, fp);//buf存放一个汉字,有32个字节
//printf("hanzi %s",buf);
if(0 > ret)
exit(0);
//显示一个汉字
int i = 0, j = 0;
while(i < bytes)//一行两次扫描,每循环一次扫描8位,共扫描16行,即是16×16
{
for(j = 0; j <8; j++)
{
if(buf[i] & (0x80>>j))
{
*((unsigned *)(fbp + location)) = color;
location+=fsize;
}
else
{
location+=fsize;
}
}
if((++i%3) == 0)//扫描换行
{
location+=(1024-24)*fsize;
//location = location-48+1024*8;
}
}
bzero(buf,bytes);
tmp += 16*fsize*2;
location=tmp;
fclose(fp);
}
}
//------------------------------------------------------
//显示ascii
//-----------------------------------------------------
void show_asc(int x0,int y0,const unsigned char *incode, char *fbp, unsigned color,int fsize)
{
unsigned char qh, wh;
unsigned long offset;
unsigned long location = 0;
location=(x0+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y0+vinfo.yoffset) * finfo.line_length;
int tmp = location;
int n;
//循环显示所有汉字
for(n = 0; incode[n] != 0; n = n+1)
{
int bytes = 144;
offset = (incode[n]-32)*bytes;//得到HZK16中的偏移位置
FILE *fp = NULL;
unsigned char buf[bytes];
bzero(buf,bytes);
//GB2312字库文件hzk16
if((fp=fopen("ASC48", "rb")) == NULL)
{
//perror("fopen");
exit(0);
}
fseek(fp, offset, SEEK_SET);
int ret = fread(buf, bytes, 1, fp);//buf存放一个汉字,有32个字节
//printf("hanzi %s",buf);
if(0 > ret)
exit(0);
//显示一个汉字
int i = 0, j = 0;
while(i < bytes)//一行两次扫描,每循环一次扫描8位,共扫描16行,即是16×16
{
for(j = 0; j <8; j++)
{
if(buf[i] & (0x80>>j))
{
*((unsigned *)(fbp + location)) = color;
location+=fsize;
}
else
{
location+=fsize;
}
}
if((++i%3) == 0)//扫描换行
{
location+=(1024-24)*fsize;
//location = location-48+1024*8;
}
}
bzero(buf,bytes);
tmp += 16*fsize*2;
location=tmp;
fclose(fp);
}
}
//这里设置动态的温度和湿度函数,供server调用
void setmsg(int tmp,int sd,int tv_state)
{
char atem[10];
char asd[3];
char agz[3];
sprintf(atem,"%d",tmp);
// printf("%d\n",atem);
sprintf(asd,"%d",sd);
show_asc(800,370,atem,fbp, 255, 4);//温度
show_asc(800,410,asd,fbp, 255, 4);//湿度
if(tv_state == 0)
{
show_asc(800,450,"OFF",fbp, 255, 4);//湿度
}
else
{
show_asc(800,450,"ON",fbp, 255, 4);//湿度
}
}
//这里设置视屏 1s钟刷新50次 读取文件的方式
void setcmr()
{
FILE *fptv;
int rc;
int line_x=0, line_y=0;
long int location = 0, BytesPerLine = 0;
char tmp[1024*600*4];
char filename[10];
static int index =1;
//printf("\n");//不可以删除
for(index=1;index<50;index++)
{
sprintf(filename,"%d.bmp",index);
//printf("%s\n",filename);
fptv = fopen(filename , "rb" );
if (fptv == NULL)
{
return;
}
rc = fread( &FileHead, sizeof(BITMAPFILEHEADER),1, fptv );
if ( rc != 1)
{
//printf("read header error!\n");
fclose( fptv );
return;
}
if (memcmp(FileHead.cfType, "BM", 2) != 0)
{
//printf("it's not a BMP file\n");
fclose( fptv );
return;
}
rc = fread( (char *)&InfoHead, sizeof(BITMAPINFOHEADER),1, fptv );
if ( rc != 1)
{
//printf("read infoheader error!\n");
fclose( fptv );
return;
}
//跳转的数据区
fseek(fptv, FileHead.cfoffBits, SEEK_SET);
//每行字节数
int weight=640;
int hight=480;
unsigned int t[weight * hight];
long Scansize=weight*hight*4;
unsigned int *p=(unsigned int *)fbp;
while(!feof(fptv))
{
//640*480
fread(t, 4, Scansize, fptv);
for(line_y=0;line_y<hight;line_y++)
for(line_x=weight-1;line_x>-1;line_x--)
{
*(p+(((hight-1)-line_y) << 10) + line_x) = t[line_y*640 + (line_x)];
}
}
fclose( fptv );
}
return;
}
//这里设置视屏 1s钟刷新50次 读取内存的方式 必须是640 *480
void setcmr_addr(int *addr,long int size)
{
int line_x=0, line_y=0;
long int location = 0, BytesPerLine = 0;
unsigned int *p=(unsigned int *)fbp;
int i=0;
for(line_y=0;line_y<480;line_y++)
for(line_x=640-1;line_x>-1;line_x--)
{
*(p+(((480-1)-line_y) << 10) + line_x) = addr[line_y*640 + (line_x)];
}
return;
}