您的位置:首页 > 产品设计 > UI/UE

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

【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


然后保存,接下来就可以编译了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐