linux上LCD應用程式編寫 柏貴林
編寫之前需要了解一些概念
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;
}