RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)<LCD驱动接口移植>
2015-04-09 17:11
1701 查看
软件环境:Win7,Keil MDK 4.72a, IAR EWARM 7.2, GCC 4.2,Python 2.7 ,SCons 2.3.2
硬件环境:Armfly STM32F103ZE-EK v3.0开发板
参考文章:RT-Thread编程指南
RT-Thread_1.2.0+lwip+rtgui0.8.0 移植心得
RT-Thread RTOS组件:RTGUI教程 Hello World
点击上面下载地址处的链接,可以直接下载。
(2)解压RTGUI-0.8源码,将components目录下的rtgui组件复制到rt-thread-1.2.2/components目录下,如下图。
/* SECTION: RT-Thread/GUI */
#define RT_USING_RTGUI
/* name length of RTGUI object */
#define RTGUI_NAME_MAX 12
/* support 16 weight font */
#define RTGUI_USING_FONT16
/* support Chinese font */
#define RTGUI_USING_FONTHZ
/* use DFS as file interface */
#define RTGUI_USING_DFS_FILERW
/* use font file as Chinese font */
#define RTGUI_USING_HZ_FILE
/* use Chinese bitmap font */
#define RTGUI_USING_HZ_BMP
/* use small size in RTGUI */
#define RTGUI_USING_SMALL_SIZE
/* use mouse cursor */
/* #define RTGUI_USING_MOUSE_CURSOR */
/* default font size in RTGUI */
#define RTGUI_DEFAULT_FONT_SIZE 16
/* image support */
/* #define RTGUI_IMAGE_XPM */
/* #define RTGUI_IMAGE_BMP */
修改完成后保存。然后再命令行窗口运行scons --tartet=mdk4 -s。打开KeilMDK 可以看到RTGUI组被加入进来,如下图。
打开rtgui_config.h,定位到53行附近,注释掉RTGUI_USING_CALIBRATION定义并加上RTGUI_USING_TOUCHPANEL定义,代码修改如下:
... ...
//#define RTGUI_USING_TOUCHPANEL
//#define RTGUI_USING_CALIBRATION
#endif
修改完毕成保存。
(2)在有关触摸屏相关的操作处添加#ifdef RTGUI_USING_TOUCHPANEL
打开application.c,定位到125行附近,代码修改如下:
#ifdef RT_USING_RTGUI
{
extern void rt_hw_lcd_init();
extern void rtgui_touch_hw_init(void);
rt_device_t lcd;
/* init lcd */
rt_hw_lcd_init();
/* init touch panel */
#ifdef RTGUI_USING_TOUCHPANEL
rtgui_touch_hw_init();
#endif /* #ifdef RTGUI_USING_TOUCHPANEL */
/* find lcd device */
lcd = rt_device_find("lcd");
/* set lcd device as rtgui graphic driver */
rtgui_graphic_set_device(lcd);
#ifndef RT_USING_COMPONENTS_INIT
/* init rtgui system server */
rtgui_system_server_init();
#endif /*#ifndef RT_USING_COMPONENTS_INIT*/
#ifdef
RTGUI_USING_CALIBRATION
//calibration_set_restore(cali_setup);
//calibration_set_after(cali_store);
//calibration_init();
#endif /* #ifdef
RTGUI_USING_CALIBRATION*/
... ...
修改完成后保存。
(3)修改编译脚本
使用Notepad++打开rt-thread-1.2.2\components\rtgui\apps目录下的sconscript文件,定位到第9行,修改如下:
... ...
cwd = GetCurrentDir()
ifGetDepend('RTGUI_USING_CALIBRATION'):
src = Glob('*.c')
group = DefineGroup('RTGUI', src, depend = ['RTGUI_USING_CALIBRATION'])
... ...
修改完成后保存。
从LCD接口原理图可以看出,PC5为触摸屏中断请求线,PA5,PA6,PA7为SPI1接口,PG11为触摸屏总线的片选信号,PB1为LCD背光控制,FSMC[15:0] 为数据总线,PD5(FSMC_NWE)为写使能,PD4(FSMC_NOE)为读使能,PF0(FSMC_A0)指令/数据选择线。
LCD地址计算方法:
A,正如上图所示,对于16位宽度的外部存储器,stm32f103的FSMC将在内部使用HADDR[25:1]产生外部存储器的地址FSMC_A[24:0],而HADDR[0]未接;
B,LCD的CS引脚决定它的起始地址,STM32的CPU仅引出了4个片选信号,也就是CPU硬件最多只能外接4个总线型设备。安富莱开发板(V3)具有5个FSMC外设,所以开发板外扩一个地址译码器电路,如下图:
将NE4空间分为4部分,第1份给LCD,第2份给LAN,即当FSMC_A19=0、FSMC_NE4=0时选通LCD。STM32的NE4的起始地址是0x6c000000,FSMC_A19对应于HADDR[20],所以安富莱开发板(V3)的LCD起始地址为0x6c000000;
C,LCD的A0引脚决定传送的是地址还是数据,安富莱开发板(V3)上连接的是FSMC_A[0]即HADDR[1],所以安富莱开发板(V3)的IO_ADDR = 0x6c000000,IO_DATA = 0x6c100002。
安富莱开发板(V3)的LCD模块的显示控制IC为OTM4001A
(1)添加otm4001a.c驱动文件
在安富莱开发板(V3)的配套光盘中可以找到有关tft_lcd实验的例子,这里将例程 Ex007-TFT显示文字图片例程\User\bsp目录下bsp_tft_lcd.h和bsp_tft_lcd.c复制到stm32f103ze-ek/drivers目录下,并分别重命名为otm4001a.h和otm4001a.c,如下图:
(3)将otm4001a.c加入KeilMDK工程,同时将ssd1289.c从KeilMDK工程中移除。
(4)修改drivers目录下的脚本,将其加入编译列表。
<1>使用Notepad++打开stm32f103ze-ek/drivers目录下SConscript文件,定位到第34行加入下面代码:
# add RTGUI drvers.
if GetDepend('RT_USING_RTGUI'):
if GetDepend('RTGUI_USING_TOUCHPANEL'):
src += ['touch_driver.c']
if rtconfig.RT_USING_LCD_TYPE == 'ILI932X':
src += ['ili_lcd_general.c']
elif rtconfig.RT_USING_LCD_TYPE == 'SSD1289':
src += ['ssd1289.c']
elif rtconfig.RT_USING_LCD_TYPE == 'OTM4001A':
src += ['otm4001a.c']
CPPPATH = [cwd]
... ...
要注意的是phython的语法格式,每一级要用四个空格缩进,否则的话在执行时会产生语法错误。
修改完成后保存并关闭。
<2>使用Notepad++打开stm32f103ze-ek目录下rtconfig.py文件,定位到第20行加入下面代码:
... ...
# lcd panel options
# 'FMT0371','ILI932X', 'SSD1289','OTM4001A'
RT_USING_LCD_TYPE = 'OTM4001A'
... ...
修改完成后保存并关闭。
注意上面代码中
需要注释掉
(2)移植后的otm4001a.h代码如下:
然后保存,接下来就可以编译了。
硬件环境:Armfly STM32F103ZE-EK v3.0开发板
参考文章:RT-Thread编程指南
RT-Thread_1.2.0+lwip+rtgui0.8.0 移植心得
RT-Thread RTOS组件:RTGUI教程 Hello World
【1】加入LCD设备驱动文件
(1)登陆http://www.rt-thread.org/node/76,可以看到如下链接,如下图:点击上面下载地址处的链接,可以直接下载。
(2)解压RTGUI-0.8源码,将components目录下的rtgui组件复制到rt-thread-1.2.2/components目录下,如下图。
【2】开启RTGUI的编译选项
打开rtconfig.h文件,定位到164行附近,在“SECTION: RT-Thread/GUI”处,打开RTGUI 编译开关,代码修改如下:/* SECTION: RT-Thread/GUI */
#define RT_USING_RTGUI
/* name length of RTGUI object */
#define RTGUI_NAME_MAX 12
/* support 16 weight font */
#define RTGUI_USING_FONT16
/* support Chinese font */
#define RTGUI_USING_FONTHZ
/* use DFS as file interface */
#define RTGUI_USING_DFS_FILERW
/* use font file as Chinese font */
#define RTGUI_USING_HZ_FILE
/* use Chinese bitmap font */
#define RTGUI_USING_HZ_BMP
/* use small size in RTGUI */
#define RTGUI_USING_SMALL_SIZE
/* use mouse cursor */
/* #define RTGUI_USING_MOUSE_CURSOR */
/* default font size in RTGUI */
#define RTGUI_DEFAULT_FONT_SIZE 16
/* image support */
/* #define RTGUI_IMAGE_XPM */
/* #define RTGUI_IMAGE_BMP */
修改完成后保存。然后再命令行窗口运行scons --tartet=mdk4 -s。打开KeilMDK 可以看到RTGUI组被加入进来,如下图。
【3】设置触摸屏驱动条件编译选项
(1)修改rtgui_config.h打开rtgui_config.h,定位到53行附近,注释掉RTGUI_USING_CALIBRATION定义并加上RTGUI_USING_TOUCHPANEL定义,代码修改如下:
... ...
//#define RTGUI_USING_TOUCHPANEL
//#define RTGUI_USING_CALIBRATION
#endif
修改完毕成保存。
(2)在有关触摸屏相关的操作处添加#ifdef RTGUI_USING_TOUCHPANEL
打开application.c,定位到125行附近,代码修改如下:
#ifdef RT_USING_RTGUI
{
extern void rt_hw_lcd_init();
extern void rtgui_touch_hw_init(void);
rt_device_t lcd;
/* init lcd */
rt_hw_lcd_init();
/* init touch panel */
#ifdef RTGUI_USING_TOUCHPANEL
rtgui_touch_hw_init();
#endif /* #ifdef RTGUI_USING_TOUCHPANEL */
/* find lcd device */
lcd = rt_device_find("lcd");
/* set lcd device as rtgui graphic driver */
rtgui_graphic_set_device(lcd);
#ifndef RT_USING_COMPONENTS_INIT
/* init rtgui system server */
rtgui_system_server_init();
#endif /*#ifndef RT_USING_COMPONENTS_INIT*/
#ifdef
RTGUI_USING_CALIBRATION
//calibration_set_restore(cali_setup);
//calibration_set_after(cali_store);
//calibration_init();
#endif /* #ifdef
RTGUI_USING_CALIBRATION*/
... ...
修改完成后保存。
(3)修改编译脚本
使用Notepad++打开rt-thread-1.2.2\components\rtgui\apps目录下的sconscript文件,定位到第9行,修改如下:
... ...
cwd = GetCurrentDir()
ifGetDepend('RTGUI_USING_CALIBRATION'):
src = Glob('*.c')
group = DefineGroup('RTGUI', src, depend = ['RTGUI_USING_CALIBRATION'])
... ...
修改完成后保存。
【4】修改LCD的硬件接口
LCD的硬件接口原理图如下:从LCD接口原理图可以看出,PC5为触摸屏中断请求线,PA5,PA6,PA7为SPI1接口,PG11为触摸屏总线的片选信号,PB1为LCD背光控制,FSMC[15:0] 为数据总线,PD5(FSMC_NWE)为写使能,PD4(FSMC_NOE)为读使能,PF0(FSMC_A0)指令/数据选择线。
LCD地址计算方法:
A,正如上图所示,对于16位宽度的外部存储器,stm32f103的FSMC将在内部使用HADDR[25:1]产生外部存储器的地址FSMC_A[24:0],而HADDR[0]未接;
B,LCD的CS引脚决定它的起始地址,STM32的CPU仅引出了4个片选信号,也就是CPU硬件最多只能外接4个总线型设备。安富莱开发板(V3)具有5个FSMC外设,所以开发板外扩一个地址译码器电路,如下图:
将NE4空间分为4部分,第1份给LCD,第2份给LAN,即当FSMC_A19=0、FSMC_NE4=0时选通LCD。STM32的NE4的起始地址是0x6c000000,FSMC_A19对应于HADDR[20],所以安富莱开发板(V3)的LCD起始地址为0x6c000000;
C,LCD的A0引脚决定传送的是地址还是数据,安富莱开发板(V3)上连接的是FSMC_A[0]即HADDR[1],所以安富莱开发板(V3)的IO_ADDR = 0x6c000000,IO_DATA = 0x6c100002。
安富莱开发板(V3)的LCD模块的显示控制IC为OTM4001A
(1)添加otm4001a.c驱动文件
在安富莱开发板(V3)的配套光盘中可以找到有关tft_lcd实验的例子,这里将例程 Ex007-TFT显示文字图片例程\User\bsp目录下bsp_tft_lcd.h和bsp_tft_lcd.c复制到stm32f103ze-ek/drivers目录下,并分别重命名为otm4001a.h和otm4001a.c,如下图:
(3)将otm4001a.c加入KeilMDK工程,同时将ssd1289.c从KeilMDK工程中移除。
(4)修改drivers目录下的脚本,将其加入编译列表。
<1>使用Notepad++打开stm32f103ze-ek/drivers目录下SConscript文件,定位到第34行加入下面代码:
# add RTGUI drvers.
if GetDepend('RT_USING_RTGUI'):
if GetDepend('RTGUI_USING_TOUCHPANEL'):
src += ['touch_driver.c']
if rtconfig.RT_USING_LCD_TYPE == 'ILI932X':
src += ['ili_lcd_general.c']
elif rtconfig.RT_USING_LCD_TYPE == 'SSD1289':
src += ['ssd1289.c']
elif rtconfig.RT_USING_LCD_TYPE == 'OTM4001A':
src += ['otm4001a.c']
CPPPATH = [cwd]
... ...
要注意的是phython的语法格式,每一级要用四个空格缩进,否则的话在执行时会产生语法错误。
修改完成后保存并关闭。
<2>使用Notepad++打开stm32f103ze-ek目录下rtconfig.py文件,定位到第20行加入下面代码:
... ...
# lcd panel options
# 'FMT0371','ILI932X', 'SSD1289','OTM4001A'
RT_USING_LCD_TYPE = 'OTM4001A'
... ...
修改完成后保存并关闭。
【5】OTM4001A的驱动代码移植
(1)otm4001a的依据是OTM4001A数据手册,初始化流程参考位于OTM4001A数据手册第78页开始部分,移植后的otm4001a.c代码如下:#include "stm32f10x.h" #include "otm4001a.h" //输出重定向.当不进行重定向时. #define printf rt_kprintf //使用rt_kprintf来输出 //#define printf(...) //无输出 /* 定义LCD驱动器的访问地址 TFT接口中的RS引脚连接FSMC_A0引脚,由于是16bit模式,RS对应A1地址线,因此 LCD_RAM的地址是+2 */ typedef struct { __IO uint16_t LCD_REG; __IO uint16_t LCD_RAM; } LCD_TypeDef; #define LCD_BASE ((uint32_t)(0x60000000 | 0x0C000000)) #define LCD ((LCD_TypeDef *)LCD_BASE) static void delay(int cnt); static void LCD_CtrlLinesConfig(void); static void LCD_FSMCConfig(void); /******************************************************************************* * 函数名: LCD_WriteReg * 参 数: LCD_Reg :寄存器地址; LCD_RegValue : 寄存器值 * 返 回: 无 * 功 能: 修改LCD控制器的寄存器的值 */ void LCD_WriteReg(__IO uint16_t LCD_Reg, uint16_t LCD_RegValue) { /* Write 16-bit Index, then Write Reg */ LCD->LCD_REG = LCD_Reg; /* Write 16-bit Reg */ LCD->LCD_RAM = LCD_RegValue; } /******************************************************************************* * 函数名: LCD_ReadReg * 参 数: LCD_Reg :寄存器地址 * 返 回: 寄存器的值 * 功 能: 读LCD控制器的寄存器的值 */ uint16_t LCD_ReadReg(__IO uint16_t LCD_Reg) { /* Write 16-bit Index (then Read Reg) */ LCD->LCD_REG = LCD_Reg; /* Read 16-bit Reg */ return (LCD->LCD_RAM); } /******************************************************************************* * *LCD_ReadData() *返 回: 寄存器的值 *功 能: 读LCD控制器的寄存器的? ********************************************************************************/ uint16_t LCD_ReadData() { /* Read 16-bit Reg */ return (LCD->LCD_RAM); } /******************************************************************************* * 函数名: LCD_WriteRAM_Prepare * 参 数: 无 * 返 回: 无 * 功 能: 写显存前的准备,即设置显存寄存器地址。 */ void LCD_WriteRAM_Prepare(void) { LCD->LCD_REG = 0x202; //write data to GRAM } /******************************************************************************* * 函数名: LCD_WriteRAM * 参 数: RGB_Code : 颜色代码 * 返 回: 无 * 功 能: 写显存,显存地址自动增加。适用于连续写。 */ void LCD_WriteRAM(uint16_t RGB_Code) { /* Write 16-bit GRAM Reg */ LCD->LCD_RAM = RGB_Code; } /******************************************************************************* * 函数名: LCD_WriteRAM1 * 参 数: RGB_Code : 颜色代码 * 返 回: 无 * 功 能: 写显存,显存地址自动增加。适用于写单个像素。 */ void LCD_WriteRAM1(uint16_t RGB_Code) { LCD->LCD_REG = 0x202; /* Write 16-bit GRAM Reg */ LCD->LCD_RAM = RGB_Code; } /******************************************************************************* * 函数名: LCD_ReadRAM * 参 数: 无 * 返 回: 显存数据 * 功 能: 读显存,地址自动增加 */ uint16_t LCD_ReadRAM(void) { /* Read 16-bit Reg */ return LCD->LCD_RAM; } /******************************************************************************* * 函数名: LCD_SetCursor * 参 数: Xpos : X坐标; Ypos: Y坐标 * 返 回: 无 * 功 能: 设置光标位置 */ void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos) { /* px,py 是物理坐标, x,y是虚拟坐标 转换公式: py = 399 - x; px = y; */ //LCD_WriteReg(0x0200, Ypos); /* px */ //gram address set horizontal //LCD_WriteReg(0x0201, 399 - Xpos); /* py */ //gram address set vertical //物理坐标和虚拟坐标对应 LCD_WriteReg(0x0200, Xpos); /* px */ //gram address set horizontal LCD_WriteReg(0x0201, Ypos); /* py */ //gram address set vertical } /******************************************************************************* * 函数名: LCD_ReadGRAM * 参 数: * 返 回: 无 * 功 能: 读取显存 */ static unsigned short LCD_ReadGRAM(int x,int y) { unsigned short temp; LCD_SetCursor(x,y); LCD_WriteRAM_Prepare(); temp=LCD_ReadData(); temp=LCD_ReadData(); return temp; } /******************************************************************************************************/ static unsigned short deviceid=0; /* ********************************************************************************************************* * 函 数 名: otm4001_hw_init * 功能说明: 初始化LCD * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void otm4001_hw_init(void) { //uint16_t id; /* 配置LCD控制口线GPIO */ LCD_CtrlLinesConfig(); /* 配置FSMC接口,数据总线 */ LCD_FSMCConfig(); /* FSMC重置后必须加延迟才能访问总线设备 */ delay(2000); //20ms deviceid = LCD_ReadReg(0x0000); /* 读取LCD驱动芯片ID */ printf("LCD ID %08x\r\n",deviceid); /* 初始化LCD,写LCD寄存器进行配置 */ LCD_WriteReg(0x0000, 0x0000); LCD_WriteReg(0x0001, 0x0100); LCD_WriteReg(0x0002, 0x0100); /* R003H 寄存器很关键, Entry Mode ,决定了扫描方向 参见:SPFD5420A.pdf 第15页 240x400屏幕物理坐标(px,py)如下: R003 = 0x1018 R003 = 0x1008 ------------------- ------------------- |(0,0) | |(0,0) | | | | | | ^ ^ | | ^ ^ | | | | | | | | | | | | | | | | | | | | | | | | | | | ------> | | | | <------ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (x=239,y=399)| | (x=239,y=399)| |-------------------| |-------------------| | | | | ------------------- ------------------- 按照安富莱开发板LCD的方向,我们期望的虚拟坐标和扫描方向如下:(和上图第1个吻合) -------------------------------- | |(0,0) | | | ---------> | | | | | | | | | | | | | | | V | | | ---------> | | | (399,239)| -------------------------------- 虚拟坐标(x,y) 和物理坐标的转换关系 x = 399 - py; y = px; py = 399 - x; px = y; */ //LCD_WriteReg(0x0003, 0x1018); /* 0x1018 1030 */ LCD_WriteReg(0x0003,0x1030); //Entry Mode LCD_WriteReg(0x0008, 0x0808);//display Control2 LCD_WriteReg(0x0009, 0x0001);//display Control3 LCD_WriteReg(0x000B, 0x0010);//low power Control LCD_WriteReg(0x000C, 0x0000);//External Display interface control 1 LCD_WriteReg(0x000F, 0x0000);//external dispaly interface Control 2 LCD_WriteReg(0x0007, 0x0001);//display control1 LCD_WriteReg(0x0010, 0x0013);//panel interface control 1 LCD_WriteReg(0x0011, 0x0501);//panel interface control 2 LCD_WriteReg(0x0012, 0x0300);//panel interface control 3 LCD_WriteReg(0x0020, 0x021E);//panel interface control 4 LCD_WriteReg(0x0021, 0x0202);//panel interface control 5 LCD_WriteReg(0x0090, 0x8000);//frame marker control LCD_WriteReg(0x0100, 0x17B0);//power control 1 LCD_WriteReg(0x0101, 0x0147);//power control 2 LCD_WriteReg(0x0102, 0x0135);//power control 3 LCD_WriteReg(0x0103, 0x0700);//power control 4 LCD_WriteReg(0x0107, 0x0000); LCD_WriteReg(0x0110, 0x0001); LCD_WriteReg(0x0210, 0x0000);//Window horizontal ramaddress start LCD_WriteReg(0x0211, 0x00EF);//window horizontal ramaddress end LCD_WriteReg(0x0212, 0x0000);//window vertical ram address start LCD_WriteReg(0x0213, 0x018F);//window vertical ramaddress end LCD_WriteReg(0x0280, 0x0000); LCD_WriteReg(0x0281, 0x0004); LCD_WriteReg(0x0282, 0x0000); LCD_WriteReg(0x0300, 0x0101);// y control LCD_WriteReg(0x0301, 0x0B2C); LCD_WriteReg(0x0302, 0x1030); LCD_WriteReg(0x0303, 0x3010); LCD_WriteReg(0x0304, 0x2C0B); LCD_WriteReg(0x0305, 0x0101); LCD_WriteReg(0x0306, 0x0807); LCD_WriteReg(0x0307, 0x0708); LCD_WriteReg(0x0308, 0x0107); LCD_WriteReg(0x0309, 0x0105); LCD_WriteReg(0x030A, 0x0F04); LCD_WriteReg(0x030B, 0x0F00); LCD_WriteReg(0x030C, 0x000F); LCD_WriteReg(0x030D, 0x040F); LCD_WriteReg(0x030E, 0x0300); LCD_WriteReg(0x030F, 0x0701);//y control LCD_WriteReg(0x0400, 0x3500);//BASE IMAGE NUMBER OF LINE LCD_WriteReg(0x0401, 0x0001);//base image display control LCD_WriteReg(0x0404, 0x0000);//base image vertical scroll control LCD_WriteReg(0x0500, 0x0000); LCD_WriteReg(0x0501, 0x0000); LCD_WriteReg(0x0502, 0x0000); LCD_WriteReg(0x0503, 0x0000); LCD_WriteReg(0x0504, 0x0000); LCD_WriteReg(0x0505, 0x0000); LCD_WriteReg(0x0600, 0x0000); LCD_WriteReg(0x0606, 0x0000); LCD_WriteReg(0x06F0, 0x0000); LCD_WriteReg(0x07F0, 0x5420); LCD_WriteReg(0x07DE, 0x0000); LCD_WriteReg(0x07F2, 0x00DF); LCD_WriteReg(0x07F3, 0x0810); LCD_WriteReg(0x07F4, 0x0077); LCD_WriteReg(0x07F5, 0x0021); LCD_WriteReg(0x07F0, 0x0000); LCD_WriteReg(0x0007, 0x0173);//diplay control /* 设置显示窗口 WINDOWS */ LCD_WriteReg(0x0210, 0); /* 水平起始地址 */ LCD_WriteReg(0x0211, 239); /* 水平结束坐标 */ LCD_WriteReg(0x0212, 0); /* 垂直起始地址 */ LCD_WriteReg(0x0213, 399); /* 垂直结束地址 */ } static void delay(int cnt) { volatile unsigned int dl; while(cnt--) { for(dl=0; dl<500; dl++); } } /* ********************************************************************************************************* * 函 数 名: LCD_CtrlLinesConfig * 功能说明: 配置LCD控制口线,FSMC管脚设置为复用功能 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void LCD_CtrlLinesConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); /* 使能 FSMC, GPIOD, GPIOE, GPIOF, GPIOG 和 AFIO 时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG | RCC_APB2Periph_AFIO, ENABLE); /* 设置 PD.00(D2), PD.01(D3), PD.04(NOE), PD.05(NWE), PD.08(D13), PD.09(D14), PD.10(D15), PD.14(D0), PD.15(D1) 为复用推挽输出 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); /* 设置 PE.07(D4), PE.08(D5), PE.09(D6), PE.10(D7), PE.11(D8), PE.12(D9), PE.13(D10), PE.14(D11), PE.15(D12) 为复用推挽输出 */ /* PE3,PE4 用于A19, A20, STM32F103ZE-EK(REV 1.0)必须使能 */ /* PE5,PE6 用于A19, A20, STM32F103ZE-EK(REV 2.0)必须使能 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6; GPIO_Init(GPIOE, &GPIO_InitStructure); /* 设置 PF.00(A0 (RS)) 为复用推挽输出 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOF, &GPIO_InitStructure); /* 设置 PG.12(NE4 (LCD/CS)) 为复用推挽输出 - CE3(LCD /CS) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_Init(GPIOG, &GPIO_InitStructure); } /* ********************************************************************************************************* * 函 数 名: LCD_FSMCConfig * 功能说明: 配置FSMC并口访问时序 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void LCD_FSMCConfig(void) { FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef FSMC_NORSRAMTimingInitStructure; /*-- FSMC Configuration ------------------------------------------------------*/ /*----------------------- SRAM Bank 4 ----------------------------------------*/ /* FSMC_Bank1_NORSRAM4 configuration */ FSMC_NORSRAMTimingInitStructure.FSMC_AddressSetupTime = 1; FSMC_NORSRAMTimingInitStructure.FSMC_AddressHoldTime = 0; FSMC_NORSRAMTimingInitStructure.FSMC_DataSetupTime = 2; FSMC_NORSRAMTimingInitStructure.FSMC_BusTurnAroundDuration = 0; FSMC_NORSRAMTimingInitStructure.FSMC_CLKDivision = 0; FSMC_NORSRAMTimingInitStructure.FSMC_DataLatency = 0; FSMC_NORSRAMTimingInitStructure.FSMC_AccessMode = FSMC_AccessMode_B; /* Color LCD configuration ------------------------------------ LCD configured as follow: - Data/Address MUX = Disable - Memory Type = SRAM - Data Width = 16bit - Write Operation = Enable - Extended Mode = Enable - Asynchronous Wait = Disable */ FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); /* - BANK 3 (of NOR/SRAM Bank 0~3) is enabled */ FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); } static uint8_t s_bright; /* 背光亮度 */ /* ********************************************************************************************************* * 函 数 名: LCD_SetBackLight * 功能说明: 初始化控制LCD背景光的GPIO,配置为PWM模式。 * 当关闭背光时,将CPU IO设置为浮动输入模式(推荐设置为推挽输出,并驱动到低电平);将TIM3关闭 省电 * 形 参:_bright 亮度,0是灭,255是最亮 * 返 回 值: 无 * * 背光口线是 PB1, 复用功能选择 TIM3_CH4 * * 当关闭背光时, * 将CPU IO设置为浮动输入模式(推荐设置为推挽输出,并驱动到低电平) * 将TIM3关闭 省电 ********************************************************************************************************* */ void lcd_set_backlight(uint8_t _bright) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; s_bright = _bright; /* 第1步:打开GPIOB RCC_APB2Periph_AFIO 的时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); if (_bright == 0) { /* 配置背光GPIO为输入模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /* 关闭TIM3 */ TIM_Cmd(TIM3, DISABLE); return; } else if (_bright == BRIGHT_MAX) /* 最大亮度 */ { /* 配置背光GPIO为推挽输出模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_1); /* 关闭TIM3 */ TIM_Cmd(TIM3, DISABLE); return; } /* 配置背光GPIO为复用推挽输出模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /* 使能TIM3的时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* TIM3 配置: 产生1路PWM信号; TIM3CLK = 72 MHz, Prescaler = 0(不分频), TIM3 counter clock = 72 MHz 计算公式: PWM输出频率 = TIM3 counter clock /(ARR + 1) 我们期望设置为100Hz 如果不对TIM3CLK预分频,那么不可能得到100Hz低频。 我们设置分频比 = 1000, 那么 TIM3 counter clock = 72KHz TIM_Period = 720 - 1; 频率下不来。 */ TIM_TimeBaseStructure.TIM_Period = 720 - 1; /* TIM_Period = TIM3 ARR Register */ TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); /* PWM1 Mode configuration: Channel1 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; /* _bright = 1 时, TIM_Pulse = 1 _bright = 255 时, TIM_Pulse = TIM_Period */ TIM_OCInitStructure.TIM_Pulse = (TIM_TimeBaseStructure.TIM_Period * _bright) / BRIGHT_MAX; /* 改变占空比 */ TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC4Init(TIM3, &TIM_OCInitStructure); TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); /* 使能 TIM3 定时器 */ TIM_Cmd(TIM3, ENABLE); } /******************************************************************************* * 函数名: GetBackLight * 输 入: 无 * 输 出: 返回当前亮度值 * 功能说明:获取当前亮度值(0-255) */ uint8_t lcd_get_backlight(void) { return s_bright; } /************************************************************************** * 配置RT-Thread LCD接口 * ****************************************************************************/ /*设置像素点颜色,x,y*/ void lcd_set_pixel(const char *pixel,int x,int y) { LCD_SetCursor(x,y); // LCD_WriteRAM1(*(rt_uint16_t *)pixel); LCD_WriteRAM_Prepare(); LCD_WriteRAM(*(rt_uint16_t*)pixel); } /*获取像素点颜色*/ void lcd_get_pixel(char *pixel,int x,int y) { *(rt_uint16_t*)pixel=LCD_ReadGRAM(x, y); } /* 画水平线*/ void lcd_draw_hline(const char *pixel,int x1,int x2,int y) { /* [5:4]-ID~ID0 [3]-AM-1垂直-0水平 */ LCD_WriteReg(0x0003,0x1030 |0<<3); //AM=0 hline LCD_SetCursor(x1, y); LCD_WriteRAM_Prepare(); while(x1<x2) { LCD_WriteRAM(*(rt_uint16_t *)pixel); x1++; } } /*垂直线*/ void lcd_draw_vline(const char *pixel,int x,int y1,int y2) { /* [5:4]-ID~ID0 [3]-AM-1垂直-0水平 */ LCD_WriteReg(0x0003,0x1030 |1<<3); //AM=1 vline LCD_SetCursor(x,y1); LCD_WriteRAM_Prepare(); while(y1<y2) { LCD_WriteRAM(*(rt_uint16_t*)pixel); y1++; } } void lcd_blit_line(const char * pixels,int x,int y,rt_size_t size) { rt_uint16_t *ptr; ptr = (rt_uint16_t*) pixels; /* [5:4]-ID~ID0 [3]-AM-1垂直-0水平 */ LCD_WriteReg(0x0003,0x1030 |0<<3); //AM=0 hline LCD_SetCursor(x,y); LCD_WriteRAM_Prepare(); while(size) { LCD_WriteRAM(*ptr ++); size --; } } struct rt_device_graphic_ops otm4001_ops = { lcd_set_pixel, lcd_get_pixel, lcd_draw_hline, lcd_draw_vline, lcd_blit_line }; struct rt_device _lcd_device; static rt_err_t lcd_init(rt_device_t dev) { return RT_EOK; } static rt_err_t lcd_open(rt_device_t dev, rt_uint16_t oflag) { return RT_EOK; } static rt_err_t lcd_close(rt_device_t dev) { return RT_EOK; } static rt_err_t lcd_control(rt_device_t dev, rt_uint8_t cmd, void *args) { switch (cmd) { case RTGRAPHIC_CTRL_GET_INFO: { struct rt_device_graphic_info *info; info = (struct rt_device_graphic_info*) args; RT_ASSERT(info != RT_NULL); info->bits_per_pixel = 16; info->pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565P; info->framebuffer = RT_NULL; info->width = 240; info->height = 400; } break; case RTGRAPHIC_CTRL_RECT_UPDATE: /* nothong to be done */ break; default: break; } return RT_EOK; } void rt_hw_lcd_init(void) { /* register lcd device */ _lcd_device.type = RT_Device_Class_Graphic; _lcd_device.init = lcd_init; _lcd_device.open = lcd_open; _lcd_device.close = lcd_close; _lcd_device.control = lcd_control; _lcd_device.read = RT_NULL; _lcd_device.write = RT_NULL; _lcd_device.user_data = &otm4001_ops; otm4001_hw_init(); lcd_set_backlight(200); /* register graphic device driver */ rt_device_register(&_lcd_device, "lcd", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); }
注意上面代码中
LCD_WriteReg(0x0003, 0x1018); /* 0x1018 1030 */
需要注释掉
(2)移植后的otm4001a.h代码如下:
#ifndef _OTM4001_H_INCLUDE #define _OTM4001_H_INCLUDE #include "stm32f10x.h" #include "rtthread.h" #include <rtgui/rtgui.h> #include <rtgui/driver.h> #include <rtgui/rtgui_server.h> #include <rtgui/rtgui_system.h> /* 定义LCD显示区域的分辨率 */ #define LCD_HEIGHT 240 /* 高度,单位:像素 */ #define LCD_WIDTH 400 /* 宽度,单位:像素 */ /* LCD 寄存器定义, LR_前缀是LCD Register的简写 */ #define LR_CTRL1 0x007 /* 读写显存的寄存器地址 */ #define LR_GRAM 0x202 /* 读写显存的寄存器地址 */ #define LR_GRAM_X 0x200 /* 显存水平地址(物理X坐标)*/ #define LR_GRAM_Y 0x201 /* 显存垂直地址(物理Y坐标)*/ /* LCD 颜色代码,CL_是Color的简写 */ enum { CL_WHITE = 0xFFFF, /* 白色 */ CL_BLACK = 0x0000, /* 黑色 */ CL_GREY = 0xF7DE, /* 灰色 */ CL_BLUE = 0x001F, /* 蓝色 */ CL_BLUE2 = 0x051F, /* 浅蓝色 */ CL_RED = 0xF800, /* 红色 */ CL_MAGENTA = 0xF81F, /* 红紫色,洋红色 */ CL_GREEN = 0x07E0, /* 绿色 */ CL_CYAN = 0x7FFF, /* 蓝绿色,青色 */ CL_YELLOW = 0xFFE0, /* 黄色 */ CL_MASK = 0x9999 /* 颜色掩码,用于文字背景透明 */ }; /* 字体代码 */ enum { FC_ST_16X16 = 0, /* 宋体15x16点阵 (宽x高) */ FC_ST_24X24 = 1 /* 宋体24x24点阵 (宽x高) */ }; /* 字体属性结构, 用于LCD_DispStr() */ typedef struct { uint16_t usFontCode; /* 字体代码 0 表示16点阵 */ uint16_t usTextColor; /* 字体颜色 */ uint16_t usBackColor; /* 文字背景颜色,透明 */ uint16_t usSpace; /* 文字间距,单位 = 像素 */ }FONT_T; /* 背景光控制 */ #define BRIGHT_MAX 255 #define BRIGHT_MIN 0 #define BRIGHT_DEFAULT 200 #define BRIGHT_STEP 5 /* 可供外部模块调用的函数 */ void lcd_set_backlight(uint8_t _bright); uint8_t lcd_get_backlight(void); #endif
然后保存,接下来就可以编译了。
相关文章推荐
- RT-Thread 学习笔记(十三)--- 开启基于RTGUI的LCD显示功能(3)<触屏屏驱动移植和测试>
- RT-Thread 学习笔记(十二)--- 开启基于RTGUI的LCD显示功能(2)<编译测试>
- RT-Thread ---开启基于RTGUI的LCD显示功能(2)<编译测试>
- RT-Thread 学习笔记(十) --- 开启基于LWIP协议的网络功能
- <Test-Driven Development with Python>学习笔记 第一部分 测试驱动开发基础
- RT-Thread 学习笔记(六)--- 开启基于SPI Flash的elmfat文件系统(上)
- RT-Thread 学习笔记(九)---开启基于SD卡中的 Elm FatFS 文件系统
- mini2440 led驱动代码(原创)基于iomap<测试初始化没有问题,其它功能待开发>
- RT-Thread 基于STM32F1xx HAL 库的学习笔记(2)---加入pin功能支持
- RT-Thread 学习笔记(七)---开启基于SPI Flash的elmfat文件系统(中)
- 【Qt编程】基于Qt的词典开发系列<十一>系统托盘的显示
- <C++学习笔记>预处理功能
- RT-Thread 学习笔记(八)---开启基于SPI Flash的elmfat文件系统(下)
- 学习java笔记 --- 一个实现Iterable<E>接口的小范例
- HTML学习笔记(十一)HTML <div> 和 <span>
- Struts2学习笔记之<s:token/>防止表单重复提交
- <<Git Community Book中文版>>学习笔记
- Android NDK学习 <六> 复杂结构动态库处理和第三方库的移植
- 学习笔记:PHPExcel PHPExcel_Style_Conditional() getStartColor()->setARGB('');结果显示黑色或无色
- C++学习笔记之对文件的操作<1>