裸机程序开发之LCD程序
先上代码,代码有点长,共409行。涉及到的寄存器比较多。本节只做代码解释,详细原理不再这里展开。
#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "mmu.h"
//================================
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_XSIZE LCD_WIDTH
#define LCD_YSIZE LCD_HEIGHT
#define SCR_XSIZE LCD_WIDTH
#define SCR_YSIZE LCD_HEIGHT
#define LCD_PIXCLOCK 4
#define LCD_RIGHT_MARGIN 100
#define LCD_LEFT_MARGIN 0
#define LCD_HSYNC_LEN 4
#define LCD_UPPER_MARGIN 0
#define LCD_LOWER_MARGIN 0
#define LCD_VSYNC_LEN 9
#define LCD_CON5 ((1<<11) | (1<<10) | (1<<8) | (1<<9) | (1<<0) )
void Delay(int time);
void TFT_LCD_Test(void);
void Lcd_Port_Init(void);
void LCD_Init(void);
void LcdBkLtSet(U32 HiRatio);
void Lcd_PowerEnable(int invpwren,int pwren);
void Lcd_EnvidOnOff(int onoff);
void Lcd_ClearScr( U16 c);
void Glib_FilledRectangle(int x1,int y1,int x2,int y2, U16 color);
void Paint_Bmp(int x0,int y0,int h,int l,const unsigned char *bmp);
void Glib_Line(int x1,int y1,int x2,int y2, U16 color);
void PutPixel(U32 x,U32 y,U16 c);
extern const unsigned char sunflower_240x320[];
volatile static unsigned short LCD_BUFFER[SCR_YSIZE][SCR_XSIZE];
void Delay(int time)
{
U32 val = (PCLK>>3)/1000-1;
rTCFG0 &= ~(0xff<<8);
rTCFG0 |= 3<<8; //prescaler = 3+1
rTCFG1 &= ~(0xf<<12);
rTCFG1 |= 0<<12; //mux = 1/2
rTCNTB3 = val;
rTCMPB3 = val>>1; // 50%
rTCON &= ~(0xf<<16);
rTCON |= 0xb<<16; //interval, inv-off, update TCNTB3&TCMPB3, start timer 3
rTCON &= ~(2<<16); //clear manual update bit
while(time--) {
while(rTCNTO3>=val>>1);
while(rTCNTO3<val>>1);
};
}
int Main(int argc, char **argv)
{
int i;
U8 key;
U32 mpll_val=0;
int data;
int readLength = 0;
mpll_val = (92<<12)|(1<<4)|(1);
//init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3);
ChangeClockDivider(key, 12);
Uart_Init( 0,115200 );
Uart_Select( 0 );
rEXTINT0 = 0x22222222; // EINT[7:0]
rEXTINT1 = 0x22222222; // EINT[15:8]
rEXTINT2 = 0x22222222; // EINT[23:16]
pISR_SWI=(_ISR_STARTADDRESS+0xf0);
Buzzer_Freq_Set( 2000 );
Delay( 200 );
Buzzer_Stop();
Uart_SendByte('\n');
Uart_Printf("<***********************************************>\n");
Uart_Printf("\nKey Test\n");
Uart_Printf("<***********************************************>\n");
// rDSC0 = 0x2aa;
// rDSC1 = 0x2aaaaaaa;
//Enable NAND, USBD, PWM TImer, UART0,1 and GPIO clock,
rCLKCON = 0xfffff0;
MMU_Init();
// MMU_DisableICache();
// MMU_DisableDCache();
TFT_LCD_Test();
data = 0x06;
while(1)
{
// Uart_Printf("\nPlease input command : \n");
// Uart_Getch();
// Uart_Printf("Your input string : \n");
// Uart_SendByte(key);
}
return 0;
}
void TFT_LCD_Test(void)
{
Uart_Printf("\nTest TFT LCD 240x320!\n");
Lcd_Port_Init();
LCD_Init();
LcdBkLtSet( 70 ) ;
Lcd_PowerEnable(0, 1);
Lcd_EnvidOnOff(1); //turn on vedio
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x00) ) ; //clear screen
Uart_Printf( "\nLCD clear screen is finished! press any key to continue!\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x1f<<11) | (0x3f<<5) | (0x1f) ) ; //clear screen
Uart_Printf( "LCD clear screen is finished! press any key to continue!\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr(0xffff); //fill all screen with some color
#define LCD_BLANK 30
#define C_UP ( LCD_XSIZE - LCD_BLANK*2 )
#define C_RIGHT ( LCD_XSIZE - LCD_BLANK*2 )
#define V_BLACK ( ( LCD_YSIZE - LCD_BLANK*4 ) / 6 )
Glib_FilledRectangle( LCD_BLANK, LCD_BLANK, ( LCD_XSIZE - LCD_BLANK ), ( LCD_YSIZE - LCD_BLANK ),0x0000); //fill a Rectangle with some color
Glib_FilledRectangle( (LCD_BLANK*2), (LCD_BLANK*2 + V_BLACK*0), (C_RIGHT), (LCD_BLANK*2 + V_BLACK*1),0x001f); //fill a Rectangle with some color
Glib_FilledRectangle( (LCD_BLANK*2), (LCD_BLANK*2 + V_BLACK*1), (C_RIGHT), (LCD_BLANK*2 + V_BLACK*2),0x07e0); //fill a Rectangle with some color
Glib_FilledRectangle( (LCD_BLANK*2), (LCD_BLANK*2 + V_BLACK*2), (C_RIGHT), (LCD_BLANK*2 + V_BLACK*3),0xf800); //fill a Rectangle with some color
Glib_FilledRectangle( (LCD_BLANK*2), (LCD_BLANK*2 + V_BLACK*3), (C_RIGHT), (LCD_BLANK*2 + V_BLACK*4),0xffe0); //fill a Rectangle with some color
Glib_FilledRectangle( (LCD_BLANK*2), (LCD_BLANK*2 + V_BLACK*4), (C_RIGHT), (LCD_BLANK*2 + V_BLACK*5),0xf81f); //fill a Rectangle with some color
Glib_FilledRectangle( (LCD_BLANK*2), (LCD_BLANK*2 + V_BLACK*5), (C_RIGHT), (LCD_BLANK*2 + V_BLACK*6),0x07ff); //fill a Rectangle with some color
Uart_Printf( "LCD color test, please look! press any key to continue!\n" );
Uart_Getch() ; //wait uart input
Paint_Bmp(0, 0, 240, 320, sunflower_240x320);
Uart_Printf( "LCD paint a bmp, please look! press any key to continue! \n" );
Uart_Getch() ; //wait uart input
Lcd_EnvidOnOff(0); //turn off vedio
}
void Lcd_Port_Init(void)
{
rGPCUP=0xffffffff; // Disable Pull-up register
rGPCCON=0xaaaa02a8; //Initialize VD[7:0],VM,VFRAME,VLINE,VCLK
rGPDUP=0xffffffff; // Disable Pull-up register
rGPDCON=0xaaaaaaaa; //Initialize VD[15:8]
}
void LCD_Init(void)
{
#define M5D(n) ((n)&0x1fffff)
#define LCD_ADDR ((U32)LCD_BUFFER)
rLCDCON1 = (LCD_PIXCLOCK << 8) | (3 << 5) | (12 << 1);
rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);
rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH - 1) << 8) | (LCD_LEFT_MARGIN << 0);
rLCDCON4 = (13 << 8) | (LCD_HSYNC_LEN << 0);
//#if !defined(LCD_CON5)
//# define LCD_CON5 ((1<<11) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 0))
//#endif
rLCDCON5 = LCD_CON5;
rLCDSADDR1 = ((LCD_ADDR >> 22) << 21) | ((M5D(LCD_ADDR >> 1)) << 0);
rLCDSADDR2 = M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2) >> 1);
rLCDSADDR3 = LCD_WIDTH;
rLCDINTMSK |= 3;
rTCONSEL &= (~7);
rTPAL = 0x0;
rTCONSEL &= ~((1<<4) | 1);
}
void LcdBkLtSet(U32 HiRatio)
{
#define FREQ_PWM1 1000
if(!HiRatio)
{
rGPBCON = rGPBCON & (~(3<<2)) | (1<<2) ; //GPB1设置为output
rGPBDAT &= ~(1<<1);
return;
}
rGPBCON = rGPBCON & (~(3<<2)) | (2<<2) ; //GPB1设置为TOUT1
if( HiRatio > 100 )
HiRatio = 100 ;
rTCON = rTCON & (~(0xf<<8)) ; // clear manual update bit, stop Timer1
rTCFG0 &= 0xffffff00; // set Timer 0&1 prescaler 0
rTCFG0 |= 15; //prescaler = 15+1
rTCFG1 &= 0xffffff0f; // set Timer 1 MUX 1/16
rTCFG1 |= 0x00000030; // set Timer 1 MUX 1/16
rTCNTB1 = ( 100000000>>8 )/FREQ_PWM1; //if set inverter off, when TCNT2<=TCMP2, TOUT is high, TCNT2>TCMP2, TOUT is low
rTCMPB1 = ( rTCNTB1*(100-HiRatio))/100 ; //if set inverter on, when TCNT2<=TCMP2, TOUT is low, TCNT2>TCMP2, TOUT is high
rTCON = rTCON & (~(0xf<<8)) | (0x0e<<8) ;
//自动重装,输出取反关闭,更新TCNTBn、TCMPBn,死区控制器关闭
rTCON = rTCON & (~(0xf<<8)) | (0x0d<<8) ; //开启背光控制
}
void Lcd_PowerEnable(int invpwren,int pwren)
{
//GPG4 is setted as LCD_PWREN
rGPGUP = rGPGUP|(1<<4); // Pull-up disable
rGPGCON = rGPGCON|(3<<8); //GPG4=LCD_PWREN
//Enable LCD POWER ENABLE Function
rLCDCON5 = rLCDCON5&(~(1<<3))|(pwren<<3); // PWREN
rLCDCON5 = rLCDCON5&(~(1<<5))|(invpwren<<5); // INVPWREN
}
void Lcd_EnvidOnOff(int onoff)
{
if(onoff==1)
rLCDCON1|=1; // ENVID=ON
else
rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off
}
void Lcd_ClearScr( U16 c)
{
unsigned int x,y ;
for( y = 0 ; y < SCR_YSIZE ; y++ )
{
for( x = 0 ; x < SCR_XSIZE ; x++ )
{
LCD_BUFFER[y][x] = c ;
}
}
}
void Glib_FilledRectangle(int x1,int y1,int x2,int y2, U16 color)
{
int i;
for(i=y1;i<=y2;i++)
Glib_Line(x1,i,x2,i,color);
}
void Paint_Bmp(int x0,int y0,int h,int l,const unsigned char *bmp)
{
int x,y;
U32 c;
int p = 0;
for( y = 0 ; y < l ; y++ )
{
for( x = 0 ; x < h ; x++ )
{
c = bmp[p+1] | (bmp[p]<<8) ;
if ( ( (x0+x) < SCR_XSIZE) && ( (y0+y) < SCR_YSIZE) )
LCD_BUFFER[y0+y][x0+x] = c ;
p = p + 2 ;
}
}
}
void Glib_Line(int x1,int y1,int x2,int y2, U16 color)
{
int dx,dy,e;
dx=x2-x1;
dy=y2-y1;
if(dx>=0)
{
if(dy >= 0) // dy>=0
{
if(dx>=dy) // 1/8 octant
{
e=dy-dx/2;
while(x1<=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1+=1;e-=dx;}
x1+=1;
e+=dy;
}
}
else // 2/8 octant
{
e=dx-dy/2;
while(y1<=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1+=1;e-=dy;}
y1+=1;
e+=dx;
}
}
}
else // dy<0
{
dy=-dy; // dy=abs(dy)
if(dx>=dy) // 8/8 octant
{
e=dy-dx/2;
while(x1<=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1-=1;e-=dx;}
x1+=1;
e+=dy;
}
}
else // 7/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1+=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
else //dx<0
{
dx=-dx; //dx=abs(dx)
if(dy >= 0) // dy>=0
{
if(dx>=dy) // 4/8 octant
{
e=dy-dx/2;
while(x1>=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1+=1;e-=dx;}
x1-=1;
e+=dy;
}
}
else // 3/8 octant
{
e=dx-dy/2;
while(y1<=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1+=1;
e+=dx;
}
}
}
else // dy<0
{
dy=-dy; // dy=abs(dy)
if(dx>=dy) // 5/8 octant
{
e=dy-dx/2;
while(x1>=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1-=1;e-=dx;}
x1-=1;
e+=dy;
}
}
else // 6/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
}
void PutPixel(U32 x,U32 y,U16 c)
{
if(x<SCR_XSIZE && y<SCR_YSIZE)
LCD_BUFFER[(y)][(x)] = c;
}
函数TFT_LCD_Test是LCD显示的核心代码。
函数Lcd_Port_Init用于初始化LCD的端口。
查看原理图可知端口GPC0~GP7为LCD控制信号,GPC8~GPC15,GPD0~GPD15为VD信号。
rGPCUP=0xffffffff;//禁用C端口所有引脚的上拉电阻功能,显示屏显示不需要上拉。
rGPCCON=0xaaaa02a8;//设置了 GPC0~GP7中VM,VFRAME,VLINE,VCLK四种LCD控制信号,GPC8~GPC15为VD信号。
rGPDUP=0xffffffff;//禁用D端口所有引脚的上拉电阻功能,显示屏显示不需要上拉。
rGPDCON=0xaaaaaaaa;//设置GPD0~GPD15为VD信号。
详细定义查看以下两张表。
rLCDCON1 = (LCD_PIXCLOCK << 8) | (3 << 5) | (12 << 1);// LCDCON1的bit8~17为CLKVAL ,而VCLK = HCLK / [(CLKVAL+1) x 2]。VCLK为显示屏的时钟信号。LCD_PIXCLOCK宏展开后为4,经简单计算VCLK=100MHz/((4+1)*2)=10MHz。LCDCON1的bit5~6为PNRMODE,用于显示模式,此处的3表示TFT LCD显示屏。LCDCON1的bit1~4为BPPMODE,表示每个像素对应的bit位数,此处的12表示每个像素对应16位。
rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);// LCDCON2的bit24~31为VBPD,表示垂直同步周期后一帧中起始位置处未激活的行数,此处设置了0。LCDCON2的bit14~23为LINEVAL,表示显示屏的垂直尺寸,LINEVAL=实际垂直尺寸-1,此处设置320-1。LCDCON2的bit6~13为VFPD,表示表示垂直同步周期后一帧中终止位置处未激活的行数,此处设置了0。LCDCON2的bit0~5为VSPW,该值通过统计未激活行数确定了VSYNC的高电平宽度,此处设置9。
rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH - 1) << 8) | (LCD_LEFT_MARGIN << 0);// LCDCON3的bit19~25为HBPD,表示HSYNC的下降沿和起始激活数据之间的VCLK周期数,此处设置100。LCDCON3的bit8~18为HOZVAL,表示显示屏的水平尺寸HOZVAL=实际水平尺寸-1,此处设置240-1。LCDCON3的bit0~7为HFPD,表示终止位置处激活数据和HSYNC上升沿之间的VCLK周期数,此处设置0。
rLCDCON4 = (13 << 8) | (LCD_HSYNC_LEN << 0);// LCDCON4的bit8~15为MVAL,官方手册只对STN进行了描述,缺少TFT的描述。LCDCON4的bit0~7为HSPW,该值通过统计VCLK周期数确定了HSYNC的高电平宽度,此处设置4。
rLCDCON5 = LCD_CON5;// LCDCON5的bit11为FRM565,表示用于选择16bpp数据视频数据的格式,此处选择的是5:6:5颜色格式,该颜色格式可自行百度查询。LCDCON5的bit10为INVVCLK,表示视频数据在上升沿处抓取。LCDCON5的bit8为INVVFRAME,表示VFRAME/VSYNC的极性,此处这是为取反。LCDCON5的bit9为INVVLINE,表示VLINE/HSYNC的极性,此处这是为取反。LCDCON5的bit0为HWSWP,此处为交换启用。
rLCDSADDR1 = ((LCD_ADDR >> 22) << 21) | ((M5D(LCD_ADDR >> 1)) << 0);//LCDSADDR1的bit21~29为LCDBANK,表示系统内存中的视频数据缓冲区地址的bit22~30。LCDSADDR1的bit0~20为LCDBASEU,表示缓冲区地址的bit1~bit21。
rLCDSADDR2 = M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2) >> 1);//LCDSADDR2的bit0~20为LCDBASEL。LCDBASEU和LCDBASEL可用于滚屏。详细说明后面补充。此处没有看懂。
rLCDSADDR3 = LCD_WIDTH;// LCDSADDR3的bit0~10为PAGEWIDTH,表示虚拟屏幕宽度,此处为实际屏幕宽度240。LCDSADDR3的bit11~21为OFFSIZE,表示虚拟屏幕偏移量。
rLCDINTMSK |= 3;//表示屏幕的帧同步中断和FIFO中断都被屏蔽。
rTCONSEL &= (~7);//表示模式选择同步模式,屏幕分辩率为320*240,禁用LPC3600。
rTPAL = 0x0;//表示禁用调色板寄存器。
rTCONSEL &= ~((1<<4) | 1);//表示禁用LCC3600,禁用LPC3600。
函数LcdBkLtSet用于设置背光灯的亮度。
rGPBCON = rGPBCON & (~(3<<2)) | (2<<2) ;//将GPB0引脚设置为pwm输出。
之后代码是定时器1操作,可查看之前章节介绍。
函数Lcd_PowerEnable用于控制LCD电源引脚使能
rGPGUP = rGPGUP|(1<<4);//禁用GPG4的上拉电阻功能
rGPGCON = rGPGCON|(3<<8);//将GPG4引脚设置为LCD_PWREN
rLCDCON5 = rLCDCON5&(~(1<<3))|(pwren<<3);//设置LCD_PWREN是否输出
rLCDCON5 = rLCDCON5&(~(1<<5))|(invpwren<<5);//设置PWREN信号的极性
函数Lcd_EnvidOnOff用于控制视频信号输出
rLCDCON1|=1;//开启视频输出信号以及LCD控制信号
函数Lcd_ClearScr用于清屏,输入参数为清屏的颜色。
函数Glib_Line为绘制线条的逻辑,具体算法此处不做介绍。
函数Paint_Bmp用于绘制图片,参数分别是左上角左边和图片长宽,以及数据缓冲区地址。
显示效果图如下: