实验九--裸机LCD

时间:2024-01-14 09:02:50

一。环境

  系统:ubuntu12.04

  开发板:jz2440

  编译器:gcc

二。说明

有空补上

三。代码

Makefile:

 CC      = arm-linux-gcc
LD = arm-linux-ld
AR = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump CFLAGS := -Wall -O2 export CC LD AR OBJCOPY OBJDUMP CFLAGS objs := head.o init.o nand.o lcddrv.o framebuffer.o main.o lcd.bin: $(objs)
${LD} -Tlcd.lds -o lcd_elf $^
${OBJCOPY} -O binary -S lcd_elf $@
${OBJDUMP} -D -m arm lcd_elf > lcd.dis %.o:%.c
${CC} $(CFLAGS) -c -o $@ $< %.o:%.S
${CC} $(CFLAGS) -c -o $@ $< clean:
rm -f lcd.bin lcd_elf lcd.dis *.o

head.S:

 @******************************************************************************
@ File: head.S
@ 功能: 设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@****************************************************************************** .extern main
.text
.global _start
_start:
@******************************************************************************
@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************
b Reset @ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI @ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort @ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort @ 0x14: 保留
HandleNotUsed:
b HandleNotUsed @ 0x18: 中断模式的向量地址
HandleIRQ:
b HandleIRQ @ 0x1c: 快中断模式的向量地址
HandleFIQ:
b HandleFIQ Reset:
ldr sp, = @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
bl memsetup @ 设置存储控制器以使用SDRAM
bl nand_init @ 初始化NAND Flash @ 复制代码到SDRAM中
ldr r0, =0x30000000 @ . 目标地址 = 0x30000000,这是SDRAM的起始地址
mov r1, # @ . 源地址 = ,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处
mov r2, #* @ . 复制长度 = 16K,对于本实验,这是足够了
bl CopyCode2SDRAM @ 调用C函数CopyCode2SDRAM bl clean_bss @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段 msr cpsr_c, #0xdf @ 进入系统模式
ldr sp, =0x34000000 @ 设置系统模式栈指针, ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数
halt_loop:
b halt_loop

上面的sdram,时钟,nand flash等同前面的,不贴出来了

现在与lcd有关的函数:

main.c:

 #include "lcddrv.h"
#include "framebuffer.h"
#include "s3c24xx.h" void delay() { unsigned long cnt; for(cnt=;cnt<;cnt++); } int main()
{
Lcd_Port_Init(); // 设置LCD引脚
Tft_Lcd_Init(); // 初始化LCD控制器
Lcd_PowerEnable(, ); // 设置LCD_PWREN有效,它用于打开LCD的电源
Lcd_EnvidOnOff(); // 使能LCD控制器输出信号 ClearScr(0x0); // 清屏,黑色
while ()
{ Mire();
delay();
//Lcd_EnvidOnOff(0); } return ;
}

由main函数可以看出,本程序只是驱动lcd来画同心圆,参考代码是韦东山先生的,此处作了较大的删改:

framebuffer.c:

 /*
* FILE: framebuffer.c
* 实现在framebuffer上画点、画线、画同心圆、清屏的函数
*/ #include "framebuffer.h" extern unsigned int fb_base_addr;
extern unsigned int bpp;
extern unsigned int xsize;
extern unsigned int ysize; /*
* 画点
* 输入参数:
* x、y : 象素坐标
* color: 颜色值
* 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
* 需要转换为5:6:5格式
* 对于8BPP: color为调色板中的索引值,
* 其颜色取决于调色板中的数值
*/
void PutPixel(unsigned int x, unsigned int y, unsigned int color)
{
unsigned char red,green,blue; switch (bpp){
case :
{
unsigned short *addr = (unsigned short *)fb_base_addr + (y * xsize + x);
red = (color >> ) & 0x1f;
green = (color >> ) & 0x3f;
blue = (color >> ) & 0x1f;
color = (red << ) | (green << ) | blue; // 格式5:6:5
*addr = (unsigned short) color;
break;
} default:
break;
}
} /*
* 绘制同心圆
*/
void Mire(void)
{
unsigned int x,y;
unsigned int color;
unsigned char red,green,blue,alpha; for (y = ; y < ysize; y++)
for (x = ; x < xsize; x++){
color = ((x-xsize/)*(x-xsize/) + (y-ysize/)*(y-ysize/))/;
red = (color/) % ;
green = (color/) % ;
blue = (color/) % ;
alpha = (color*) % ; color |= ((unsigned int)alpha << );
color |= ((unsigned int)red << );
color |= ((unsigned int)green << );
color |= ((unsigned int)blue ); PutPixel(x,y,color);
}
} /*
* 将屏幕清成单色
* 输入参数:
* color: 颜色值
* 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
* 需要转换为5:6:5格式
* 对于8BPP: color为调色板中的索引值,
* 其颜色取决于调色板中的数值
*/
void ClearScr(unsigned int color)
{
unsigned int x,y; for (y = ; y < ysize; y++)
for (x = ; x < xsize; x++)
PutPixel(x, y, color);
}

lcddrv.c:

 /*
* FILE: lcddrv.c
* 提供操作LCD控制器、调色板等的底层函数
*/ #include "s3c24xx.h"
#include "lcddrv.h" #define GPB0_tout0 (2<<(0*2))
#define GPB0_out (1<<(0*2))
#define GPB1_out (1<<(1*2)) #define GPB0_MSK (3<<(0*2))
#define GPB1_MSK (3<<(1*2)) unsigned int fb_base_addr;
unsigned int bpp;
unsigned int xsize;
unsigned int ysize; /*
* 初始化用于LCD的引脚
*/
void Lcd_Port_Init(void)
{
GPCUP = 0xffffffff; // 禁止内部上拉
GPCCON = 0xaaaaaaaa; // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
GPDUP = 0xffffffff; // 禁止内部上拉
GPDCON = 0xaaaaaaaa; // GPIO管脚用于VD[23:8]
GPBCON &= ~(GPB0_MSK); // Power enable pin
GPBCON |= GPB0_out;
GPBDAT &= ~(<<); // Power off } /*
* 初始化LCD控制器
* 输入参数:
* type: 显示模式
* MODE_TFT_8BIT_240320 : 240*320 8bpp的TFT LCD
* MODE_TFT_16BIT_240320 : 240*320 16bpp的TFT LCD
* MODE_TFT_8BIT_640480 : 640*480 8bpp的TFT LCD
* MODE_TFT_16BIT_640480 : 640*480 16bpp的TFT LCD
*/
void Tft_Lcd_Init()
{ /*
* 设置LCD控制器的控制寄存器LCDCON1~5
* 1. LCDCON1:
* 设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
* 选择LCD类型: TFT LCD
* 设置显示模式: 16BPP
* 先禁止LCD信号输出
* 2. LCDCON2/3/4:
* 设置控制信号的时间参数
* 设置分辨率,即行数及列数
* 现在,可以根据公式计算出显示器的频率:
* 当HCLK=100MHz时,
* Frame Rate = 1/[{(VSPW+1)+(VBPD+1)+(LIINEVAL+1)+(VFPD+1)}x
* {(HSPW+1)+(HBPD+1)+(HFPD+1)+(HOZVAL+1)}x
* {2x(CLKVAL+1)/(HCLK)}]
* = 60Hz
* 3. LCDCON5:
* 设置显示模式为16BPP时的数据格式: 5:6:5
* 设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转
* 半字(2字节)交换使能
*/
LCDCON1 = (<<) | (LCDTYPE_TFT<<) | \
(BPPMODE_16BPP<<) | (ENVID_DISABLE<<);
LCDCON2 = (<<) | (<<) | \
(<<) | ();
LCDCON3 = (<<) | (<<) | ();
LCDCON4 = ;
LCDCON5 = (FORMAT8BPP_565<<) | (HSYNC_INV<<) | (VSYNC_INV<<) | \
(HWSWP<<); /*
* 设置LCD控制器的地址寄存器LCDSADDR1~3
* 帧内存与视口(view point)完全吻合,
* 图像数据格式如下:
* |----PAGEWIDTH----|
* y/x 0 1 2 239
* 0 rgb rgb rgb ... rgb
* 1 rgb rgb rgb ... rgb
* 1. LCDSADDR1:
* 设置LCDBANK、LCDBASEU
* 2. LCDSADDR2:
* 设置LCDBASEL: 帧缓冲区的结束地址A[21:1]
* 3. LCDSADDR3:
* OFFSIZE等于0,PAGEWIDTH等于(240*2/2)
*/
LCDSADDR1 = ((LCDFRAMEBUFFER>>)<<) | LOWER21BITS(LCDFRAMEBUFFER>>);
LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ \
()*()*)>>);
LCDSADDR3 = (<<) | (*/); /* 禁止临时调色板寄存器 */
TPAL = ; fb_base_addr = LCDFRAMEBUFFER;
bpp = ;
xsize = ;
ysize = ; } /*
* 设置是否输出LCD电源开关信号LCD_PWREN
* 输入参数:
* invpwren: 0 - LCD_PWREN有效时为正常极性
* 1 - LCD_PWREN有效时为反转极性
* pwren: 0 - LCD_PWREN输出有效
* 1 - LCD_PWREN输出无效
*/
void Lcd_PowerEnable(int invpwren, int pwren)
{
GPGCON = (GPGCON & (~(<<))) | (<<); // GPG4用作LCD_PWREN
GPGUP = (GPGUP & (~(<<))) | (<<); // 禁止内部上拉 LCDCON5 = (LCDCON5 & (~(<<))) | (invpwren<<); // 设置LCD_PWREN的极性: 正常/反转
LCDCON5 = (LCDCON5 & (~(<<))) | (pwren<<); // 设置是否输出LCD_PWREN
} /*
* 设置LCD控制器是否输出信号
* 输入参数:
* onoff:
* 0 : 关闭
* 1 : 打开
*/
void Lcd_EnvidOnOff(int onoff)
{
if (onoff == )
{
LCDCON1 |= ; // ENVID ON
GPBDAT |= (<<); // Power on
}
else
{
LCDCON1 &= 0x3fffe; // ENVID Off
GPBDAT &= ~(<<); // Power off
}
}

现在贴出重要的头文件做为理解用:

lcddrv.h:

 /*
* FILE: lcddrv.h
* 操作LCD控制器、调色板等的底层函数接口
*/ #ifndef __LCDDRV_H__
#define __LCDDRV_H__ #define LOWER21BITS(n) ((n) & 0x1fffff) #define BPPMODE_16BPP 0xC #define LCDTYPE_TFT 0x3 #define ENVID_DISABLE 0
#define ENVID_ENABLE 1 #define FORMAT8BPP_5551 0
#define FORMAT8BPP_565 1 #define HSYNC_NORM 0
#define HSYNC_INV 1 #define VSYNC_NORM 0
#define VSYNC_INV 1 #define VDEN_NORM 0
#define VDEN_INV 1 #define BSWP 1
#define HWSWP 1 #define LCDFRAMEBUFFER 0x30400000 /*
* 初始化用于LCD的引脚
*/
void Lcd_Port_Init(void); /*
* 初始化LCD控制器
* 输入参数:
* type: 显示模式
* MODE_TFT_8BIT_640480 : 640*640 8bpp的TFT LCD
* MODE_TFT_16BIT_640480 : 640*640 16bpp的TFT LCD
*/
void Tft_Lcd_Init(); void Lcd_EnvidOnOff(int onoff); /*
* 设置是否输出LCD电源开关信号LCD_PWREN
* 输入参数:
* invpwren: 0 - LCD_PWREN有效时为正常极性
* 1 - LCD_PWREN有效时为反转极性
* pwren: 0 - LCD_PWREN输出有效
* 1 - LCD_PWREN输出无效
*/
void Lcd_PowerEnable(int invpwren, int pwren); #endif /*__LCDDRV_H__*/

自然不难看出,仍然有不少冗余项,这里先不做深究。

上面代码经过烧写验证,没有问题。

代码删减了串口,中断,以及print函数的硬件重定向等内容,以便更直观理解lcd驱动。

关于代码中重要函数,有时间补上。