RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)

软件环境: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

【1】加入LCD设备驱动文件

(1)登陆http://www.rt-thread.org/node/76,可以看到如下链接,如下图:

RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第1张图片


点击上面下载地址处的链接,可以直接下载。


(2)解压RTGUI-0.8源码,将components目录下的rtgui组件复制到rt-thread-1.2.2/components目录下,如下图。

RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第2张图片

【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组被加入进来,如下图。

RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第3张图片

【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的硬件接口原理图如下:

RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第4张图片

从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外设,所以开发板外扩一个地址译码器电路,如下图:

RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第5张图片RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第6张图片

将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,如下图:

RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)_第7张图片

(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(x1bits_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 
#include 
#include 
#include 

/* 定义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显示功能(1))