STM32學習筆記-FSMC驅動LCD
文章目錄
一、FSMC原理
大容量,且引腳數在 100 腳以上的 STM32F103 晶片都帶有 FSMC 介面。
FSMC,即靈活的靜態儲存控制器,能夠與同步或非同步儲存器和 16 位 PC 儲存器卡連線, 介面支援包括 SRAM、 NAND FLASH、 NOR FLASH 和 PSRAM 等儲存器。
1.為什麼可以用FSMC驅動LCD?
外部 SRAM 的控制一般有:地址線(如 A0~A18)、資料線(如 D0~D15)、寫訊號(WE)、讀訊號(OE)、片選訊號(CS),如果 SRAM 支援位元組控制,那麼還有 UB/LB 訊號。而 TFTLCD包括: RS、 D0~D15、 WR、 RD、 CS、 RST 和 BL 等,其中真正在操作 LCD 的時候需要用到的就只有: RS、 D0~D15、 WR、 RD 和 CS。其操作時序和 SRAM的控制完全類似,唯一不同就是 TFTLCD 有 RS 訊號,但是沒有地址訊號。
而RS訊號是用來控制LCD是寫命令還是寫資料的,本質上可以理解為一個地址,我們可以將RS訊號接在FSMC的地址線上,可以選任意一根地址線,但注意接在不同地址線,地址的值會不同,後面會具體講到。
這樣資料引腳,地址引腳,寫訊號(WE)、讀訊號(OE)、片選訊號(CS)都可以和LCD對應腳連線起來,寫程式時,這些引腳初始化時,複用FSMC輸出即可。LCD中還有RST,BL,接微控制器的普通IO口即可。
2.定址問題
FSMC 總共管理 1GB 空間,擁有 4 個儲存塊(Bank),BANK1是屬於NOR/PSRAM,BANK2、BANK3屬於NAND,BANK4屬於PC卡。
STM32 的 FSMC 儲存塊 1(Bank1)被分為 4 個區,每個區管理 64M 位元組空間,每個區都有獨立的暫存器對所連線的儲存器進行配置。 Bank1 的 256M 位元組空間由 28 根地址線(HADDR[27:0])定址。這裡 HADDR 是內部 AHB 地址 匯流排 ,其 中 HADDR[25:0] 來自外部儲存器地 址FSMC_A[25:0],而 HADDR[26:27]對 4 個區進行定址。
Bank1 所選區 | 片選訊號 | 地址範圍 |
---|---|---|
第一區 | FSMC_NE1 | 0X6000,0000~63FF,FFFF |
第二區 | FSMC_NE2 | 0X6400,0000~67FF,FFFF |
第三區 | FSMC_NE3 | 0X6800,0000~6BFF,FFFF |
第四區 | FSMC_NE4 | 0X6C00,0000~6FFF,FFFF |
那麼為什麼是這個地址呢?計算方法也很簡單,之前說過,HADDR[27:26]是對4個區進行定址,顯然,00對應第一區,01對應第二區,10對應第三區,11對應第四區,注意是26位和27位。而最高4位為“6”,如果是BANK2便是“7”,BANK3便是8,BANK4,便是9.
我們要特別注意 HADDR[25:0]的對應關係:
當 Bank1 接的是 16 位寬度儲存器的時候:
HADDR[25:1]->FSMC_A[24:0]。
當 Bank1 接的是 8 位寬度儲存器的時候: HADDR[25:0]>FSMC_A[25:0]。
很多TFTLCD使用的是 16 位資料寬度,所以 HADDR[0]並沒有用到,只有 HADDR[25:1]是有效的,對應關
系變為: HADDR[25:1]-> FSMC_A[24:0],相當於右移了一位,這裡請大家特別留意。
3.時序模式
FSMC 的 NOR FLASH 控制器支援同步和非同步突發兩種訪問方式。選用同步突發訪問方式時, FSMC 將 HCLK(系統時鐘)分頻後,傳送給外部儲存器作為同步時鐘訊號 FSMC_CLK。
對於非同步突發訪問方式, FSMC 主要設定 3 個時間引數:地址建立時間(ADDSET)、資料建立時間(DATAST)和地址保持時間(ADDHLD)。 FSMC 綜合了 SRAM/ROM、 PSRAM 和 NOR Flash 產品的訊號特點,定義了 4 種不同的非同步時序模型。選用不同的時序模型時,需要設定不同的時序引數
時序模型 | 簡單描述 | 時間引數 |
---|---|---|
Mode1 | SRAM/CRAM 時序 | DATAST、 ADDSET |
ModeA | SRAM/CRAM OE 選通型時序 | DATAST、 ADDSET |
Mode2/B | NOR FLASH 時序 | DATAST、 ADDSET |
ModeC | NOR FLASH OE 選通型時序 | DATAST、 ADDSET |
ModeD | 延長地址保持時間的非同步時序 | DATAST、 ADDSET、 ADDHLK |
我用的是非同步突發模式,模式 A 支援獨立的讀寫時序控制,這個對我們驅動 TFTLCD 來說非常有用,因為 TFTLCD在讀的時候,一般比較慢,而在寫的時候可以比較快,如果讀寫用一樣的時序,那麼只能以讀的時序為基準,從而導致寫的速度變慢,或者在讀資料的時候,重新配置 FSMC 的延時,在讀操作完成的時候,再配置回寫的時序,這樣雖然也不會降低寫的速度,但是頻繁配置,比較麻煩。而如果有獨立的讀寫時序控制,那麼我們只要初始化的時候配置好,之後就不用再配置,既可以滿足速度要求,又不需要頻繁改配置。
當然,我也試了一下模式B。
二、程式碼部分
該專案基於STM32F103
#define Bank1_LCD_D ((u32)0x6C010000) // 寫資料對應地址
#define Bank1_LCD_C ((u32)0x6C000000) // 寫命令對應地址
#define Set_Rst GPIOA->BSRR = GPIO_Pin_9;
#define Clr_Rst GPIOA->BRR = GPIO_Pin_9;
#define Lcd_Light_ON GPIOB->BSRR = GPIO_Pin_11;
#define Lcd_Light_OFF GPIOB->BRR = GPIO_Pin_11;
需要說明的是,我的專案選用的是BANK1的NE4,即第四區,LCD的RS引腳接的是PA15,因此地址從0x6C000000開始,而PA15置1為寫資料,置0為寫命令。注意,PA15對應暫存器D16位。因此寫資料對應地址0x6C010000.
如果LCD的RS引腳接的是PA16,則該引腳對應暫存器的D17位,那麼寫資料的地址變為0x6C020000,需要換成二進位制計算,在此不在贅述。
/*
* 函式名:LCD_GPIO_Config
* 描述 :根據FSMC配置LCD的I/O
* 輸入 :無
* 輸出 :無
* 呼叫 :內部呼叫
*/
void LCD_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable the FSMC Clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);
/* config lcd gpio clock base on FSMC */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |
RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE| RCC_APB2Periph_GPIOG , ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* config tft rst gpio */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* config tft back_light gpio base on the PT4101 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* config tft data lines base on FSMC
* data lines,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);
/* config tft control lines base on FSMC
* PD4-FSMC_NOE :LCD-RD
* PD5-FSMC_NWE :LCD-WR
* PG12-FSMC_NE4 :LCD-CS
* PG5-FSMC_A15 :LCD-DC
*/
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 = GPIO_Pin_12;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 ;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* tft control gpio init */
GPIO_SetBits(GPIOA, GPIO_Pin_9); // RST = 1
GPIO_SetBits(GPIOD, GPIO_Pin_4); // RD = 1
GPIO_SetBits(GPIOD, GPIO_Pin_5); // WR = 1
GPIO_SetBits(GPIOG, GPIO_Pin_12); // CS = 1
這是引腳初始化程式碼,FSMC部分資料引腳大部分相同,初始化複用為FSMC模式,讀寫資料,片選等引腳根據原理圖進行配置,也複用為FSMC,特別注意LCD的RS引腳接FSMC的地址線,這個在上面也重點強調了。
/*
* 函式名:LCD_FSMC_Config
* 描述 :LCD FSMC 模式配置
* 輸入 :無
* 輸出 :無
* 呼叫 :內部呼叫
*/
static void LCD_FSMC_Config(void)
{
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef p;
p.FSMC_AddressSetupTime = 0x02; //地址建立時間
p.FSMC_AddressHoldTime = 0x00; //地址保持時間
p.FSMC_DataSetupTime = 0x05; //資料建立時間
p.FSMC_BusTurnAroundDuration = 0x00;
p.FSMC_CLKDivision = 0x00;
p.FSMC_DataLatency = 0x00;
//norflash一般用模式B,SRAM一般用模式A,和時序有關
p.FSMC_AccessMode = FSMC_AccessMode_B; // 一般使用模式B來控制LCD
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; //使用NE4
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; //不復用資料地址
FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR; //設定為NOR型別
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; //儲存器資料寬度為16bit
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 = &p;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);
/* Enable FSMC Bank1_SRAM Bank */
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); //使能BNAK1,NE4
}
這是FSMC的配置程式碼,用的是非同步模式B,讀寫時序相同,具體可以看備註
以下是使用模式A(參考正點原子程式碼)
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
FSMC_NORSRAMTimingInitTypeDef writeTiming;
readWriteTiming.FSMC_AddressSetupTime = 0x01; //地址建立時間(ADDSET)為2個HCLK 1/36M=27ns
readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持時間(ADDHLD)模式A未用到
readWriteTiming.FSMC_DataSetupTime = 0x0f; // 資料儲存時間為16個HCLK,因為液晶驅動IC的讀資料的時候,速度不能太快,尤其對1289這個IC。
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
readWriteTiming.FSMC_CLKDivision = 0x00;
readWriteTiming.FSMC_DataLatency = 0x00;
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
writeTiming.FSMC_AddressSetupTime = 0x00; //地址建立時間(ADDSET)為1個HCLK
writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持時間(A
writeTiming.FSMC_DataSetupTime = 0x03; ////資料儲存時間為4個HCLK
writeTiming.FSMC_BusTurnAroundDuration = 0x00;
writeTiming.FSMC_CLKDivision = 0x00;
writeTiming.FSMC_DataLatency = 0x00;
writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;// 這裡我們使用NE4 ,也就對應BTCR[6],[7]。
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不復用資料地址
FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM; //SRAM
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//儲存器資料寬度為16bit
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
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_Enable; // 讀寫使用不同的時序
FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; //讀寫時序
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming; //寫時序
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); //初始化FSMC配置
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); // 使能BANK1