引言:
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提供接口。
虽然第一种方案被客户否定,但在实现的过程中对截屏,图片数据转换,屏驱动的相关机制有比较深的了解,这也算是一种收获吧。