Stm32FSMC及TFTLED屏笔记
截图的图片出自正点原子的参考手册和网络资料,如有侵权,请联系我删除(因为没怎么写过博客,有注明出处,但对版权的具体细节不清楚)
1.FSMC*(Flexiable Static Memory Controler)*
1)FSMC的应用:
-
以上是STM32官方手册的内容,可以认为,FSMC,是芯片内部专门可以用于读取外部接入的(SRAM,PC卡等)需要并口协议读写的存储器的一个控制器,起到一个连接器(或者是桥)的作用。 只要我们按芯片要求将外部SRAM等于GPIO连接好,并配置好FSMC的相关配置,就可以给这些SRAM配置一个向内部寄存器那样的地址,就可以直接向SRAM的具体地址写入数据(软件上和寄存器操作一样,直接给对应地址赋值,但实际是通过并口数据线将数据写入)这样就可以是我们对外部SRAM的使用效率提升,而不用再使用GPIO去模拟对应的时序*(可以直接向内部寄存器一样赋值)*。
- 从上面的框图也可以看出,因为并口信号线FSMC[15:0]是公用的,所以一次只能同时外接一个SRAM类的存储器。
- 如上图,实际上,是内部连接的AHB将FSMC可控制的寄存器区域划分为以下这四个,其中NOR/PSRAM内再划分为Block1-4四块。FSMC在这片区域寻址(这片区域的地址在配置完成后,将会称为外设的地址 即把外设地址命名为这些地址)时,使用HADDR地址线(共有28位[27:0]),HADDR[27:26]两位决定NOR/PSRAM内部的的Block1-4(00-1;01-2;10-3;11-4)。
2)NOR/SRAM:
(1)映象部分:
-
值得注意的是在外部SRAM连接的FSMC_A在存储器位数不同时,地址线的对齐方式不同。 因为在内部AHB的地址里(即HADDR[25:0])是以字节为单位的,即一个地址可以写8位数据; 当外部是以半字(16位)为单位时,这时候每当在外部地址写完一次(16位数据)的过程中,内部AHB对应的地址应该是移动两位的(2*8位);所以为了使用方便,这儿有一个很巧妙(也很常用)的方法就是将HADDR[25:0]左移一位与FSMC_A对齐;
-
可以看出刚好是左移一位对齐,而且000 位仍有对应 000位地址,即起始地址仍一样。
(2)信号线及8080并口协议:
-
以下将SRAM常用的8080并口协议的连接线与FSMC的输出信号做些整理 (参考博文,如侵权请联系)
SRAM*(以IS62WV51216为例)* | FSMC | 说明 |
---|
CS | NE[x] | 片选,低电平表示选中。 | OE | NOE | 输出使能,即(对主机MCU来说)读使能 | WE | NWE | 写使能 | UB/LB | NBL[1]/NBL[0] | 高位/地位数据掩码(是否允许访问高8位/低8位数据,低电平允许) | A0-A18 | A[25:0] | 地址总线 | I/O0-7;I/O8-15 | D[15:0] | 双向数据线(可选8位或16位) |
-
8080并口协议时序:(这里只做简单介绍) 在CS选中后,地址线发送对应地址信息表示该地址;(结束时拉高CS)
-
读时;NWE拉高,禁止写;后拉低NOE写使能,SRAM数据放于数据口上,NOE拉高,上升沿数据读出; -
写时;NOE拉高,禁止读;后拉低NWE写使能,主机MCU数据放于数据口上,NWE拉高,上升沿数据写入; -
当只需要高8位/低8位用于数据传输时,可使用地址掩码,NBL[1:0]进行控制,低电平表示允许访问。 -
而在FMSC内当我们按照要求连接好硬件电路后,可以直接向写读内部寄存器一样,直接写 ”xxxx*(地址)* = 0xyyyyy (数据) “ 即可。FSMC会自动帮我们完成上述对各种信号线的操作;
(3)硬件连接:
(FSMC只有大容量,多引脚封装的stm32有,我用的是STM32ZET6)具体的引脚连接可参照下图、下表,或者上面的参考博客
(4)寄存器:
主要有FSMC_BCRx*(片选控制寄存器),FSMC_BTRx(读时序控制寄存器),FSMC——BWTRx(写时序控制寄存器)*
按如下组合进行访问:
(5)库函数使用:(见如下代码及注释,参考正点原子)
void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct);
typedef struct
{
uint32_t FSMC_Bank;
uint32_t FSMC_DataAddressMux;
uint32_t FSMC_MemoryType;
uint32_t FSMC_MemoryDataWidth;
uint32_t FSMC_BurstAccessMode;
uint32_t FSMC_AsynchronousWait;
uint32_t FSMC_WaitSignalPolarity;
uint32_t FSMC_WrapMode;
uint32_t FSMC_WaitSignalActive;
uint32_t FSMC_WriteOperation;
uint32_t FSMC_WaitSignal;
uint32_t FSMC_ExtendedMode;
uint32_t FSMC_WriteBurst;
FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct;
FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;
} FSMC_NORSRAMInitTypeDef;
关于读写时序的结构体参数如下*(即上面结构体的最后两个成员变量)*:
typedef struct
{
uint32_t FSMC_AddressSetupTime;
uint32_t FSMC_AddressHoldTime;
uint32_t FSMC_DataSetupTime;
uint32_t FSMC_BusTurnAroundDuration;
uint32_t FSMC_CLKDivision;
uint32_t FSMC_DataLatency;
uint32_t FSMC_AccessMode;
}FSMC_NORSRAMTimingInitTypeDef;
FSMC的使能函数如下:(较简单)
void FSMC_NORSRAMCmd(uint32_t FSMC_Bank, FunctionalState NewState);
void FSMC_NANDCmd(uint32_t FSMC_Bank, FunctionalState NewState);
void FSMC_PCCARDCmd(FunctionalState NewState);
2.TFTLED屏(ILI9341驱动)
1)ILI9341驱动:
(1)接口时序:8080并口:
接口线 | 作用简介 |
---|
CS | 片选 | WR | 写使能 | RD | 读使能· | D[15:0] | 16位双向数据线 | RST | 硬件复位线 | RS/DCX | 数据1*(写入9341的SRAM)/命令0(写入9341的寄存器)* 控制线 |
在接线上,原子用的方法比较巧妙;
-
将RST复位接在系统的复位上; -
将RS线接在FSMC的任意一个地址线上(A10上)如下: typedef struct
{
vu16 LCD_REG;
vu16 LCD_RAM;
} LCD_TypeDef;
#define LCD_BASE ((u32)(0x6C000000 | 0x000007FE))
#define LCD ((LCD_TypeDef *) LCD_BASE)
-
利用结构体地址自增的特性: 将LCD这个结构体定义做LCD_BASE*(A10)的地址*;***(注意:16位数据传输时,将外设地址右移一对齐位,所以实际是 HADDR[11] 位)***,这样: LCD_REG == 0x6C0007FE ,A10位为0(7为0111); LCD_RAM == 0x6C000800,A10位为1(8为1000)这里的A10均指外设的A10即FSMC_A[10]. 如此在,对地址LCD_REG = 0xyyyy*(数据)时就是,写入命令(写道9341的寄存器中)(因为此时,RS=0)*; 同理,对地址LCD_RAM = 0xyyyy*(数据)时就是,写入数据(写到9341的SRAM上)(因为此时,RS=1)*。
2)ILI9341的部分命令:
-
0xD3:读ID. -
0x36:控制GRAM的指针增长方向。 -
0x2A:设置列坐标,(即x轴横坐标) 0x2B:与2A一样,设置的是行坐标(即y轴纵坐标) 注:以下的SC可以理解为 Start Column;EC可以理解为Ending Column. -
0x2C:填入颜色数据(RGB565格式) RGB565格式如下: 同时,我们从高亮那部分可以得知,为什么我们之前法横纵坐标的数据的时候,每个坐标(如SC)必须用16位(为了兼容足够大的屏幕),却被拆成两个8位来发的原因。 -
0x2E:读GRAM,即读处对应点的颜色:
3)原子代码使用:
原子帮我们将许多底层的代码都封装好了,但在使用时有一些注意事项:
-
使用时,需先执行LCD_Init();函数进行初始化; -
**因为LCD_Init()函数里默认将芯片信号printf到串口1输出;所以在执行LCD_Init()初始化函数前,需先执行串口初始化uart_init();或者直接将LCD_Init()内的printf();注释掉,否则程序会卡死在printf中 ** -
将原子的LCD.h中可以调用的函数整理在这儿,方便自己查找: void LCD_Init(void);
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_Clear(u16 Color);
void LCD_SetCursor(u16 Xpos, u16 Ypos);
void LCD_DrawPoint(u16 x,u16 y);
void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color);
u16 LCD_ReadPoint(u16 x,u16 y);
void LCD_Draw_Circle(u16 x0,u16 y0,u8 r);
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p);
|