要了解LCD的规格,弄清楚怎么设置各个寄存器,比如设置LCD的时钟、分辨率、象素
② 划出一块内存给LCD使用
③ 编写一个函数,实现在指定坐标描点。比如根据x、y坐标在这块内存里找到这个象素对应的小区域,填入数据。
基于操作系统时,我们首先是找到类似的驱动,弄清楚驱动结构,找到要修改的地方进行修改。
下面是单片机操作LCD的代码:
① 初始化:
void Tft_Lcd_Init(int type)
{
LCDCON1 = (CLKVAL_TFT_320240<8) | (LCDTYPE_TFT<5) |
(BPPMODE_16BPP<1) | (ENVID_DISABLE<0);
LCDCON2 = (VBPD_320240<24) | (LINEVAL_TFT_320240<14) |
(VFPD_320240<6) | (VSPW_320240);
LCDCON3 = (HBPD_320240<19) | (HOZVAL_TFT_320240<8) | (HFPD_320240);
LCDCON4 = HSPW_320240;
//LCDCON5 = (FORMAT8BPP_565<11) | (HSYNC_INV<9) | (VSYNC_INV<8) |
//(HWSWP<1);
LCDCON5 = (FORMAT8BPP_565<11) | (HSYNC_INV<9) | (VSYNC_INV<8) | (VDEN_INV < 6) |
(HWSWP<0);
LCDSADDR1 = ((LCDBUFFER>>22)<21) | LOWER21BITS(LCDBUFFER>>1);
LCDSADDR2 = LOWER21BITS((LCDBUFFER+
(LINEVAL_TFT_320240+1)*(HOZVAL_TFT_320240+1)*2)>>1);
LCDSADDR3 = (0<11) | (LCD_XSIZE_TFT_320240*2/2);
TPAL = 0;
fb_base_addr = LCDBUFFER;
bpp = 16;
xsize = 320;
ysize = 240;
}
② 描点:
void PutPixel(UINT32 x, UINT32 y, UINT32 color)
{
UINT8 red,green,blue;
switch (bpp){
case 16:
{
UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x);
red = (color >> 19) & 0x1f;
green = (color >> 10) & 0x3f;
blue = (color >> 3) & 0x1f;
color = (red < 11) | (green < 5) | blue; // 格式5:6:5
*addr = (UINT16) color;
break;
}
case 8:
{
UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x);
*addr = (UINT8) color;
break;
}
default:
break;
}
}
下面是在Linux的LCD驱动里修改的地方(archarmmach-s3c2440mach-smdk2440.c):
static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {
.regs = {
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
S3C2410_LCDCON1_TFT |
S3C2410_LCDCON1_CLKVAL(0x04),
.lcdcon2 = S3C2410_LCDCON2_VBPD(1) |
S3C2410_LCDCON2_LINeval_r(239) |
S3C2410_LCDCON2_VFPD(5) |
S3C2410_LCDCON2_VSPW(1),
.lcdcon3 = S3C2410_LCDCON3_HBPD(36) |
S3C2410_LCDCON3_HOZVAL(319) |
S3C2410_LCDCON3_HFPD(19),
.lcdcon4 = S3C2410_LCDCON4_MVAL(13) |
S3C2410_LCDCON4_HSPW(5),
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVV |
S3C2410_LCDCON5_INVVDEN |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
},
.gpccon = 0xaaaa56aa,
.gpccon_mask = 0xffffffff,
.gpcup = 0xffffffff,
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaaaaaaaa,
.gpdcon_mask = 0xffffffff,
.gpdup = 0xffffffff,
.gpdup_mask = 0xffffffff,
.fixed_syncs = 1,
.type = S3C2410_LCDCON1_TFT,
.width = 320,
.height = 240,
.xres = {
.min = 320,
.max = 320,
.defval = 320,
},
.yres = {
.max = 240,
.min = 240,
.defval = 240,
},
.bpp = {
.min = 16,
.max = 16,
.defval = 16,
},
};
这并不表示代码Linux的驱动程序就比单片机的驱动程序好写,怎么在几万个文件中找到要修改的代码,这也是需要艰苦的学习的。基于操作系统的驱动开发,既要懂得芯片的具体操作,也要理解操作系统的软件结构。
2.1.2 是否通用
有些单片机厂家也给客户提供了大量的驱动程序,比如USB HOST驱动程序,这可以让客户很容易就可以在它的上面编写程序读写U盘。但是客户写的这些程序,只能在这种芯片、这个驱动程序上使用;更换另一种芯片后,即使芯片公司也提供了驱动程序,但是接口绝对不一样,客户又得重新编写应用程序。
基于操作系统的驱动程序要遵循统一的接口,比如对于不同的芯片的USB HOST驱动,它们都要向上提供一个相同的数据结构,在里面实现了各自的USB操作。
下面是S3C2410/S3C2440的USB驱动向上层提供的数据结构:
static const struct hc_driver ohci_s3c2410_hc_driver = {
.deion = hcd_name,
.product_desc = "S3C24XX OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
.start = ohci_s3c2410_start,
.stop = ohci_stop,
.shutdown = ohci_shutdown,
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
.get__number = ohci_get_,
.hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
下面是ATMEL公司的ARM芯片的USB驱动向上层提供的数