FSMC控制LCD(TFTLCD:Z350IT002)顯示案例

顯存不一定要擦除,只要來一個地址就可以對其進行讀寫,而且一般的需求是不停的寫入(不同的像素點給不同的值),所以是RAM(flash和E2PROM要擦除才能寫入),由于FSMC沒有DRAM所以我們只能選用SRAM

我們要哪個地址塊,要看硬件連接的設定

LCD硬件電路圖:液晶模塊Z350IT002內部使用的控制芯片是:ILI9486

  1. D0-D15是16位數據總線接口。分別接FSMC的D0-D15。
  2. RST是LCD復位引腳,低電平復位。接LCD-RST(PG15)。
  3. RD是讀控制引腳,上升沿時讀數據。接FSMC-NOE(PD4)。
  4. WR是寫控制引腳,上升沿時寫數據。接FSMC-NWE(PD5)。
  5. 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機制完成硬件級抽象,提高傳輸效率,簡化地址管理
  6. CS是片選引腳,低電平有效。接FSMC-NE4(PG12)。所以其對應的STM32的地址就是bank1的第4塊地址

    地址映射范圍:

    子塊編號地址范圍對應使能引腳引腳物理連接
    Bank1-10x60000000-0x63FFFFFFNE1如PD7(具體型號可能不同)
    Bank1-20x64000000-0x67FFFFFFNE2如PG9
    Bank1-30x68000000-0x6BFFFFFFNE3如PG10
    Bank1-40x6C000000-0x6FFFFFFFNE4如PG12
  7. LEDA是背光電源(3.0V-3.4V)引腳。
  8. LEDK是背光亮度控制引腳。通過LCD-BG(PB0來驅動MOS管Q5的導通電流。可以通過給LCD-BG輸出PWM波來控制背光的亮度。占空比越大,背光就會越亮。
  9. 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++)}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/910633.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/910633.shtml
英文地址,請注明出處:http://en.pswp.cn/news/910633.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

云原生周刊:Argo CD v3.1 正式發布

開源項目推薦 Kubewall Kubewall 是一個輕量級的開源 Kubernetes 儀表盤&#xff0c;支持多集群管理&#xff0c;主打單二進制部署和瀏覽器訪問&#xff0c;提供實時資源監控、YAML 編輯、拓撲視圖、日志查看等功能。它使用 Go 與 React 構建&#xff0c;支持通過 Docker、He…

Aerotech系列(3)開發庫介紹

庫對象模型 名空間列表 NamespaceDescriptionAerotech.A3200 The main namespace of the Aerotech A3200 .NET library Aerotech.A3200.Callbacks Contains the classes that allow interacting with callbacks Aerotech.A3200.Commands Contains the classes that allows …

Spring--IOC容器的一些擴展屬性

一、BeanFactoryPostProcessor和BeanPostProcessor BeanFactoryPostProcessor的作用是在實例化前修改BeanDefinition的屬性 BeanPostProcessor的作用是在bean完成創建實例、填充屬性之后&#xff0c;初始化階段的前后都會對bean進行操作&#xff0c;使用postProcessBeforeIni…

8w字:推薦系統技術體系深度解析:從理論基礎到工業實踐的完整指南

插話&#xff1a;剛接觸推薦系統還是大一下作比賽&#xff0c;然后找資料&#xff0c;順便在巧合下在“識典百科”&#xff08;現在叫快懂百科&#xff0c;抖音的&#xff0c;改好幾回名了&#xff0c;還要一條條插入引用資料&#xff0c;現在看來&#xff0c;好像抖音也不在乎…

RA4M2開發IOT(8)----IIC驅動OLED

RA4M2開發IOT.8--IIC驅動OLED 概述視頻教學樣品申請硬件準備參考程序修改IIC驅動OLED屬性配置移植SSD1306字符取模ASCII顯示圖片取模顯示圖片 概述 本章旨在通過 IC 接口驅動 OLED 顯示屏&#xff08;常見型號如 SSD1306&#xff09;&#xff0c;實現圖形和文本的顯示功能。OL…

數組題解——?輪轉數組【LeetCode】

189. 輪轉數組 通過三次反轉操作&#xff0c;可以實現數組的輪轉&#xff1a; 反轉整個數組: 將數組完全反轉&#xff0c;使得原數組的后 k 個元素移動到數組的前面。反轉前 k 個元素: 將前 k 個元素反轉&#xff0c;恢復它們的原始順序。反轉后 n - k 個元素: 將后 n - k 個元…

AR 眼鏡之-條形碼識別-實現方案

目錄 &#x1f4c2; 前言 AR 眼鏡系統版本 條形碼識別 1. &#x1f531; 技術方案 1.1 技術方案概述 1.2 實現方案 1&#xff09;相機App顯示模塊 2&#xff09;算法so庫JNI模塊 3&#xff09;算法條形碼識別模塊 2. &#x1f4a0; 實現相機App顯示模塊 2.1 創建 Ba…

華為云 Flexus+DeepSeek 征文|基于 CCE 集群部署 Dify 平臺工作流:科研論文翻譯與 SEO 優化工具的全流程設計實踐

華為云 FlexusDeepSeek 征文&#xff5c;基于 CCE 集群部署 Dify 平臺工作流&#xff1a;科研論文翻譯與 SEO 優化工具的全流程設計實踐 背景 作為被科研論文折磨已久的大學生&#xff0c;希望研究成果能被更多人看到&#xff0c;尤其是在學術全球化的趨勢下&#xff0c;論文翻…

C++對象繼承詳解:從入門到精通

繼承是面向對象編程的三大特性之一&#xff0c;也是C中實現代碼復用和多態的重要機制。本文將帶你深入理解C繼承的核心概念與應用。 一、繼承的基本概念 1.1 什么是繼承&#xff1f; 繼承允許我們基于已有的類創建新類&#xff0c;新類&#xff08;派生類&#xff09;可以繼…

Jenkins安裝與配置全攻略:從入門到高級功能實戰

在DevOps實踐中,Jenkins作為最流行的持續集成工具之一,扮演著至關重要的角色。本文將全面介紹Jenkins的安裝、配置及高級功能使用,幫助開發、運維和測試團隊快速搭建高效的CI/CD流水線。 一、Jenkins安裝 1.1 環境準備 Jenkins官網:https://jenkins.io 注意:Jenkins 2…

[OS_26] 計算機系統安全 | CIA原則 | 側信道攻擊

系統調用是唯一訪問操作系統對象的途徑 拒絕越權訪問 →→ Confidentiality拒絕越權修改 →→ Integrity(再加上公平資源調度 →→ Availability) 在操作系統 API 上&#xff0c;我們可以構建命令行工具、編譯器、數據庫、瀏覽器等豐富的應用。 當越來越多用戶開始共享計算機、…

Chromium 136 編譯指南 macOS篇:編譯優化技巧(六)

1. 引言 在現代軟件開發的高效化進程中&#xff0c;編譯優化已經從簡單的性能調優發展為一門綜合性的工程科學。對于Chromium 136這樣一個包含超過2500萬行代碼的超大規模項目而言&#xff0c;編譯時間往往成為制約開發效率的關鍵瓶頸。在典型的開發場景中&#xff0c;一次完整…

Spark教程6:Spark 底層執行原理詳解

文章目錄 一、整體架構概述二、核心組件詳解1. SparkContext2. DAG Scheduler3. Task Scheduler4. Executor 三、作業執行流程1. DAG 生成與 Stage 劃分2. Task 調度與執行3. 內存管理 四、Shuffle 機制詳解1. Shuffle 過程2. Shuffle 優化 五、內存管理機制1. 統一內存管理&am…

xlsx-style 插件批量導出多個sheet表格excel中遇到的問題及解決

Vue2中 前端界面導出表格&#xff0c;使用XLSXS插件版本(^0.8.13)導出表格存在表格背景顏色無法正常展示&#xff0c;百分比數據沒有正常展示 【有條件的盡量先升級高版本插件&#xff0c;此插件版本對樣式支持度不夠】 優先考慮插件版本升級 同樣的使用方法在vue3中沒有出現錯…

Java后端與Vue前端項目部署全流程:從環境配置到Nginx反向代理

文章目錄 1. 準備項目所需的環境2. 后端項目打包步驟 1&#xff1a;使用 Maven 打包步驟 2&#xff1a;定位生成的 JAR 包步驟 3&#xff1a;上傳 JAR 包到 Linux 系統步驟 4&#xff1a;驗證 Java 環境步驟 5&#xff1a;啟動 JAR 包 3. 前端項目打包步驟 1&#xff1a;執行 B…

Mybatis踩坑之一天

background: 對接AML系統&#xff0c;日間實時需要送交易對手要素過去&#xff08;目前主要是交易對手全名&#xff09;&#xff0c;夜間需要將歷史交易送AML進行回溯&#xff0c;交互方式是文件。文件要素為日期、對手類型、對手名、交易流水之類。 設置對送AML的文件設計表…

【PyTorch】分布式訓練報錯記錄-ERROR:torch.distributed.elastic.multiprocessing.api:failed (exitcode: 1)

最近&#xff0c;我在服務器上起基于PyTorch分布式框架的預訓練實驗&#xff0c;起初實驗都在順利進行&#xff0c;但是當我們把模型的深度與寬度調大之后&#xff0c;模型在訓練幾代之后便會出現如下的報錯&#xff1a; WARNING:torch.distributed.elastic.multiprocessing.a…

有哪些詞編碼模型

有哪些詞編碼模型 詞編碼模型:是將自然語言符號映射為稠密的高維向量,使語義相近的詞匯在向量空間中位置接近。 不過,也有部分模型會考慮字母或字符信息,如基于字節對編碼(BPE)的模型會將單詞拆分成子詞,這里的子詞可能是字母組合。 詞編碼模型的原理主要是通過機器學…

Mono 功能介紹與使用示例

Mono 功能介紹與使用示例 一、核心概念與特性 Mono 是 Spring Reactor 框架中的核心組件&#xff0c;屬于響應式編程&#xff08;Reactive Programming&#xff09;模型&#xff0c;專注于處理包含 0 或 1 個元素 的異步序列[1][2][5]。其核心特點包括&#xff1a; 異步非阻…

5060Ti雙顯卡+LLaMA-factory大模型微調環境搭建

查看環境確定安裝版本安裝CUDA12.8安裝Anaconda安裝Visual Studio C桌面開發環境&#xff08;編譯llama.cpp需要&#xff09;安裝cmake(編譯llama.cpp需要)安裝llama.cpp(用于量化)安裝huggingface-cli安裝llama-factory安裝PyTorch2.7.0安裝bitsandbytes安裝flash-attention加…