顯存不一定要擦除,只要來一個地址就可以對其進行讀寫,而且一般的需求是不停的寫入(不同的像素點給不同的值),所以是RAM(flash和E2PROM要擦除才能寫入),由于FSMC沒有DRAM所以我們只能選用SRAM
我們要哪個地址塊,要看硬件連接的設定
LCD硬件電路圖:液晶模塊Z350IT002內部使用的控制芯片是:ILI9486
- D0-D15是16位數據總線接口。分別接FSMC的D0-D15。
- RST是LCD復位引腳,低電平復位。接LCD-RST(PG15)。
- RD是讀控制引腳,上升沿時讀數據。接FSMC-NOE(PD4)。
- WR是寫控制引腳,上升沿時寫數據。接FSMC-NWE(PD5)。
- RS是數據或命令選擇引腳RS=1寫數據,RS=0寫命令。接FSMC-A10(PG0)。我們沒有地址線,只有控制位,那我們的地址如何傳輸?在FSMC接口架構中,液晶模塊通過地址總線和控制信號的協同編碼實現高效的地址傳輸和命令寫入。FSMC將液晶模塊的命令和數據寄存器映射到微控制器的SRAM地址空間,例如0x6C000000-0x6FFFFFFF。使用地址線A10作為RS控制信號,通過地址編碼切換命令寫入和數據寫入模式。訪問基地址時(0x6C000000),A10為低,數據總線傳輸的內容被視為命令;訪問偏移地址時,A10為高,數據總線內容則被當作顯示數據。通過指針運算直接操作硬件寄存器,簡化了命令寫入過程。FSMC控制器負責地址譯碼、控制信號生成和總線仲裁,液晶模塊通過A10狀態解析總線內容。A10為0時,數據被鎖存到命令寄存器;為1時,數據寫入顯示緩沖區。通過地址偏移量擴展,實現多寄存器統一訪問接口,利用內存映射I/O機制完成硬件級抽象,提高傳輸效率,簡化地址管理。
- CS是片選引腳,低電平有效。接FSMC-NE4(PG12)。所以其對應的STM32的地址就是bank1的第4塊地址
![]()
地址映射范圍:
子塊編號 地址范圍 對應使能引腳 引腳物理連接 Bank1-1 0x60000000-0x63FFFFFF
NE1 如PD7(具體型號可能不同) Bank1-2 0x64000000-0x67FFFFFF
NE2 如PG9 Bank1-3 0x68000000-0x6BFFFFFF
NE3 如PG10 Bank1-4 0x6C000000-0x6FFFFFFF
NE4 如PG12 - LEDA是背光電源(3.0V-3.4V)引腳。
- LEDK是背光亮度控制引腳。通過LCD-BG(PB0)來驅動MOS管Q5的導通電流。可以通過給LCD-BG輸出PWM波來控制背光的亮度。占空比越大,背光就會越亮。
- YD,XL,YU,XR是觸摸屏控制引腳
注意:當使用16位寬的外部存儲器時,用HADDR[25:1]表示外部的FSMC_A[24:0],內部地址相當于左移了1位,所以計算地址的時候要注意。
LCD我們選擇的是16位寬度的,選擇地址線時,我們選擇的是A10接LCD的D/CX(數據/命令引腳)。
當A10=0時,表示寫命令,所以地址是:0x6C00 0000。
當A10=1時,表示寫數據,所以地址是:0x6C00 0000 + 1<<11 = 0x6C00 0800
只有A10置1,其他置0,不要的數據位全置0,從右往左數1~10第11位,1后面10個0,寫成16進制數就是0x6c00400(|0100|0000|0000),如果我們的地址線是直連的沒有問題,但是現在的地址線不是直連的,數據寬度是8位,那我們高速系統總線上的地址線和FSMC的地址線完全是直連的模式,連到A10也是32位從右往左數的第10位,但我們現在是16位的,要操作的LCD模塊顯存都是16位的RGB模塊,那么就有交叉連接的問題,就是A10,最后FSMC最后輸出的A10來自于PHA11,以此類推PHA1連到A0,最后一位就不需要了,要表示就用高低字節的掩碼來表示,關鍵的怎么配置A10對應的是之前原始地址的PHA11,就是1000|0000|0000,換成16進制就是0x6c00800,
如果大家不想這么麻煩就全部置1即可,反正其他內存又沒用到
寄存器代碼:
FSMC配置:
void FSMC_GPIO_Init(){/* 1 配置 A0-A18 地址端口的輸出模式 復用推挽輸出CNF:10 50MHz速度 MODE:11*//* =============MODE=============== */GPIOG->CRL |= GPIO_CRL_MODE0;/* =============CNF=============== */GPIOG->CRL |= GPIO_CRL_CNF0_1 ;GPIOG->CRL &= ~GPIO_CRL_CNF0_0;/*2 數據端口 復用推挽輸出在實際應用中,即使數據線被配置為輸出模式,FSMC控制器仍然能夠管理數據線的方向,使其在需要時成為輸入線。這種自動切換是由FSMC控制器硬件管理的,不需要軟件干預。因此,即使GPIO配置為復用推挽輸出,FSMC依然可以實現讀取操作。*//* =============MODE=============== */GPIOD->CRL |= (GPIO_CRL_MODE0 |GPIO_CRL_MODE1);GPIOD->CRH |= (GPIO_CRH_MODE8 |GPIO_CRH_MODE9 |GPIO_CRH_MODE10 |GPIO_CRH_MODE14 |GPIO_CRH_MODE15);GPIOE->CRL |= (GPIO_CRL_MODE7);GPIOE->CRH |= (GPIO_CRH_MODE8 |GPIO_CRH_MODE9 |GPIO_CRH_MODE10 |GPIO_CRH_MODE11 |GPIO_CRH_MODE12 |GPIO_CRH_MODE13 |GPIO_CRH_MODE14 |GPIO_CRH_MODE15);/* =============CNF=============== */GPIOD->CRL |= (GPIO_CRL_CNF0_1 |GPIO_CRL_CNF1_1);GPIOD->CRL &= ~(GPIO_CRL_CNF0_0 |GPIO_CRL_CNF1_0);GPIOD->CRH |= (GPIO_CRH_CNF8_1 |GPIO_CRH_CNF9_1 |GPIO_CRH_CNF10_1 |GPIO_CRH_CNF14_1 |GPIO_CRH_CNF15_1);GPIOD->CRH &= ~(GPIO_CRH_CNF8_0 |GPIO_CRH_CNF9_0 |GPIO_CRH_CNF10_0 |GPIO_CRH_CNF14_0 |GPIO_CRH_CNF15_0);GPIOE->CRL |= (GPIO_CRL_CNF7_1);GPIOE->CRL &= ~(GPIO_CRL_CNF7_0);GPIOE->CRH |= (GPIO_CRH_CNF8_1 |GPIO_CRH_CNF9_1 |GPIO_CRH_CNF10_1 |GPIO_CRH_CNF11_1 |GPIO_CRH_CNF12_1 |GPIO_CRH_CNF13_1 |GPIO_CRH_CNF14_1 |GPIO_CRH_CNF15_1);GPIOE->CRH &= ~(GPIO_CRH_CNF8_0 |GPIO_CRH_CNF9_0 |GPIO_CRH_CNF10_0 |GPIO_CRH_CNF11_0 |GPIO_CRH_CNF12_0 |GPIO_CRH_CNF13_0 |GPIO_CRH_CNF14_0 |GPIO_CRH_CNF15_0);/* 3 其他控制端口 ?復用推挽輸出 *//* 3.1 PD4: 讀控制引腳 ?PD5: 寫控制引腳 */GPIOD->CRL |= (GPIO_CRL_MODE4 |GPIO_CRL_MODE5);GPIOD->CRL |= (GPIO_CRL_CNF4_1 |GPIO_CRL_CNF5_1);GPIOD->CRL &= ~(GPIO_CRL_CNF4_0 |GPIO_CRL_CNF5_0);/* 3.2 ?PG12:NE4*/GPIOG->CRH |= (GPIO_CRH_MODE12);GPIOG->CRH |= (GPIO_CRH_CNF12_1);GPIOG->CRH &= ~(GPIO_CRH_CNF12_0);/* 3.3 ?背光引腳PB0:通用推挽輸出*/GPIOB->CRL |= GPIO_CRL_MODE0;GPIOB->CRL &= ~GPIO_CRL_CNF0;/* 3.4 ?重置引腳PG15:通用推挽輸出*/GPIOG->CRH |= GPIO_CRH_MODE15;GPIOG->CRH &= ~GPIO_CRH_CNF15;}
開啟時鐘
void FSMC_Init(){/* 1. 時鐘開啟 */RCC->APB2ENR |= (RCC_APB2ENR_IOPDEN |RCC_APB2ENR_IOPEEN |RCC_APB2ENR_IOPFEN |RCC_APB2ENR_IOPGEN |RCC_APB2ENR_AFIOEN);RCC->AHBENR |= RCC_AHBENR_FSMCEN;/* 2. 各個引腳的模式的配置 */Driver_FSMC_GPIO_Init();/* 3. fsmc的配置 Bank1的 4區 BCR4 *//* 3.1 存儲塊使能 */FSMC_Bank1->BTCR[6] |= FSMC_BCR3_MBKEN;/* 3.2 設置存儲類型 00=SRAM ROM*/FSMC_Bank1->BTCR[6] &= ~FSMC_BCR3_MTYP;/* 3.3 禁止閃存訪問 */FSMC_Bank1->BTCR[6] &= ~FSMC_BCR3_FACCEN;/* 3.4 地址和數據復用: 不復用 ,邏輯上是做復用,但是物理上我們還是不復用,因為就一個線0地址,1數據,*/FSMC_Bank1->BTCR[6] &= ~FSMC_BCR3_MUXEN;/* 3.5 數據總線的寬度 16位寬度=01 */FSMC_Bank1->BTCR[6] &= ~FSMC_BCR3_MWID_1;FSMC_Bank1->BTCR[6] |= FSMC_BCR3_MWID_0;/* 3.6 寫使能 */;FSMC_Bank1->BTCR[6] |= FSMC_BCR3_WREN;/* 3. fsmc的 時序 *//* 3.1 地址建立時間 對同步讀寫來說,永遠一個周期 */FSMC_Bank1->BTCR[7] &= ~FSMC_BTR3_ADDSET;/* 3.2 地址保持時間 對同步讀寫來說,永遠一個周期 */FSMC_Bank1->BTCR[7] &= ~FSMC_BTR3_ADDHLD;/* 3.3 數據保持時間 手冊不能低于55ns 我們設置1us*/FSMC_Bank1->BTCR[7] &= ~FSMC_BTR3_DATAST;
//看寄存器它是在8~15位定義的,所以左移8位,可能LCD時序上有其他要求但是1US就足夠了FSMC_Bank1->BTCR[7] |= (71 << 8);}
LCD配置:
LCD.h:基礎底層操作
#ifndef __INF_LCD_H
#define __INF_LCD_H#include "Driver_FSMC.h"
#include "Delay.h"
#include "math.h"
//bank1的第4個分區基地址
#define SRAM_BANK4 0x6C000000
//寫命令,轉換成一個指針
#define LCD_ADDR_CMD (uint16_t *)SRAM_BANK4
//寫數據
#define LCD_ADDR_DATA (uint16_t *)(SRAM_BANK4 + (1 << 11))
//液晶屏的寬,這個要看你LCD的像素
#define DISPLAY_W 320
//液晶屏的高
#define DISPLAY_H 480/* 常見顏色 */
#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 ?// 灰色//基本控制操作:
//1.1初始化:
void LCD_Init(void);//1.1.1復位
void LCD_Reset(void);//1.1.2開關背光
void LCD_BGOn(void);
void LCD_BGOff(void);//1.1.3初始化LCD寄存器
void LCD_ReConfig(void);//2.寫命令(發出一個指令)看LCD數據手冊是8位的但是我們FSMC配是16位的,強轉就行
void LCD_WriteCmd(uint16_t cmd);//3.寫數據(發出一個數據)
void LCD_WriteData(uint16_t cmd);//4.讀數據
uint16_t LCD_ReadData(void);
//2.具體命令操作
//讀取ID信息,看LCD的數據手冊上的指令表
uint32_t LCD_ReadID(void);//向液晶屏發送信息,看數據手冊
//底層的指令值就是對某一個像素點寫入RGB數據
//有列地址和行地址的劃分,即將矩陣屏劃分了多個點
void LCD_SetArea(uint16_t x,uint16_t y,uint16_t w,uint16_t h);void LCD_ClearAll(uint16_t color);#endif
LCD.c基礎底層操作
//基本控制操作:
//1.1初始化:
void LCD_Init(void)
{
FSMC_Init();LCD_Reset();LCD_BGOn();LCD_RegConfig();
}//1.1.1復位
void LCD_Reset(void)
{
//直接將PG15拉低
GPIOG->ODR &=~GPIO_ODR_ODR15;
//稍作延時,然后拉高
Delay_ms(100);
GPIOG->ODR |=GPIO_ODR_ODR15;
}//1.1.2開關背光
void LCD_BGOn(void)
{
//電路圖是PB0高電平有效,背光點亮
GPIOB->ODR |=GPIO_ODR_ODR0;
}void LCD_BGOff(void)
{
//電路圖是PB0高電平有效,背光點亮拉低
GPIOB->ODR &=~GPIO_ODR_ODR0;
}//1.1.3初始化LCD寄存器
void LCD_ReConfig(void)
{
//直接抄寫LCD數據手冊給的/* 1. 設置灰階電壓以調整TFT面板的伽馬特性(當前輸出的電壓和人眼感知亮度是非線性的,校準當前輸出電壓服務人眼感知), 正校準。一般出廠就設置好了 */Inf_LCD_WriteCmd(0xE0);Inf_LCD_WriteData(0x00);Inf_LCD_WriteData(0x07);Inf_LCD_WriteData(0x10);Inf_LCD_WriteData(0x09);Inf_LCD_WriteData(0x17);Inf_LCD_WriteData(0x0B);Inf_LCD_WriteData(0x41);Inf_LCD_WriteData(0x89);Inf_LCD_WriteData(0x4B);Inf_LCD_WriteData(0x0A);Inf_LCD_WriteData(0x0C);Inf_LCD_WriteData(0x0E);Inf_LCD_WriteData(0x18);Inf_LCD_WriteData(0x1B);Inf_LCD_WriteData(0x0F);/* 2. 設置灰階電壓以調整TFT面板的伽馬特性,負校準 */Inf_LCD_WriteCmd(0XE1);Inf_LCD_WriteData(0x00);Inf_LCD_WriteData(0x17);Inf_LCD_WriteData(0x1A);Inf_LCD_WriteData(0x04);Inf_LCD_WriteData(0x0E);Inf_LCD_WriteData(0x06);Inf_LCD_WriteData(0x2F);Inf_LCD_WriteData(0x45);Inf_LCD_WriteData(0x43);Inf_LCD_WriteData(0x02);Inf_LCD_WriteData(0x0A);Inf_LCD_WriteData(0x09);Inf_LCD_WriteData(0x32);Inf_LCD_WriteData(0x36);Inf_LCD_WriteData(0x0F);/* 3. ?Adjust Control 3 (F7h) ?*//*LCD_WriteCmd(0XF7);Inf_LCD_WriteData(0xA9);Inf_LCD_WriteData(0x51);Inf_LCD_WriteData(0x2C);Inf_LCD_WriteData(0x82);*//* DSI write DCS command, use loose packet RGB 666 *//* 4. 電源控制1*/Inf_LCD_WriteCmd(0xC0);Inf_LCD_WriteData(0x11); /* 正伽馬電壓 */Inf_LCD_WriteData(0x09); /* 負伽馬電壓 *//* 5. 電源控制2 */Inf_LCD_WriteCmd(0xC1);Inf_LCD_WriteData(0x02);Inf_LCD_WriteData(0x03);/* 6. VCOM控制 */Inf_LCD_WriteCmd(0XC5);Inf_LCD_WriteData(0x00);Inf_LCD_WriteData(0x0A);Inf_LCD_WriteData(0x80);/* 7. Frame Rate Control (In Normal Mode/Full Colors) (B1h) */Inf_LCD_WriteCmd(0xB1);Inf_LCD_WriteData(0xB0);Inf_LCD_WriteData(0x11);/* 8. ?Display Inversion Control (B4h) (正負電壓反轉,減少電磁干擾)*/Inf_LCD_WriteCmd(0xB4);Inf_LCD_WriteData(0x02);/* 9. ?Display Function Control (B6h) ?*/Inf_LCD_WriteCmd(0xB6);Inf_LCD_WriteData(0x0A);Inf_LCD_WriteData(0xA2);/* 10. Entry Mode Set (B7h) ?*/Inf_LCD_WriteCmd(0xB7);Inf_LCD_WriteData(0xc6);/* 11. HS Lanes Control (BEh) */Inf_LCD_WriteCmd(0xBE);Inf_LCD_WriteData(0x00);Inf_LCD_WriteData(0x04);/* 12. ?Interface Pixel Format (3Ah) */Inf_LCD_WriteCmd(0x3A);Inf_LCD_WriteData(0x55); /* 0x55 : 16 bits/pixel ?*//* 13. Sleep Out (11h) 關閉休眠模式 */Inf_LCD_WriteCmd(0x11);/* 14. 設置屏幕方向和RGB */Inf_LCD_WriteCmd(0x36);Inf_LCD_WriteData(0x08);Delay_ms(120);/* 14. display on */Inf_LCD_WriteCmd(0x29);}//2.寫命令(發出一個指令)看LCD數據手冊是8位的但是我們FSMC配是16位的,強轉就行
void LCD_WriteCmd(uint16_t cmd)
{*LCD_ADDR_CMD=cmd;
}//3.寫數據(發出一個數據)
void LCD_WriteData(uint16_t )data
{
*LCD_ADDR_DATA =data;
}//4.讀數據
uint16_t LCD_ReadData(void)
{
//總線占用權給LCD喂數據到這個地址上
return *LCD_ADDR_DATA;
}
具體的命令:
uint32_t LCD_ReadID(void)
{
//首先發送讀取ID的命令
LCD_WriteCmd(0x04);//要讀取3個字節的ID數據,定義變量接收數據
uint32_t id=0;
//主要第一次讀取的數據是無效的,就進行讀的一個空操作
LCD_ReadData();//依次讀取3個字節的數據,放置在id的相應位置
//高位的第1個字節
id |=(LCD_ReadData()& 0xff)<<16;
//第2個字節
id |=(LCD_ReadData()& 0xff)<<8;
//第3個字節
id |=(LCD_ReadData()& 0xff)<<0;}
列:?
行:?
//設置區域范圍,給定起始點的坐標,以及寬和高
void LCD_SetArea(uint16_t x,uint16_t y,uint16_t w,uint16_t h)
{
//發送命令,先設置列范圍
LCD_WriteCmd(0x2a);
//設置開始列
LCD_WriteData((x>>8)&0xff);//高8位
LCD_WriteData(x&0xff);//低8位//設置結束列
LCD_WriteData( ( (x+w-1)>>8 )&0xff);//高8位,減1為了
LCD_WriteData( (x+w-1) &0xff);//低8位//發送命令,再設置行范圍
LCD_WriteCmd(0x2b);//設置開始行
LCD_WriteData((y>>8)&0xff);//高8位
LCD_WriteData(Y&0xff);//低8位//設置結束行
LCD_WriteData( ( (y+h-1)>>8 )&0xff);//高8位
LCD_WriteData( (y+h-1) &0xff);//低8位}//清屏,顏色都是16位
void LCD_ClearAll(uint16_t color)
{
//設置區域范圍為全屏范圍
LCD_SetArea(0,0,LCD_W,LCD_H);//向對應區域范圍發送數據
LCD_WriteCmd(0x2c);//用循環遍歷所有的像素點寫入你想要以什么顏色進行清屏,依次寫入數據
for(uint16_t i=0;i<LCD_W*LCD_H ;i++)
{
LCD_WriteData(color);
}}
?用液晶顯示屏輸出字符:一堆像素點拼成字符,我要截取一個空間,很多行很多列,字符表示位是給1不是給0,每個字符對應的點陣,有對應的工具自行生成
#ifndef __LCDFONT_H
#define __LCDFONT_H
//其中1206指定是每個字節占用的位置為寬6高12,二維數組[][12]表示n個字符,每個字符都是用12行*1個字節表示
const unsigned char ascii_1206[][12] = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x12, 0x1C, 0x12, 0x3C, 0x00, 0x00}, /*"a",65*/{0x00, 0x03, 0x02, 0x02, 0x02, 0x0E, 0x12, 0x12, 0x12, 0x0E, 0x00, 0x00}, /*"b",66*///....由于內容過多,使用的時候可以自行填充
};
//有16行,每一行有8個點用1個字節表示,一個字符就占用16*1個字節表示,如果是24行12列的話就要用24*2(1行有12位,1個半字節用兩個字節表示)個字節表示一個字符
const unsigned char ascii_1608[][16] = {{0x00, 0x00, 0x00, 0x08, 0x08, 0x18, 0x14, 0x14, 0x24, 0x3C, 0x22, 0x42, 0x42, 0xE7, 0x00, 0x00}, /*"A",33*/{0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x42, 0x42, 0x42, 0x22, 0x1F, 0x00, 0x00}, /*"B",34*/{0x00, 0x00, 0x00, 0x7C, 0x42, 0x42, 0x01, 0x01, 0x01, 0x01, 0x01, 0x42, 0x22, 0x1C, 0x00, 0x00}, /*"C",35*///......
};const unsigned char ascii_2412[][48] = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*" ",0*/
//......}//32行32列,1行32個位,32*32/8=128個字節
const unsigned char chinese_3232[][128]={好(0) 運(1) 來(2){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x01,0x00,0x04,0x80,0x81,0xFF,0x0F,0x80,0x01,0x00,0x0E,0x80,0x00,0x00,0x03,0xC0,0x10,0x00,0x01,0xFC,0x3F,0x80,0x00,0xC0,0x10,0x60,0x00,0xC0,0x10,0x60,0x00,0x40,0x18,0x60,0x00,0x60,0x18,0x60,0x00,0x60,0x18,0x60,0x00,0x20,0x18,0x60,0x18,0x20,0xE8,0xFF,0x3F,0x30,0x0C,0x60,0x00,0x30,0x0C,0x60,0x00,0x10,0x0C,0x60,0x00,0x10,0x06,0x60,0x00,0x70,0x06,0x60,0x00,0x80,0x03,0x60,0x00,0x00,0x0F,0x60,0x00,0x00,0x3D,0x60,0x00,0x80,0x31,0x60,0x00,0xC0,0x20,0x60,0x00,0x20,0x00,0x60,0x00,0x10,0x00,0x3F,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00},/*"好",0*/
/* (32 X 32 , 宋體 )*/{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x60,0x00,0x00,0x03,0xC0,0xF0,0xFF,0x07,0xC0,0x01,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x80,0xFC,0xFF,0x1F,0xFC,0x01,0x04,0x00,0xC0,0x00,0x0E,0x00,0xC0,0x00,0x06,0x00,0xC0,0x00,0x03,0x00,0xC0,0x00,0x03,0x00,0xC0,0x80,0xC1,0x00,0xC0,0x80,0x80,0x01,0xC0,0x40,0x00,0x03,0xC0,0x20,0x00,0x06,0xC0,0xF0,0xFF,0x0F,0xC0,0xF0,0x00,0x0E,0xC0,0x00,0x00,0x04,0x30,0x01,0x00,0x00,0x1C,0x06,0x00,0x00,0x0C,0x3C,0x00,0x60,0x00,0xF0,0xFF,0x1F,0x00,0x80,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"運",1*/
/* (32 X 32 , 宋體 )*/{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x06,0xF0,0xFF,0xFF,0x0F,0x00,0x80,0x01,0x00,0x80,0x80,0x81,0x00,0x00,0x83,0x81,0x01,0x00,0x87,0xC1,0x01,0x00,0x8E,0xC1,0x00,0x00,0x8E,0x61,0x00,0x00,0x84,0x31,0x00,0x00,0x84,0x11,0x18,0xFC,0xFF,0xFF,0x3F,0x00,0xE0,0x05,0x00,0x00,0xE0,0x05,0x00,0x00,0xB0,0x09,0x00,0x00,0x98,0x19,0x00,0x00,0x98,0x11,0x00,0x00,0x8C,0x61,0x00,0x00,0x86,0xE1,0x00,0x00,0x83,0xC1,0x01,0x80,0x81,0x81,0x07,0x40,0x80,0x01,0x3E,0x30,0x80,0x01,0x1C,0x08,0x80,0x01,0x00,0x04,0x80,0x01,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00},/*"來",2*/
/* (32 X 32 , 宋體 )*/
}#endif
顯示字符:?以下圖的思想為例子,其他的以此類推
//上層操作接口
//顯示ASCII碼字符,起始位置(行列),有多少行,列就行的一半吧,因為lcdfont的文件里就是設置的行列大小關系
void LCD_WriteAsciiChar(uint16_t x,uint16_t y,uint16_t height,uint8_t c,uint16_t fontcolor,uint16_t backgroundcolor)
{
//先計算當前字符在數組中的位置
uint8_t index=c-'';//先確定區域
LCD_SetArea(x,y,height/2,heigth);//發送寫入顯存指令
LCD_WriteCmd(0x2c);
//遍歷整個區域判斷區域當前像素點是不是對應字符的筆畫,到點陣里找是1就是筆畫就給字符顏色,0就是非筆畫就給背景顏色
//按照字節大小,到不同的數組中尋找字符對應的點陣
//如果字體的1608或者1206,都是用一個字節表示一行點陣,核心是遍歷每一行每一列if(height=16||height=12){
for(uint8_t i=0;i<height;i++)
{
//根據字體大小在字庫中選擇當前行對應的i字節,這就是第i行的點陣uint8_t tempByte = (height ==16)?ascii_1608[index][i] :ascii_1206[index][i];//遍歷當前行的每一列(每個像素點,即字節中的每一位)
for(uint8_t j=0;j<height/2;j++)
{
//每次只取最低位,因為我用工具生成時選擇的
if( tempByte & 0x01)
{
//如果是1,顯示字體顏色
LCD_WriteData(fontcolor);
}
//如果是0,顯示背景顏色
else{LCD_WriteData(backgrountcolor);}
//右移一位
tempByte>>=1;
}}}else if (height==24){
//i為字節編號
for(uint8_t i=0;i<height;i++)
{
//根據字體大小在字庫中選擇當前行對應的i字節,這就是第i行的點陣
uint8_t tempByte = ascii_2412[index][i];//根據當前i的值,判斷字節中遍歷多少位數據(奇數的話只遍歷4位)
uint8_t jcount=(i%2)?4:8;//遍歷當前行的每一列(每個像素點,即字節中的每一位)
for(uint8_t j=0;j<jcount;j++)
{
//每次只取最低位,因為我用工具生成時選擇的
if( tempByte & 0x01)
{
//如果是1,顯示字體顏色
LCD_WriteData(fontcolor);
}
//如果是0,顯示背景顏色
else{LCD_WriteData(backgrountcolor);}
//右移一位
tempByte>>=1;
}}}//如果的3216,用兩個字節表示一行點陣
else if (height==32){
//i為字節編號
for(uint8_t i=0;i<height;i++)
{
//根據字體大小在字庫中選擇當前行對應的i字節,這就是第i行的點陣
uint8_t tempByte = ascii_2412[index][i];//遍歷當前行的每一列(每個像素點,即字節中的每一位),當前字節中每一位
for(uint8_t j=0;j<8;j++)
{
//每次只取最低位,因為我用工具生成時選擇的
if( tempByte & 0x01)
{
//如果是1,顯示字體顏色
LCD_WriteData(fontcolor);
}
//如果是0,顯示背景顏色
else{LCD_WriteData(backgrountcolor);}
//右移一位
tempByte>>=1;
}}}}
顯示字符串代碼:
void LCD_WriteAsciiString (uint16_t x,uint16_t y,uint16_t height,uint8_t *str,uint16_t fontcolor,uint16_t backgroundcolor)
{
uint8_t i=0;while(str[i] !='\0')
{
//判斷是否遇到\n需要換行,如果沒有遇到直接增加x,繼續顯示
if(str[i]!='\n'){
//另外判斷一下是否到達當前行的末尾如果到達自動換行
if(x+height/2>LCD_W){
//換行
x=0;y+=height;}
LCD_WriteAsciiChar(x,y,height, str[i] , fontcolor,backgroundcolor);
x+=height/2;}
else{
//如果str字符內有換行符
x=0;y+=height;}
i++;
}}
顯示漢字:在英文例子的本質上都是一樣的
void LCD_WriteChineseChar(uint16_t x,uint16_t y,uint16_t height,uint8_t index,uint16_t fontcolor,uint16_t backgroundcolor)
{
//設置顯示區域
LCD_SetArea(x,y,height,height);//發送指令
LCD_WriteCmd(0x2c);
//我給的例子里中文字體的就32*32的,所以這里height就定死32了,i能遍歷的最大值也就128次了
for(uint8_t i=0;i<128;i++)
{
//根據字體大小在字庫中選擇當前行對應的i字節,這就是第i行的點陣
uint8_t tempByte = chinese_3232[index][i];//遍歷當前行的每一列(每個像素點,即字節中的每一位),當前字節中每一位
for(uint8_t j=0;j<8;j++)
{
//每次只取最低位,因為我用工具生成時選擇的
if( tempByte & 0x01)
{
//如果是1,顯示字體顏色
LCD_WriteData(fontcolor);
}
//如果是0,顯示背景顏色
else{LCD_WriteData(backgrountcolor);}
//右移一位
tempByte>>=1;
}}}}
}
顯示圖片:加載一個圖片展示到液晶屏中
我用工具生成時選擇的,所以低位在后,高位在前,
注意:每次取兩個字節拼接成一個像素點的RGB數據(因為我們用工具生成的時候勾選的16位表示RGB,而拼接字符的像素點的RGB也是16位的(可以看宏定義的顏色))?
const unsigned char gImage_logo[115200] = { /* 0X10,0X10,0X00,0XF0,0X00,0XF0,0X01,0X1B, */
0X20,0X60,0X20,0X60,0X20,0X60,0X20,0X60,0X20,0X60,0X20,0X60,0X20,0X60,0X20,0X60,
......英文過多數據我直接省略
0X94,0X0E,0X94,0X0E,0X8B,0XED,0X8B,0XAD,0X8B,0XAD,0X8B,0XAD,0X8B,0XAE,0X8B,0XCE,
};
//我們不考慮延展和壓縮,直接按照原圖像素展示
void LCD_Displaypicture(uint16_t x,uint16_ y)
{
//設置顯示區域
LCD_SetArea(x,y,240,240);//發送指令
LCD_WriteCmd(0x2c);uint16_t len=sizeof(gImage_logo);
//兩個字節,兩個字節的判斷所以i+=2
for(uint16_t i=0;i<len;i+=2)
{
//因為我用工具生成時選擇的,所以低位在后,高位在前,每次取兩個字節拼接成一個像素點的RGB數據(因為我們用工具生成的時候勾選的16位表示RGB,而字符的像素大小我們選的是8位的)
uint16_t tempByte=gImage_logo[i]<<8+gImage_logo[i+1];LCD_WriteData(p);
}}
思想總結:本質都是像素點,就是給每個像素點填不同的顏色(16位RGB)就可以展示我們想要的效果到LCD上。字符和畫圖代碼上有差異但都為本質服務
自己手動畫圖:這個我后面再補更吧,畢竟前面的代碼已經足夠新手用的了,其實也可以將你想要的圖形以圖片的形式展出
//畫出一個點,給定左上角起始點是坐標,以及放點的寬度
void LCD_DrawPoint(uint16_t x,uint16_t y,uint16_t height,uint16_t color)
{
//設置區域
LCD_SetArea(x,y,height,height);//發送命令
LCD_WriteCmd(0x2c);//將區域內的所有像素點涂上給定的顏色,至于液晶先掃描行還是列是LCD一開始配置時就定好了,我們不用管,默認是行掃描,它會自動換行的
for(uint16_t i=0;i<height*height;i++)
{
LCD_WriteData(color);
}
}//畫線,給定起點和終點的坐標void LCD_DrawPoint(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t w,uint16_t color)
{
//計算直線方程中的k和b,y=kx+b,k=(y1-y2)/(x1-x2),b=y1-k*x1
//先考慮x1=x2的情況,一條豎線
if(x1==x2){for(uint16_t i=0;i<count ;i++)}
}