1.中断体系
1.1 NVIC的关键点
NVIC:Nest Vector Interrupt Controller,嵌套中断向量控制器,是用来管理中断嵌套的,核心任务在于其优先级的管理。NVIC给每个中断赋予先占优先级(抢占优先级)和次占优先级(响应优先级)。
CM3 内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。但STM32并没有使用CM3内核的全部东西,而是只用了它的一部分,STM32有76 个中断,包括16 个内核中断和60 个可屏蔽中断,具有16级可编程的中断优先级。而我们常用的就是这60个可屏蔽中断。
STM32将CM3内核的中断向量表进行了重新编排,将编号-3至6的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,从编号7开始为外部中断,这些中断优先级都是可以自行设置的。
(1)数据手册中相关部分浏览
(2)地址映射时0地址映射到Flash或SRAM
(3)中断向量表可以被人为重新映射,一般用来IAP中
(4)STM32采用一维的中断向量表
(5)中断优先级设置有点复杂,后面细说
1.2 起始代码中的ISR
(1)其实代码中定义了一个Vector数组
(2)WEAK声明的默认ISR
(3)用户根据需要提供自己真正有用的ISR
(4)中断要配置使能,ISR中要清挂起等,这一点和其他CPU一样
NVIC Nested vectoredinterrupt controller
嵌套向量中断控制器。
起始代码帮我们建立了中断向量表
EXTI(External interrupt/event controller)—外部中断/事件控制器
挂起寄存器保持着状态线的中断请求 挂起 标志位 从0 - 1 产生中断,没有屏蔽 ->事件处理 然后 变0;
EXTI控制器的主要特性如下:
● 每个中断/事件都有独立的触发和屏蔽
● 每个中断线都有专用的状态位
● 支持多达20个软件的中断/事件请求
● 检测脉冲宽度低于APB2时钟宽度的外部信号。(速度不能太快)参见数据手册中电气特性部分的相关参数。
这张图可以很直观的看出中断和事件的区别,当外部有信号输入时,如果通过了事件屏蔽寄存器,那么事件信号就进入脉冲触发器,引发一个脉冲信号,直接传递给相应的外设,用于触发,这就是一个纯硬件的过程,这个方式不需要CPU参与,但是这也有它的缺点,如功能比较单一,仅能提供信号,不能提供信息,也就是只能产生指定功能的事件。如果通过中断屏蔽寄存器,就被直接送到CPU中,产生中断,如进入上面的入口函数开始处理。从这就可看出,事件是单纯硬件触发执行的过程,与CPU本身设计支持有关,而中断中则可以软件实现各种功能,而低功耗模式和事件唤醒就是stm32支持的事件之一。
1.3 如何实际编程使用外部中断
(1)时钟设置并打开相应GPIO模块时钟
(2)将相应GPIO配置为浮空输入
(3)NVIC设置
(4)将外部中断线和配套的GPIO进行连接映射
(5)外部中断线使能触发
(6)准备好ISR,并在ISR处等待执行中断程序即可
typedef struct
{
uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
//终端号 This parameter can be a value of @ref IRQn_Type
(For the complete STM32 Devices IRQ Channels list, please
refer to stm32f10x.h file) */
uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
//抢占优先级 specified in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
//次优先级 in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
//是否开启 will be enabled or disabled.
This parameter can be set either to ENABLE or DISABLE */
} NVIC_InitTypeDef;
- 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
- 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
- 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
- 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
数字小的优先级高
A B C D
抢占优先级 0 0 1 1
(相应)次优先级 0 1 0 1
先看抢占优先级
再看次优先级
人为设置抢占优先级和次级优先级
优先级组:4
选择合适的优先级组
#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority 2^0 = 2
4 bits for subpriority */ 2^4 = 8
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */
1.4 EXTI
typedef struct
{
uint32_t EXTI_Line; //中断线 /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination of @ref EXTI_Lines */
EXTIMode_TypeDef EXTI_Mode; //中断模式 /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef */
EXTITrigger_TypeDef EXTI_Trigger;// 触发模式/*!< Specifies the trigger signal active edge for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef */
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
This parameter can be set either to ENABLE or DISABLE */
}EXTI_InitTypeDef;
Key1 ->PA0 配置为中断模式
key2 -> pC13
LED1->PG6
LED2->PG7
key1->LED1
PA0->PG6
因此选择EXTI0
代码如下:
#include "stm32f10x.h"
void KEY_Init(void);
void LED_Init(void);
void My_EXTI_Init(void);
int main(void)
{
//起始代码 已经将时钟设置为72MHZ
LED_Init();
KEY_Init();
My_EXTI_Init();
//当我们按下按键 会运行中断函数
while(1)
{
};
}
// key1->LED1
// PA0 -> PG6
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
//GPIOA使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置第0个引脚为输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//初始化LED IO
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
//配置第6个引脚为输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG,&GPIO_InitStructure);
}
void My_EXTI_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure; //NVIC
EXTI_InitTypeDef EXTI_InitStructure; //EXTI结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //选择A0管脚用作外部中断线路
//抢占优先级 2个抢占优先级 8个次优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
//EXTI0 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 中断使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//EXTI配置
EXTI_InitStructure.EXTI_Line = EXTI_Line0; //lin0 EXTI中断/事件线选择
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //初始化中断
}
//stm32f10x_it.c
//中断处理函数 在这里进行处理
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
GPIO_WriteBit(GPIOG, GPIO_Pin_6, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOG, GPIO_Pin_6)));
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
2.FSMC
1.概念
- 基本概念
(1)flexible static memory controller 灵活的静态内存控制器
(2)内部外设(寄存器+IO口)
(3)功能目标:让STM32可以对接外部存储器(NorFlash、NandFlash、SRAM、PCCard···)
- SoC如何扩展外部存储器
(1)专用内部控制器一对一服务 SD
(2)FSMC一对多服务
- FSMC的优缺点
(1)优点:灵活适配,有限资源实现多种外部存储器的支持
(2)缺点:配置复杂,需要考虑接口时序的细节
2.FSMC操控LCD
- LCD的操作接口
(1)8080接口:控制接口WR(写时钟)+RD(读时钟)+RS(cmd/data)+DataPort8/16 CS片选
(2)接口本质:低层的时序+高层的类SRAM
(3)操作要求:只要按照8080接口时序,任何SoC任何方式都可以与LCD进行互操作
- STM32的FSMC初步学习
(1)支持Nor/PSRAM(接口类似SRAM)、Nand、PCCard等多种类型外存
(2)FSMC为其支持的各种各个外存都提供了地址映射空间
(3)4个块共1GB地址空间,每块256MB;每块又分为4个子存储块,每字块64MB 份子快是为了多接几个设备
(4)LCD的接口类似于NorFlash/SRAM,所以可以用第1块的4部分任何一部分
3.相关GPIO
(1)DB0-15,16数据位,因此FSMC控制器FSMC_A[24:0]内部地址 对应HADDR[25:1] 外部地址 通过内部地址来访问外部地址
最简单的是1-1对应,
8位:1个字节
16位:2个字节
对齐访问:
详细解释:
实质就是将FSMC_A的值,左移一位,然后放到HADDR中,也就是将其乘以2倍,这样FSMC_A和HADDR的值就对应了,只要传对应值就可以了
2^26 = 0X0400 0000 = 64MB,每个 BANK 有4*64MB = 256MB
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF
选择BANK1-BORSRAM4 连接 TFT,地址范围为0X6C00 0000 ~ 0X6FFF FFFF
YS-F1P开发板选择 FSMC_A0 接LCD的DC(寄存器/数据选择)脚
寄存器基地址 = 0X6C00 0000 命令总线地址
RAM基地址 = 0X6C00 0002 = 0X6C00 0000+(1<<(0+1)) 数据总线地址
如果电路设计时选择不同的地址线时,地址要重新计算
(2)GPD、GPE、GPF、GPG共4个端口和FSMC有关,但是GPF都是A线(地址)所以LCD用不到,用不到地址线
(3)NOE接RD 读,NWE接WR 写,A0接RS(0 寄存器 写配置数值/1 数据选择 写数据),NE4接CS
block1:60000000-6FFFFFFF
block1-子块1:60000000-63FFFFFF NE1
block1-子块2:64000000-67FFFFFF
block1-子块3:68000000-6BFFFFFF
block1-子块4:6C000000-6FFFFFFF NE4
RS接了FSMC地址线A0,只需要A0为0就是cmd,为1就是DATA
如何控制?
不能直接单独IO控制,通过写地址来完成。
6C00 0000 这个地址对应A0的地方(bit2)为0,所以直接写6C00 0000时就是写cmd 写命令
6C00 0002 这个地址对应A0的地方(bit2)为1,所以直接写6C00 0002就是写DATA 写数据
4.标准库
(1)只看NOR/SRAM部分,其余Nand、PCCard等不看
(2)重点就FSMC_NORSRAMInit一个函数,FSMC_NORSRAMInitTypeDef一个结构体
FSMC_NORSRAMCmd
总线操作:
RS接的可不是GPIO,是FSMC地址总线的一根.FSMC进行读写操作的时候会在地址总线根据要读写的地址输出电平的.
不是通过操作GPIO来操作RS,而是直接根据地址总线地址的不同来完成操作RS
#include "stm32f10x.h"
#include "lcd_driver.h"
#include "stdlib.h"
uint16_t color;
static void delay(void)
{
uint32_t i,j;
for(i=0;i<0x7FF;i++)
for(j=0;j<0xFFF;j++);
}
int main(void)
{
//初始化LCD
BSP_LCD_Init();
//开启背光
LCD_BK_ON();
//设置颜色为蓝色
LCD_Clear(0, 0, LCD_DEFAULT_WIDTH, LCD_DEFAULT_HEIGTH, BLUE);
//随机颜色
srand(0xffff);
while(1)
{
//随机颜色
color = rand();
//设置颜色
LCD_Clear(0,0,LCD_DEFAULT_WIDTH, LCD_DEFAULT_HEIGTH, color);
//延时
delay();
};
return 0;
}
#ifndef __LEC_DRIVER__H
#define __LEC_DRIVER__H
#include "stm32f10x.h"
typedef enum
{
USB_FONT_16=16,
USB_FONT_24=24,
}USB_FONT_Typdef;
#define IS_USB_FONT(FONT) (((FONT) == USB_FONT_16) || ((FONT) == USB_FONT_24))
/******************************* ILI9488 显示屏的 FSMC 参数定义 ***************/
#define FSMC_LCD_CMD ((uint32_t)0x6C000000) //FSMC_Bank1_NORSRAM1用于LCD命令操作的地址
#define FSMC_LCD_DATA ((uint32_t)0x6C000002) //FSMC_Bank1_NORSRAM1用于LCD数据操作的地址
#define LCD_WRITE_CMD(x) *(__IO uint16_t *)FSMC_LCD_CMD = x
#define LCD_WRITE_DATA(x) *(__IO uint16_t *)FSMC_LCD_DATA = x
#define LCD_READ_DATA() *(__IO uint16_t *)FSMC_LCD_DATA
#define FSMC_LCD_BACKx FSMC_Bank1_NORSRAM4
/************************* ILI9488 显示屏8080通讯引脚定义 *********************/
//片选CS
#define FSMC_LCD_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FSMC_LCD_CS_CLK RCC_APB2Periph_GPIOG
#define FSMC_LCD_CS_PORT GPIOG
#define FSMC_LCD_CS_PIN GPIO_Pin_12
//DR->区分写命令还是写地址
#define FSMC_LCD_DC_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FSMC_LCD_DC_CLK RCC_APB2Periph_GPIOF
#define FSMC_LCD_DC_PORT GPIOF
#define FSMC_LCD_DC_PIN GPIO_Pin_0
//背光
#define FSMC_LCD_BK_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FSMC_LCD_BK_CLK RCC_APB2Periph_GPIOF
#define FSMC_LCD_BK_PORT GPIOF
#define FSMC_LCD_BK_PIN GPIO_Pin_10
#define LCD_BK_ON() GPIO_SetBits(FSMC_LCD_BK_PORT,FSMC_LCD_BK_PIN)
#define LCD_BK_OFF() GPIO_ResetBits(FSMC_LCD_BK_PORT,FSMC_LCD_BK_PIN)
/**************** 显示方向选择,可选(1,2,3,4)四个方向 *************************/
//#define LCD_DIRECTION 1 // 原点在屏幕左上角 X*Y=320*480
#define LCD_DIRECTION 2 // 原点在屏幕右上角 X*Y=480*320
//#define LCD_DIRECTION 3 // 原点在屏幕右下角 X*Y=320*480
//#define LCD_DIRECTION 4 // 原点在屏幕左下角 X*Y=480*320
/******** ILI934 显示屏全屏默认(扫描方向为1时)最大宽度和最大高度*************/
#if (LCD_DIRECTION==1)||(LCD_DIRECTION==3)
#define LCD_DEFAULT_WIDTH 320 // X轴长度
#define LCD_DEFAULT_HEIGTH 480 // Y轴长度
#else
#define LCD_DEFAULT_WIDTH 480 // X轴长度
#define LCD_DEFAULT_HEIGTH 320 // Y轴长度
#endif
/******************************* 定义 ILI9488 显示屏常用颜色 ********************************/
#define BACKGROUND WHITE //默认背景颜色
#define WHITE 0xFFFF //白色
#define BLACK 0x0000 //黑色
#define GREY 0xF7DE //灰色
#define BLUE 0x001F //蓝色
#define RED 0xF800 //红色
#define MAGENTA 0xF81F //红紫色,洋红色
#define GREEN 0x07E0 //绿色
#define CYAN 0x7FFF //蓝绿色,青色
#define YELLOW 0xFFE0 //黄色
#define BRED 0xF81F
#define GRED 0xFFE0
#define GBLUE 0x07FF
void LCD_SetDirection( uint8_t ucOption );
void LCD_Clear(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight,uint16_t usColor);
void LCD_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight);
uint32_t BSP_LCD_Init(void);
static __inline void LCD_FillColor ( uint32_t ulAmout_Point, uint16_t usColor );
#endif
#include "lcd_driver.h"
// 保存当前检查到的液晶模块ID
volatile uint32_t lcd_id=0;
//delay函数
static void LCD_DELAY( __IO uint32_t nCount )
{
for ( ; nCount != 0; nCount -- );
}
//初始化使用的引脚
static void LCD_GPIO_Config ( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能复用IO时钟:复用为fsmc功能 */
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_AFIO, ENABLE );
/* 使能FSMC对应相应管脚时钟 */
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE );
FSMC_LCD_CS_APBxClock_FUN ( FSMC_LCD_CS_CLK, ENABLE ); // FSMC_LCD_CS_APBxClock_FUN = RCC_APB2PeriphClockCmd
FSMC_LCD_DC_APBxClock_FUN ( FSMC_LCD_DC_CLK, ENABLE );
FSMC_LCD_BK_APBxClock_FUN ( FSMC_LCD_BK_CLK, ENABLE );
/* 配置FSMC相对应的数据线,FSMC-D0~D15: PD 14 15 0 1,PE 7 8 9 10 11 12 13 14 15,PD 8 9 10 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 |
GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init ( GPIOD, & GPIO_InitStructure );
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_Init ( GPIOE, & GPIO_InitStructure );
/* 配置FSMC相对应的控制线
* PD4-FSMC_NOE :LCD-RD
* PD5-FSMC_NWE :LCD-WR
* PG12-FSMC_NE4 :LCD-CS
* PF0-FSMC_A0 :LCD-DC
*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init (GPIOD, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init (GPIOD, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = FSMC_LCD_CS_PIN;
GPIO_Init ( FSMC_LCD_CS_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = FSMC_LCD_DC_PIN;
GPIO_Init ( FSMC_LCD_DC_PORT, & GPIO_InitStructure );
/* 配置LCD背光控制管脚BK*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Pin = FSMC_LCD_BK_PIN;
GPIO_Init ( FSMC_LCD_BK_PORT, & GPIO_InitStructure );
/* 初始化时先不开背光 */
GPIO_ResetBits(FSMC_LCD_BK_PORT,FSMC_LCD_BK_PIN);
}
//FSMC模式配置和时序
static void LCD_FSMC_Config ( void )
{
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef fsmc_lcd;
/* 使能FSMC时钟*/
RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_FSMC, ENABLE );
fsmc_lcd.FSMC_AddressSetupTime = 0x02; //地址建立时间
fsmc_lcd.FSMC_AddressHoldTime = 0x00; //地址保持时间
fsmc_lcd.FSMC_DataSetupTime = 0x05; //数据建立时间
fsmc_lcd.FSMC_BusTurnAroundDuration = 0x00;
fsmc_lcd.FSMC_CLKDivision = 0x00;
fsmc_lcd.FSMC_DataLatency = 0x00;
fsmc_lcd.FSMC_AccessMode = FSMC_AccessMode_B; //模式B比较适用于LCD
//数据手册有相关介绍
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_LCD_BACKx;
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
//这里使用的是NOR来代替 其实和SARM都可以驱动
FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR;
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_lcd;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = & fsmc_lcd;
FSMC_NORSRAMInit ( & FSMC_NORSRAMInitStructure );
/* 使能 FSMC_Bank1_NORSRAM4 */
FSMC_NORSRAMCmd ( FSMC_LCD_BACKx, ENABLE );
}
//初始化LCD寄存器
//设置了像素点格式、屏幕扫描方式、横屏竖屏等配置,由厂家提供
static void ILI9488_REG_Config ( void )
{
//************* Start Initial Sequence **********//
/* PGAMCTRL (Positive Gamma Control) (E0h) */
LCD_WRITE_CMD(0xE0);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x07);
LCD_WRITE_DATA(0x10);
LCD_WRITE_DATA(0x09);
LCD_WRITE_DATA(0x17);
LCD_WRITE_DATA(0x0B);
LCD_WRITE_DATA(0x41);
LCD_WRITE_DATA(0x89);
LCD_WRITE_DATA(0x4B);
LCD_WRITE_DATA(0x0A);
LCD_WRITE_DATA(0x0C);
LCD_WRITE_DATA(0x0E);
LCD_WRITE_DATA(0x18);
LCD_WRITE_DATA(0x1B);
LCD_WRITE_DATA(0x0F);
/* NGAMCTRL (Negative Gamma Control) (E1h) */
LCD_WRITE_CMD(0XE1);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x17);
LCD_WRITE_DATA(0x1A);
LCD_WRITE_DATA(0x04);
LCD_WRITE_DATA(0x0E);
LCD_WRITE_DATA(0x06);
LCD_WRITE_DATA(0x2F);
LCD_WRITE_DATA(0x45);
LCD_WRITE_DATA(0x43);
LCD_WRITE_DATA(0x02);
LCD_WRITE_DATA(0x0A);
LCD_WRITE_DATA(0x09);
LCD_WRITE_DATA(0x32);
LCD_WRITE_DATA(0x36);
LCD_WRITE_DATA(0x0F);
/* Adjust Control 3 (F7h) */
LCD_WRITE_CMD(0XF7);
LCD_WRITE_DATA(0xA9);
LCD_WRITE_DATA(0x51);
LCD_WRITE_DATA(0x2C);
LCD_WRITE_DATA(0x82);/* DSI write DCS command, use loose packet RGB 666 */
/* Power Control 1 (C0h) */
LCD_WRITE_CMD(0xC0);
LCD_WRITE_DATA(0x11);
LCD_WRITE_DATA(0x09);
/* Power Control 2 (C1h) */
LCD_WRITE_CMD(0xC1);
LCD_WRITE_DATA(0x41);
/* VCOM Control (C5h) */
LCD_WRITE_CMD(0XC5);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x0A);
LCD_WRITE_DATA(0x80);
/* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
LCD_WRITE_CMD(0xB1);
LCD_WRITE_DATA(0xB0);
LCD_WRITE_DATA(0x11);
/* Display Inversion Control (B4h) */
LCD_WRITE_CMD(0xB4);
LCD_WRITE_DATA(0x02);
/* Display Function Control (B6h) */
LCD_WRITE_CMD(0xB6);
LCD_WRITE_DATA(0x02);
LCD_WRITE_DATA(0x22);
/* Entry Mode Set (B7h) */
LCD_WRITE_CMD(0xB7);
LCD_WRITE_DATA(0xc6);
/* HS Lanes Control (BEh) */
LCD_WRITE_CMD(0xBE);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x04);
/* Set Image Function (E9h) */
LCD_WRITE_CMD(0xE9);
LCD_WRITE_DATA(0x00);
/* 设置屏幕方向和尺寸 */
LCD_SetDirection(LCD_DIRECTION);
/* Interface Pixel Format (3Ah) */
LCD_WRITE_CMD(0x3A);
LCD_WRITE_DATA(0x55);/* 0x55 : 16 bits/pixel */
/* Sleep Out (11h) */
LCD_WRITE_CMD(0x11);
LCD_DELAY(120*2000);
/* Display On */
LCD_WRITE_CMD(0x29);
}
void LCD_SetDirection( uint8_t ucOption )
{
/**
* Memory Access Control (36h)
* This command defines read/write scanning direction of the frame memory.
*
* These 3 bits control the direction from the MPU to memory write/read.
*
* Bit Symbol Name Description
* D7 MY Row Address Order -- 以X轴镜像
* D6 MX Column Address Order -- 以Y轴镜像
* D5 MV Row/Column Exchange -- X轴与Y轴交换
* D4 ML Vertical Refresh Order LCD vertical refresh direction control.
*
* D3 BGR RGB-BGR Order Color selector switch control
* (0 = RGB color filter panel, 1 = BGR color filter panel )
* D2 MH Horizontal Refresh ORDER LCD horizontal refreshing direction control.
* D1 X Reserved Reserved
* D0 X Reserved Reserved
*/
switch ( ucOption )
{
case 1:
// 左上角->右下角
// (0,0) ___ x(320)
// |
// |
// | y(480)
LCD_WRITE_CMD(0x36);
LCD_WRITE_DATA(0x08);
LCD_WRITE_CMD(0x2A);
LCD_WRITE_DATA(0x00); /* x start */
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01); /* x end */
LCD_WRITE_DATA(0x3F);
LCD_WRITE_CMD(0x2B);
LCD_WRITE_DATA(0x00); /* y start */
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01); /* y end */
LCD_WRITE_DATA(0xDF);
break;
case 2:
// 右上角-> 左下角
// y(320)___ (0,0)
// |
// |
// |x(480)
LCD_WRITE_CMD(0x36);
LCD_WRITE_DATA(0x68);
LCD_WRITE_CMD(0x2A);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01);
LCD_WRITE_DATA(0xDF);
LCD_WRITE_CMD(0x2B);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01);
LCD_WRITE_DATA(0x3F);
break;
case 3:
// 右下角->左上角
// |y(480)
// |
// x(320) ___|(0,0)
LCD_WRITE_CMD(0x36);
LCD_WRITE_DATA(0xC8);
LCD_WRITE_CMD(0x2A);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01);
LCD_WRITE_DATA(0x3F);
LCD_WRITE_CMD(0x2B);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01);
LCD_WRITE_DATA(0x3F);
break;
case 4:
// 左下角->右上角
// |x(480)
// |
// |___ y(320)
LCD_WRITE_CMD(0x36);
LCD_WRITE_DATA(0xA8);
LCD_WRITE_CMD(0x2A);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01);
LCD_WRITE_DATA(0xDF);
LCD_WRITE_CMD(0x2B);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x00);
LCD_WRITE_DATA(0x01);
LCD_WRITE_DATA(0x3F);
break;
}
/* 开始向GRAM写入数据 */
LCD_WRITE_CMD (0x2C);
}
/**
* 函数功能: 读取液晶模组ID
* 输入参数: 无
* 返 回 值: 液晶模组的ID
* 说 明:这是通过读取04H寄存器获取得到液晶模组ID,该ID值有液晶厂家编程,不同液晶
* 厂家的液晶模组得到的ID值可能不同。这也可以分辨不同型号的液晶模组。
*/
static uint32_t LCD_ReadID(void)
{
uint16_t buf[4];
LCD_WRITE_CMD(0x04);
buf[0] = LCD_READ_DATA(); // 第一个读取数据无效
buf[1] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
buf[2] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
buf[3] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
return (buf[1] << 16) + (buf[2] << 8) + buf[3];
}
/**
* 函数功能: 液晶模组初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
uint32_t BSP_LCD_Init(void)
{
LCD_GPIO_Config();
LCD_FSMC_Config();
lcd_id = LCD_ReadID();
if(lcd_id == 0x548066 || lcd_id == 0x8066)
{
ILI9488_REG_Config();
}
LCD_Clear(0,0,LCD_DEFAULT_WIDTH,LCD_DEFAULT_HEIGTH,BLACK);
LCD_DELAY(2000);
return lcd_id;
}
void LCD_Clear(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight,uint16_t usColor)
{
#if 0 /* 优化代码执行速度 */
uint32_t i;
uint32_t n,m;
/* 在LCD显示器上开辟一个窗口 */
LCD_OpenWindow(usX,usY,usWidth,usHeight);
/* 开始向GRAM写入数据 */
LCD_WRITE_CMD(0x2C);
m = usWidth * usHeight; //320 * 480
n = m / 8; // 320 * 480 / 8
m = m - 8 * n; // 320 * 480 - 320 * 480
for(i=0;i<n;i++)
{
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
LCD_WRITE_DATA(usColor);
}
for(i=0;i<m;i++)
{
LCD_WRITE_DATA(usColor);
}
#else
/* 在LCD显示器上开辟一个窗口 */
LCD_OpenWindow(usX,usY,usWidth,usHeight);
/* 在LCD显示器上以某一颜色填充像素点 */
LCD_FillColor(usWidth*usHeight, usColor);
#endif
}
//开窗函数
//该函数的作用是在LCD显示器上开辟一个窗口,该函数的参数有四个,
//从左到右的分别代表的含义为:
//在特定扫描方向下窗口的起点x坐标、
//起点Y坐标、窗口的宽度、窗口的高度。通过查看ILI9488手册可以找到
//Ox2A命令的含义是列地址控制命令,0x2B页(行)地址控制命令,两个命令都有
//四个参数,对应起点终点、高和低8位。
void LCD_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{
LCD_WRITE_CMD(0x2A); /* 设置X坐标 */
LCD_WRITE_DATA(usX>>8); /* 设置起始点:先高8位 */
LCD_WRITE_DATA(usX&0xff); /* 然后低8位 */
LCD_WRITE_DATA((usX+usWidth-1)>>8); /* 设置结束点:先高8位 */
LCD_WRITE_DATA((usX+usWidth-1)&0xff);/* 然后低8位 */
LCD_WRITE_CMD(0x2B); /* 设置Y坐标*/
LCD_WRITE_DATA(usY>>8); /* 设置起始点:先高8位 */
LCD_WRITE_DATA(usY&0xff); /* 然后低8位 */
LCD_WRITE_DATA((usY+usHeight-1)>>8); /* 设置结束点:先高8位 */
LCD_WRITE_DATA((usY+usHeight-1)&0xff);/* 然后低8位 */
}
//以某色素填充点
//主要使用Ox2C命令,
//本命令用于表示开始写入像素显示数据,紧
//跟着本命令后面的即为写入到GRAM的 RGB5:6:5的颜色数据,
static __inline void LCD_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
{
uint32_t i = 0;
/* 开始向GRAM写入数据 */
LCD_WRITE_CMD ( 0x2C );
for ( i = 0; i < ulAmout_Point; i ++ )
LCD_WRITE_DATA ( usColor );
}