GT20L16S1Y字库芯片比较常用,现把调试通过的Arduino代码记录一下。以下代码需使用U8G2图形库。
接线方面字库芯片和LCD屏共用SPI接口,以CS引脚区分数据传输。
#include <Arduino.h>
#include <U8g2lib.h>
#include <SPI.h>
// --- 硬件定义 ---
#define PIN_LCD_CS 7
#define PIN_LCD_DC 6
#define PIN_LCD_RST 11
#define PIN_FONT_CS 5
// --- U8g2 初始化 ---
// 你的屏幕构造函数
U8G2_ST7565_NHD_C12864_1_4W_HW_SPI u8g2(U8G2_R0, PIN_LCD_CS, PIN_LCD_DC, PIN_LCD_RST);
// 汉字点阵缓冲区 (16x16点阵 = 32字节)
uint8_t fontBuffer[32];
// 英文字符点阵缓冲8*16
uint8_t asciiBuffer[16];
void setup() {
// 初始化串口 (调试用)
Serial.begin(115200);
// 初始化字库芯片引脚
pinMode(PIN_FONT_CS, OUTPUT);
digitalWrite(PIN_FONT_CS, HIGH); // 默认拉高,取消选中
// 初始化 SPI
SPI.begin();
// 初始化屏幕
u8g2.begin();
}
// 从GT20L16S1Y读取16x16汉字点阵数据
void readFontData(uint8_t msb, uint8_t lsb, uint8_t *buffer) {
unsigned long address;
// GT20L16S1Y GB2312 地址计算公式:
// BaseAddr + ((MSB - 0xB0) * 94 + (LSB - 0xA1)) * 32
if (msb >= 0xA1 && msb <= 0xA3 && lsb >= 0xA1) {
address = ((unsigned long)(msb - 0xA1) * 94 + (lsb - 0xA1)) * 32;
} else if (msb >= 0xB0 && msb <= 0xF7 && lsb >= 0xA1) {
address = ((unsigned long)(msb - 0xB0) * 94 + (lsb - 0xA1) + 846) * 32;
} else if (msb == 0xA9 && lsb >= 0xA1) {
address = (282 + (lsb - 0xA1)) * 32;
} else {
address = 0;
}
// 开始SPI事务读取字库
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); // GT20L16S1Y支持高达30MHz+
digitalWrite(PIN_FONT_CS, LOW);
// 发送读取命令 0x03
SPI.transfer(0x03);
// 发送地址 (24位地址,高位在前)
SPI.transfer((address >> 16) & 0xFF);
SPI.transfer((address >> 8) & 0xFF);
SPI.transfer(address & 0xFF);
// 读取32个字节的点阵数据
for (int i = 0; i < 32; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(PIN_FONT_CS, HIGH);
SPI.endTransaction();
}
// 从GT20L16S1Y读取ASCII8x16点阵数据
void readASCIIData(uint8_t asciichar, uint8_t *buffer) {
unsigned long address = (asciichar-0x20)*16 + 0x3cf80;
// 开始SPI事务读取字库
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); // GT20L16S1Y支持高达30MHz+
digitalWrite(PIN_FONT_CS, LOW);
// 发送读取命令 0x03
SPI.transfer(0x03);
// 发送地址 (24位地址,高位在前)
SPI.transfer((address >> 16) & 0xFF);
SPI.transfer((address >> 8) & 0xFF);
SPI.transfer(address & 0xFF);
// 读取32个字节的点阵数据
for (int i = 0; i < 16; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(PIN_FONT_CS, HIGH);
SPI.endTransaction();
}
// 手动打点绘制汉字
void drawChineseChar(int x, int y, uint8_t msb, uint8_t lsb) {
readFontData(msb, lsb, fontBuffer);
for (int i = 0; i < 32; i++) {
uint8_t byteData = fontBuffer[i];
for (int bit = 0; bit < 8; bit++) {
if (byteData & (0x01 << bit)) {
// 根据具体的排列方式计算 x, y
u8g2.drawPixel(x + (i % 16) , y + (i / 16) * 8 + bit);
}
}
}
}
// 手动打点绘制ascii字符
void drawASCIIChar(int x, int y, uint8_t asciichar) {
readASCIIData(asciichar, asciiBuffer);
for (int i = 0; i < 16; i++) {
uint8_t byteData = asciiBuffer[i];
for (int bit = 0; bit < 8; bit++) {
if (byteData & (0x01 << bit)) {
// 根据具体的排列方式计算 x, y
u8g2.drawPixel(x + (i % 8) , y + (i / 8) * 8 + bit);
}
}
}
}
void drawExternalFontString(int x, int y, const uint8_t *str) {
int cursorX = x;
while (*str) {
if (*str < 128) {
// ASCII
// 英文字体向下压2个像素,与汉字基本取齐。
drawASCIIChar(cursorX, y + 2 , *str);
cursorX += 8;
str++;
} else {
// 中文
uint8_t msb = *str;
uint8_t lsb = *(str + 1);
if (lsb == 0) break;
drawChineseChar(cursorX, y, msb, lsb);
cursorX += 16;
str += 2;
}
}
}
void loop() {
u8g2.firstPage();
do {
// 由于 Arduino IDE 是 UTF-8,我们不能直接写 "你好World"。
// 我们需要用 16进制 来表示 GB2312 编码。
const uint8_t textCN[] = {0xB5, 0xB7, 0xB9, 0xC4, 0xD2, 0xD7, 0xD7, 0xE5, 0x00}; //捣鼓易族
const uint8_t textMix[] = {'H', 'e', 'l', 'l', 'o', 0xCA, 0xC0, 0xBD, 0xE7, 0xA1, 0xA3, 0x00}; //Hello世界
// 显示
drawExternalFontString(0, 0, textCN);
drawExternalFontString(0, 20, textMix);
} while (u8g2.nextPage());
delay(1000);
}效果如下: