对Adafruit_GFX库函数进行了一些更改,让它支持中文

准备工作2023.12.20重新编辑对部分文件进行优化

1、软件的安装

arduinoide安装就不说了,一搜就有。
字体制作:
1、 安装gcc,具体安装方式和操作系统有关,我是mac,使用的是brew安装,其他系统自行搜教程。
2、安装freetype2库,自行搜。
3、Adafruit_GFX库中有字体制作程序,但只能制作ascii码字库,现在对这个程序进行了更改,支持中文字库制作。
程序放后面,先来写那些进行了更改

这个是make文件

all: fontconvert

CC     = gcc #这个不用动
#这个是加入头文件,让在打包时能找到头文件,找到自己的头文件地址进行更换
CFLAGS = -Wall -I/opt/homebrew/Cellar/freetype/2.13.0_1/include/freetype2 -I/opt/homebrew/Cellar/freetype/2.13.0_1/include
#这个是库,mac下需要指定freetype位置,不然make报错
LIBS   = -lfreetype -L/opt/homebrew/Cellar/freetype/2.13.0_1/lib

fontconvert: fontconvert.c
	$(CC) $(CFLAGS) $< $(LIBS) -o $@
	strip $@

clean:
	rm -f fontconvert

使用方法:直接在终端 make ,
然后使用下面命令制作字库

./fontconvert 字体.ttf 16 温度计高差长摄氏 >> ch.h

./fontconvert 程序主体
字体.ttf 你使用什么字体制作字库
16 使用字号
温度******** 需要编写进去的中文
ch.h 字库头文件输出
这是fontconvert.c


#ifndef ARDUINO
#include <string.h>
#include <locale.h>
#include <time.h>
#include <ctype.h>
#include <ft2build.h>
#include <stdint.h>
#include <stdio.h>
#include FT_GLYPH_H
#include FT_MODULE_H
#include FT_TRUETYPE_DRIVER_H
#include "gfxfont.h" // Adafruit_GFX font structures
#include <assert.h>
#define DPI 141 // Approximate res. of Adafruit 2.8" TFT
FT_Library library;
FT_Face face;
FT_Glyph glyph;
FT_Bitmap* bitmap;
FT_BitmapGlyphRec* g;
GFXglyph* table;
uint8_t bit;
int i, j, i_1;
int err;
int size;
int first = ' ';
int last = '~';
int bitmapOffset = 0, x, y, byte;
char* fontName, c, * ptr;
wchar_t mb;
int cdat;
unsigned char s [2] = { '0' };
char str [1000];
wchar_t Chinese_unicode [1000];
int Chinese_code[] = { 0 };
void enbit(uint8_t value) {
  static uint8_t row = 0, sum = 0, bit = 0x80, firstCall = 1;
  if (value)
    sum |= bit;          // 如果需要,设置位
  if (!(bit >>= 1)) {    // 前进到下一位,是否到达字节末尾?
    if (!firstCall) {    // 很好地格式化输出表
      if (++row >= 12) { // 行上的最后一个条目?
        printf(",\n  "); //   Newline format output
        row = 0;         //   Reset row counter
      }
      else {           // Not end of line
        printf(", ");    //   Simple comma delim
      }
    }
    printf("0x%02X", sum); // 写入字节值
    sum = 0;               // Clear for next byte
    bit = 0x80;            // Reset bit counter
    firstCall = 0;         // Formatting flag
  }
}
//Unicode编码转utf-8,将中文Unicode编码以汉字输出
int enc_unicode_to_utf8_one(unsigned long unic, unsigned char* pOutput, int outSize) {
  assert(pOutput != NULL);
  assert(outSize >= 6);
  if (unic <= 0x0000007F) {
    // * U-00000000 - U-0000007F:  0xxxxxxx
    *pOutput = (unic & 0x7F);
    return 1;
  }
  else if (unic >= 0x00000080 && unic <= 0x000007FF) {
    // * U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
    *(pOutput + 1) = (unic & 0x3F) | 0x80;
    *pOutput = ((unic >> 6) & 0x1F) | 0xC0;
    return 2;
  }
  else if (unic >= 0x00000800 && unic <= 0x0000FFFF) {
    // * U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
    *(pOutput + 2) = (unic & 0x3F) | 0x80;
    *(pOutput + 1) = ((unic >> 6) & 0x3F) | 0x80;
    *pOutput = ((unic >> 12) & 0x0F) | 0xE0;
    return 3;
  }
  else if (unic >= 0x00010000 && unic <= 0x001FFFFF) {
    // * U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    *(pOutput + 3) = (unic & 0x3F) | 0x80;
    *(pOutput + 2) = ((unic >> 6) & 0x3F) | 0x80;
    *(pOutput + 1) = ((unic >> 12) & 0x3F) | 0x80;
    *pOutput = ((unic >> 18) & 0x07) | 0xF0;
    return 4;
  }
  else if (unic >= 0x00200000 && unic <= 0x03FFFFFF) {
    // * U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    *(pOutput + 4) = (unic & 0x3F) | 0x80;
    *(pOutput + 3) = ((unic >> 6) & 0x3F) | 0x80;
    *(pOutput + 2) = ((unic >> 12) & 0x3F) | 0x80;
    *(pOutput + 1) = ((unic >> 18) & 0x3F) | 0x80;
    *pOutput = ((unic >> 24) & 0x03) | 0xF8;
    return 5;
  }
  else if (unic >= 0x04000000 && unic <= 0x7FFFFFFF) {
    // * U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    *(pOutput + 5) = (unic & 0x3F) | 0x80;
    *(pOutput + 4) = ((unic >> 6) & 0x3F) | 0x80;
    *(pOutput + 3) = ((unic >> 12) & 0x3F) | 0x80;
    *(pOutput + 2) = ((unic >> 18) & 0x3F) | 0x80;
    *(pOutput + 1) = ((unic >> 24) & 0x3F) | 0x80;
    *pOutput = ((unic >> 30) & 0x01) | 0xFC;
    return 6;
  }
  return 0;
}

void English_convert_Bitmaps() {
  for (i = first, j = 0; i <= last; i++, j++) {

    // MONO渲染器通过位图结构提供具有完美裁剪(没有浪费像素)的干净图像。
    if ((err = FT_Load_Char(face, i, FT_LOAD_TARGET_MONO))) {
      fprintf(stderr, "Error %d loading char '%c'\n", err, i);
      continue;
    }

    if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) {
      fprintf(stderr, "Error %d rendering char '%c'\n", err, i);
      continue;
    }

    if ((err = FT_Get_Glyph(face->glyph, &glyph))) {
      fprintf(stderr, "Error %d getting glyph '%c'\n", err, i);
      continue;
    }

    bitmap = &face->glyph->bitmap;
    g = (FT_BitmapGlyphRec*)glyph;

    //存储最小的字体和每个字形信息,以减少闪存空间需求。
    //Glyph位图是完全位压缩的;没有按扫描线填充,
    //不过在需要时可以将每个字符的末尾填充到下一个字节边界。
    //16位偏移量意味着位图的最大64K,代码当前不检查溢出。
    //(也不检查大小和偏移是否在范围内……请负责任地转换字体。)
    table [j].bitmapOffset = bitmapOffset;
    table [j].width = bitmap->width;
    table [j].height = bitmap->rows;
    table [j].xAdvance = face->glyph->advance.x >> 6;
    table [j].xOffset = g->left;
    table [j].yOffset = 1 - g->top;

    for (y = 0; y < bitmap->rows; y++) {
      for (x = 0; x < bitmap->width; x++) {
        byte = x / 8;
        bit = 0x80 >> (x & 7);
        enbit(bitmap->buffer [y * bitmap->pitch + byte] & bit);
      }
    }

    // 如果需要,将字符位图的末尾填充到下一个字节边界
    int n = (bitmap->width * bitmap->rows) & 7;
    if (n) {     // Pixel count not an even multiple of 8?
      n = 8 - n; // # bits to next multiple
      while (n--)
        enbit(0);
    }
    bitmapOffset += (bitmap->width * bitmap->rows + 7) / 8;

    FT_Done_Glyph(glyph);
  }
}

void English_convert_Glyphs() {
  for (i = first, j = 0; i <= last; i++, j++) {
    printf("  { %5d, %3d, %3d, %3d, %4d, %4d }", table [j].bitmapOffset,
      table [j].width, table [j].height, table [j].xAdvance, table [j].xOffset,
      table [j].yOffset);
    printf(",   // 0x%02X", i);
    printf(" '%c'", i);
    putchar('\n');
  }
}



void Chinese_convert_Bitmaps() {
  for (i = 0; i < i_1; i++, j++) {
    //printf("\n");
    //printf("开始第%d个", i);
    // MONO渲染器通过位图结构提供具有完美裁剪(没有浪费像素)的干净图像。
    if ((err = FT_Load_Char(face, Chinese_code [i], FT_LOAD_TARGET_MONO))) {
      fprintf(stderr, "Error %d loading char '%c'\n", err, Chinese_code [i]);
      continue;
    }

    if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) {
      fprintf(stderr, "Error %d rendering char '%c'\n", err, Chinese_code [i]);
      continue;
    }

    if ((err = FT_Get_Glyph(face->glyph, &glyph))) {
      fprintf(stderr, "Error %d getting glyph '%c'\n", err, Chinese_code [i]);
      continue;
    }

    bitmap = &face->glyph->bitmap;
    g = (FT_BitmapGlyphRec*)glyph;

    //存储最小的字体和每个字形信息,以减少闪存空间需求。
    //Glyph位图是完全位压缩的;没有按扫描线填充,
    //不过在需要时可以将每个字符的末尾填充到下一个字节边界。
    //16位偏移量意味着位图的最大64K,代码当前不检查溢出。
    //(也不检查大小和偏移是否在范围内……请负责任地转换字体。)
    table [j].bitmapOffset = bitmapOffset;
    table [j].width = bitmap->width;
    table [j].height = bitmap->rows;
    table [j].xAdvance = face->glyph->advance.x >> 6;
    table [j].xOffset = g->left;
    table [j].yOffset = 1 - g->top;

    for (y = 0; y < bitmap->rows; y++) {
      for (x = 0; x < bitmap->width; x++) {
        byte = x / 8;
        bit = 0x80 >> (x & 7);
        enbit(bitmap->buffer [y * bitmap->pitch + byte] & bit);
      }
    }

    // 如果需要,将字符位图的末尾填充到下一个字节边界
    int n = (bitmap->width * bitmap->rows) & 7;
    if (n) {     // 像素数不是8的偶数倍吗?
      n = 8 - n; // # 位到下一个倍数
      while (n--)
        enbit(0);
    }
    bitmapOffset += (bitmap->width * bitmap->rows + 7) / 8;

    FT_Done_Glyph(glyph);
  }
}


void Chinese_convert_Glyphs() {
  //int icode = Chinese_unicode [0];
  for (i = 0; i < i_1; i++, j++) {
    printf("  { %5d, %3d, %3d, %3d, %4d, %4d }", table [j].bitmapOffset,
      table [j].width, table [j].height, table [j].xAdvance, table [j].xOffset,
      table [j].yOffset);
    if (i < i_1 - 1) {
      printf(",   // 0x%02X", Chinese_code [i]);
      enc_unicode_to_utf8_one(Chinese_code [i], s, 10);
      printf(" '%s'", s);
      putchar('\n');
    }
    else {
      printf("    // 0x%02X", Chinese_code [i]);
      enc_unicode_to_utf8_one(Chinese_code [i], s, 10);
      printf(" '%s'", s);
      putchar('\n');
    }

  }

}



int main(int argc, char* argv[]) {
  size = atoi(argv [2]);
  if (argc == 4) {
    setlocale(LC_ALL, "");
    strcpy(str, argv [3]);
    mbstowcs(Chinese_unicode, str, 1000);
    Chinese_code [0] = Chinese_unicode [0];
    for (i = 0;Chinese_code [i] != 0;i++) {
      Chinese_code [i] = Chinese_unicode [i];
    }
    i_1 = i - 1;
    int temp;
    for (i = 0;i < i_1 - 1;i++) {
      for (j = i + 1;j < i_1;j++) {
        if (Chinese_code [i] > Chinese_code [j]) {
          temp = Chinese_code [i];
          Chinese_code [i] = Chinese_code [j];
          Chinese_code [j] = temp;
        }
      }
    }
    //printf("0x%d \n", i_1);
    //printf("0x%x \n", Chinese_unicode [1]);
    //printf("0x%x \n", Chinese_unicode [2]);
    ptr = strrchr(argv [1], '/'); // 查找文件名中的最后一个斜线
    if (ptr)
      ptr++; // 文件名的第一个字符(去掉路径)
    else
      ptr = argv [1]; // 没有路径;本地目录中的字体。
    // 为字体名称和字形表分配空间
    if ((!(fontName = malloc(strlen(ptr) + 20))) ||
      (!(table = (GFXglyph*)malloc((last - first + 1 + i_1) * sizeof(GFXglyph))))) {
      fprintf(stderr, "Malloc错误\n");
      return 1;
    }
    strcpy(fontName, ptr);
    ptr = strrchr(fontName, '.'); // 查找最后一个句点(文件文本)
    if (!ptr)
      ptr = &fontName [strlen(fontName)]; // If none, append
    sprintf(ptr, "%dpt%db", size, (last > 127) ? 8 : 7);
    for (i = 0; (c = fontName [i]); i++) {
      if (isspace(c) || ispunct(c))
        fontName [i] = '_';
    }
    if ((err = FT_Init_FreeType(&library))) {
      fprintf(stderr, "FreeType init error: %d", err);
      return err;
    }
    FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35;
    FT_Property_Set(library, "truetype", "interpreter-version",
      &interpreter_version);
    if ((err = FT_New_Face(library, argv [1], 0, &face))) {
      fprintf(stderr, "Font load error: %d", err);
      FT_Done_FreeType(library);
      return err;
    }
    // << 6 because '26dot6' fixed-point format
    FT_Set_Char_Size(face, size << 6, 0, DPI, 0);
    printf("const uint8_t %sBitmaps[] PROGMEM = {\n  ", fontName);
    English_convert_Bitmaps();//转换英文字母及符号
    Chinese_convert_Bitmaps();
    printf(" };\n\n"); // End bitmap array
    printf("const GFXglyph %sGlyphs[] PROGMEM = {\n", fontName);
    English_convert_Glyphs();
    Chinese_convert_Glyphs();
    printf(" }; ");
    printf("\n\n");
    //输出中文unicode码
    printf("uint16_t Chinese_code[] ={\n");
    for (i = 0;i < i_1;i++) {
      if (i > 0 && /*((i / 5) - ((int)(i / 5))*/((i+1)%5 == 0)) {
        printf("0x%02X,\n", Chinese_code [i]);
      }
      else {
      printf("0x%02X,", Chinese_code [i]);
      }
    }
    //printf("0x%02X", Chinese_code [i + 1]);
    printf("}; \n");
    // 输出字体结构
    printf("const GFXfont %s PROGMEM = {\n", fontName);
    printf("  (uint8_t  *)%sBitmaps,\n", fontName);
    printf("  (GFXglyph *)%sGlyphs,\n", fontName);
    printf("  (uint16_t*)Chinese_code,\n");
    if (face->size->metrics.height == 0) {
      // 没有面部高度信息,假设固定宽度并从字形中获取。
      printf("  0x%02X, 0x%02X, %d };\n\n", first, last + i_1, table [0].height);
    }
    else {
      printf("  0x%02X, 0x%02X, %ld };\n\n", first, last + i_1,
        face->size->metrics.height >> 6);
    }
    printf("// Approx. %d bytes\n", bitmapOffset + (last + i_1 - first + 1) * 7 + 7);
    // Size estimate is based on AVR struct and pointer sizes;
    // actual size may vary.

    FT_Done_FreeType(library);

    return 0;
  }
  else {
    printf("Usage:fontfile size [first] [last]\n");
    return 1;
  }
}


#endif /* !ARDUINO */

这是gfxfont.h的更改

typedef struct {
  uint8_t *bitmap;  ///< 字形位图,串联
  GFXglyph *glyph;  ///< 字形阵列
  uint16_t *Chinese_; //增加了中文unicode编码库
  uint16_t first;   ///< ASCII extents (first char)
  uint16_t last;    ///< ASCII extents (last char)
  uint8_t yAdvance; ///< 新线距离 (y 轴)
} GFXfont;

以上已完成字库制作
将制作完的字库##include 进你的ino文件
使用tft.setFont(&songti16pt7b);设置字库,
使用tft.drawChar(100,150, "温", ST77XX_WHITE, ST77XX_BLACK, 1);输出中文字体
目前未解决问题tft.println()/tft.print()输出中文,没找到定义在那_

新版程序已解决此问题全套程序请从gitee下载:stm_tftSTM32SPIDMA
新版使用dma驱动tft屏幕且只适配了st7789,如果需要使用其他屏幕 请自行改库。(使用的库最好都自己进行变种已适配自己的编程习惯)

SPIDMAClass spi_1;
STM_ST7789 tft(&spi_1, SPI1NSS, TFT_DC, -1);
void setup() {
    Serial.begin(115200);                  //初始化串口
    tft.begin(240, 240);                   //屏幕初始化
    delay(100);
    tft.setRotation(2);                    //设置屏幕旋转
    tft.setFont(&songti12pt7b);            //设置字体
    tft.fillScreen(ST77XX_BLACK);          //清屏
    tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
    tft.setCursor(5, 60);
    tft.print("温度测量"); //新版已适配print()函数,目前支持123个汉字。
    }

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐