TFTLCD
TDTLCD即薄膜晶體管液晶顯示器,在液晶顯示屏每個像素上都設置有一個薄膜晶體管(TFT),圖像質量高
一般TFTLCD模塊位3.3V供電,不支持5V電壓MCU,如果使用5V MCU需在信號線串接120R電阻使用
LCD使用16位80並口驅動,與OLED並口驅動類似
電容觸摸模塊使用SPI串口驅動
採用廠商提供配置文件或參考數據手冊即可完成LCD和觸摸屏的驅動配置
LCD驅動流程
- 硬復位
- 發送初始化序列(按照廠家提供設置)
- 設置座標
- 寫點或讀點
- 寫點步驟:
- 寫GRAM指令
- 寫入顏色數據
- LCD顯示點
- 讀點步驟
- 讀GRAM指令
- 讀出顏色數據
- 輸出數據給單片機處理
RGB565格式
LCD模塊對外採用16位80並口,顏色深度為16為,格式為RGB565
對應關係如下:5位R、6位G、5位B——RGB565
|
數據線
|
D15
|
D14
|
D13
|
D12
|
D11
|
D10
|
D9
|
D8
|
D7
|
D6
|
D5
|
D4
|
D3
|
D2
|
D1
|
D0
|
|
LCD GRAM
|
R[4]
|
R[3]
|
R[2]
|
R[1]
|
R[0]
|
G[5]
|
G[4]
|
G[3]
|
G[2]
|
G[1]
|
G[0]
|
B[4]
|
B[3]
|
B[2]
|
B[1]
|
B[0]
|
重點代碼
font.h
//這裏省略
//可利用字符點陣畫圖軟件或已有字庫導出對應字符的點陣集
//應#include到lcd.c文件中
lcd.h
#ifndef __LCD_H
#define __LCD_H
#include "sys.h"
#include "stdlib.h"
/*-----------------LCD重要參數集定義----------------*/
typedef struct
{
u16 width; //LCD 寬度
u16 height; //LCD 高度
u16 id; //LCD ID
u8 dir; //橫屏還是豎屏控制:0,豎屏;1,橫屏
u16 wramcmd; //開始寫gram指令
u16 setxcmd; //設置x座標指令
u16 setycmd; //設置y座標指令
}_lcd_dev;
//LCD參數
extern _lcd_dev lcddev;//管理LCD重要參數
extern u16 POINT_COLOR;//畫筆顏色,默認紅色
extern u16 BACK_COLOR;//背景顏色.默認為白色
/*-----------------LCD重要參數集定義----------------*/
/*-----------------LCD端口定義----------------*/
#define LCD_LED PBout(15) //LCD背光 PB15
/*-----------------LCD端口定義----------------*/
/*-----------------LCD地址定義----------------*/
//LCD地址結構體
typedef struct
{
vu16 LCD_REG;
vu16 LCD_RAM;
} LCD_TypeDef;
//使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A6作為數據命令區分線
//注意設置時STM32內部會右移一位對其! 111 1110=0X7E
#define LCD_BASE ((u32)(0x6C000000|0x0000007E))
//Bank1Sector4基地址為0x6C000000,A10的偏移地址為0x000007EF
#define LCD ((LCD_TypeDef *) LCD_BASE)
//將上述地址強制轉換為LCD地址結構體指針,即得到LCD->LCD_REG的地址,實現對RS的控制
/*-----------------LCD地址定義----------------*/
//掃描方向定義
#define L2R_U2D 0 //從左到右,從上到下
#define L2R_D2U 1 //從左到右,從下到上
#define R2L_U2D 2 //從右到左,從上到下
#define R2L_D2U 3 //從右到左,從下到上
#define U2D_L2R 4 //從上到下,從左到右
#define U2D_R2L 5 //從上到下,從右到左
#define D2U_L2R 6 //從下到上,從左到右
#define D2U_R2L 7 //從下到上,從右到左
#define DFT_SCAN_DIR L2R_U2D//默認的掃描方向
//畫筆顏色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕紅色
#define GRAY 0X8430 //灰色
//GUI顏色
#define DARKBLUE 0X01CF //深藍色
#define LIGHTBLUE 0X7D7C //淺藍色
#define GRAYBLUE 0X5458 //灰藍色
//以上三色為PANEL的顏色
#define LIGHTGREEN 0X841F //淺綠色
//#define LIGHTGRAY 0XEF5B //淺灰色(PANNEL)
#define LGRAY 0XC618 //淺灰色(PANNEL),窗體背景色
#define LGRAYBLUE 0XA651 //淺灰藍色(中間層顏色)
#define LBBLUE 0X2B12 //淺棕藍色(選擇條目的反色)
void LCD_Init(void); //初始化LCD
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); //顯示一個字符串,可用12/16字體
void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue);
u16 LCD_ReadReg(u16 LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(u16 RGB_Code);
void LCD_SSD_BackLightSet(u8 pwm); //SSD1963 背光控制
void LCD_Scan_Dir(u8 dir); //設置屏掃描方向
void LCD_Display_Dir(u8 dir); //設置屏幕顯示方向
void LCD_Set_Window(u16 sx,u16 sy,u16 width,u16 height); //設置窗口
//LCD分辨率設置參數
#define SSD_HOR_RESOLUTION 800 //LCD水平分辨率
#define SSD_VER_RESOLUTION 480 //LCD垂直分辨率
//LCD驅動設置參數
#define SSD_HOR_PULSE_WIDTH 1 //水平脈寬
#define SSD_HOR_BACK_PORCH 46 //水平前廊
#define SSD_HOR_FRONT_PORCH 210 //水平後廊
#define SSD_VER_PULSE_WIDTH 1 //垂直脈寬
#define SSD_VER_BACK_PORCH 23 //垂直前廊
#define SSD_VER_FRONT_PORCH 22 //垂直前廊
//如下幾個參數,自動計算
#define SSD_HT (SSD_HOR_RESOLUTION+SSD_HOR_BACK_PORCH+SSD_HOR_FRONT_PORCH)
#define SSD_HPS (SSD_HOR_BACK_PORCH)
#define SSD_VT (SSD_VER_RESOLUTION+SSD_VER_BACK_PORCH+SSD_VER_FRONT_PORCH)
#define SSD_VPS (SSD_VER_BACK_PORCH)
#endif
lcd.c
這裏僅寫了顯示函數(讀寫數據函數),LCD控制函數參考正點原子示例文檔
u16 POINT_COLOR=0x0000;//畫筆顏色
u16 BACK_COLOR=0xFFFF;//背景色
//默認為豎屏
_lcd_dev lcddev;
//寫寄存器函數
//regval:寄存器值
void LCD_WR_REG(vu16 regval)
{
regval=regval;//使用-O2優化的時候,必須插入的延時
LCD->LCD_REG=regval;//寫入要寫的寄存器序號
}
//寫LCD數據
//data:要寫入的值
void LCD_WR_DATA(vu16 data)
{
data=data;//使用-O2優化的時候,必須插入的延時
LCD->LCD_RAM=data;
}
//讀LCD數據
//返回值:讀到的值
u16 LCD_RD_DATA(void)
{
vu16 ram;//防止被優化
ram=LCD->LCD_RAM;
return ram;
}
//寫寄存器
//LCD_Reg:寄存器地址
//LCD_RegValue:要寫入的數據
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{
LCD->LCD_REG=LCD_Reg;//寫入要寫的寄存器序號
LCD->LCD_RAM=LCD_RegValue;//寫入數據
}
//讀寄存器
//LCD_Reg:寄存器地址
//返回值:讀到的數據
u16 LCD_ReadReg(u16 LCD_Reg)
{
LCD_WR_REG(LCD_Reg);//寫入要讀的寄存器序號
delay_us(5);
return LCD_RD_DATA();//返回讀到的值
}
//開始寫GRAM
void LCD_WriteRAM_Prepare(void)
{
LCD->LCD_REG=lcddev.wramcmd;
}
//LCD寫GRAM
//RGB_Code:顏色值
void LCD_WriteRAM(u16 RGB_Code)
{
LCD->LCD_RAM = RGB_Code;//寫十六位GRAM
}
//從ILI93xx讀出的數據為GBR格式,而我們寫入的時候為RGB格式。
//通過該函數轉換
//c:GBR格式的顏色值
//返回值:RGB格式的顏色值
u16 LCD_BGR2RGB(u16 c)
{
u16 r,g,b,rgb;
b=(c>>0)&0x1f;
g=(c>>5)&0x3f;
r=(c>>11)&0x1f;
rgb=(b<<11)+(g<<5)+(r<<0);
return(rgb);
}
//當mdk -O1時間優化時需要設置
//延時i
void opt_delay(u8 i)
{
while(i--);
}
//讀取個某點的顏色值
//x,y:座標
//返回值:此點的顏色
u16 LCD_ReadPoint(u16 x,u16 y)
{
u16 r=0,g=0,b=0;
if(x>=lcddev.width||y>=lcddev.height)return 0;//超過了範圍,直接返回
LCD_SetCursor(x,y);
if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310||lcddev.id==0X1963)LCD_WR_REG(0X2E);//9341/6804/3510/1963 發送讀GRAM指令
else if(lcddev.id==0X5510)LCD_WR_REG(0X2E00); //5510 發送讀GRAM指令
else LCD_WR_REG(0X22); //其他IC發送讀GRAM指令
if(lcddev.id==0X9320)opt_delay(2); //FOR 9320,延時2us
r=LCD_RD_DATA(); //dummy Read
if(lcddev.id==0X1963)return r; //1963直接讀就可以
opt_delay(2);
r=LCD_RD_DATA(); //實際座標顏色
if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)//9341/NT35310/NT35510要分2次讀出
{
opt_delay(2);
b=LCD_RD_DATA();
g=r&0XFF;//對於9341/5310/5510,第一次讀取的是RG的值,R在前,G在後,各佔8位
g<<=8;
}
if(lcddev.id==0X9325||lcddev.id==0X4535||lcddev.id==0X4531||lcddev.id==0XB505||lcddev.id==0XC505)
return r;//這幾種IC直接返回顏色值
else if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)
return (((r>>11)<<11)|((g>>10)<<5)|(b>>11));//ILI9341/NT35310/NT35510需要公式轉換一下
else
return LCD_BGR2RGB(r); //其他IC
}
//LCD開啓顯示
void LCD_DisplayOn(void)
{
if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310||lcddev.id==0X1963)
LCD_WR_REG(0X29);//開啓顯示
else if(lcddev.id==0X5510)
LCD_WR_REG(0X2900);//開啓顯示
else
LCD_WriteReg(0X07,0x0173);//開啓顯示
}
//LCD關閉顯示
void LCD_DisplayOff(void)
{
if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310||lcddev.id==0X1963)
LCD_WR_REG(0X28);//關閉顯示
else if(lcddev.id==0X5510)
LCD_WR_REG(0X2800);//關閉顯示
else
LCD_WriteReg(0X07,0x0);//關閉顯示
}
//畫點
//x,y:座標
//POINT_COLOR:此點的顏色
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y);//設置光標位置
LCD_WriteRAM_Prepare();//開始寫入GRAM
LCD->LCD_RAM=POINT_COLOR;
}
//快速畫點
//x,y:座標
//color:顏色
void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color)
{
if(lcddev.id==0X9341||lcddev.id==0X5310)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(x>>8);
LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(y>>8);
LCD_WR_DATA(y&0XFF);
}
else if(lcddev.id==0X5510)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(x>>8);
LCD_WR_REG(lcddev.setxcmd+1);
LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(y>>8);
LCD_WR_REG(lcddev.setycmd+1);
LCD_WR_DATA(y&0XFF);
}
else if(lcddev.id==0X1963)
{
if(lcddev.dir==0)
x=lcddev.width-1-x;
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(x>>8);
LCD_WR_DATA(x&0XFF);
LCD_WR_DATA(x>>8);
LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(y>>8);
LCD_WR_DATA(y&0XFF);
LCD_WR_DATA(y>>8);
LCD_WR_DATA(y&0XFF);
}
else if(lcddev.id==0X6804)
{
if(lcddev.dir==1)
x=lcddev.width-1-x;//橫屏時處理
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(x>>8);
LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(y>>8);
LCD_WR_DATA(y&0XFF);
}
else
{
if(lcddev.dir==1)
x=lcddev.width-1-x;//橫屏其實就是調轉x,y座標
LCD_WriteReg(lcddev.setxcmd,x);
LCD_WriteReg(lcddev.setycmd,y);
}
LCD->LCD_REG=lcddev.wramcmd;
LCD->LCD_RAM=color;
}
//清屏函數
//color:要清屏的填充色
void LCD_Clear(u16 color)
{
u32 index=0;
u32 totalpoint=lcddev.width;
totalpoint*=lcddev.height;//得到總點數
if((lcddev.id==0X6804)&&(lcddev.dir==1))//6804橫屏的時候特殊處理
{
lcddev.dir=0;
lcddev.setxcmd=0X2A;
lcddev.setycmd=0X2B;
LCD_SetCursor(0x00,0x0000);//設置光標位置為初始位置
lcddev.dir=1;
lcddev.setxcmd=0X2B;
lcddev.setycmd=0X2A;
}
else
LCD_SetCursor(0x00,0x0000);//設置光標位置
LCD_WriteRAM_Prepare();//開始寫入GRAM
for(index=0;index<totalpoint;index++)
{
LCD->LCD_RAM=color;
}
}
//在指定位置顯示一個字符
//x,y:起始座標
//num:要顯示的字符:" "--->"~"
//size:字體大小 12/16/24
//mode:疊加方式(1)還是非疊加方式(0)
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)
{
u8 temp,t1,t;
u16 y0=y;
u8 csize=(size/8+((size%8)?1:0))*(size/2);//得到字體一個字符對應點陣集所佔的字節數
num=num-' ';//得到偏移後的值(ASCII字庫是從空格開始取模,所以-' '就是對應字符的字庫)
for(t=0;t<csize;t++)
{
if(size==12)
temp=asc2_1206[num][t];//調用1206字體
else if(size==16)
temp=asc2_1608[num][t];//調用1608字體
else if(size==24)
temp=asc2_2412[num][t];//調用2412字體
else
return;//沒有的字庫
for(t1=0;t1<8;t1++)
{
if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
temp<<=1;
y++;
if(y>=lcddev.height)
return;//超區域了
if((y-y0)==size)
{
y=y0;
x++;
if(x>=lcddev.width)
return;//超區域了
break;
}
}
}
}
//顯示數字,高位為0,還是顯示
//x,y:起點座標
//num:數值(0~999999999);
//len:長度(即要顯示的位數)
//size:字體大小
//mode:
//[7]:0,不填充;1,填充0.
//[6:1]:保留
//[0]:0,非疊加顯示;1,疊加顯示.
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)
{
u8 t,temp;
u8 enshow=0;
for(t=0;t<len;t++)
{
temp=(num/LCD_Pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
if(mode&0X80)
LCD_ShowChar(x+(size/2)*t,y,'0',size,mode&0X01);
else
LCD_ShowChar(x+(size/2)*t,y,' ',size,mode&0X01);
continue;
}
else
enshow=1;
}
LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,mode&0X01);
}
}
//顯示字符串
//x,y:起點座標
//width,height:區域大小
//size:字體大小
//*p:字符串起始地址
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
{
u8 x0=x;
width+=x;
height+=y;
while((*p<='~')&&(*p>=' '))//判斷是不是非法字符!
{
if(x>=width)
{
x=x0;
y+=size;
}
if(y>=height)
break;//退出
LCD_ShowChar(x,y,*p,size,0);
x+=size/2;
p++;
}
}
lcd.c
這裏用偽代碼説明LCD初始化函數內容,詳細內容見例程
//LCD初始化函數
void LCD_init(void)
{
vu32 i=0;
GPIO_InitTypeDef GPIO_InitStructure;
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
FSMC_NORSRAMTimingInitTypeDef writeTiming;
/*初始化GPIO和FSMC*/
//使能PD,PE,PF,PG和FSMC時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|\
RCC_AHB1Periph_GPIOD|\
RCC_AHB1Periph_GPIOE|\
RCC_AHB1Periph_GPIOF|\
RCC_AHB1Periph_GPIOG, ENABLE);
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推輓輸出,控制背光
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//內部上拉
//應用設置
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14);//PD0,1,4,5,8,9,10,14,15複用輸出
//應用設置
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = (0X1FF<<7);//PE7~15複用輸出
//應用設置
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
//應用設置
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
//應用設置
GPIO_Init(GPIOG, &GPIO_InitStructure);
/*設置引腳複用*/
GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12
GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);
/*讀寫時序初始化*/
readWriteTiming.FSMC_AddressSetupTime=0XF;//地址建立時間(ADDSET)為16個HCLK 1/168M=6ns*16=96ns
readWriteTiming.FSMC_AddressHoldTime=0x00;//地址保持時間(ADDHLD)模式A未用到
readWriteTiming.FSMC_DataSetupTime=60;//數據保存時間為60個HCLK=6*60=360ns
readWriteTiming.FSMC_BusTurnAroundDuration=0x00;
readWriteTiming.FSMC_CLKDivision=0x00;
readWriteTiming.FSMC_DataLatency=0x00;
readWriteTiming.FSMC_AccessMode=FSMC_AccessMode_A;//模式A
/*寫時序初始化*/
writeTiming.FSMC_AddressSetupTime =9;//地址建立時間(ADDSET)為9個HCLK =54ns
writeTiming.FSMC_AddressHoldTime = 0x00;//地址保持時間(A
writeTiming.FSMC_DataSetupTime = 8;//數據保存時間為6ns*9個HCLK=54ns
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;//外部SRAM操作
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth=FSMC_MemoryDataWidth_16b;//存儲器數據寬度為16位
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode=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_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);//使能BANK1
/*讀取LCD ID並進行核驗*/
delay_ms(50);
LCD_WriteReg(0x0000,0x0001);
delay_ms(50);
lcddev.id=LCD_ReadReg(0x0000);
printf("LCD ID:%x\r\n",lcddev,id);//在串口1打印LCD ID
根據不同ID執行不同的LCD初始化代碼;//全都是if...else if... else if...else...
LCD_Display_Dir(0);//默認為豎屏
LCD_LED=1;//點亮背光
LCD_Clear(WHITE);//清屏,準備顯示
}
特殊情況:使用GPIO連接LCD
在不使用FSMC的情況下,可以使用GPIO模擬FSMC讀取/寫入GRAM
直接操作GPIO會佔用CPU且速度較慢,但可作為替代方式
FSMC
FSMC(Flexible Static Memory Controller)即“靈活的靜態存儲控制器”,能夠與同步/異步存儲器和16位PC存儲器卡連接,STM32的FSMC接口支持SRAM、NANDFLASH、NORFLASH、PSRAM等存儲器,對f407來説不支持SDRAM,由HCLK直接提供時鐘,能夠設置FSMC中斷
FSMC驅動外部SRAM時,一般通過地址線(A0~A25)、數據線(D0~D15)、寫信號(WE/WR)、讀信號(OD/RD)、片選信號(CS)、UB/LB信號(僅支持字節控制的SRAM可使用)進行控制
FSMC控制TFTLCD
將LCD的GRAM看作外部SRAM即可使用FSMC控制LCD
TFTLCD通過RS信號決定傳輸的數據是數據還是命令,即可把LCD映射為一個具有2個地址的SRAM,向其中存入數據就等效為向LCD發送指令或發送數據
FSMC外設接口
stm32f4的FSMC支持8/16/32位數據寬度,自動將外部存儲器劃分為固定大小為256M字節的四個存儲塊
如下所示
|
地址
|
存儲塊
|
支持的存儲器類型
|
|
6000 0000H
|
塊1
|
NORFLASH/PSRAM
|
|
6FFF FFFFH
|
4*64MB
|
NORFLASH/PSRAM
|
|
7000 0000H
|
塊2
|
NANDFLASH
|
|
7FFF FFFFH
|
4*64MB
|
NANDFLASH
|
|
8000 0000H
|
塊3
|
NANDFLASH
|
|
8FFF FFFFH
|
4*64MB
|
NANDFLASH
|
|
9000 0000H
|
塊4
|
PC卡
|
|
9FFF FFFFH
|
4*64MB
|
PC卡
|
存儲塊1
用於驅動NORFLASH/PSRAM/SRAM
Bank1被劃分為4個區,每個區64MB,都有獨立的寄存器進行配置,由28根地址線(HADDR[27:0])尋址,HADDR是內部AHB地址總線的外延,其中HADDR[25:0]來自外部存儲器地址FSMC_A[25:0],HADDR[26:27]對四個區進行尋址
當外接16位存儲器,HADDR[25:1]->FAMC_A[24:0](右移1位對齊 即 除以2)
當外接8位存儲器,HADDR[25:0]->FAMC_A[25:0]
不管外接8位還是16位寬設備,FSMC_A[0]永遠接在外部設備地址A[0]
支持多種異步突發訪問模式,詳見芯片手冊
存儲塊2、3
用於驅動NANDFLASH
存儲塊4
用於驅動PC卡
詳見數據手冊
寄存器庫函數封裝
在標準庫中,ST將FSMC_BCRx FSMC_BTRx FSMC_BWTRx三個單獨寄存器進行組合封裝
- FSMC_BCRx和FSMC_BTRx組合成BTCR[8]寄存器數組
|
BTCR[0]
|
FSMC_BCR1
|
對應關係
|
BTCR[1]
|
FSMC_BTR1
|
|
BTCR[2]
|
FSMC_BCR2
|
BTCR[3]
|
FSMC_BTR2
|
|
|
BTCR[4]
|
FSMC_BCR3
|
BTCR[5]
|
FSMC_BTR3
|
|
|
BTCR[6]
|
FSMC_BCR4
|
BTCR[7]
|
FSMC_BTR4
|
- 將FSMC_BWTRx組合位BWTR[7]數組
BWTR[0]對應FSMC_BWTR1
BWTR[2]對應FSMC_BWTR2
BWTR[4]對應FSMC_BWTR3
BWTR[6]對應FSMC_BWTR4
BWTR[1]、BWTR[3]、BWTR[5]保留,未用到
外部SRAM操作
IS62WV51216ALL參數簡介
IS62WV51216是ISSI(Intergrated Silicon Solution,Inc)公司生產的16位寬512K(512*16=1MB)容量的CMOS靜態內存(SRAM)芯片
特點:
- 高速(45ns/55ns訪問速度)
- 低功耗(操作電流36mW,待機12uW)
- 兼容TTL電平
- 全靜態操作(SRAM特點,不需要刷新和時鐘電路)
- 支持三態輸出
- 字節控制(支持高/低字節控制)
引腳:
- A0~A18共19根地址線(2^19=512K)
- IO0~IO15共16根數據線
- CS1、CS2:片選信號,其中CS1低電平有效,CS2高電平有效
- OE:輸出使能信號(讀使能信號)
- WE:輸入使能信號(寫使能信號)
- UB、LB:高字節/低字節控制信號,低電平有效
讀寫時序參考芯片數據手冊
示例項目使用55ns的IS62WV51216,讀週期時間tRC=寫週期時間tWC=55ns;讀取尋址時間tAA=55ns(MAX);寫入尋址時間tAA=0ns;讀信號OE建立時間tOE=25ns(MAX);寫信號WE建立時間tPWE=45ns
硬件連接
|
連接方式
|
數據線
|
地址線
|
讀使能
|
寫使能
|
片選
|
高字節控制
|
低字節控制
|
|
SRAM
|
IO0~15
|
A0~18
|
OE
|
WE
|
CS
|
UB
|
LB
|
|
FSMC
|
D0~15
|
A0~18
|
OE
|
WE
|
CS
|
UB
|
LB
|
軟件配置
sram.c
#include "sram.h"
//使用NOR/SRAM的Bank1.sector3,地址位HADDR[27,26]=10
//對IS61LV25616/IS62WV25616,地址線範圍為A0~A17
//對IS61LV51216/IS62WV51216,地址線範圍為A0~A18
#define Bank1_SRAM3_ADDR ((u32)(0x68000000))
//初始化FSMC與外部SRAM
void FSMC_SRAM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
//使能PB、PD、PE、PF、PG與FSMC時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|\
RCC_AHB1Periph_GPIOD|\
RCC_AHB1Periph_GPIOE|\
RCC_AHB1Periph_GPIOF|\
RCC_AHB1Periph_GPIOG, ENABLE);
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);
//PB15推輓輸出,控制背光
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//內部上拉
//應用設置
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(0XFF<<8);//PD0,1,4,5,8~15 AF OUT
//應用設置
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = (3<<0)|(0X1FF<<7);//PE0,1,7~15,AF OUT
//應用設置
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = (0X3F<<0)|(0XF<<12);//PF0~5,12~15
//應用設置
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin =(0X3F<<0)| GPIO_Pin_10;//PG0~5,10
//應用設置
GPIO_Init(GPIOG, &GPIO_InitStructure);
/*讀寫時序設置*/
readWriteTiming.FSMC_AddressSetupTime=0x00;//地址建立時間(ADDSET)為1個HCLK 1/36M=27ns
readWriteTiming.FSMC_AddressHoldTime=0x00;//地址保持時間(ADDHLD)模式A未用到
readWriteTiming.FSMC_DataSetupTime=0x08;//數據保持時間(DATAST)為9個HCLK 6*9=54ns
readWriteTiming.FSMC_BusTurnAroundDuration=0x00;
readWriteTiming.FSMC_CLKDivision=0x00;
readWriteTiming.FSMC_DataLatency=0x00;
readWriteTiming.FSMC_AccessMode=FSMC_AccessMode_A;//模式A
/*配置引腳複用*/
GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource11,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12
GPIO_PinAFConfig(GPIOE,GPIO_PinSource0,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource1,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource0,GPIO_AF_FSMC);//PF0,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource1,GPIO_AF_FSMC);//PF1,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource2,GPIO_AF_FSMC);//PF2,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource3,GPIO_AF_FSMC);//PF3,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource4,GPIO_AF_FSMC);//PF4,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource5,GPIO_AF_FSMC);//PF5,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource13,GPIO_AF_FSMC);//PF13,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource14,GPIO_AF_FSMC);//PF14,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource15,GPIO_AF_FSMC);//PF15,AF12
GPIO_PinAFConfig(GPIOG,GPIO_PinSource0,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource1,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource2,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource3,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource4,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource5,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource10,GPIO_AF_FSMC);
FSMC_NORSRAMInitStructure.FSMC_Bank=FSMC_Bank1_NORSRAM3;//NE3對應BTCR[4],[5]
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux=FSMC_DataAddressMux_Disable;//關閉數據地址複用
FSMC_NORSRAMInitStructure.FSMC_MemoryType=FSMC_MemoryType_SRAM;//操作外部SRAM
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth=FSMC_MemoryDataWidth_16b;//存儲器數據寬度16位
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode=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_Disable;//不使用擴展模式,讀寫使用相同時序
FSMC_NORSRAMInitStructure.FSMC_WriteBurst=FSMC_WriteBurst_Disable;//關閉寫突發
FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct=&readWriteTiming;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct=&readWriteTiming;//讀寫使用同步時序,設置生效
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);//初始化FSMC配置
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3, ENABLE);//使能BANK1區域3
}
//在指定地址(WriteAddr+Bank1_SRAM3_ADDR)開始,連續寫入n個字節.
//pBuffer:字節指針
//WriteAddr:要寫入地址
//n:寫入字節數
void FSMC_SRAM_WriteBuffer(u8* pBuffer,u32 WriteAddr,u32 n)
{
for(;n!=0;n--)
{
//Bank1_SRAM3_ADDR為((u32)(0x68000000))
*(vu8*)(Bank1_SRAM3_ADDR+WriteAddr)=*pBuffer;
WriteAddr++;
pBuffer++;
}
}
//在指定地址((WriteAddr+Bank1_SRAM3_ADDR))開始,連續讀出n個字節.
//pBuffer:字節指針
//ReadAddr:要讀出的起始地址
//n:讀取字節數
//將寫入函數位置調換即可
void FSMC_SRAM_ReadBuffer(u8* pBuffer,u32 ReadAddr,u32 n)
{
for(;n!=0;n--)
{
//Bank1_SRAM3_ADDR為((u32)(0x68000000))
*pBuffer++=*(vu8*)(Bank1_SRAM3_ADDR+ReadAddr);
ReadAddr++;
}
}
sram.h
#ifndef __SRAM_H
#define __SRAM_H
#include "sys.h"
void FSMC_SRAM_Init(void);//初始化FSMC
void FSMC_SRAM_WriteBuffer(u8* pBuffer,u32 WriteAddr,u32 NumHalfwordToWrite);//向外部SRAM寫字節
void FSMC_SRAM_ReadBuffer(u8* pBuffer,u32 ReadAddr,u32 NumHalfwordToRead);//從外部SRAM讀字節
void fsmc_sram_test_write(u32 addr,u8 data);//寫SRAM
u8 fsmc_sram_test_read(u32 addr);//讀SRAM
#endif