一。环境
系统:ubuntu12.04
开发板:jz2440
编译器:gcc
二。说明
有空补上
三。代码
Makefile:
1 CC = arm-linux-gcc 2 LD = arm-linux-ld 3 AR = arm-linux-ar 4 OBJCOPY = arm-linux-objcopy 5 OBJDUMP = arm-linux-objdump 6 7 CFLAGS := -Wall -O2 8 9 10 export CC LD AR OBJCOPY OBJDUMP CFLAGS 11 12 objs := head.o init.o nand.o lcddrv.o framebuffer.o main.o 13 14 lcd.bin: $(objs) 15 ${LD} -Tlcd.lds -o lcd_elf $^ 16 ${OBJCOPY} -O binary -S lcd_elf $@ 17 ${OBJDUMP} -D -m arm lcd_elf > lcd.dis 18 19 20 %.o:%.c 21 ${CC} $(CFLAGS) -c -o $@ $< 22 23 %.o:%.S 24 ${CC} $(CFLAGS) -c -o $@ $< 25 26 clean: 27 rm -f lcd.bin lcd_elf lcd.dis *.o 28
head.S:
1 @****************************************************************************** 2 @ File: head.S 3 @ 功能: 设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 4 @****************************************************************************** 5 6 .extern main 7 .text 8 .global _start 9 _start: 10 @****************************************************************************** 11 @ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用 12 @****************************************************************************** 13 b Reset 14 15 @ 0x04: 未定义指令中止模式的向量地址 16 HandleUndef: 17 b HandleUndef 18 19 @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式 20 HandleSWI: 21 b HandleSWI 22 23 @ 0x0c: 指令预取终止导致的异常的向量地址 24 HandlePrefetchAbort: 25 b HandlePrefetchAbort 26 27 @ 0x10: 数据访问终止导致的异常的向量地址 28 HandleDataAbort: 29 b HandleDataAbort 30 31 @ 0x14: 保留 32 HandleNotUsed: 33 b HandleNotUsed 34 35 @ 0x18: 中断模式的向量地址 36 HandleIRQ: 37 b HandleIRQ 38 39 @ 0x1c: 快中断模式的向量地址 40 HandleFIQ: 41 b HandleFIQ 42 43 Reset: 44 ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈 45 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 46 bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK 47 bl memsetup @ 设置存储控制器以使用SDRAM 48 bl nand_init @ 初始化NAND Flash 49 50 @ 复制代码到SDRAM中 51 ldr r0, =0x30000000 @ 1. 目标地址 = 0x30000000,这是SDRAM的起始地址 52 mov r1, #4096 @ 2. 源地址 = 4096,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处 53 mov r2, #16*1024 @ 3. 复制长度 = 16K,对于本实验,这是足够了 54 bl CopyCode2SDRAM @ 调用C函数CopyCode2SDRAM 55 56 bl clean_bss @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段 57 58 59 60 msr cpsr_c, #0xdf @ 进入系统模式 61 ldr sp, =0x34000000 @ 设置系统模式栈指针, 62 63 64 65 ldr lr, =halt_loop @ 设置返回地址 66 ldr pc, =main @ 调用main函数 67 halt_loop: 68 b halt_loop
上面的sdram,时钟,nand flash等同前面的,不贴出来了
现在与lcd有关的函数:
main.c:
1 #include "lcddrv.h" 2 #include "framebuffer.h" 3 #include "s3c24xx.h" 4 5 void delay() 6 7 { 8 9 unsigned long cnt; 10 11 for(cnt=0;cnt<100000;cnt++); 12 13 } 14 15 int main() 16 { 17 Lcd_Port_Init(); // 设置LCD引脚 18 Tft_Lcd_Init(); // 初始化LCD控制器 19 Lcd_PowerEnable(0, 1); // 设置LCD_PWREN有效,它用于打开LCD的电源 20 Lcd_EnvidOnOff(1); // 使能LCD控制器输出信号 21 22 ClearScr(0x0); // 清屏,黑色 23 while (1) 24 { 25 26 Mire(); 27 delay(); 28 //Lcd_EnvidOnOff(0); 29 30 } 31 32 return 0; 33 }
由main函数可以看出,本程序只是驱动lcd来画同心圆,参考代码是韦东山先生的,此处作了较大的删改:
framebuffer.c:
1 /* 2 * FILE: framebuffer.c 3 * 实现在framebuffer上画点、画线、画同心圆、清屏的函数 4 */ 5 6 #include "framebuffer.h" 7 8 extern unsigned int fb_base_addr; 9 extern unsigned int bpp; 10 extern unsigned int xsize; 11 extern unsigned int ysize; 12 13 /* 14 * 画点 15 * 输入参数: 16 * x、y : 象素坐标 17 * color: 颜色值 18 * 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度), 19 * 需要转换为5:6:5格式 20 * 对于8BPP: color为调色板中的索引值, 21 * 其颜色取决于调色板中的数值 22 */ 23 void PutPixel(unsigned int x, unsigned int y, unsigned int color) 24 { 25 unsigned char red,green,blue; 26 27 switch (bpp){ 28 case 16: 29 { 30 unsigned short *addr = (unsigned short *)fb_base_addr + (y * xsize + x); 31 red = (color >> 19) & 0x1f; 32 green = (color >> 10) & 0x3f; 33 blue = (color >> 3) & 0x1f; 34 color = (red << 11) | (green << 5) | blue; // 格式5:6:5 35 *addr = (unsigned short) color; 36 break; 37 } 38 39 40 default: 41 break; 42 } 43 } 44 45 /* 46 * 绘制同心圆 47 */ 48 void Mire(void) 49 { 50 unsigned int x,y; 51 unsigned int color; 52 unsigned char red,green,blue,alpha; 53 54 for (y = 0; y < ysize; y++) 55 for (x = 0; x < xsize; x++){ 56 color = ((x-xsize/2)*(x-xsize/2) + (y-ysize/2)*(y-ysize/2))/64; 57 red = (color/8) % 256; 58 green = (color/4) % 256; 59 blue = (color/2) % 256; 60 alpha = (color*2) % 256; 61 62 color |= ((unsigned int)alpha << 24); 63 color |= ((unsigned int)red << 16); 64 color |= ((unsigned int)green << 8 ); 65 color |= ((unsigned int)blue ); 66 67 PutPixel(x,y,color); 68 } 69 } 70 71 /* 72 * 将屏幕清成单色 73 * 输入参数: 74 * color: 颜色值 75 * 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度), 76 * 需要转换为5:6:5格式 77 * 对于8BPP: color为调色板中的索引值, 78 * 其颜色取决于调色板中的数值 79 */ 80 void ClearScr(unsigned int color) 81 { 82 unsigned int x,y; 83 84 for (y = 0; y < ysize; y++) 85 for (x = 0; x < xsize; x++) 86 PutPixel(x, y, color); 87 }
lcddrv.c:
1 /* 2 * FILE: lcddrv.c 3 * 提供操作LCD控制器、调色板等的底层函数 4 */ 5 6 #include "s3c24xx.h" 7 #include "lcddrv.h" 8 9 #define GPB0_tout0 (2<<(0*2)) 10 #define GPB0_out (1<<(0*2)) 11 #define GPB1_out (1<<(1*2)) 12 13 #define GPB0_MSK (3<<(0*2)) 14 #define GPB1_MSK (3<<(1*2)) 15 16 17 unsigned int fb_base_addr; 18 unsigned int bpp; 19 unsigned int xsize; 20 unsigned int ysize; 21 22 23 /* 24 * 初始化用于LCD的引脚 25 */ 26 void Lcd_Port_Init(void) 27 { 28 GPCUP = 0xffffffff; // 禁止内部上拉 29 GPCCON = 0xaaaaaaaa; // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND 30 GPDUP = 0xffffffff; // 禁止内部上拉 31 GPDCON = 0xaaaaaaaa; // GPIO管脚用于VD[23:8] 32 GPBCON &= ~(GPB0_MSK); // Power enable pin 33 GPBCON |= GPB0_out; 34 GPBDAT &= ~(1<<0); // Power off 35 36 } 37 38 /* 39 * 初始化LCD控制器 40 * 输入参数: 41 * type: 显示模式 42 * MODE_TFT_8BIT_240320 : 240*320 8bpp的TFT LCD 43 * MODE_TFT_16BIT_240320 : 240*320 16bpp的TFT LCD 44 * MODE_TFT_8BIT_640480 : 640*480 8bpp的TFT LCD 45 * MODE_TFT_16BIT_640480 : 640*480 16bpp的TFT LCD 46 */ 47 void Tft_Lcd_Init() 48 { 49 50 /* 51 * 设置LCD控制器的控制寄存器LCDCON1~5 52 * 1. LCDCON1: 53 * 设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2] 54 * 选择LCD类型: TFT LCD 55 * 设置显示模式: 16BPP 56 * 先禁止LCD信号输出 57 * 2. LCDCON2/3/4: 58 * 设置控制信号的时间参数 59 * 设置分辨率,即行数及列数 60 * 现在,可以根据公式计算出显示器的频率: 61 * 当HCLK=100MHz时, 62 * Frame Rate = 1/[{(VSPW+1)+(VBPD+1)+(LIINEVAL+1)+(VFPD+1)}x 63 * {(HSPW+1)+(HBPD+1)+(HFPD+1)+(HOZVAL+1)}x 64 * {2x(CLKVAL+1)/(HCLK)}] 65 * = 60Hz 66 * 3. LCDCON5: 67 * 设置显示模式为16BPP时的数据格式: 5:6:5 68 * 设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转 69 * 半字(2字节)交换使能 70 */ 71 LCDCON1 = (4<<8) | (LCDTYPE_TFT<<5) | 72 (BPPMODE_16BPP<<1) | (ENVID_DISABLE<<0); 73 LCDCON2 = (1<<24) | (271<<14) | 74 (1<<6) | (9); 75 LCDCON3 = (1<<19) | (479<<8) | (1); 76 LCDCON4 = 40; 77 LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) | 78 (HWSWP<<1); 79 80 /* 81 * 设置LCD控制器的地址寄存器LCDSADDR1~3 82 * 帧内存与视口(view point)完全吻合, 83 * 图像数据格式如下: 84 * |----PAGEWIDTH----| 85 * y/x 0 1 2 239 86 * 0 rgb rgb rgb ... rgb 87 * 1 rgb rgb rgb ... rgb 88 * 1. LCDSADDR1: 89 * 设置LCDBANK、LCDBASEU 90 * 2. LCDSADDR2: 91 * 设置LCDBASEL: 帧缓冲区的结束地址A[21:1] 92 * 3. LCDSADDR3: 93 * OFFSIZE等于0,PAGEWIDTH等于(240*2/2) 94 */ 95 LCDSADDR1 = ((LCDFRAMEBUFFER>>22)<<21) | LOWER21BITS(LCDFRAMEBUFFER>>1); 96 LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ 97 (480)*(272)*2)>>1); 98 LCDSADDR3 = (0<<11) | (480*2/2); 99 100 /* 禁止临时调色板寄存器 */ 101 TPAL = 0; 102 103 fb_base_addr = LCDFRAMEBUFFER; 104 bpp = 16; 105 xsize = 480; 106 ysize = 272; 107 108 109 } 110 111 112 /* 113 * 设置是否输出LCD电源开关信号LCD_PWREN 114 * 输入参数: 115 * invpwren: 0 - LCD_PWREN有效时为正常极性 116 * 1 - LCD_PWREN有效时为反转极性 117 * pwren: 0 - LCD_PWREN输出有效 118 * 1 - LCD_PWREN输出无效 119 */ 120 void Lcd_PowerEnable(int invpwren, int pwren) 121 { 122 GPGCON = (GPGCON & (~(3<<8))) | (3<<8); // GPG4用作LCD_PWREN 123 GPGUP = (GPGUP & (~(1<<4))) | (1<<4); // 禁止内部上拉 124 125 LCDCON5 = (LCDCON5 & (~(1<<5))) | (invpwren<<5); // 设置LCD_PWREN的极性: 正常/反转 126 LCDCON5 = (LCDCON5 & (~(1<<3))) | (pwren<<3); // 设置是否输出LCD_PWREN 127 } 128 129 /* 130 * 设置LCD控制器是否输出信号 131 * 输入参数: 132 * onoff: 133 * 0 : 关闭 134 * 1 : 打开 135 */ 136 void Lcd_EnvidOnOff(int onoff) 137 { 138 if (onoff == 1) 139 { 140 LCDCON1 |= 1; // ENVID ON 141 GPBDAT |= (1<<0); // Power on 142 } 143 else 144 { 145 LCDCON1 &= 0x3fffe; // ENVID Off 146 GPBDAT &= ~(1<<0); // Power off 147 } 148 }
现在贴出重要的头文件做为理解用:
lcddrv.h:
1 /* 2 * FILE: lcddrv.h 3 * 操作LCD控制器、调色板等的底层函数接口 4 */ 5 6 #ifndef __LCDDRV_H__ 7 #define __LCDDRV_H__ 8 9 10 #define LOWER21BITS(n) ((n) & 0x1fffff) 11 12 #define BPPMODE_16BPP 0xC 13 14 15 #define LCDTYPE_TFT 0x3 16 17 #define ENVID_DISABLE 0 18 #define ENVID_ENABLE 1 19 20 #define FORMAT8BPP_5551 0 21 #define FORMAT8BPP_565 1 22 23 #define HSYNC_NORM 0 24 #define HSYNC_INV 1 25 26 #define VSYNC_NORM 0 27 #define VSYNC_INV 1 28 29 #define VDEN_NORM 0 30 #define VDEN_INV 1 31 32 #define BSWP 1 33 #define HWSWP 1 34 35 #define LCDFRAMEBUFFER 0x30400000 36 37 /* 38 * 初始化用于LCD的引脚 39 */ 40 void Lcd_Port_Init(void); 41 42 /* 43 * 初始化LCD控制器 44 * 输入参数: 45 * type: 显示模式 46 * MODE_TFT_8BIT_640480 : 640*640 8bpp的TFT LCD 47 * MODE_TFT_16BIT_640480 : 640*640 16bpp的TFT LCD 48 */ 49 void Tft_Lcd_Init(); 50 51 void Lcd_EnvidOnOff(int onoff); 52 53 /* 54 * 设置是否输出LCD电源开关信号LCD_PWREN 55 * 输入参数: 56 * invpwren: 0 - LCD_PWREN有效时为正常极性 57 * 1 - LCD_PWREN有效时为反转极性 58 * pwren: 0 - LCD_PWREN输出有效 59 * 1 - LCD_PWREN输出无效 60 */ 61 void Lcd_PowerEnable(int invpwren, int pwren); 62 63 64 #endif /*__LCDDRV_H__*/
自然不难看出,仍然有不少冗余项,这里先不做深究。
上面代码经过烧写验证,没有问题。
代码删减了串口,中断,以及print函数的硬件重定向等内容,以便更直观理解lcd驱动。
关于代码中重要函数,有时间补上。