1. 程式人生 > >STM32學習筆記-FSMC驅動LCD

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