首页 > 其他分享 >AI 加持下的 arduino ESP32S3 GT30L32S4W 汉字显示

AI 加持下的 arduino ESP32S3 GT30L32S4W 汉字显示

时间:2025-01-18 10:33:41浏览次数:3  
标签:加持 arduino AI unsigned char SPI print println Serial

    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

相关文章

  • 个人如何通过AI大模型变现赚钱
    1.内容创作领域自媒体写作与运营:公众号写作:可利用AI生成文章框架和部分内容,结合自己的观点和风格进行创作,吸引粉丝关注。当粉丝量达到一定规模后,通过流量主广告、品牌合作推广等方式变现。如一些情感类、职场类公众号,通过AI辅助创作优质内容,月收入可达数万元CSDN博客......
  • AI智能推广营销系统怎么用
    一、引言AI智能推广营销系统是现代企业提升营销效率和效果的重要工具。通过利用人工智能技术和大数据分析,该系统能够精准定位目标客户、制定个性化营销策略,并实现自动化营销。本文将详细介绍AI智能推广营销系统的使用方法,并特别介绍万达宝LAIDFU(来福)系统的特点和使用技巧。二、......
  • Nuxt+tailwindcss+element 零帧起手
    文章目录前言一、项目搭建二、全局样式使用(一)、动态样式引入(二)、全局样式引入(三)、tailwindcss引入使用通用引入nuxt3快捷引入三、引入elementplus前言一、项目搭建node版本需要18以上npxnuxi@latestinit<project-name>二、全局样式使用(一)、动态样式引入......
  • 强推未发表!3D图!Transformer-LSTM+NSGAII工艺参数优化、工程设计优化!
    目录效果一览基本介绍程序设计参考资料效果一览基本介绍1.Transformer-LSTM+NSGAII多目标优化算法,工艺参数优化、工程设计优化!(Matlab完整源码和数据)Transformer-LSTM模型的架构:输入层:多个变量作为输入,形成一个多维输入张量。Transformer编码器:该编码器由多个T......
  • 用Mermaid画图
    1、用Mermaid画图mermaid.mdTyporaPortable.rarmermaid.zip目录1Mermaid是什么1.1概述1.2网址官网地址:Github地址:图形图形几种图形名字节点与无名字节点设置样式:style,classDef,class,:::线条图形连线(--)及注释(%%)线条样式实线与虚线箭头实线与粗实线及箭头延长线其......
  • Agent系列(一)——利用OpenAI快速搭建简易Agent
    目录1、Agent 简介1.1Agents的核心组件1.1.1模型(Model):1.1.2工具(Tools):1.1.3编排层(OrchestrationLayer):1.2Agents的运作机制:从输入到输出 2、搭建简易的Agent 2.1模型准备2.1.1获取 api_key2.1.2获取base_url和chat_model2.2搭建Agent2.2.......
  • [ARC108F] Paint Tree
    前言复习什么的就留到下周了,顺便把格式调好现在把每日一练打了差不多今天补了一下午的\(\rm{T2}\),终于还是被码力问题击碎了,不过也还好这道题是模拟赛\(\rm{T3}\)吉司机线段树和左偏树都只能明天搞了,明天把\(\rm{C}\)打了开摆思路首先那几个\(\rm{subtask}\)......
  • 每天一个优秀提示词学习收藏 - 文本去AI化(四)
    ......
  • 阿里云通义实验室自然语言处理方向负责人黄非:通义灵码2.0,迈入 Agentic AI
    通义灵码是基于阿里巴巴通义大模型研发的AI智能编码助手,在通义灵码1.0 时代,我们针对代码的生成、补全和问答,通过高效果、低时延,研发出了国内最受欢迎的编码助手。在通义灵码2.0发布会上,阿里云通义实验室自然语言处理方向负责人黄非分享了代码大模型的演进。过去一年来,随着大......
  • 阿里云通义实验室自然语言处理方向负责人黄非:通义灵码2.0,迈入 Agentic AI
    通义灵码是基于阿里巴巴通义大模型研发的AI智能编码助手,在通义灵码1.0 时代,我们针对代码的生成、补全和问答,通过高效果、低时延,研发出了国内最受欢迎的编码助手。在通义灵码2.0发布会上,阿里云通义实验室自然语言处理方向负责人黄非分享了代码大模型的演进。过去一年来,随着大......