AI 加持下的 arduino ESP32S3 GT30L32S4W 汉字显示
程序小白,手上一块中景园1.54寸ST7789 显示屏,自带GT30L32S4W汉字字库显示芯片,因为不知道怎么在arduino 平台下使用硬字库一直闲置着。在网上翻阅了大量资料针对arduino 平台下使用此类硬字库芯片的代码例程没有找到。应该是使用软字库能解决的问题无需大费周章的用那么多代码来实现。
本着不浪费的原则,用官方C51代码喂了几天AI,生成了arduino 平台下读取汉字点阵数据并显示在屏幕上的代码。
代码使用了Arduino_GFX_Library.h显示驱动库和 UTF8ToGB2312.h字符内码转换库,基本实现了在屏幕上显示GB2312汉字字符串的功能,可以实现12x12,16x16,24x24,32x32,字库自带汉字点阵的显示。不过未过多的考虑字符中带ASCII字符的情况。有兴趣的朋友可以自行添加。
程序使用时 更换String utf8_str = ""中内容即可在屏幕上显示需要的汉字字符串。
另一直都没搞定字库芯片和显示模块共用SPI的情况(SCLK,MOSI),共享SPI情况下,读取字库汉字点阵数据全为0,小白毕竟是小白。程序实例其实是用了另外的GT30L32S4W芯片模块和独立的SPI通信。同时汉字显示速度太过感人。如果有朋友搞定以上两点的还麻烦赐教一下代码不胜感激!
重新修改了部分代码,修复了12x12点阵汉字字形显示不全的问题。修改了屏幕大小自适应,测试了1.54英寸 240X240和4.3英寸 480x320屏的汉字显示情况。
以下是程序代码
#include <Arduino.h>
#include <Arduino_GFX_Library.h> //https://github.com/moononournation/Arduino_GFX 显示
#include <SPI.h>
#include <UTF8ToGB2312.h> //https://github.com/TikaFlow/UTF8ToGB2312 转码
// 定义SPI引脚
#define Rom_cs 15 // 片选引脚
#define MOSI 13 // 主输出从输入引脚
#define MISO 12 // 主输入从输出引脚1
#define Rom_sck 14 // 时钟引脚
// 定义LCD引脚
#define LCD_DC 41
#define LCD_RES 42
#define LCD_BLK 48
#define LCD_CS 40
#define LCD_MOSI 35 // 主输出从输入引脚
#define LCD_MISO -1 // 主输入从输出引脚
#define LCD_SCK 36
// 定义颜色
#define BLACK 0x0000
#define WHITE 0xFFFF
// 定义函数
void zk_init(void); //初始化字库芯片
void Send_Byte(unsigned char out); //发送数据函数
unsigned char Get_Byte(void); //接收数据函数
void SPI_Address(unsigned char AddH, unsigned char AddM, unsigned char AddL); //发送地址函数
unsigned long r_dat_bat(unsigned long address, unsigned long DataLen, unsigned char *pBuff); //读取字符点阵数据函数
unsigned char gt_read_data(unsigned char *sendbuf, unsigned char sendlen, unsigned char *receivebuf, unsigned int receivelen); //读取字符点阵数据函数 方式2
#define GFX_BL 48
Arduino_DataBus *bus = new Arduino_ESP32SPI(41 /* DC */, 40 /* CS */, 36 /* SCK */, 35 /* MOSI */, -1 /* MISO */,true);
//Arduino_GFX *lcd = new Arduino_ST7796(bus, 42 /* RST */, 3/* rotation */, false /* IPS */, 480 /* width */, 320 /* height */, 0 /* col offset 1 */, 0 /* row offset 1 */);
Arduino_GFX *lcd = new Arduino_ST7796(bus, 42 /* RST */, 3/* rotation */, false /* IPS */);
String gb2312_str_cs;
/**
* @brief 字符字体数据排置方式
*/
#define FONT_DATA_ARRAY_TYPE_W (0) //横置横排
#define FONT_DATA_ARRAY_TYPE_Y (1) //竖置竖排
//SPIClass spi(HSPI); // 使用HSPI实例
void setup() {
// 初始化串口,波特率为115200
Serial.begin(115200);
String utf8_str = "我是中国人,我爱自己的祖国!!! ";
String gb2312_str_cs = GB.get(utf8_str);
Serial.println("GB2312 String: " + gb2312_str_cs);
SPI.begin();
// 初始化字库芯片
zk_init();
delay(1000);
// 初始化LCD
lcd->begin();
lcd->fillScreen(BLACK); // 清屏
//测试显示
displayGB2312String(50,50, gb2312_str_cs, 24);
}
void loop() {
delay(1000);
}
//初始化字库芯片
void zk_init(void) {
/*SPI_CLOCK_DIV2:SPI速度为系统时钟的1/2
SPI_CLOCK_DIV4:SPI速度为系统时钟的1/4
SPI_CLOCK_DIV8:SPI速度为系统时钟的1/8
SPI_CLOCK_DIV16:SPI速度为系统时钟的1/16
SPI_CLOCK_DIV32:SPI速度为系统时钟的1/32
SPI_CLOCK_DIV64:SPI速度为系统时钟的1/64
SPI_CLOCK_DIV128:SPI速度为系统时钟的1/128
*/
// SPI.setClockDivider(SPI_CLOCK_DIV128); // 调整SPI时钟频率
//SPI.beginTransaction(SPISettings(1000000, LSBFIRST, SPI_MODE0)); // 调整SPI时钟频率及模式
// 初始化引脚
pinMode(Rom_cs, OUTPUT);
pinMode(MOSI, OUTPUT);
pinMode(MISO, INPUT);
pinMode(Rom_sck, OUTPUT);
digitalWrite(Rom_cs, HIGH); //HIGH
digitalWrite(MOSI, HIGH);
digitalWrite(Rom_sck, HIGH);
}
//发送数据函数
void Send_Byte(unsigned char out) {
unsigned char i = 0;
for (i = 0; i < 8; i++) { //<8
digitalWrite(Rom_sck, LOW);
if (((out << i) & 0x80) == 0)
digitalWrite(MOSI, LOW);
else
digitalWrite(MOSI, HIGH);
digitalWrite(Rom_sck, HIGH);
}
}
//接收数据函数
unsigned char Get_Byte(void) {
unsigned char i;
unsigned char ret_data = 0;
// 读取数据
for (i = 0; i < 8; i++) { //<8
digitalWrite(Rom_sck, LOW); // 字库时钟拉低
delayMicroseconds(100); // 添加适当的延时
ret_data <<= 1;
if (digitalRead(MISO)) {
ret_data++;
}
digitalWrite(Rom_sck, HIGH); // 字库时钟拉高
delayMicroseconds(100); // 添加适当的延时
}
return ret_data;
}
//发送地址函数
void SPI_Address(unsigned char AddH, unsigned char AddM, unsigned char AddL) {
Send_Byte(AddH);
Send_Byte(AddM);
Send_Byte(AddL);
// 输出 AddH、AddM 和 AddL 的值到串口
Serial.print("Sending address: 0x");
Serial.print(AddH, HEX);
Serial.print(AddM, HEX);
Serial.println(AddL, HEX);
}
bool isASCII(unsigned int data) {
// 根据具体的编码范围判断是否为ASCII字符
return (data < 0x80);
}
//普通读取字符点阵数据函数
unsigned long r_dat_bat(unsigned long address, unsigned long DataLen, unsigned char *pBuff) {
unsigned long i;
unsigned char addrHigh;
unsigned char addrMid;
unsigned char addrLow;
Serial.print("Address1: 0x");
Serial.print(address, HEX);
addrHigh = address >> 16;
addrMid = address >> 8;
addrLow = (unsigned char)address;
Serial.print("Reading from address: 0x");
Serial.print(addrHigh, HEX);
Serial.print(addrMid, HEX);
Serial.println(addrLow, HEX);
digitalWrite(Rom_cs, LOW);
Send_Byte(0x03);
SPI_Address(addrHigh, addrMid, addrLow);
// 输出读取的数据到串口
Serial.print("Read data: "); //检查数据时候使用
for (i = 0; i < DataLen; i++) {
*(pBuff + i) = Get_Byte();
// 添加前导零,确保每个字节都以两位十六进制数显示
if (*(pBuff + i) < 0x10) {
Serial.print("0"); //检查数据时候使用
}
Serial.print(*(pBuff + i), HEX); //检查数据时候使用
Serial.print(" "); //检查数据时候使用
}
Serial.println();
digitalWrite(Rom_cs, HIGH);
return DataLen;
}
//读取字符点阵数据函数
unsigned char gt_read_data(unsigned char *sendbuf, unsigned char sendlen, unsigned char *receivebuf, unsigned int receivelen) {
unsigned int i;
digitalWrite(Rom_cs, LOW);
for (i = 0; i < sendlen; i++) {
Send_Byte(sendbuf[i]);
}
for (i = 0; i < receivelen; i++) {
receivebuf[i] = Get_Byte();
}
digitalWrite(Rom_cs, HIGH);
return 1;
}
//获取ASCII 字符地址
unsigned long calculateAddress_ASCII(char asciiCode) {
unsigned long baseAdd = 0x1DD780;
if (asciiCode >= 0x20 && asciiCode <= 0x7E) {
return ((asciiCode - 0x20) * 16 + baseAdd);
} else {
// 处理非法ASCII码的情况
return 0;
}
}
//获取32*32点阵汉字字符地址
unsigned long calculateAddress_GB2312(unsigned int asciiCode) {
unsigned long baseAdd = 0XEDF00;
unsigned char MSB = (asciiCode >> 8) & 0xFF; // 提取高8位
unsigned char LSB = asciiCode & 0xFF; // 提取低8位
// 输出 MSB 和 LSB 的值到串口
Serial.print("MSB_2: 0x");
Serial.print(MSB, HEX);
Serial.print(", LSB_2: 0x");
Serial.println(LSB, HEX);
if (MSB >= 0xA1 && MSB <= 0XA9 && LSB >= 0xA1) {
return ((MSB - 0xA1) * 94 + (LSB - 0xA1)) * 128 + baseAdd ; //+ 2
} else if (MSB >= 0xB0 && MSB <= 0xF7 && LSB >= 0xA1) {
return ((MSB - 0xB0) * 94 + (LSB - 0xA1) + 846) * 128 + baseAdd ; //+2
} else {
// 处理非法ASCII码的情况
return 0;
}
}
//显示汉字字符函数
void displayGB2312Character(int x,int y ,unsigned char MSB, unsigned char LSB, unsigned char zk_num) {
unsigned long address;
unsigned char dotMatrix[128]; // 假设最大点阵为32x32,需要128字节
unsigned long dataLen;
int dataLen_dz;
unsigned long BaseAdd;
unsigned char dianzheng = zk_num; //点阵大小为字号
switch (zk_num) {
case 12:
BaseAdd = 0x00;
dataLen = 24;
break;
case 16:
BaseAdd = 0x2C9D0;
dataLen = 32;
break;
case 24:
BaseAdd = 0x68190;
dataLen = 72;
break;
case 32:
BaseAdd = 0xEDF00;
dataLen = 128;
break;
default:
// 处理错误情况
return;
}
if (MSB >= 0xA1 && MSB <= 0xA9 && LSB >= 0xA1) {
address = ((MSB - 0xA1) * 94 + (LSB - 0xA1)) * dataLen + BaseAdd;
} else if (MSB >= 0xB0 && MSB <= 0xF7 && LSB >= 0xA1) {
address = ((MSB - 0xB0) * 94 + (LSB - 0xA1) + 846) * dataLen + BaseAdd;
} else {
// 处理错误情况
return;
}
// 读取点阵数据
r_dat_bat(address, dataLen, dotMatrix);
/*
// 添加串口输出代码,打印读取到的字库数据
Serial.print("Dot matrix data for character: ");
Serial.print(MSB, HEX);
Serial.print(LSB, HEX);
Serial.println(":");
for (int i = 0; i < dataLen; i++) {
if (dotMatrix[i] < 0x10) {
Serial.print("0");
}
Serial.print(dotMatrix[i], HEX);
Serial.print(" ");
}
*/
Serial.println();
//点阵大小选择
switch (dianzheng) {
case 12:
dataLen_dz = 12;
break;
case 16:
dataLen_dz = 16;
break;
case 24:
dataLen_dz = 24;
break;
case 32:
dataLen_dz = 32;
break;
default:
// 处理错误情况
return;
}
// 在LCD上显示汉字
displayCharacter(dotMatrix, x, y, dataLen_dz , dataLen_dz ,FONT_DATA_ARRAY_TYPE_W,zk_num); // x和y是屏幕上的坐标
// fontDisplay_DZ(dotMatrix, x, y, dataLen , dataLen , WHITE, BLACK, FONT_DATA_ARRAY_TYPE_W);
}
//显示字符串函数,有自动换行功能
void displayGB2312String(int x, int y, String gb2312_internalCodes, unsigned char zk_num) {
unsigned char gb2312Codes[1000]; // 假设字符串最长为1000个字符
int numCodes = 0; // 初始化 numCodes 为0
Serial.print("gb2312_internalCodes length: ");
Serial.println(gb2312_internalCodes.length());
Serial.print("gb2312_internalCodes content: ");
Serial.println(gb2312_internalCodes);
Serial.print("numCodes :");
Serial.println(numCodes, HEX);
// 去除字符串中的空格和其他非GB2312字符
String cleanedString = "";
for (int i = 0; i < gb2312_internalCodes.length(); i++) {
char c = gb2312_internalCodes.charAt(i);
if ((c >= 0x80 && c <= 0xFF) || (c >= 0x00 && c <= 0x7F)) {
cleanedString += c;
}
}
// 重新计算 numCodes
numCodes = cleanedString.length() / 2;
Serial.print("numCodes :");
Serial.println(numCodes, HEX);
if (numCodes > 0) {
// 输出转换后的GB2312内码
Serial.println("Converted GB2312 Codes:");
for (int i = 0, j = 0; i < numCodes * 2; i += 2, j++) {
// 跳过空格
while (i < gb2312_internalCodes.length() && gb2312_internalCodes.charAt(i) == ' ') i++;
// 从 gb2312_internalCodes 中提取字节
gb2312Codes[j * 2] = (unsigned char)(gb2312_internalCodes.charAt(i) & 0xFF); // 高字节
gb2312Codes[j * 2 + 1] = (unsigned char)(gb2312_internalCodes.charAt(i + 1) & 0xFF); // 低字节
Serial.print(gb2312Codes[j * 2], HEX);
Serial.print(" ");
Serial.print(gb2312Codes[j * 2 + 1], HEX);
Serial.print(" ");
}
Serial.println();
Serial.print("zk_num: ");
Serial.println(zk_num);
int spacing = zk_num; // 字符之间的间距
int screenWidth = lcd->width();; // 假设屏幕宽度为240像素
// 遍历GB2312编码并显示每个字符
for (int i = 0; i < numCodes * 2; i += 2) {
unsigned char MSB = gb2312Codes[i]; // 高字节
unsigned char LSB = gb2312Codes[i + 1]; // 低字节
// 输出 MSB 和 LSB 的值到串口
Serial.print("MSB: 0x");
Serial.print(MSB, HEX);
Serial.print(", LSB: 0x");
Serial.println(LSB, HEX);
// 显示字符前检查是否需要换行
if (x + zk_num > screenWidth) {
x = 0; // 重置x坐标
y += spacing; // 增加y坐标以换行 y += zk_num;
}
displayGB2312Character(x, y, MSB, LSB, zk_num);
x += spacing; // 更新x坐标为下一个字符的起始位置
}
} else {
Serial.println("Error converting string to GB2312");
Serial.println("Input string: " + gb2312_internalCodes);
}
}
//LCD上画点显示字符函数
void displayCharacter(unsigned char dotMatrix[], int x, int y, int width, int height, unsigned char type, unsigned char zk_num) {
unsigned int i, j, k, cnt = 0;
unsigned char temp;
unsigned char multiple = 0;
Serial.print("Width: ");
Serial.println(width);
Serial.print("Height: ");
Serial.println(height);
int disp_x = x;
int disp_y = y;
// 横置横排显示W
if (type == FONT_DATA_ARRAY_TYPE_W) {
multiple = ((width + 7) >> 3);
for (i = 0; i < height; i++) {
for (j = 0; j < multiple; j++) {
if (width == 12) {
if (cnt >= width * height / 6) {
Serial.println("Error: Index out of bounds!");
return; // 直接返回,避免继续访问越界数组
}
} else {
if (cnt >= width * height / 8) {
Serial.println("Error: Index out of bounds!");
return; // 直接返回,避免继续访问越界数组
}
}
temp = dotMatrix[cnt++];
for (k = 0; k < 8; k++) {
disp_x = (x + j * 8 + (7 - k)); // 调整位的顺序
disp_y = (y + i);
// 检查坐标是否超出LCD范围
if (disp_x >= lcd->width() || disp_y >= lcd->height()) {
Serial.print("Error: Coordinate out of bounds! (");
Serial.print(disp_x);
Serial.print(", ");
Serial.print(disp_y);
Serial.println(")");
return; // 直接返回,避免继续访问越界数组
}
if ((temp & (1 << k)) == 0) {
lcd->drawPixel(disp_x, disp_y, BLACK); // 熄灭像素点
} else {
lcd->drawPixel(disp_x, disp_y, WHITE); // 点亮像素点
}
}
}
}
}
}
// 快速读取点阵数据的函数,未使用
void fastReadData(unsigned long address, unsigned long dataLen, unsigned char *pBuff) {
unsigned long i;
unsigned char addrHigh;
unsigned char addrMid;
unsigned char addrLow;
Serial.print("Address1_k: 0x");
Serial.print(address, HEX);
addrHigh = address >> 16;
addrMid = address >> 8;
addrLow = (unsigned char)address;
Serial.print("Reading from address_k: 0x");
Serial.print(addrHigh, HEX);
Serial.print(addrMid, HEX);
Serial.println(addrLow, HEX);
digitalWrite(Rom_cs, LOW);
Send_Byte(0x0B); // 发送快速读取指令
SPI_Address(addrHigh, addrMid, addrLow);
Send_Byte(0x00); // 发送一个Dummy Byte
// 输出读取的数据到串口
Serial.print("Read data_k: ");
for (i = 0; i < dataLen; i++) {
pBuff[i] = Get_Byte(); // 读取数据
Serial.print(pBuff[i], HEX);
Serial.print(" ");
}
Serial.println();
digitalWrite(Rom_cs, HIGH); // 结束读取操作
}
标签:加持,arduino,AI,unsigned,char,SPI,print,println,Serial
From: https://blog.csdn.net/Bigmouth_2004/article/details/145125003