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时钟宽度的外部信号。(速度不能太快)参见数据手册中电气特性部分的相关参数。

image.png
这张图可以很直观的看出中断和事件的区别,当外部有信号输入时,如果通过了事件屏蔽寄存器,那么事件信号就进入脉冲触发器,引发一个脉冲信号,直接传递给相应的外设,用于触发,这就是一个纯硬件的过程,这个方式不需要CPU参与,但是这也有它的缺点,如功能比较单一,仅能提供信号,不能提供信息,也就是只能产生指定功能的事件。如果通过中断屏蔽寄存器,就被直接送到CPU中,产生中断,如进入上面的入口函数开始处理。从这就可看出,事件是单纯硬件触发执行的过程,与CPU本身设计支持有关,而中断中则可以软件实现各种功能,而低功耗模式和事件唤醒就是stm32支持的事件之一。

1.3 如何实际编程使用外部中断

(1)时钟设置并打开相应GPIO模块时钟
(2)将相应GPIO配置为浮空输入
(3)NVIC设置
(4)将外部中断线和配套的GPIO进行连接映射
(5)外部中断线使能触发
(6)准备好ISR,并在ISR处等待执行中断程序即可

  1. typedef struct
  2. {
  3. uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
  4. //终端号 This parameter can be a value of @ref IRQn_Type
  5. (For the complete STM32 Devices IRQ Channels list, please
  6. refer to stm32f10x.h file) */
  7. uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
  8. //抢占优先级 specified in NVIC_IRQChannel. This parameter can be a value
  9. between 0 and 15 as described in the table @ref NVIC_Priority_Table */
  10. uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
  11. //次优先级 in NVIC_IRQChannel. This parameter can be a value
  12. between 0 and 15 as described in the table @ref NVIC_Priority_Table */
  13. FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
  14. //是否开启 will be enabled or disabled.
  15. This parameter can be set either to ENABLE or DISABLE */
  16. } NVIC_InitTypeDef;
  1. 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
  2. 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
  3. 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
  4. 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

数字小的优先级高
A B C D
抢占优先级 0 0 1 1
(相应)次优先级 0 1 0 1

先看抢占优先级
再看次优先级

人为设置抢占优先级和次级优先级

优先级组:4
选择合适的优先级组

  1. #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority 2^0 = 2
  2. 4 bits for subpriority */ 2^4 = 8
  3. #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
  4. 3 bits for subpriority */
  5. #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
  6. 2 bits for subpriority */
  7. #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
  8. 1 bits for subpriority */
  9. #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
  10. 0 bits for subpriority */

1.4 EXTI

  1. typedef struct
  2. {
  3. uint32_t EXTI_Line; //中断线 /*!< Specifies the EXTI lines to be enabled or disabled.
  4. This parameter can be any combination of @ref EXTI_Lines */
  5. EXTIMode_TypeDef EXTI_Mode; //中断模式 /*!< Specifies the mode for the EXTI lines.
  6. This parameter can be a value of @ref EXTIMode_TypeDef */
  7. EXTITrigger_TypeDef EXTI_Trigger;// 触发模式/*!< Specifies the trigger signal active edge for the EXTI lines.
  8. This parameter can be a value of @ref EXTIMode_TypeDef */
  9. FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
  10. This parameter can be set either to ENABLE or DISABLE */
  11. }EXTI_InitTypeDef;

Key1 ->PA0 配置为中断模式
key2 -> pC13

LED1->PG6
LED2->PG7

key1->LED1
PA0->PG6

因此选择EXTI0
image.png

代码如下:

  1. #include "stm32f10x.h"
  2. void KEY_Init(void);
  3. void LED_Init(void);
  4. void My_EXTI_Init(void);
  5. int main(void)
  6. {
  7. //起始代码 已经将时钟设置为72MHZ
  8. LED_Init();
  9. KEY_Init();
  10. My_EXTI_Init();
  11. //当我们按下按键 会运行中断函数
  12. while(1)
  13. {
  14. };
  15. }
  16. // key1->LED1
  17. // PA0 -> PG6
  18. void KEY_Init(void)
  19. {
  20. GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
  21. //GPIOA使能
  22. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  23. //配置第0个引脚为输入模式
  24. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  25. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空
  26. GPIO_Init(GPIOA, &GPIO_InitStructure);
  27. }
  28. //初始化LED IO
  29. void LED_Init(void)
  30. {
  31. GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
  32. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
  33. //配置第6个引脚为输出模式
  34. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  35. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  36. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  37. GPIO_Init(GPIOG,&GPIO_InitStructure);
  38. }
  39. void My_EXTI_Init(void)
  40. {
  41. NVIC_InitTypeDef NVIC_InitStructure; //NVIC
  42. EXTI_InitTypeDef EXTI_InitStructure; //EXTI结构体
  43. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  44. GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //选择A0管脚用作外部中断线路
  45. //抢占优先级 2个抢占优先级 8个次优先级
  46. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  47. NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
  48. //EXTI0 NVIC 配置
  49. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
  50. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
  51. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 中断使能
  52. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
  53. //EXTI配置
  54. EXTI_InitStructure.EXTI_Line = EXTI_Line0; //lin0 EXTI中断/事件线选择
  55. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  56. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
  57. EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  58. EXTI_Init(&EXTI_InitStructure); //初始化中断
  59. }
  60. //stm32f10x_it.c
  61. //中断处理函数 在这里进行处理
  62. void EXTI0_IRQHandler(void)
  63. {
  64. if(EXTI_GetITStatus(EXTI_Line0) != RESET)
  65. {
  66. GPIO_WriteBit(GPIOG, GPIO_Pin_6, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOG, GPIO_Pin_6)));
  67. EXTI_ClearITPendingBit(EXTI_Line0);
  68. }
  69. }

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片选
image.png
(2)接口本质:低层的时序+高层的类SRAM
(3)操作要求:只要按照8080接口时序,任何SoC任何方式都可以与LCD进行互操作

  • STM32的FSMC初步学习

(1)支持Nor/PSRAM(接口类似SRAM)、Nand、PCCard等多种类型外存
(2)FSMC为其支持的各种各个外存都提供了地址映射空间
image.png
(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个字节
对齐访问:
9. 中断体系和FSMC控制LCD - 图5

详细解释:
实质就是将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

  1. #include "stm32f10x.h"
  2. #include "lcd_driver.h"
  3. #include "stdlib.h"
  4. uint16_t color;
  5. static void delay(void)
  6. {
  7. uint32_t i,j;
  8. for(i=0;i<0x7FF;i++)
  9. for(j=0;j<0xFFF;j++);
  10. }
  11. int main(void)
  12. {
  13. //初始化LCD
  14. BSP_LCD_Init();
  15. //开启背光
  16. LCD_BK_ON();
  17. //设置颜色为蓝色
  18. LCD_Clear(0, 0, LCD_DEFAULT_WIDTH, LCD_DEFAULT_HEIGTH, BLUE);
  19. //随机颜色
  20. srand(0xffff);
  21. while(1)
  22. {
  23. //随机颜色
  24. color = rand();
  25. //设置颜色
  26. LCD_Clear(0,0,LCD_DEFAULT_WIDTH, LCD_DEFAULT_HEIGTH, color);
  27. //延时
  28. delay();
  29. };
  30. return 0;
  31. }
  1. #ifndef __LEC_DRIVER__H
  2. #define __LEC_DRIVER__H
  3. #include "stm32f10x.h"
  4. typedef enum
  5. {
  6. USB_FONT_16=16,
  7. USB_FONT_24=24,
  8. }USB_FONT_Typdef;
  9. #define IS_USB_FONT(FONT) (((FONT) == USB_FONT_16) || ((FONT) == USB_FONT_24))
  10. /******************************* ILI9488 显示屏的 FSMC 参数定义 ***************/
  11. #define FSMC_LCD_CMD ((uint32_t)0x6C000000) //FSMC_Bank1_NORSRAM1用于LCD命令操作的地址
  12. #define FSMC_LCD_DATA ((uint32_t)0x6C000002) //FSMC_Bank1_NORSRAM1用于LCD数据操作的地址
  13. #define LCD_WRITE_CMD(x) *(__IO uint16_t *)FSMC_LCD_CMD = x
  14. #define LCD_WRITE_DATA(x) *(__IO uint16_t *)FSMC_LCD_DATA = x
  15. #define LCD_READ_DATA() *(__IO uint16_t *)FSMC_LCD_DATA
  16. #define FSMC_LCD_BACKx FSMC_Bank1_NORSRAM4
  17. /************************* ILI9488 显示屏8080通讯引脚定义 *********************/
  18. //片选CS
  19. #define FSMC_LCD_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
  20. #define FSMC_LCD_CS_CLK RCC_APB2Periph_GPIOG
  21. #define FSMC_LCD_CS_PORT GPIOG
  22. #define FSMC_LCD_CS_PIN GPIO_Pin_12
  23. //DR->区分写命令还是写地址
  24. #define FSMC_LCD_DC_APBxClock_FUN RCC_APB2PeriphClockCmd
  25. #define FSMC_LCD_DC_CLK RCC_APB2Periph_GPIOF
  26. #define FSMC_LCD_DC_PORT GPIOF
  27. #define FSMC_LCD_DC_PIN GPIO_Pin_0
  28. //背光
  29. #define FSMC_LCD_BK_APBxClock_FUN RCC_APB2PeriphClockCmd
  30. #define FSMC_LCD_BK_CLK RCC_APB2Periph_GPIOF
  31. #define FSMC_LCD_BK_PORT GPIOF
  32. #define FSMC_LCD_BK_PIN GPIO_Pin_10
  33. #define LCD_BK_ON() GPIO_SetBits(FSMC_LCD_BK_PORT,FSMC_LCD_BK_PIN)
  34. #define LCD_BK_OFF() GPIO_ResetBits(FSMC_LCD_BK_PORT,FSMC_LCD_BK_PIN)
  35. /**************** 显示方向选择,可选(1,2,3,4)四个方向 *************************/
  36. //#define LCD_DIRECTION 1 // 原点在屏幕左上角 X*Y=320*480
  37. #define LCD_DIRECTION 2 // 原点在屏幕右上角 X*Y=480*320
  38. //#define LCD_DIRECTION 3 // 原点在屏幕右下角 X*Y=320*480
  39. //#define LCD_DIRECTION 4 // 原点在屏幕左下角 X*Y=480*320
  40. /******** ILI934 显示屏全屏默认(扫描方向为1时)最大宽度和最大高度*************/
  41. #if (LCD_DIRECTION==1)||(LCD_DIRECTION==3)
  42. #define LCD_DEFAULT_WIDTH 320 // X轴长度
  43. #define LCD_DEFAULT_HEIGTH 480 // Y轴长度
  44. #else
  45. #define LCD_DEFAULT_WIDTH 480 // X轴长度
  46. #define LCD_DEFAULT_HEIGTH 320 // Y轴长度
  47. #endif
  48. /******************************* 定义 ILI9488 显示屏常用颜色 ********************************/
  49. #define BACKGROUND WHITE //默认背景颜色
  50. #define WHITE 0xFFFF //白色
  51. #define BLACK 0x0000 //黑色
  52. #define GREY 0xF7DE //灰色
  53. #define BLUE 0x001F //蓝色
  54. #define RED 0xF800 //红色
  55. #define MAGENTA 0xF81F //红紫色,洋红色
  56. #define GREEN 0x07E0 //绿色
  57. #define CYAN 0x7FFF //蓝绿色,青色
  58. #define YELLOW 0xFFE0 //黄色
  59. #define BRED 0xF81F
  60. #define GRED 0xFFE0
  61. #define GBLUE 0x07FF
  62. void LCD_SetDirection( uint8_t ucOption );
  63. void LCD_Clear(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight,uint16_t usColor);
  64. void LCD_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight);
  65. uint32_t BSP_LCD_Init(void);
  66. static __inline void LCD_FillColor ( uint32_t ulAmout_Point, uint16_t usColor );
  67. #endif
  1. #include "lcd_driver.h"
  2. // 保存当前检查到的液晶模块ID
  3. volatile uint32_t lcd_id=0;
  4. //delay函数
  5. static void LCD_DELAY( __IO uint32_t nCount )
  6. {
  7. for ( ; nCount != 0; nCount -- );
  8. }
  9. //初始化使用的引脚
  10. static void LCD_GPIO_Config ( void )
  11. {
  12. GPIO_InitTypeDef GPIO_InitStructure;
  13. /* 使能复用IO时钟:复用为fsmc功能 */
  14. RCC_APB2PeriphClockCmd ( RCC_APB2Periph_AFIO, ENABLE );
  15. /* 使能FSMC对应相应管脚时钟 */
  16. RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE );
  17. FSMC_LCD_CS_APBxClock_FUN ( FSMC_LCD_CS_CLK, ENABLE ); // FSMC_LCD_CS_APBxClock_FUN = RCC_APB2PeriphClockCmd
  18. FSMC_LCD_DC_APBxClock_FUN ( FSMC_LCD_DC_CLK, ENABLE );
  19. FSMC_LCD_BK_APBxClock_FUN ( FSMC_LCD_BK_CLK, ENABLE );
  20. /* 配置FSMC相对应的数据线,FSMC-D0~D15: PD 14 15 0 1,PE 7 8 9 10 11 12 13 14 15,PD 8 9 10 */
  21. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  22. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 |
  24. GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;
  25. GPIO_Init ( GPIOD, & GPIO_InitStructure );
  26. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |
  27. GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 |
  28. GPIO_Pin_15;
  29. GPIO_Init ( GPIOE, & GPIO_InitStructure );
  30. /* 配置FSMC相对应的控制线
  31. * PD4-FSMC_NOE :LCD-RD
  32. * PD5-FSMC_NWE :LCD-WR
  33. * PG12-FSMC_NE4 :LCD-CS
  34. * PF0-FSMC_A0 :LCD-DC
  35. */
  36. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  37. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  38. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  39. GPIO_Init (GPIOD, & GPIO_InitStructure );
  40. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  41. GPIO_Init (GPIOD, & GPIO_InitStructure );
  42. GPIO_InitStructure.GPIO_Pin = FSMC_LCD_CS_PIN;
  43. GPIO_Init ( FSMC_LCD_CS_PORT, & GPIO_InitStructure );
  44. GPIO_InitStructure.GPIO_Pin = FSMC_LCD_DC_PIN;
  45. GPIO_Init ( FSMC_LCD_DC_PORT, & GPIO_InitStructure );
  46. /* 配置LCD背光控制管脚BK*/
  47. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  48. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  49. GPIO_InitStructure.GPIO_Pin = FSMC_LCD_BK_PIN;
  50. GPIO_Init ( FSMC_LCD_BK_PORT, & GPIO_InitStructure );
  51. /* 初始化时先不开背光 */
  52. GPIO_ResetBits(FSMC_LCD_BK_PORT,FSMC_LCD_BK_PIN);
  53. }
  54. //FSMC模式配置和时序
  55. static void LCD_FSMC_Config ( void )
  56. {
  57. FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
  58. FSMC_NORSRAMTimingInitTypeDef fsmc_lcd;
  59. /* 使能FSMC时钟*/
  60. RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_FSMC, ENABLE );
  61. fsmc_lcd.FSMC_AddressSetupTime = 0x02; //地址建立时间
  62. fsmc_lcd.FSMC_AddressHoldTime = 0x00; //地址保持时间
  63. fsmc_lcd.FSMC_DataSetupTime = 0x05; //数据建立时间
  64. fsmc_lcd.FSMC_BusTurnAroundDuration = 0x00;
  65. fsmc_lcd.FSMC_CLKDivision = 0x00;
  66. fsmc_lcd.FSMC_DataLatency = 0x00;
  67. fsmc_lcd.FSMC_AccessMode = FSMC_AccessMode_B; //模式B比较适用于LCD
  68. //数据手册有相关介绍
  69. FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_LCD_BACKx;
  70. FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
  71. //这里使用的是NOR来代替 其实和SARM都可以驱动
  72. FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR;
  73. FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
  74. //地址和数据复用使能关闭
  75. FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
  76. FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
  77. FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
  78. FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
  79. FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
  80. FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
  81. FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
  82. FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
  83. FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = & fsmc_lcd;
  84. FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = & fsmc_lcd;
  85. FSMC_NORSRAMInit ( & FSMC_NORSRAMInitStructure );
  86. /* 使能 FSMC_Bank1_NORSRAM4 */
  87. FSMC_NORSRAMCmd ( FSMC_LCD_BACKx, ENABLE );
  88. }
  89. //初始化LCD寄存器
  90. //设置了像素点格式、屏幕扫描方式、横屏竖屏等配置,由厂家提供
  91. static void ILI9488_REG_Config ( void )
  92. {
  93. //************* Start Initial Sequence **********//
  94. /* PGAMCTRL (Positive Gamma Control) (E0h) */
  95. LCD_WRITE_CMD(0xE0);
  96. LCD_WRITE_DATA(0x00);
  97. LCD_WRITE_DATA(0x07);
  98. LCD_WRITE_DATA(0x10);
  99. LCD_WRITE_DATA(0x09);
  100. LCD_WRITE_DATA(0x17);
  101. LCD_WRITE_DATA(0x0B);
  102. LCD_WRITE_DATA(0x41);
  103. LCD_WRITE_DATA(0x89);
  104. LCD_WRITE_DATA(0x4B);
  105. LCD_WRITE_DATA(0x0A);
  106. LCD_WRITE_DATA(0x0C);
  107. LCD_WRITE_DATA(0x0E);
  108. LCD_WRITE_DATA(0x18);
  109. LCD_WRITE_DATA(0x1B);
  110. LCD_WRITE_DATA(0x0F);
  111. /* NGAMCTRL (Negative Gamma Control) (E1h) */
  112. LCD_WRITE_CMD(0XE1);
  113. LCD_WRITE_DATA(0x00);
  114. LCD_WRITE_DATA(0x17);
  115. LCD_WRITE_DATA(0x1A);
  116. LCD_WRITE_DATA(0x04);
  117. LCD_WRITE_DATA(0x0E);
  118. LCD_WRITE_DATA(0x06);
  119. LCD_WRITE_DATA(0x2F);
  120. LCD_WRITE_DATA(0x45);
  121. LCD_WRITE_DATA(0x43);
  122. LCD_WRITE_DATA(0x02);
  123. LCD_WRITE_DATA(0x0A);
  124. LCD_WRITE_DATA(0x09);
  125. LCD_WRITE_DATA(0x32);
  126. LCD_WRITE_DATA(0x36);
  127. LCD_WRITE_DATA(0x0F);
  128. /* Adjust Control 3 (F7h) */
  129. LCD_WRITE_CMD(0XF7);
  130. LCD_WRITE_DATA(0xA9);
  131. LCD_WRITE_DATA(0x51);
  132. LCD_WRITE_DATA(0x2C);
  133. LCD_WRITE_DATA(0x82);/* DSI write DCS command, use loose packet RGB 666 */
  134. /* Power Control 1 (C0h) */
  135. LCD_WRITE_CMD(0xC0);
  136. LCD_WRITE_DATA(0x11);
  137. LCD_WRITE_DATA(0x09);
  138. /* Power Control 2 (C1h) */
  139. LCD_WRITE_CMD(0xC1);
  140. LCD_WRITE_DATA(0x41);
  141. /* VCOM Control (C5h) */
  142. LCD_WRITE_CMD(0XC5);
  143. LCD_WRITE_DATA(0x00);
  144. LCD_WRITE_DATA(0x0A);
  145. LCD_WRITE_DATA(0x80);
  146. /* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
  147. LCD_WRITE_CMD(0xB1);
  148. LCD_WRITE_DATA(0xB0);
  149. LCD_WRITE_DATA(0x11);
  150. /* Display Inversion Control (B4h) */
  151. LCD_WRITE_CMD(0xB4);
  152. LCD_WRITE_DATA(0x02);
  153. /* Display Function Control (B6h) */
  154. LCD_WRITE_CMD(0xB6);
  155. LCD_WRITE_DATA(0x02);
  156. LCD_WRITE_DATA(0x22);
  157. /* Entry Mode Set (B7h) */
  158. LCD_WRITE_CMD(0xB7);
  159. LCD_WRITE_DATA(0xc6);
  160. /* HS Lanes Control (BEh) */
  161. LCD_WRITE_CMD(0xBE);
  162. LCD_WRITE_DATA(0x00);
  163. LCD_WRITE_DATA(0x04);
  164. /* Set Image Function (E9h) */
  165. LCD_WRITE_CMD(0xE9);
  166. LCD_WRITE_DATA(0x00);
  167. /* 设置屏幕方向和尺寸 */
  168. LCD_SetDirection(LCD_DIRECTION);
  169. /* Interface Pixel Format (3Ah) */
  170. LCD_WRITE_CMD(0x3A);
  171. LCD_WRITE_DATA(0x55);/* 0x55 : 16 bits/pixel */
  172. /* Sleep Out (11h) */
  173. LCD_WRITE_CMD(0x11);
  174. LCD_DELAY(120*2000);
  175. /* Display On */
  176. LCD_WRITE_CMD(0x29);
  177. }
  178. void LCD_SetDirection( uint8_t ucOption )
  179. {
  180. /**
  181. * Memory Access Control (36h)
  182. * This command defines read/write scanning direction of the frame memory.
  183. *
  184. * These 3 bits control the direction from the MPU to memory write/read.
  185. *
  186. * Bit Symbol Name Description
  187. * D7 MY Row Address Order -- 以X轴镜像
  188. * D6 MX Column Address Order -- 以Y轴镜像
  189. * D5 MV Row/Column Exchange -- X轴与Y轴交换
  190. * D4 ML Vertical Refresh Order LCD vertical refresh direction control.
  191. *
  192. * D3 BGR RGB-BGR Order Color selector switch control
  193. * (0 = RGB color filter panel, 1 = BGR color filter panel )
  194. * D2 MH Horizontal Refresh ORDER LCD horizontal refreshing direction control.
  195. * D1 X Reserved Reserved
  196. * D0 X Reserved Reserved
  197. */
  198. switch ( ucOption )
  199. {
  200. case 1:
  201. // 左上角->右下角
  202. // (0,0) ___ x(320)
  203. // |
  204. // |
  205. // | y(480)
  206. LCD_WRITE_CMD(0x36);
  207. LCD_WRITE_DATA(0x08);
  208. LCD_WRITE_CMD(0x2A);
  209. LCD_WRITE_DATA(0x00); /* x start */
  210. LCD_WRITE_DATA(0x00);
  211. LCD_WRITE_DATA(0x01); /* x end */
  212. LCD_WRITE_DATA(0x3F);
  213. LCD_WRITE_CMD(0x2B);
  214. LCD_WRITE_DATA(0x00); /* y start */
  215. LCD_WRITE_DATA(0x00);
  216. LCD_WRITE_DATA(0x01); /* y end */
  217. LCD_WRITE_DATA(0xDF);
  218. break;
  219. case 2:
  220. // 右上角-> 左下角
  221. // y(320)___ (0,0)
  222. // |
  223. // |
  224. // |x(480)
  225. LCD_WRITE_CMD(0x36);
  226. LCD_WRITE_DATA(0x68);
  227. LCD_WRITE_CMD(0x2A);
  228. LCD_WRITE_DATA(0x00);
  229. LCD_WRITE_DATA(0x00);
  230. LCD_WRITE_DATA(0x01);
  231. LCD_WRITE_DATA(0xDF);
  232. LCD_WRITE_CMD(0x2B);
  233. LCD_WRITE_DATA(0x00);
  234. LCD_WRITE_DATA(0x00);
  235. LCD_WRITE_DATA(0x01);
  236. LCD_WRITE_DATA(0x3F);
  237. break;
  238. case 3:
  239. // 右下角->左上角
  240. // |y(480)
  241. // |
  242. // x(320) ___|(0,0)
  243. LCD_WRITE_CMD(0x36);
  244. LCD_WRITE_DATA(0xC8);
  245. LCD_WRITE_CMD(0x2A);
  246. LCD_WRITE_DATA(0x00);
  247. LCD_WRITE_DATA(0x00);
  248. LCD_WRITE_DATA(0x01);
  249. LCD_WRITE_DATA(0x3F);
  250. LCD_WRITE_CMD(0x2B);
  251. LCD_WRITE_DATA(0x00);
  252. LCD_WRITE_DATA(0x00);
  253. LCD_WRITE_DATA(0x01);
  254. LCD_WRITE_DATA(0x3F);
  255. break;
  256. case 4:
  257. // 左下角->右上角
  258. // |x(480)
  259. // |
  260. // |___ y(320)
  261. LCD_WRITE_CMD(0x36);
  262. LCD_WRITE_DATA(0xA8);
  263. LCD_WRITE_CMD(0x2A);
  264. LCD_WRITE_DATA(0x00);
  265. LCD_WRITE_DATA(0x00);
  266. LCD_WRITE_DATA(0x01);
  267. LCD_WRITE_DATA(0xDF);
  268. LCD_WRITE_CMD(0x2B);
  269. LCD_WRITE_DATA(0x00);
  270. LCD_WRITE_DATA(0x00);
  271. LCD_WRITE_DATA(0x01);
  272. LCD_WRITE_DATA(0x3F);
  273. break;
  274. }
  275. /* 开始向GRAM写入数据 */
  276. LCD_WRITE_CMD (0x2C);
  277. }
  278. /**
  279. * 函数功能: 读取液晶模组ID
  280. * 输入参数: 无
  281. * 返 回 值: 液晶模组的ID
  282. * 说 明:这是通过读取04H寄存器获取得到液晶模组ID,该ID值有液晶厂家编程,不同液晶
  283. * 厂家的液晶模组得到的ID值可能不同。这也可以分辨不同型号的液晶模组。
  284. */
  285. static uint32_t LCD_ReadID(void)
  286. {
  287. uint16_t buf[4];
  288. LCD_WRITE_CMD(0x04);
  289. buf[0] = LCD_READ_DATA(); // 第一个读取数据无效
  290. buf[1] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
  291. buf[2] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
  292. buf[3] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
  293. return (buf[1] << 16) + (buf[2] << 8) + buf[3];
  294. }
  295. /**
  296. * 函数功能: 液晶模组初始化
  297. * 输入参数: 无
  298. * 返 回 值: 无
  299. * 说 明:无
  300. */
  301. uint32_t BSP_LCD_Init(void)
  302. {
  303. LCD_GPIO_Config();
  304. LCD_FSMC_Config();
  305. lcd_id = LCD_ReadID();
  306. if(lcd_id == 0x548066 || lcd_id == 0x8066)
  307. {
  308. ILI9488_REG_Config();
  309. }
  310. LCD_Clear(0,0,LCD_DEFAULT_WIDTH,LCD_DEFAULT_HEIGTH,BLACK);
  311. LCD_DELAY(2000);
  312. return lcd_id;
  313. }
  314. void LCD_Clear(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight,uint16_t usColor)
  315. {
  316. #if 0 /* 优化代码执行速度 */
  317. uint32_t i;
  318. uint32_t n,m;
  319. /* 在LCD显示器上开辟一个窗口 */
  320. LCD_OpenWindow(usX,usY,usWidth,usHeight);
  321. /* 开始向GRAM写入数据 */
  322. LCD_WRITE_CMD(0x2C);
  323. m = usWidth * usHeight; //320 * 480
  324. n = m / 8; // 320 * 480 / 8
  325. m = m - 8 * n; // 320 * 480 - 320 * 480
  326. for(i=0;i<n;i++)
  327. {
  328. LCD_WRITE_DATA(usColor);
  329. LCD_WRITE_DATA(usColor);
  330. LCD_WRITE_DATA(usColor);
  331. LCD_WRITE_DATA(usColor);
  332. LCD_WRITE_DATA(usColor);
  333. LCD_WRITE_DATA(usColor);
  334. LCD_WRITE_DATA(usColor);
  335. LCD_WRITE_DATA(usColor);
  336. }
  337. for(i=0;i<m;i++)
  338. {
  339. LCD_WRITE_DATA(usColor);
  340. }
  341. #else
  342. /* 在LCD显示器上开辟一个窗口 */
  343. LCD_OpenWindow(usX,usY,usWidth,usHeight);
  344. /* 在LCD显示器上以某一颜色填充像素点 */
  345. LCD_FillColor(usWidth*usHeight, usColor);
  346. #endif
  347. }
  348. //开窗函数
  349. //该函数的作用是在LCD显示器上开辟一个窗口,该函数的参数有四个,
  350. //从左到右的分别代表的含义为:
  351. //在特定扫描方向下窗口的起点x坐标、
  352. //起点Y坐标、窗口的宽度、窗口的高度。通过查看ILI9488手册可以找到
  353. //Ox2A命令的含义是列地址控制命令,0x2B页(行)地址控制命令,两个命令都有
  354. //四个参数,对应起点终点、高和低8位。
  355. void LCD_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
  356. {
  357. LCD_WRITE_CMD(0x2A); /* 设置X坐标 */
  358. LCD_WRITE_DATA(usX>>8); /* 设置起始点:先高8位 */
  359. LCD_WRITE_DATA(usX&0xff); /* 然后低8位 */
  360. LCD_WRITE_DATA((usX+usWidth-1)>>8); /* 设置结束点:先高8位 */
  361. LCD_WRITE_DATA((usX+usWidth-1)&0xff);/* 然后低8位 */
  362. LCD_WRITE_CMD(0x2B); /* 设置Y坐标*/
  363. LCD_WRITE_DATA(usY>>8); /* 设置起始点:先高8位 */
  364. LCD_WRITE_DATA(usY&0xff); /* 然后低8位 */
  365. LCD_WRITE_DATA((usY+usHeight-1)>>8); /* 设置结束点:先高8位 */
  366. LCD_WRITE_DATA((usY+usHeight-1)&0xff);/* 然后低8位 */
  367. }
  368. //以某色素填充点
  369. //主要使用Ox2C命令,
  370. //本命令用于表示开始写入像素显示数据,紧
  371. //跟着本命令后面的即为写入到GRAM的 RGB5:6:5的颜色数据,
  372. static __inline void LCD_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
  373. {
  374. uint32_t i = 0;
  375. /* 开始向GRAM写入数据 */
  376. LCD_WRITE_CMD ( 0x2C );
  377. for ( i = 0; i < ulAmout_Point; i ++ )
  378. LCD_WRITE_DATA ( usColor );
  379. }