
簡介本資源是面向嵌入式開發初學者與STM32項目實踐者的ST7567驅動解決方案專為驅動128×64點陣單色LCDLCD12864設計解決在無專用LCD庫或硬件SPI受限場景下的顯示控制難題。壓縮包僅含2個核心文件C語言實現的LcdDriver.c封裝初始化、指令/數據寫入、屏幕刷新等關鍵函數和配套頭文件LcdDriver.h定義寄存器地址、命令宏及接口聲明總大小11KB輕量易集成。已有307人學習下載適用于STM32平臺同時兼容標準SPI與軟件模擬SPI——后者特別適配引腳資源緊張或無硬件SPI外設的MCU型號。讀者可直接移植至Keil/STM32CubeIDE工程結合ST7567控制器時序手冊快速完成顯示適配代碼結構清晰、注釋完整便于理解SPI通信機制、LCD底層驅動邏輯及圖形顯示原理是嵌入式GUI入門與硬件接口實戰的實用參考。1. 用 STM32 驅動 12864 LCDST7567 控制器SPI 協議不是“接上就能亮”時序、片選、初始化序列缺一不可你手頭有一塊常見的 12864 圖形點陣液晶屏背面絲印寫著 ST7567引腳標著 VDD、VSS、CS、RS、SCLK、SDA —— 這不是一塊能靠“寫幾個 GPIO 拉高拉低”就點亮的模塊。它依賴嚴格的 SPI 通信協議且 ST7567 的初始化流程包含至少 12 條寄存器寫入指令順序錯一條屏幕就黑屏或顯示亂碼。很多工程師在 CubeMX 里配好 SPI 外設、生成 HAL 代碼后仍無法驅動問題往往出在CS片選信號未按 ST7567 數據手冊要求在每個字節傳輸前后精確置低/置高SPI 模式被誤設為 Mode 0 而非必需的 Mode 3CPOL1, CPHA0或未在初始化前執行關鍵的“電子復位”即拉低 RES 引腳 10ms 以上。本文面向已掌握 STM32 基礎外設但卡在 LCD 驅動環節的嵌入式開發者不講抽象協議只拆解從硬件連接到首幀圖像渲染的完整鏈路覆蓋 cubemx spi 配置陷阱、ST7567 初始化參數表、SPI 硬件片選與軟件片選的實際取舍以及如何用邏輯分析儀驗證 spi 時序是否達標。2. ST7567 控制器與 12864 LCD 的硬件綁定關系及 SPI 接口選型依據2.1 為什么必須是 ST7567它和常見 SSD1306/OLED 的根本差異12864 LCD 是一種 128×64 像素的單色圖形液晶模組其核心控制器型號直接決定驅動方式。ST7567 是 Epson 推出的 COGChip-on-Glass型 LCD 控制器專為段碼/點陣 LCD 設計支持 1/65 偏壓、內置 64×128 顯存、提供 8-bit 并行與 4-wire SPI 兩種接口。它與 SSD1306OLED、ST7735TFT存在本質區別ST7567 不支持自動地址遞增需手動更新 RAM 地址指針無硬件滾動功能且所有命令/數據均需通過“指令模式”與“數據模式”切換發送——這正是 RSRegister Select引腳存在的意義。若誤用 SSD1306 的驅動代碼加載到 ST7567 屏上最典型現象是全屏白塊或垂直條紋因為顯存映射方式page/column 尋址 vs. x/y 坐標尋址完全不同。網絡熱詞中頻繁出現的 “spi協議” 在此處具象化為ST7567 要求 SCLK 在空閑時為高電平CPOL1數據在 SCLK 下降沿采樣CPHA0即 SPI Mode 3而多數默認配置為 Mode 0這是導致通信失敗的第一高頻原因。2.2 STM32 端 SPI 外設選型硬件片選NSSvs 軟件控制 GPIOST7567 的 CSChip Select引腳功能是使能當前設備參與 SPI 總線通信。STM32 提供兩種控制方式硬件 NSS由 SPI 外設自動管理需將 CS 連接到對應 SPI 的 NSS 引腳如 SPI1_NSS并在 CubeMX 中啟用NSS Hardware。優點是時序精準、釋放 CPU缺點是僅限固定引腳且部分 STM32 型號如 F103C8T6的 SPI1_NSS 與 SWDIO 沖突調試時易被占用。軟件 GPIO 片選推薦用于 12864將 CS 連接到任意 GPIO如 PB0初始化時設為推挽輸出并置高每次傳輸前拉低、傳輸后拉高。優勢在于引腳自由、可精確控制 CS 電平寬度ST7567 要求 CS 低電平持續時間 ≥ 100ns且在 SCLK 邊沿前后需穩定便于適配多設備共用 SPI 總線場景。提示ST7567 數據手冊明確要求“CS must be low during data transfer and high otherwise”。若使用硬件 NSS需確認 CubeMX 生成的HAL_SPI_Transmit()是否在函數入口/出口自動操作 NSSHAL 庫默認行為是不自動管理 NSS即使配置為 Hardware必須手動調用HAL_SPI_EnableNss()或在SPI_HandleTypeDef中設置Init.NSS SPI_NSS_SOFT并全程用 GPIO 控制。2.3 關鍵引腳連接與電平匹配驗證ST7567 引腳STM32 引腳示例功能說明注意事項VDD3.3V電源必須 3.3V5V 會擊穿VSSGND地共地避免噪聲CSPB0GPIO 輸出片選上電默認高電平RSPB1GPIO 輸出寄存器/數據選擇指令RS0數據RS1SCLKPA5SPI1_SCKSPI 時鐘頻率 ≤ 10MHzST7567 最大支持SDAMOSIPA7SPI1_MOSI主機輸出無需 MISOST7567 為單向寫入RESPB10GPIO 輸出復位上電后需拉低 ≥10ms再拉高實際布線中SCLK 與 SDA 走線長度應盡量相等遠離高頻干擾源如開關電源、電機驅動。若使用長排線連接建議在 SDA 和 SCLK 線上各串接 33Ω 電阻抑制振鈴——這是排查“偶發亂碼”的關鍵物理層措施。3. CubeMX 配置與 HAL 庫驅動代碼實現繞過初始化陷阱的最小可行路徑3.1 CubeMX 中 SPI1 的精確配置步驟以 STM32F103C8T6 為例開啟 SPI1在 Connectivity → SPI1 中勾選Mode 選擇Full-Duplex Master參數設置Prescaler設為2對應 APB2 時鐘 36MHz → SCLK 18MHz但 ST7567 實際用 4MHz 更穩妥Data Size8 BitsClock Polarity (CPOL)High即 CPOL1Clock Phase (CPHA)1 Edge即 CPHA0注意 HAL 庫文檔中 1 Edge 對應下降沿采樣NSS SignalSoftware強制禁用硬件 NSSBaud Rate Prescaler8最終 SCLK 36MHz / 8 4.5MHz在 ST7567 的 10MHz 范圍內且留有余量GPIO 分配PA5 → SPI1_SCKAlternate Function Push-PullPA7 → SPI1_MOSIAlternate Function Push-PullPB0 → CSGPIO Output Push-PullInitial State HighPB1 → RSGPIO Output Push-PullInitial State HighPB10 → RESGPIO Output Push-PullInitial State High生成代碼勾選Generate peripheral initialization as a pair of .c/.h files點擊 Generate。注意CubeMX 生成的MX_SPI1_Init()函數中hi2s1.Init.NSS默認為SPI_NSS_HARD必須手動改為SPI_NSS_SOFT否則 HAL 庫會嘗試控制硬件 NSS 引腳導致 CS 電平失控。3.2 ST7567 初始化序列12 條指令的執行邏輯與參數表ST7567 上電后需執行嚴格順序的初始化指令任何跳過或顛倒都將導致顯存未就緒。以下為最小必要序列基于官方 datasheet Rev.1.1步驟指令字節Hex參數字節Hex功能說明關鍵約束10xE2—軟件復位必須第一條RES 硬復位后仍需此指令20xA2—設置偏壓比為 1/9若為 1/7 則對比度異常30xA0—設置 SEG 方向正常0xA1為鏡像40xC0—設置 COM 方向正常0xC8為鏡像50x40—設置起始行0~63通常為0x40第 0 行60x280x00設置放大倍率1x0x012x,0x023x70x2C—退出放大模式必須緊跟0x28后執行80x26—設置 V5 電壓調節啟用內部電阻分壓90x810x20設置電子音量0x00~0x3F0x20為中等亮度100xAF—開啟顯示屏幕在此步后才可能亮起110xB0—設置頁地址Page 0后續寫入從 Page 0 開始120x10—設置列地址高位Column 0與0x00組合為列 0// st7567_init.c - 初始化函數需在 HAL_SPI_MspInit() 后調用 void ST7567_Init(void) { // 1. 硬件復位拉低 RES 10ms HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET); // 2. 發送初始化序列 uint8_t init_cmds[] {0xE2, 0xA2, 0xA0, 0xC0, 0x40, 0x28, 0x2C, 0x26, 0x81, 0xAF, 0xB0, 0x10}; uint8_t init_params[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; for (int i 0; i 12; i) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // CS low HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); // RS 0 (command) HAL_SPI_Transmit(hspi1, init_cmds[i], 1, HAL_MAX_DELAY); if (init_params[i] ! 0x00) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); // RS 1 (data) HAL_SPI_Transmit(hspi1, init_params[i], 1, HAL_MAX_DELAY); } HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // CS high HAL_Delay(1); // 每條指令間加 1ms 延時確保控制器響應 } }代碼邏輯說明HAL_GPIO_WritePin()控制 CS 和 RS確保每條指令前 CS 拉低、RS 根據指令/數據切換HAL_SPI_Transmit()發送單字節因 ST7567 的 SPI 接口為 4-wire 模式無 MISO無需接收HAL_Delay(1)是安全冗余ST7567 內部指令執行時間最長為 100μs1ms 可覆蓋所有情況參數0x81后必須跟0x20否則電子音量為 0屏幕全黑這是新手最常忽略的坑。3.3 繪制單個像素顯存地址計算與逐頁寫入機制ST7567 的顯存為 64×128 bit按“頁Page”組織每頁 8 行0~7共 8 頁Page 0~7每頁含 128 字節對應 128 列。要點亮坐標 (x, y) 的像素需計算頁號page y / 8計算頁內行偏移bit_pos y % 8計算字節地址byte_index x構造掩碼mask 1 bit_pos讀-改-寫讀出當前字節置位對應 bit寫回。但 ST7567不支持隨機讀取顯存無 MISO 引腳因此實際采用“全頁擦寫”策略先發送0xB0 page設置頁地址再發送0x10 (x4)設置列高位0x00 (x0x0F)設置列低位然后連續發送 128 字節數據。以下為繪制單點的簡化版假設已清屏// 繪制點 (x, y)x∈[0,127], y∈[0,63] void ST7567_DrawPixel(uint8_t x, uint8_t y) { uint8_t page y / 8; uint8_t bit y % 8; uint8_t mask 1 bit; // 設置頁地址 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); uint8_t cmd_page 0xB0 | page; HAL_SPI_Transmit(hspi1, cmd_page, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 設置列地址高位低位 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); uint8_t cmd_col_h 0x10 | (x 4); HAL_SPI_Transmit(hspi1, cmd_col_h, 1, HAL_MAX_DELAY); uint8_t cmd_col_l x 0x0F; HAL_SPI_Transmit(hspi1, cmd_col_l, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 發送 128 字節數據僅第 x 字節置位 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); uint8_t buffer[128] {0}; buffer[x] | mask; HAL_SPI_Transmit(hspi1, buffer, 128, HAL_MAX_DELAY); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); }參數說明cmd_page0xB0是頁地址設置指令| page將頁號嵌入低 3 位cmd_col_h/l列地址分高低 4 位發送0x10為高位指令0x00為低位指令buffer[x] | mask構造單點數據其余字節為 0實現“點亮一個像素”此函數效率低每次寫 128 字節實際應用中應維護本地顯存緩沖區批量修改后再刷屏。4. 調試與驗證用邏輯分析儀抓取 SPI 時序定位 CS/RS 電平錯誤4.1 使用 Saleae Logic 16 抓取真實 SPI 波形的關鍵設置當屏幕無反應時最高效的方式是用邏輯分析儀驗證物理層信號。針對 ST7567 的 SPI 通信需捕獲四路信號SCLK、MOSI、CS、RS。設置要點如下采樣率≥ 50MS/s確保能解析 4.5MHz SCLK 的邊沿觸發條件設置CS Falling觸發因為每次傳輸始于 CS 下降沿協議解碼在 Saleae 軟件中添加SPI解碼器手動配置Clock polarityActive HighCPOL1Clock phaseSample on FallingCPHA0Bit orderMSB firstCS pin選擇 CS 通道觀察重點CS 低電平寬度是否 ≥ 100nsSCLK 空閑狀態是否為高電平MOSI 數據是否在 SCLK 下降沿穩定即 CPHA0 成立RS 電平是否在每字節傳輸前正確切換指令前 RS0數據前 RS1。若解碼結果顯示“Unknown”或數據錯亂90% 概率為 CPOL/CPHA 配置錯誤。此時需返回 CubeMX 修改 SPI 參數并重新生成代碼。4.2 常見故障現象與對應排查表現象可能原因驗證方法解決方案屏幕全黑無任何反應RES 未正確復位或0xAF開啟顯示未發送用萬用表測 RES 引腳上電后是否經歷低→高跳變邏輯分析儀查0xAF是否發出檢查 PB10 硬件連接確認初始化序列包含0xAF屏幕全白或灰屏電子音量0x81參數過小如0x00或偏壓比0xA2錯為0xA3邏輯分析儀查0x81后是否跟0x20查0xA2是否誤發0xA3修改 init_params[8] 0x20確保指令為0xA2顯示垂直條紋或錯位列地址設置錯誤0x10/0x00未發送或頁地址0xB0后未發送數據抓取波形看0xB0后是否有連續 128 字節 MOSI 數據確保ST7567_DrawPixel()中列地址指令完整發送畫面閃爍或間歇性消失CS 電平在傳輸中意外變高或 SCLK 頻率超限邏輯分析儀查 CS 在 SCLK 期間是否保持低電平測 SCLK 頻率優化 GPIO 控制時序避免中斷打斷降低 Baud Rate Prescaler4.3 用 STM32CubeIDE 的 SWV ITM 輸出實時調試信息在初始化函數中插入 ITM 調試語句可免接邏輯分析儀快速定位卡點// 在 MX_GPIO_Init() 后添加 CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; ITM-LAR 0xC5ACCE55; // 解鎖 ITM ITM-TCR | ITM_TCR_ITMENA_Msk; // 使能 ITM ITM-TER[0] | 1; // 使能端口 0 // 在 ST7567_Init() 中每步后添加 ITM_SendChar(1); // 步驟1完成 ITM_SendChar(2); // 步驟2完成 // ...在 STM32CubeIDE 的Console視圖中啟用SWV ITM Data Console若看到12345...連續輸出說明初始化執行到某步中斷結合字符停止位置即可精確定位失敗指令。5. 進階技巧構建輕量級顯存緩沖區與中文字符渲染5.1 本地顯存Frame Buffer設計1KB 內存換取 10 倍刷新效率前述ST7567_DrawPixel()每次操作都寫入 128 字節效率極低。實際項目中應申請 1KB 內存作為顯存鏡像// 定義全局顯存緩沖區64×128 bit 1024 bytes uint8_t lcd_buffer[1024] {0}; // 初始化為全 0黑屏 // 更新緩沖區后一次性刷屏 void ST7567_Refresh(void) { for (uint8_t page 0; page 8; page) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); uint8_t cmd_page 0xB0 | page; HAL_SPI_Transmit(hspi1, cmd_page, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); HAL_SPI_Transmit(hspi1, lcd_buffer[page * 128], 128, HAL_MAX_DELAY); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); } }優勢CPU 修改lcd_buffer無需 SPI 通信ST7567_Refresh()僅在需要時調用適合動畫、菜單等場景。5.2 16×16 點陣中文字符渲染GB2312 編碼與字模提取12864 屏幕常用于顯示中文需處理 GB2312 編碼。一個實用技巧是將常用漢字字模16×1632 bytes預存在 Flash 中通過查表渲染// 示例漢字“測”GB2312 區位碼 10-21 → 0x2A35 const uint8_t hanzi_cha[32] { 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 }; // 實際字模需從字庫文件提取 // 在 lcd_buffer 中繪制漢字左上角 x,y void ST7567_DrawHanzi(uint8_t x, uint8_t y, const uint8_t *font) { for (uint8_t row 0; row 16; row) { for (uint8_t col 0; col 2; col) { // 每行 2 字節 uint8_t byte font[row * 2 col]; for (uint8_t bit 0; bit 8; bit) { uint8_t px_x x col * 8 bit; uint8_t px_y y row; if (px_x 128 px_y 64) { uint8_t page px_y / 8; uint8_t bit_pos px_y % 8; uint8_t mask 1 bit_pos; if (byte (1 (7 - bit))) { // MSB 在前 lcd_buffer[page * 128 px_x] | mask; } else { lcd_buffer[page * 128 px_x] ~mask; } } } } } }參數說明hanzi_cha是“測”字的 16×16 點陣字模每行 2 字節16 bits共 32 字節ST7567_DrawHanzi()將字模逐位映射到lcd_buffer支持任意坐標byte (1 (7 - bit))處理字模的 MSB 優先存儲格式確保筆畫方向正確此方法避免了動態編碼轉換適用于資源受限的 STM32F1 系列。用ST7567_DrawHanzi(10, 10, hanzi_cha)即可在屏幕 (10,10) 位置顯示“測”字調用ST7567_Refresh()刷新即可。本文還有配套的精品資源點擊獲取