打印
[STM32F1]

【转】基于stm32f103zet6之使用FSMC驱动TFT的学习

[复制链接]
839|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

在完成IO驱动彩屏的试验后,就准备着手使用FSMC来驱动彩屏,先了解一下预备知识

一、所谓的FSMC机制

简单介绍FSMC在这篇博文里面很清楚,推荐一下 http://blog.csdn.net/king_bingge/article/details/8718566

然后还有就是这篇学习笔记,也还行 http://www.cnblogs.com/hduxyc/archive/2011/05/17/2048099.html

个人觉得有了这两篇博文再加上我们的参考手册足够搞定FSMC驱动彩屏了

二、FSMC之我见

开始只是谈到别人对FSMC的理解,注意这里只讨论FSMC控制TFT,也就是在FSMC的NOR\PSRAM模式控制LCD,所以我们以下的分析都是基于这种模式的。

1、我们之前通过使用GPIO来模拟8080/6800时序从而达到驱动彩屏的,同样需要明白的一点就是我们也只是使用FSMC来模拟8080/6800时序,只不过这个读写速度有些快(使用了总线嘛),仅此而已!如果不明白8080/6800时序是怎样的或许在这个文库里面能找到你想要的http://wenku.baidu.com/view/a8c98600cc1755270722083e.html

简单一点就是:8080是通过“读使能(RE)”和“写使能(WE)”两条控制线进行读写操作。  6800是通过“总使能(E)”和“读写选择(W/R)”两条控制线进行

2、那么了解到FSMC的三总线如下!

数据线:这个可以分为8位的和16位,这个不难理解,就是之指一次穿上红8位还是16位数据,我的是16位的,8位的有一个懒得用。

地址线:既然我们访问的外NOR FLASH,那么一定会有相应的地址线,那么这些地址线在哪里呢?肯定是通过GPIO引脚复用的。有A0 -- A23 24根,能够控制访问16M的空间,也就是一个子bank;

控制总线:它的控制总线只有三根:读使能信号,写使能信号,片选信号。所以这里和我们8080时序相比,少了复位信号线和数据/命令控制线,怎么办?继续看!

3、了解了FSMC会有这三总线的概念,那么接下来就是如何转化为我们需要的时序了。

对比一下FSMC访问外nor flash和8080访问时序如下



差别似乎很小是吧,简单说就是在数据/指令选择和复位信号上的区别。

4、在这里我们使用的软件方法来完善FSMC转化为8080的读写时序

在参考手册上的存储系统能找到,芯片留给我们外扩的存储器(NOR FLASH、PSRAM这类可直接寻址的器
件)地址是从0x60000000开始的,意思就是当我们访问0x60000000的时候,那就是相当于访问外部nor flash了(我们只讨论这种情况),那么他就会自动产生FSMC的时序

在这里,我们所需要添加的就是D/C选择信号,如何实现呢?我们是通过,一根地址线来实现的,当我们把A0多对应的GPIOF0(引脚默认复用)接到TFT的RS端,

然后执行访问0x60000000的指令,那么RS是否就是低电平选择为数据呢?又加入我们访问的地址是0x60000001的时候,那么RS是否就是高电平,从而选择的就是指令传送呢?答案当然是肯定的!所以我们就解决了这个问题,复位信号就更好解决了,直接和开发板的复位引脚接在一起就好了,就这么简单!

三、说了这么久的理论,来个实例分析更加形象了,首先硬件连线要明白

在原理图或者开发手册上面能够确定引脚复用问题

地址引脚:

(A0-A5 :PF0 - PF5)            (A6-A9: F12-F15 )                     (A10-A15:PG0-PG5)               

(A16-A18:PD11-PD13)             (A19-A22:PE3-PE6)               (A23-PE2)

片选信号(NEx:PG12)因为我选择的是block4

写使能(NWR:PD5)

读使能(NOE:PD4)

至此控制引脚基本完成

下面就是数据引脚:

PD14-FSMC-D0       ----LCD-DB0  
PD15-FSMC-D1      ----LCD-DB1
PD0-FSMC-D2         ----LCD-DB2
PD1-FSMC-D3         ----LCD-DB3
PE7-FSMC-D4         ----LCD-DB4
PE8-FSMC-D5         ----LCD-DB5
PE9-FSMC-D6         ----LCD-DB6
PE10-FSMC-D7       ----LCD-DB7
PE11-FSMC-D8         ----LCD-DB8  
PE12-FSMC-D9       ----LCD-DB9  
PE13-FSMC-D10       ----LCD-DB10
PE14-FSMC-D11         ----LCD-DB11  
PE15-FSMC-D12         ----LCD-DB12  
PD8-FSMC-D13       ----LCD-DB13         
PD9-FSMC-D14         ----LCD-DB14         
PD10-FSMC-D15        ----LCD-DB15

有了这些硬件连线是没有任何问题的


沙发
叫我女王思密达|  楼主 | 2016-11-11 20:56 | 只看该作者

四、正式分析程序

1、硬件引脚配置函数


[csharp] view plain copy
print?


  • void LCD_CtrlLinesConfig(void)  
  • {  
  •     GPIO_InitTypeDef GPIO_InitStructure;      
  •     /* Enable FSMC, GPIOD, GPIOE, GPIOF, GPIOG and AFIO clocks */  
  •     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);     //使能FSMC  
  •       
  •     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE |  
  •                          RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG |  
  •                          RCC_APB2Periph_AFIO, ENABLE);  
  •     //IO口复用功能时钟  
  •     /* Set 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) as alternate  
  •      function push pull */   
  •   
  •     /*D端口初始化*/  
  •     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);  
  •   
  •     /*E端口初始化*/  
  •     /* Set 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) as alternate function push pull */  
  •   
  •     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_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  •     GPIO_Init(GPIOE, &GPIO_InitStructure);                      //将配置写入GPIOE管脚  
  •   
  •     /*A0地址线*/  
  •     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  •     GPIO_Init(GPIOF, &GPIO_InitStructure);  
  •       
  •     /* Set PG.12(NE4 (LCD/CS)) as alternate function push pull - CE3(LCD /CS) */  
  •     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;  
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  •     GPIO_Init(GPIOG, &GPIO_InitStructure);  
  •       
  •       
  •     /*复位端口PE6*/  
  •     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
  •     GPIO_Init(GPIOE, &GPIO_InitStructure);  
  •       
  •       
  •     GPIO_ResetBits(GPIOE,GPIO_Pin_6);    //复位脚  
  •       
  •     DelayMs(50);       //延时50ms  
  •       
  •     GPIO_SetBits(GPIOE, GPIO_Pin_6);     //将复位脚拉高  
  • }  

细心观察,能够发现就是上面说的那些引脚嘛!

2、接下来的就是比较重要的FSMC的配置了

先上代码,然后慢慢分析吧


[csharp] view plain copy
print?


  • void LCD_FSMCConfig(void)  
  • {  
  •     FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;  
  •     FSMC_NORSRAMTimingInitTypeDef  p;  
  •       
  •     /*-- FSMC Configuration ------------------------------------------------------*/  
  •     /*----------------------- SRAM Bank 4 ----------------------------------------*/  
  •     /* FSMC_Bank1_NORSRAM4 configuration */  
  •     p.FSMC_AddressSetupTime = 0;  
  •     p.FSMC_AddressHoldTime = 0;  
  •     p.FSMC_DataSetupTime = 2;  
  •     p.FSMC_BusTurnAroundDuration = 0;  
  •     p.FSMC_CLKDivision = 0;  
  •     p.FSMC_DataLatency = 0;  
  •     p.FSMC_AccessMode = FSMC_AccessMode_A;  
  •       
  •     /*  
  •      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_AsyncWait = FSMC_AsyncWait_Disable;  
  •     FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;  
  •     FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;  
  •     FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;  
  •       
  •     FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);   
  •       
  •     /* BANK 4 (of NOR/SRAM Bank 1~4) is enabled */  
  •     FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);  
  • }  





使用特权

评论回复
板凳
叫我女王思密达|  楼主 | 2016-11-11 20:57 | 只看该作者
a、首先是时间参数的配置,我们在初始化的时候设置的bank4,所以这里对应的也是bank4,本函数主要使用了两种类型的结构体对FSMC进行配置,第一种
为 FSMC_NORSRAMInitTypeDef类型的结构体主要用于NOR FLASH的模式配置,包括存储器类型、数据宽度等。另一种的类型为FSMC_NORSRAMTimingInitTypeDef

首先是这个结构体FSMC_NORSRAMTimingInitTypeDef,找到他的定义:

成员变量有


[csharp] view plain copy
print?


  • uint32_t  FSMC_AccessMode   
  • uint32_t  FSMC_AddressHoldTime   
  • uint32_t  FSMC_AddressSetupTime   
  • uint32_t  FSMC_BusTurnAroundDuration   
  • uint32_t  FSMC_CLKDivision   
  • uint32_t  FSMC_DataLatency   
  • uint32_t  FSMC_DataSetupTime   


FSMC_AccessMode:Specifies the asynchronous access mode,用于同步模式,它的取值有以下几种,参考手册上面显示:模式A —— SRAM/PSRAM(CRAM) OE翻转,所以这里我们选择的是模式A

#define
FSMC_AccessMode_A   ((uint32_t)0x00000000)
#define
FSMC_AccessMode_B   ((uint32_t)0x10000000)
#define
FSMC_AccessMode_C   ((uint32_t)0x20000000)
#define
FSMC_AccessMode_D   ((uint32_t)0x30000000


FSMC_AddressHoldTime:Defines the number of HCLK cycles to configure the duration of the address hold time. This parameter can be a value between 0 and 0xF.地址保持的时钟周期!
FSMC_AddressSetupTime:Defines the number of HCLK cycles to configure the duration of the address setup time. This parameter can be a value between 0 and 0xF. 明显是地址建立飞时间周期
FSMC_BusTurnAroundDuration:Defines the number of HCLK cycles to configure the duration of the bus turnaround. This parameter can be a value between 0 and 0xF.这个应该是指总线翻转周期么,不是很了解
FSMC_CLKDivision:Defines the period of CLK clock output signal, expressed in number of HCLK cycles. This parameter can be a value between 1 and 0xF
明显是指HCLK的分频系数
FSMC_DataLatency:Defines the number of memory clock cycles to issue to the memory before getting the first data. The value of this parameter depends on the memory type as shown below:

  • It must be set to 0 in case of a CRAM
  • It is don't care in asynchronous NOR, SRAM or ROM accesses
  • It may assume a value between 0 and 0xF in NOR Flash memories with synchronous burst mode enable
:数据延迟时间注意了,这个可是有限制的,在我们控制LCD的时候倒是不用管它,应该是可以设为0的
FSMC_DataSetupTime:这个相应的就是数据的建立时间了

通过对比,发现上述配置是可行的,不过大家也可以按照要求更改。理论上,在速度要求不是很高的场合,大一点是没有关系的,但是下限得注意,具体是多少,我也走不知道,等以后再说吧!

然后就是看这个结构体了FSMC_NORSRAMInitStructure,成员如下:


uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t
uint32_t


似乎有些复杂,同时也说明了它的功能强大吧!


FSMC_AsynchronousWait:Enables or disables wait signal during asynchronous transfers, valid only with asynchronous Flash memories,明显就是使能等待同步信号否?
FSMC_Bank:这个应该是bank的选择吧,明显取值有以下几种:
#define
FSMC_Bank1_NORSRAM1   ((uint32_t)0x00000000)
#define
FSMC_Bank1_NORSRAM2   ((uint32_t)0x00000002)
#define
FSMC_Bank1_NORSRAM3   ((uint32_t)0x00000004)
#define
FSMC_Bank1_NORSRAM4   ((uint32_t)0x00000006)

FSMC_BurstAccessMode:Enables or disables the burst access mode for Flash memory, valid only with synchronous burst Flash memories.这个什么呢?不懂继续看吧!
FSMC_DataAddressMux:Specifies whether the address and data values are multiplexed on the databus or not,数据地址引脚是否复用,明显这里我们不需要!!
FSMC_ExtendedMode:Enables or disables the extended mode.,外扩模式否?应该是不用的
FSMC_MemoryDataWidth:位宽,我使用的是16位的TFT,所以应该是16,看看取值
果然:
#define
FSMC_MemoryDataWidth_16b   ((uint32_t)0x00000010)
#define
FSMC_MemoryDataWidth_8b   ((uint32_t)0x00000000)
FSMC_MemoryType:Specifies the type of external memory attached to the corresponding memory bank.意思应该是当我们外扩存储器的时候,分配哪一个bank吧,看取值。应该是0-3吧!看结果似乎有些问题,它是指外扩的存储器类型
#define
FSMC_MemoryType_NOR   ((uint32_t)0x00000008)
#define
FSMC_MemoryType_PSRAM   ((uint32_t)0x00000004)
#define
FSMC_MemoryType_SRAM   ((uint32_t)0x00000000

FSMC_WaitSignal:Enables or disables the wait-state insertion via wait signal等待信号,等待状态与否
FSMC_WaitSignalActive:Specifies if the wait signal is asserted by the memory one clock cycle before the wait state or during the wait state, valid only when accessing memories in burst mode,似乎有些搞混淆了,先这样继续看吧!
FSMC_WaitSignalPolarity:Specifies the wait signal polarity, valid only when accessing the Flash memory in burst mode,是指奇偶校验信号么?但是这个只需在flash的burst模式里面设置。
FSMC_WrapMode:Enables or disables the Wrapped burst access mode for Flash memory翻转模式?
FSMC_WriteBurst :Enables or disables the write burst operation写操作的
FSMC_WriteOperation:Enables or disables the write operation in the selected bank by the FSMC
FSMC_WriteTimingStruct
FSMC_ReadWriteTimingStruct还有两个这样的结构体!Timing Parameters for write and read access if the ExtendedMode is not used  ,当我们没有使用外扩模式的时候需要配置,显然这里需要配置的!
这两个结构体就是上面分析了的。所以我们只要执行这个语句,就能完成FSMC的配置了
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  

至此分析完毕!

使用特权

评论回复
地板
叫我女王思密达|  楼主 | 2016-11-11 20:58 | 只看该作者
然后就是简单的初始化TFT了
个人觉得有几点需要注意的地方,也总结一下!
关于使用16位宽的时候的地址的问题!
用的第17位,插16位的线
假如我们这样访问*(volatile unsigned short int *)(0x60020000)=val
这时候该地址的bit17位为1,因为我们使用16位宽模式,先看下面一张图


现在明白了我们的bit17对应的是FSMC的bit16,也就是A16了,所以我们的硬件连线应该是PA16了,也就是说HADDR
信号线是需要转换到外部存储器的内部AHB地址线,是字节地址。
给地址、将地址映射到外存储器(由HADDR的A25--A1映射到A24---A0)
注意点就是这个地方了,其他的时序问题和前面用IO模拟是差不多的,唯一的好处就是,我们有很多控制信号就不需要管了。最后贴上代码吧

[csharp] view plain copy
print?

  • /*******************************************************************************
  • * Function Name  : LCD_WriteReg
  • * Description    : Writes to the selected LCD register.
  • * Input          : - LCD_Reg: address of the selected register.
  • *                  - LCD_RegValue: value to write to the selected register.
  • * Output         : None
  • * Return         : None
  • *******************************************************************************/  
  • void LCD_WriteReg(unsigned char LCD_Reg,unsigned int LCD_RegValue)  
  • {  
  •     /* Write 16-bit Index, then Write Reg */  
  •     LCD->LCD_REG = LCD_Reg;                  //这里表示写寄存器  
  •     /* Write 16-bit Reg */                    
  •     LCD->LCD_RAM = LCD_RegValue;     //这里表示写寄存器的值(也就是数据)  
  • }  
  •   
  • /*******************************************************************************
  • * Function Name  : LCD_ReadReg
  • * Description    : Reads the selected LCD Register.
  • * Input          : None
  • * Output         : None
  • * Return         : LCD Register Value.
  • *******************************************************************************/  
  • u16 LCD_ReadReg(unsigned char LCD_Reg)  
  • {  
  •     /* Write 16-bit Index (then Read Reg) */  
  •     LCD->LCD_REG = LCD_Reg;              //这里表示先写寄存器  
  •     /* Read 16-bit Reg */  
  •     return (LCD->LCD_RAM);               //读取值  
  • }  
  •   
  • /*******************************************************************************
  • * Function Name  : LCD_WriteRAM_Prepare
  • * Description    : Prepare to write to the LCD RAM.
  • * Input          : None
  • * Output         : None
  • * Return         : None
  • *******************************************************************************/  
  • void LCD_WriteRAM_Prepare(void)  
  • {  
  •     LCD->LCD_REG = R34;  
  • }  
  •   
  • /*******************************************************************************
  • * Function Name  : LCD_WriteRAM
  • * Description    : Writes to the LCD RAM.
  • * Input          : - RGB_Code: the pixel color in RGB mode (5-6-5).
  • * Output         : None
  • * Return         : None
  • *******************************************************************************/  
  • void LCD_WriteRAM(u16 RGB_Code)                    
  • {  
  •     /* Write 16-bit GRAM Reg */  
  •     LCD->LCD_RAM = RGB_Code;  
  • }  
  •   
  • /*******************************************************************************
  • * Function Name  : LCD_ReadRAM
  • * Description    : Reads the LCD RAM.
  • * Input          : None
  • * Output         : None
  • * Return         : LCD RAM Value.
  • *******************************************************************************/  
  • unsigned int LCD_ReadRAM(void)  
  • {  
  •     unsigned int dummy;  
  •     /* Write 16-bit Index (then Read Reg) */  
  •     LCD->LCD_REG = R34; /* Select GRAM Reg */  
  •     /* Read 16-bit Reg */  
  •     dummy = LCD->LCD_RAM;   
  •     return dummy;  
  • }  
  •   
  • /*******************************************************************************
  • * Function Name  : LCD_SetCursor
  • * Description    : Sets the cursor position.
  • * Input          : - Xpos: specifies the X position.
  • *                  - Ypos: specifies the Y position.  
  • * Output         : None
  • * Return         : None
  • *******************************************************************************/  
  •   
  •   
  • void LCD_SetCursor(unsigned int Xpos, unsigned int Ypos)  
  • {  
  •     LCD_WriteReg(0x06,Ypos>>8);  
  •     LCD_WriteReg(0x07,Ypos);  
  •       
  •     LCD_WriteReg(0x02,Xpos>>8);  
  •     LCD_WriteReg(0x03,Xpos);   
  • }              


对比下之前的用IO驱动的方式,确实是少了一些控制信号!

使用特权

评论回复
5
dongnanxibei| | 2016-11-12 11:48 | 只看该作者
FSMC的功能实在是太强大了。

使用特权

评论回复
6
598330983| | 2016-11-12 21:26 | 只看该作者
LCD有如下控制线:
CS:Chip Select 片选,低电平有效
RS:Register Select 寄存器选择
WR:Write 写信号,低电平有效
RD:Read 读信号,低电平有效
RESET:重启信号,低电平有效
DB0-DB15:数据线
假如这些线,全部用普通IO口控制。根据LCD控制芯片手册(大部分控制芯片时序差不多):
如果情况如下:
DB0-DB15的IO全部为1(表示数据0xff),也可以为其他任意值,这里以0xff为例。
CS为0(表示选上芯片,CS拉低时,芯片对传入的数据才会有效)
RS为1(表示DB0-15上传递的是要被写到寄存器的值),如果为0,表示传递的是数据。
WR为0,RD为1(表示是写动作),反过来就是读动作。
RESET一直为高,如果RESET为低,会导致芯片重启。
这种情况,会导致一个值0xff被传入芯片,被LCD控制芯片当作写寄存器值去解析。LCD控制芯片收到DB0-15上的值之后,根据其他控制线的情况,它得出结论,这个0xff是用来设置寄存器的。一般情况下,LCD控制芯片会把传入的寄存器值的高8位当做寄存器地址(因为芯片内部肯定不止一个寄存器),低8位当做真正的要赋给对应寄存器值。这样,就完成了一个写LCD控制芯片内部寄存器的时序。
如果上述情况不变,只将RS置低,那么得到的情况如下:LCD控制芯片会把DB0-15上的数据当做单纯的数据值来处理。那么假如LCD处在画图状态,这个传入的值0xff,就会被显示到对应的点上,0xffff就表示白色,那么对应的点就是白色。在这个数据值传递过来之前,程序肯定会通过设置寄存器值,告诉LCD控制芯片要写的点的位置在哪里。
如果上述两种情况都不变,分别把WR和RD的信号反过来(WR=1,RD=0),那么写信号就会被变成读信号。读信号下,主控芯片需要去读DB0-15的值,而LCD控制芯片就会去设置DB0-15的值,从而完成读数据的时序。
读寄存器的时序麻烦一点。第一步,先要将WR和RD都置低,主控芯片通过DB0-15传入寄存器地址。第二步就和前面读数据一样,将WR置高,RD置低,读出DB0-15的值即可。在这整个的过程中,RS一直为低。
好了,上面就是IO直接控制LCD的方法。假如放到STM32里面,用IO直接控制显得效率很低。STM32有FSMC(其实其他芯片基本都有类似的总线功能),FSMC的好处就是你一旦设置好之后,WR、RD、DB0-DB15这些控制线和数据线,都是FSMC自动控制的。打个比方,当你在程序中写到:
*(volatile unsigned short int *)(0x60000000)=val;
那么FSMC就会自动执行一个写的操作,其对应的主控芯片的WE、RD这些脚,就会呈现出写的时序出来(即WE=0,RD=1),数据val的值也会通过DB0-15自动呈现出来(即FSMC-D0:FSMC-D15=val)。地址0x60000000会被呈现在数据线上(即A0-A25=0,地址线的对应最麻烦,要根据具体情况来,好好看看FSMC手册)。
那么在硬件上面,我们需要做的,仅仅是MCU和LCD控制芯片的连接关系:
WE-WR,均为低电平有效
RD-RD,均为低电平有效
FSMC-D0-15接LCD DB0-15
连接好之后,读写时序都会被FSMC自动完成。但是还有一个很关键的问题,就是RS没有接,CS没有接。因为在FSMC里面,根本就没有对应RS和CS的脚。怎么办呢?这个时候,有一个好方法,就是用某一根地址线来接RS。比如我们选择了A16这根地址线来接,那么当我们要写寄存器的时候,我们需要RS,也就是A16置高。软件中怎么做呢?也就是将FSMC要写的地址改成0x60020000,如下:
*(volatile unsigned short int *)(0x60020000)=val;
这个时候,A16在执行其他FSMC的同时会被拉高,因为A0-A18要呈现出地址0x60020000。0x60020000里面的Bit17=1,就会导致A16为1。
当要读数据时,地址由0x60020000改为了0x60000000,这个时候A16就为0了。
那么有朋友就会有疑问,第一,为什么地址是0x6xxxxxxx而不是0x0xxxxxxx;第二,CS怎么接;第三,为什么Bit17对应A16?
先来看前两个问题,大家找到STM32的FSMC手册,在FSMC手册里面,我们很容易找到,FSMC将0x60000000-0x6fffffff的地址用作NOR/PRAM(共256M地址范围)。而这个存储块,又被分成了四部分,每部分64M地址范围。当对其中某个存储块进行读写时,对应的NEx就会置低。这里,就解决了我们两个问题,第一,LCD的操作时序,和NOR/PRAM是一样的(为什么一样自己找找NOR/PRAM的时序看看),所以我们选择0x6xxxxxxx这个地址范围(选择这个地址范围,操作这个地址时,FSMC就会呈现出NOR/PRAM的时序)。第二,我们可以将NEx连接到LCD的CS,只要我们操作的地址是第一个存储块内即可(即0-0x3ffffff地址范围)。
第三个问题再来看一看FSMC手册关于存储器字宽的描述,我们发现,当外部存储器是16位时,硬件管脚A0-A24表示的是地址线A1-A25的值,所以我们要位移一下,Bit17的值,实际会被反应到A16这根IO来。关于数据宽度及位移的问题,初学的朋友可能会比较疑惑,当你接触了多NOR/PRAM这样的器件后,你会发现,很多芯片的总线,都是这样设计的,为的是节省地址线。
那么上面就完全解决了LCD驱动如何接FSMC的问题,如果读者没懂,建议将上述文字抄上一遍,FSMC手册对应NOR/PRAM的章节抄一遍。还没懂,就继续抄一遍,抄到懂为止。
虽然上述只是针对LCD讲解了FSMC,但是其实对NOR和外部RAM的操作也是类似的,只不过多了些地址线来寻址而已。--By YuanYin.

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

51

主题

81

帖子

1

粉丝