引言:
Android平臺爲彩色屏,圖片格式爲RGB8888,而1.3寸OLED屏爲黑白屏,即像素爲1,讓屏正常工作能採用的方案有:
1. 方便apk開發,減少應用層開發的工作量,採取讀取framebuf中的數據,將彩色轉爲單色圖片,用8080並口的方式發送數據至GRAM.
2. 加字庫與圖片,用單色屏開發的方式,省卻圖片數據轉換的步驟。
當時與客戶討論屏幕顯示的效果等細節時,客戶需要至少一種字體,二種字號字體顯示及跑馬燈等動畫要求。因沒接觸過增加字庫,不瞭解apk軟件對屏幕的處理;另客戶要求的時間短,綜合考慮,先採用第1種實現方案。
實現步驟:
1. 在FAE的協助下,OLED屏能正常點亮,並且發送單色圖片通過Img2Lcd.exe轉換工具轉換好的圖片數據能正常顯示該圖片。
2. 圖片格式轉換:
修改frameworks\base\cmds\screenshot\Screenshot.c,先用截圖的方式嘗試將RGB8888 轉換成RGB888、RGB565、單色圖,保存爲BMP格式,adb pull出來看實際效果。在網上參考相關轉換算法後,轉換代碼如下:
1). RGB888,其實質是將RGB8888的後一字節給丟掉。
static int get_rgb888_header(int w, int h, BMPHEADER * head, BMPINFO * info)
{
int size = 0;
if (head && info) {
size = w * h * 3;
memset(head, 0, sizeof(* head));
memset(info, 0, sizeof(* info));
head->bfType[0] = ‘B’;
head->bfType[1] = ‘M’;
head->bfOffBits = 14 + sizeof(* info);
head->bfSize = head->bfOffBits + size;
head->bfSize = (head->bfSize + 3) & ~3;//windows要求文件大小必須是4的倍數
size = head->bfSize - head->bfOffBits;
info->biSize = sizeof(BMPINFO);
info->biWidth = w;
info->biHeight = -h;
info->biPlanes = 1;
info->biBitCount = 24;
info->biCompression = 0;//BI_RGB;
info->biSizeImage = size;
printf(“rgb888:%dbit,%d*%d,%d\n”, info->biBitCount, w, h, head->bfSize);
}
return size;
}
void bmp_create_rgb888(FILE *fb_in, unsigned int rowlen, unsigned int col)
{
FILE * hfile = NULL;
char imgbuf[0x10000];
unsigned int r;
int i;
int size = 0;
BMPHEADER head;
BMPINFO info;
hfile = fopen(BMP_FILE_PATH, “wb”);
if (hfile == NULL) {
printf(“open(%s) failed!\n”, BMP_FILE_PATH);
return;
}
size = get_rgb888_header(128, 64, &head, &info);
fwrite(head.bfType, 1, 14, hfile);
fwrite(&info, 1, sizeof(info), hfile);
fseek(fb_in, 0, SEEK_SET);
for(r=0; r<col; r++) {
int len = fread(imgbuf, 1, rowlen, fb_in);
if (len <= 0) break;
for(i=0; i<rowlen; i+=4)
{
fwrite(&imgbuf[i], 1, 3, hfile);
}
}
if (hfile != NULL)
fclose(hfile);
}
2). RGB565
static int get_rgb565_header(int w, int h, BMPHEADER * head, BITMAPINFO * info)
{
int size = 0;
if (head && info) {
size = w * h * 2;
memset(head, 0, sizeof(* head));
memset(info, 0, sizeof(* info));
head->bfType[0] = ‘B’;
head->bfType[1] = ‘M’;
head->bfOffBits = 14 + sizeof(* info);
head->bfSize = head->bfOffBits + size;
head->bfSize = (head->bfSize + 3) & ~3;
size = head->bfSize - head->bfOffBits;
info->bmiHeader.biSize = sizeof(info->bmiHeader);
info->bmiHeader.biWidth = w;
info->bmiHeader.biHeight = -h;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 16;
info->bmiHeader.biCompression = 0;//BI_BITFIELDS;
info->bmiHeader.biSizeImage = size;
info->rgb[0] = 0xF800;
info->rgb[1] = 0x07E0;
info->rgb[2] = 0x001F;
printf(“rgb565:%dbit,%d*%d,%d\n”, info->bmiHeader.biBitCount, w, h, head->bfSize);
}
return size;
}
void bmp_create_rgb565(FILE *fb_in, unsigned int rowlen, unsigned int col)
{
FILE * hfile = NULL;
char imgbuf[0x10000];
unsigned int r;
int i;
int size = 0;
BMPHEADER head;
BITMAPINFO info;
unsigned short data;
hfile = fopen(BMP_FILE_PATH, “wb”);
if (hfile == NULL) {
printf(“open(%s) failed!\n”, BMP_FILE_PATH);
return;
}
size = get_rgb565_header(128, 64, &head, &info);
fwrite(head.bfType, 1, 14, hfile);
fwrite(&info, 1, sizeof(info), hfile);
fseek(fb_in, 0, SEEK_SET);
for(r=0; r<col; r++) {
int len = fread(imgbuf, 1, rowlen, fb_in);
if (len <= 0) break;
for(i=0; i<rowlen; i+=4)
{
data = (unsigned short)((((imgbuf[i] >> 3) & 0x1F)<<0)
| (((imgbuf[i+1] >> 2)&0x3F)<<5)
| (((imgbuf[i+2] >> 3)&0x1F)<<11));
fwrite(&data, 1, sizeof(unsigned short), hfile);
}
}
if (hfile != NULL)
fclose(hfile);
}
3). 單色圖
#define LCD_DRV_RGB_TO_BW(color,bit) ( (unsigned char)((( (((color)&0xf800)>>11) + (((color)&0x07e0)>>5) + ((color)&0x001f) ) & 0x40) ? (1<<bit) : 0) )
const unsigned char bmp_head[64] =
{
0X42,0X4D,0X40,0X04,0X00,0X00,0X00,0X00,0X00,0X00,0X3E,0X00,0X00,0X00,0X28,0X00,
0X00,0X00,0X80,0X00,0X00,0X00,0X40,0X00,0X00,0X00,0X01,0X00,0X01,0X00,0X00,0X00,
0X00,0X00,0X02,0X04,0X00,0X00,0X12,0X0B,0X00,0X00,0X12,0X0B,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
};
void F_LCD_ColorTranslate(unsigned short *image, unsigned short size, unsigned char *tmp)
{
unsigned short cnt;
unsigned short index = 0;
unsigned short bit = 0;
int temp = 128;
for (cnt = size-1; cnt != 0; –cnt)
{
index = cnt/temp/8*temp + cnt%temp;
bit = cnt/temp%8;
tmp[index] |= LCD_DRV_RGB_TO_BW(image[cnt], bit);
}
}
void F_LCD_ColorTranslate2(unsigned short *image, unsigned short size, unsigned char *tmp)
{
unsigned short cnt;
unsigned short index = 0;
unsigned short bit = 0;
int temp = 128;
int i = 0, j = 0;
for (cnt = 0; cnt < size; cnt+=8)
{
for(j=0; j<8; j++)
{
tmp[i] |= ((( (((image[cnt+j])&0xf800)>>11) + (((image[cnt+j])&0x07e0)>>5) + ((image[cnt+j])&0x001f) ) & 0x40)?1<<j:0);
}
i++;
}
}
void bmp_create(FILE *fb_in, unsigned int rowlen, unsigned int col)
{
FILE * hfile = NULL;
char imgbuf[0x10000];
unsigned int r;
int i, j;
int size = 0;
BMPHEADER head;
BMPINFO info;
unsigned short data[8192] = {0};
unsigned char tmp[1032] = {0};
hfile = fopen(BMP_FILE_PATH, “wb”);
if (hfile == NULL) {
printf(“open(%s) failed!\n”, BMP_FILE_PATH);
return;
}
fwrite(bmp_head, 1, 64, hfile);
fseek(fb_in, 0, SEEK_SET);
j = 0;
for(r=0; r<col; r++) {
int len = fread(imgbuf, 1, rowlen, fb_in);
if (len <= 0) break;
for(i=0; i<rowlen; i+=4)
{
data[j] = (unsigned short)((((imgbuf[i] >> 3) & 0x1F)<<0)
| (((imgbuf[i+1] >> 2)&0x3F)<<5)
| (((imgbuf[i+2] >> 3)&0x1F)<<11));
j++;
}
}
F_LCD_ColorTranslate2(data, 8192, tmp);
fwrite(tmp, 1, 1024, hfile);
if (hfile != NULL)
fclose(hfile);
}
4). BMP相關結構體
typedef struct bmp_header
{
short twobyte ;//兩個字節,用來保證下面成員緊湊排列,這兩個字符不能寫到文件中
//14B
char bfType[2];//!文件的類型,該值必需是0x4D42,也就是字符‘BM’
unsigned int bfSize ;//!說明文件的大小,用字節爲單位
unsigned int bfReserved1;//保留,必須設置爲0
unsigned int bfOffBits;//!說明從文件頭開始到實際的圖象數據之間的字節的偏移量,這裏爲14B+sizeof(BMPINFO)
}BMPHEADER;
typedef struct bmp_info
{
//40B
unsigned int biSize;
int biWidth ;
int biHeight;
unsigned short biPlanes ;
unsigned short biBitCount ;
unsigned int biCompression;
#define BI_RGB0L
#define BI_RLE8 1L
#define BI_RLE4 2L
#define BI_BITFIELDS3L
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
}BMPINFO;
typedef struct tagBITMAPINFO {
BMPINFO bmiHeader;
//RGBQUADbmiColors[1];
unsigned int rgb[3];
} BITMAPINFO;
3. 經過上一步驟,已能將framebuf中的數據轉換成單色數據,數據與像素顯示的關係如右圖,而屏的像素排列與圖片的數據關係如左圖,故還需要將數據進行一次調整。具體轉換見後續代碼。
驅動層相關代碼修改如下(爲減少數據轉換的計算量,將RGB8888直接轉換成單色圖並如3所述進行數據調整):
static int p = 0;
static u8 __iomem *src = NULL;
static unsigned char flush_flag = 0;
extern void flush_screen_handle(unsigned char *image);
void flush_screen()
{
if(src == NULL)
{
src = kmalloc(32768 , GFP_KERNEL);
}
if(mtkfb_fbi->var.bits_per_pixel != 32 || src == NULL)
{
return;
}
p = (mtkfb_fbi->var.xoffset + mtkfb_fbi->var.xres * mtkfb_fbi->var.yoffset) * 4;
if(flush_flag == 0)
{
flush_flag = 1;
fb_memcpy_fromfb(src, (mtkfb_fbi->screen_base + p), 32768);
flush_screen_handle(src);
flush_flag = 0;
}
}
EXPORT_SYMBOL(flush_screen);
void F_LCD_WriteAll(unsigned char * pbtData)
{
unsigned int j,i;
for(i=0xB0;i<0xB8;i++)
{
send_ctrl_cmd(i);
send_ctrl_cmd(0x02);
send_ctrl_cmd(0x10);
for(j=0;j<128;j++)
{
send_data_cmd(*(pbtData++));
}
}
}
static int LCD_ColorTranslate(unsigned char * psrc, unsigned short size)
{
unsigned short i;
unsigned short cnt = 0;
unsigned short index = 0;
unsigned short bit = 0;
if (!psrc || size <= 0) {
return -1;
}
memset(R_LCD_BWBuffer, 0, 1024);
for(i=0; i<size; i+=4)
{
index = cnt/FRAME_WIDTH/8*FRAME_WIDTH + cnt%FRAME_WIDTH;
bit = cnt/FRAME_WIDTH%8;
R_LCD_BWBuffer[index] |= (unsigned char)(((((psrc[i] >> 3) & 0x1F)
+ ((psrc[i+1] >> 2)&0x3F)
+ ((psrc[i+2] >> 3)&0x1F))& 0x40)? (1<<bit) : 0);
cnt++;
}
return 0;
}
void flush_screen_handle(unsigned char *image)
{
LCD_ColorTranslate(image, 32768);
F_LCD_WriteAll(R_LCD_BWBuffer);
}
結論:
按照原計劃,基本功能已完成,在交付給客戶看效果時,提出兩個問題:
1. 屏幕刷新有一點點延時,原因是系統原有的刷屏方式是通過DMA傳輸數據,而修改後,a、有圖片數據轉換的計算量,b、屏幕數據刷新時,是整個屏幕數據更新。
2. 圖片數據轉換有失真,字體顯示效果不好。
籤於這兩個大問題,這種方案被否定,採用第二種方案。 客戶在HAL添加一種字庫,所有屏幕數據的處理都用C在內存中模擬處理,數據處理好後,通過kernel提供的接口,直接發送給GRAM,kernel僅僅發送數據,不作任何處理。HAL層向apk提供接口。
雖然第一種方案被客戶否定,但在實現的過程中對截屏,圖片數據轉換,屏驅動的相關機制有比較深的瞭解,這也算是一種收穫吧。