||
嵌入式平台LCD
液晶屏底层时序调试的一种方法
在用ARM的驱动LCD的时候,首先要做的就是将一块全新的屏进行驱动时序的调试,如果每次都将修改好了参数再烧录,一操作繁琐,二不直观。想到一个比较好的方法,说一说,其实很简单,就是在单步调试的状态下,手工修改LCD相关寄存器的值,实时看效果。是用ADS的朋友,可能知道,在ADS下,设备外设寄存器只能通过MEM窗口查看,需要手工输入寄存器地址,很是繁琐,而且不直观,也容易看错。方法如下:
定义一个基于寄存器字段的位定义联合体类型,以S
/*** LCDCON2 -
LCDCON2 Register; 0x4d000004 ***/
typedef union {
int Word;
struct {
int bVSPW: 6; // 低位
int bVFPD: 8;
int bLINEVAL: 10;
int bVBPD: 8; //
高位
} Bits;
} LCDCON2STR;
如果用过飞思卡尔MCU的朋友,对这个应该不会陌生,这是参照了飞思卡尔MCU寄存器定义的方式做的。用起来很爽的,呵呵。有兴趣的朋友可以找找飞思卡尔MCU的芯片头文件看看。可以学到不少东西。接着往下,LCDCON3,LCDCON4 的定义如下:
/*** LCDCON3 -
LCDCON3 Register; 0x4d000008 ***/
typedef union {
int Word;
struct {
int bLineblank_hfpd: 8;
int bHozval: 11;
int bWdly_hbpd: 7;
int rev1:
6; // 未定义的位
} Bits;
} LCDCON3STR;
/*** LCDCON4 -
LCDCON4 Register; 0x4d
typedef union {
int Word;
struct {
int bWLH_HSPW: 8;
int bMVAL: 8;
int rev1: 16;
} Bits;
} LCDCON4STR;
如何使用呢?有经验的朋友,肯定能想到使用指针,是的,哈哈,卖关子啦。就是指针啦。通过定义一个相关联合体指针,然后赋予寄存器绝对地址,如下:
volatile
LCDCON2STR* LcdC_2 = (volatile LCDCON2STR *)&rLCDCON2;
volatile
LCDCON3STR* LcdC_3 = (volatile LCDCON3STR *)&rLCDCON3;
volatile
LCDCON4STR* LcdC_4 = (volatile LCDCON4STR *)&rLCDCON4;
或者如下:
volatile
LCDCON2STR* LcdC_2 = (volatile LCDCON2STR *)0x4d000004 ;
volatile
LCDCON3STR* LcdC_3 = (volatile LCDCON3STR *)0x4d000008 ;
volatile LCDCON4STR*
LcdC_4 = (volatile LCDCON4STR *)0x4d
那么在调试的时候,就可以在ADS中通过添加变量的方式来查看相关寄存器的值,在调试挂起状态的时候,就可以方便的用手工修改相关寄存器位域的值,很简单,很直观,见下图的变量显示:
在挂起调试的时候,通过实时修改这些值,能实时的看到LCD上面的显示是否正确。
直到显示正确,然后记录下那些位域的值,再回写到你的底层驱动配置代码中去。一次就搞定啦。呵呵。我这里使用的是TQ2440的板子,我这里以官方发布的裸奔测试进行了添加,文件名是TQ2440_Test_20100607,通过宏开关来使能调试功能,限于篇幅,我只把变动的部分贴上来。很简单。只需要改改main.c文件就可以了。
在main.c文件中添加:
#define DEBUG_LCD
// 调试LCD开关,不用的话,注释掉就可了。
#ifdef DEBUG_LCD
#include
"lcd_tft.h"
/* 联合体定义的外设寄存器 */
/*** LCDCON2 -
LCDCON2 Register; 0x4d000004 ***/
typedef union {
int Word;
struct {
int bVSPW: 6;
int bVFPD: 8;
int bLINEVAL: 10;
int bVBPD: 8;
} Bits;
} LCDCON2STR;
//#define
_LCDCON2 (*(volatile LCDCON2STR
*)0x4d000004)
#define LCDCON2
_LCDCON2.Word
#define
LCDCON2_VBPD
_LCDCON2.Bits.bVBPD
#define
LCDCON2_LINEVAL
_LCDCON2.Bits.bLINEVAL
#define
LCDCON2_VFPD
_LCDCON2.Bits.bVFPD
#define
LCDCON2_VSPW
_LCDCON2.Bits.bVSPW
/*** LCDCON3 -
LCDCON3 Register; 0x4d000008 ***/
typedef union {
int Word;
struct {
int bLineblank_hfpd: 8;
int bHozval: 11;
int bWdly_hbpd: 7;
int rev1:
6;
} Bits;
} LCDCON3STR;
//#define
_LCDCON3 (*(volatile LCDCON3STR
*)0x4d000008)
#define LCDCON3
_LCDCON3.Word
#define
LCDCON3_LINEBLANK
_LCDCON3.Bits.bLineblank_hfpd
#define
LCDCON3_HFPD _LCDCON3.Bits.bLineblank_hfpd
#define
LCDCON3_HOZVAL
_LCDCON3.Bits.bHozval
#define
LCDCON3_HBPD
_LCDCON3.Bits.bWdly_hbpd
#define
LCDCON3_WDLY
_LCDCON3.Bits.bWdly_hbpd
/*** LCDCON4 -
LCDCON4 Register; 0x4d
typedef union {
int Word;
struct {
int bWLH_HSPW: 8;
int bMVAL: 8;
int rev1: 16;
} Bits;
} LCDCON4STR;
//#define
_LCDCON4 (*(volatile LCDCON4STR
*)0x4d
#define LCDCON4
_LCDCON4.Word
#define
LCDCON4_WLH _LCDCON4.Bits.bWLH_HSPW
#define
LCDCON4_HSPW _LCDCON4.Bits.bWLH_HSPW
#define
LCDCON4_MVAL _LCDCON4.Bits.bMVAL
#define GUI_BLUE
0xFF0000
#define GUI_GREEN
0x00FF00
#define GUI_RED
0x0000FF
#define GUI_CYAN
0xFFFF00 //
青色
#define
GUI_MAGENTA 0xFF00FF // 品红
#define GUI_YELLOW
0x00FFFF
#define GUI_WHITE
0xFFFFFF
#define GUI_BLACK
0x000000
#define
RGB2RGB565(rgb) ((WORD)(((((WORD)((rgb)>>3))&(0x
|((((WORD)((rgb)>>10))&(0x
|(((WORD)((rgb)>>19))&(0x
//===================================================================//
volatile
LCDCON2STR* LcdC_2;
volatile
LCDCON3STR* LcdC_3;
volatile
LCDCON4STR* LcdC_4;
extern void
Glib_FilledRectangle(int x1,int y1,int x2,int y2,int color);
#define
RECT_BOX_WITHD 3
#define
RECT_BOX_COLOR RGB2RGB565(GUI_YELLOW)
#define RECT_COLOR RGB2RGB565(GUI_BLUE)
//===================================================================//
#endif
在 main函数修改如下:只添加了红色部分代码
void
{
char *mode;
int i;
U8 key;
U32 mpll_val = 0 ;
//U32 divn_upll = 0 ;
#if ADS10
// __rt_lib_init(); //for ADS 1.0
#endif
Port_Init();
Isr_Init();
i = 2 ; //don't
use
switch ( i ) {
case 0: //200
key = 12;
mpll_val =
(92<<12)|(4<<4)|(1);
break;
case 1: //300
key = 13;
mpll_val =
(67<<12)|(1<<4)|(1);
break;
case 2: //400
key = 14;
mpll_val =
(92<<12)|(1<<4)|(1);
break;
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val =
(92<<12)|(1<<4)|(1);
break;
}
//init FCLK=
ChangeMPllValue((mpll_val>>12)&0xff,
(mpll_val>>4)&0x
ChangeClockDivider(key, 12);
cal_cpu_bus_clk();
consoleNum = 0; // Uart 1 select for debug.
Uart_Init( 0,115200 );
Uart_Select( consoleNum );
Beep(2000, 100);
Uart_SendByte('\n');
Uart_Printf("<***************************************>\n");
Uart_Printf(" TQ2440 Test Program\n");
Uart_Printf(" www.embedsky.net\n");
// Uart_Printf(" Build time is: %s %s\n", __DATE__ , __TIME__ );
Uart_Printf("<***************************************>\n");
rMISCCR=rMISCCR&~(1<<3); //
USBD is selected instead of USBH1
rMISCCR=rMISCCR&~(1<<13); //
USB port 1 is enabled.
rDSC0 = 0x2aa;
rDSC1 = 0x2aaaaaaa;
//Enable NAND, USBD, PWM TImer, UART0,1
and GPIO clock,
//the others must be enabled in OS!!!
rCLKCON = 0xfffff0;
MMU_Init(); //
pISR_SWI=(_ISR_STARTADDRESS+0xf0); //for pSOS
Led_Display(0x66);
mode="DMA";
Clk0_Disable();
Clk1_Disable();
mpll_val = rMPLLCON;
//=============================================================================
#ifndef
DEBUG_LCD
Lcd_TFT_Init() ; // LCD initial
#else
Lcd_Init();
Lcd_PowerEnable(0, 1);
Lcd_EnvidOnOff(1); //turn on vedio
Glib_FilledRectangle(0,0,LCD_XSIZE_TFT-1,LCD_YSIZE_TFT-1,RECT_BOX_COLOR);
Glib_FilledRectangle( RECT_BOX_WITHD,
RECT_BOX_WITHD,
LCD_XSIZE_TFT-1-RECT_BOX_WITHD,
LCD_YSIZE_TFT-1-RECT_BOX_WITHD,
RECT_COLOR);
LcdC_2 = (volatile LCDCON2STR
*)&rLCDCON2;
LcdC_3 = (volatile LCDCON3STR
*)&rLCDCON3;
LcdC_4 = (volatile LCDCON4STR
*)&rLCDCON4;
#endif
//=============================================================================
download_run=1; //The default menu is the
Download & Run mode.
while(1)
{
#ifndef
DEBUG_LCD
U8 idx;
Uart_Printf("\nPlease select
function : \n");
for(i=0; CmdTip[i].fun!=0; i++)
Uart_Printf("%d :
%s\n", i, CmdTip[i].tip);
idx = Uart_GetIntNum_GJ() ;
if(idx<i)
{
(*CmdTip[idx].fun)();
Delay(20);
Uart_Init( 0,115200 );
}
#endif
}
}
编译后,加载进SDRAM或者FALSH,将3个指针变量添加进变量表中,然后全速运行,再点击挂起。进入单步运行模式,然后就可以直接修改相关位域进行LCD的时序的调试了。程序不要运行。
关于联合体位域的其他应用说明
/* 联合体定义的外设寄存器 */
/*** LCDCON2 -
LCDCON2 Register; 0x4d000004 ***/
typedef union {
int Word;
struct {
int bVSPW: 6;
int bVFPD: 8;
int bLINEVAL: 10;
int bVBPD: 8;
} Bits;
} LCDCON2STR;
#define _LCDCON2 (*(volatile LCDCON2STR *)0x4d000004)
#define LCDCON2
_LCDCON2.Word
#define
LCDCON2_VBPD
_LCDCON2.Bits.bVBPD
#define
LCDCON2_LINEVAL
_LCDCON2.Bits.bLINEVAL
#define
LCDCON2_VFPD _LCDCON2.Bits.bVFPD
#define
LCDCON2_VSPW
_LCDCON2.Bits.bVSPW
这个联合体下方的定义,可以在应用中,方便我们编程和调试,很直观。把2种应用方式列一下:
方式1:
LCDCON2_VBPD =
VBPD;
LCDCON2_LINEVAL =
LINEVAL_TFT;
LCDCON2_VFPD =
VFPD;
LCDCON2_VSPW =
VSPW;
方式2:
rLCDCON2 = (VBPD
<< 24)|(LINEVAL_TFT << 14)|(VFPD << 6)|(VSPW);
方式1和2实现了同样的功能。很明显,方式1非常直观和方便。
朋友,由此你是否想将你的MCU的寄存器头文件都定义成这样子呢?呵呵。
如果你真那么干的话,千万不要忘记给我传一份你定义好的头文件啊!