news 2026/6/6 17:46:51

嵌入式汉字显示:从GBK编码到点阵绘制的完整实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式汉字显示:从GBK编码到点阵绘制的完整实现

1. 项目概述:从点阵到屏幕,汉字显示的底层逻辑

在嵌入式开发,尤其是涉及人机交互界面的项目中,显示汉字是一个绕不开的基础需求。无论是智能家居的温控面板、工业设备的参数显示屏,还是手持仪器的操作菜单,都需要清晰、准确地呈现中文信息。很多新手工程师在初次接触这个任务时,往往会感到困惑:为什么我向屏幕发送一串数据,屏幕上就能出现一个“小”字?这背后涉及从字符编码、字库组织到像素绘制的完整技术链条。

简单来说,这个过程可以类比为“查字典”和“按图描红”。计算机内部存储的汉字,比如“小”,并不是一个直接的图片,而是一个“索引号”(内码)。我们需要一个“字典”(字库文件),根据这个索引号找到对应的“笔画图样”(点阵数据)。最后,我们拿着这个图样,指挥屏幕的每一个像素点“亮”或“灭”,最终在屏幕上“描”出这个汉字。本文将从一个嵌入式工程师的视角,彻底拆解这个过程,从最基础的点阵概念讲起,涵盖编码原理、字库解析、到在STM32等MCU上的具体实现步骤,并分享我在实际项目中积累的调试技巧和避坑经验。

2. 汉字显示的核心原理拆解

2.1 汉字的数字化表示:从字形到点阵码

汉字是象形文字,要在数字世界里表示它,最直观的方法就是将其“画像”。点阵法正是这种思路的体现。我们可以把一个汉字放在一个由M行×N列组成的网格里,网格的每一个小格子对应屏幕上的一个像素。如果汉字笔画经过某个格子,这个格子就标记为“1”(点亮),否则标记为“0”(熄灭)。这样,一个汉字就变成了一串由0和1组成的二进制数据,这就是点阵码

以你提到的16×16点阵的“小”字为例。我们可以把它想象成一个16行高、16列宽的方格纸。用笔把这个“小”字描在纸上,凡是笔画覆盖的格子就涂黑(1),没覆盖的就留白(0)。然后,我们按照从左到右、从上到下的顺序(这是最常用的扫描顺序),逐行读取每个格子是黑是白。通常,我们会把每8个格子(即8个比特)组合成一个字节(Byte)。对于16×16的点阵,总共有256个格子,正好对应32个字节(256/8=32)。

你给出的那串十六进制数据:0x01,0x00,0x01,0x00...,每一个字节就对应着一行中连续的8个像素点的状态。例如第一个字节0x01,二进制是0000 0001,这意味着在当前行的前7个像素都是0(不亮),最后一个像素是1(亮)。通过将这32个字节的数据,按照相同的行列顺序“画”到液晶屏对应的区域,一个“小”字就显示出来了。

注意:点阵数据的具体值取决于字体的设计。不同的字体(如宋体、黑体)或不同的点阵大小(12×12, 24×24),其点阵码完全不同。因此,点阵码是字形相关的,它直接决定了屏幕上显示出来的样子。

2.2 字符编码:汉字的“身份证”系统

如果每个汉字我们都直接存储它的点阵码,那会非常低效。一篇文档里“的”字可能出现几十次,我们就要重复存储几十次相同的点阵数据。为了解决这个问题,计算机采用了编码的方案。我们给每一个汉字分配一个唯一的数字编号,就像身份证号一样。在存储文档时,只存储这个编号(内码),而不是庞大的点阵图形。

这就引出了你提到的各种编码概念:GB2312、GBK、Unicode、UTF-8等。它们都是不同的“身份证号”分配规则。

  • GB2312:中国大陆最早的国家标准,收录了6763个常用汉字和符号。它采用区位码的概念,将汉字放入一个94行×94列的表格中,行号叫“区”,列号叫“位”。例如“啊”字在16区01位,其区位码就是1601。为了在计算机中与ASCII码区分,GB2312规定在区位码的区和位各加上0xA0,得到国标码。国标码再加上0x8080(或区、位各加0xA0后,再各加0x80,结果一样),就得到了计算机内部实际使用的机内码(内码)。你提到的公式“内码 = 国标码 + 0x8080”是正确的简化表述。
  • GBK:GB2312的扩展,兼容GB2312,并增加了大量汉字(包括繁体字和生僻字),共收录21886个字符。在嵌入式领域,GBK字库比GB2312更通用,因为它能显示更多汉字。
  • Unicode:一个旨在包含全世界所有字符的编码标准,它为每个字符分配一个唯一的数字(码点),与平台、语言无关。例如“小”字的Unicode码点是U+5C0F
  • UTF-8:Unicode的一种可变长度编码实现。它的一大优点是与ASCII码完全兼容,且节省存储空间(英文字符1字节,汉字通常3字节),非常适合网络传输和文件存储。但在嵌入式系统内存中处理时,UTF-8不如固定长度的编码方便。

你的理解基本正确。在嵌入式系统的中文文档或UI资源中,我们通常使用GBK内码。当你在串口调试助手中选择“GBK”编码发送“小”字,实际发出的两个字节就是0xD0, 0xA1(或0xA1, 0xD0,取决于字节序)。

2.3 字库文件:编码与点阵的映射字典

字库文件(如经典的HZK16)的本质,就是一个巨大的“编码-点阵”查询表。它的结构设计得非常巧妙,直接利用了GBK/GB2312编码的规律。

对于一个16×16点阵的GBK字库文件(.bin或特定格式文件):

  1. 排列规则:字库中的汉字严格按照GBK编码的顺序排列。GBK编码范围从0xA1A1开始。
  2. 定位算法:要找到某个汉字(内码为byte1, byte2)的点阵数据在文件中的起始位置,可以使用如下公式:offset = ((byte1 - 0xA1) * 94 + (byte2 - 0xA1)) * 32
    • byte1 - 0xA1:计算出该汉字所在的“区”索引(从0开始)。
    • byte2 - 0xA1:计算出该汉字在该区中的“位”索引(从0开始)。GBK每区有94个位。
    • (区索引*94 + 位索引):得到该汉字在字库中的逻辑序号。
    • * 32:因为每个16×16点阵汉字占用32字节,所以乘以32得到该汉字点阵数据在文件中的字节偏移量

你下载的HZK16文件正是这样一个按此规则组织的二进制文件。你可以编写一个简单的PC端测试程序来验证:打开HZK16文件,根据“小”字的内码0xD0A1计算偏移量,然后读取紧接着的32个字节,应该就是你之前看到的“小”字的点阵数据。

3. 嵌入式系统汉字显示方案设计与选型

3.1 方案对比:内置点阵 vs. 外挂字库

在资源有限的MCU上实现汉字显示,主要有两种思路:

方案一:程序内置点阵数组

  • 做法:将需要用到的有限汉字(如菜单项“设置”、“确定”、“取消”)的点阵数据,以常量数组的形式直接编译进程序。
  • 优点
    • 速度极快:数据在ROM中,读取就是内存访问,没有文件IO开销。
    • 实现简单:无需文件系统,代码逻辑直截了当。
    • 可靠性高:不依赖外部存储,无字库文件损坏风险。
  • 缺点
    • 灵活性极差:要增加或修改显示内容,必须修改源代码并重新编译。
    • 占用程序空间:大量汉字会迅速撑爆Flash。一个16×16汉字占32字节,1000个字就需32KB。
  • 适用场景:显示内容固定、已知且数量很少(通常少于50个汉字)的场合,如简单的状态指示灯标签。

方案二:外挂字库文件

  • 做法:将完整的字库文件(如HZK16)存储在MCU的外部Flash、SD卡或SPI Flash中。程序运行时,根据字符内码动态地从存储设备中读取点阵数据。
  • 优点
    • 灵活性高:支持显示任意GBK汉字,只需修改显示字符串即可,无需改动程序。
    • 节省程序空间:字库占用的是存储空间,而非宝贵的MCU内部Flash。
  • 缺点
    • 速度相对慢:涉及存储设备的读操作,速度取决于接口(SPI, SDIO)和文件系统。
    • 实现复杂:需要驱动存储设备并实现文件系统(如FATFS)。
    • 依赖外部器件:增加了硬件复杂性和潜在故障点。
  • 适用场景:需要显示动态、不确定中文内容的系统,如数据日志显示、从网络接收的中文信息展示等。

对于大多数需要良好人机交互的项目,方案二(外挂字库)是更实用和主流的选择。接下来的实操也将基于此方案展开。

3.2 硬件与软件准备清单

在开始编码前,需要准备好以下环境:

  1. 硬件平台:以STM32F4系列(如F407)为例,它具备足够的RAM和Flash,并通常外扩了SPI Flash或SD卡槽用于存储字库。
  2. 显示设备:一块SPI或8080并口驱动的LCD液晶屏(如ILI9341、ST7789等控制器),分辨率建议至少为240x320,以便舒适地显示多行汉字。
  3. 字库文件
    • HZK16:16×16点阵GBK字库。这是显示小字号菜单、提示信息的核心。
    • HZK24/HZK32(可选):24×24或32×32点阵字库,用于显示标题等大字号内容。获取后需一同存入存储设备。
  4. 存储介质:一片SPI Flash(如W25Q128,16MB)或一张MicroSD卡。我们将把字库文件放在这里。
  5. 开发环境:Keil MDK或STM32CubeIDE。
  6. 关键软件库
    • STM32 HAL库或标准外设库:用于驱动SPI、FSMC等硬件接口。
    • FATFS:开源文件系统中间件,用于在存储设备上以文件形式访问字库。
    • LCD驱动代码:实现画点、画线、填充等基本图形功能的底层驱动。

4. 汉字显示功能的实现步骤

4.1 底层驱动搭建:存储、文件系统与屏幕

在显示第一个汉字之前,必须确保三条通路是畅通的:能读到字库文件能把数据画到屏幕上

第一步:存储设备初始化与挂载如果使用SPI Flash,你需要:

  1. 初始化SPI外设。
  2. 实现W25Q128的底层读写函数(基于SPI收发)。
  3. 在FATFS的diskio.c中,将你的SPI Flash读写函数映射到FATFS的磁盘访问接口上。
  4. main函数中调用f_mount挂载文件系统。

如果使用SD卡,则通过SDIO或SPI接口初始化SD卡,并挂载FATFS。这一步的调试要点是,确保调用f_open能成功打开根目录下的一个测试文件。

第二步:LCD屏幕初始化与画点函数这是显示的基础。无论显示什么,最终都是调用画点函数在指定坐标点亮一个像素。

// 这是一个最基本的画点函数示例 void LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t color) { // 1. 设置光标位置 (x, y) LCD_SetCursor(x, y); // 2. 准备写入GRAM LCD_Write_Cmd(GRAM_CMD); // 3. 写入颜色数据 LCD_Write_Data(color); }

你必须首先确保你的LCD驱动代码中的LCD_DrawPoint函数工作正常。可以通过编写一个测试函数,在屏幕对角线画线或画一个矩形框来验证。

4.2 核心功能实现:字库解析与显示函数

这是整个项目的核心代码块。我们将实现一个函数LCD_ShowChinese,它接收坐标、字符串、颜色等参数,并完成显示。

第一步:从字库文件中读取点阵数据

// 函数:从字库文件中读取一个汉字的点阵数据 // 参数:code - 汉字的GBK内码(两个字节组成的16位数) // buffer - 用于存放读取到的32字节点阵数据的缓冲区 // 返回:成功返回0,失败返回非0 int Get_HzMat(const uint16_t *code, uint8_t *buffer) { FIL file; UINT br; FRESULT res; unsigned long offset; // 1. 计算偏移量 // 假设code指向两个字节,高字节为区码,低字节为位码 uint8_t qh = (*code >> 8) & 0xFF; // 高字节,区码 uint8_t wh = *code & 0xFF; // 低字节,位码 if (qh < 0xA1 || wh < 0xA1) { // 简单的GBK范围校验 return -1; // 非GBK汉字内码 } offset = ((unsigned long)(qh - 0xA1) * 94 + (wh - 0xA1)) * 32; // 2. 打开字库文件 res = f_open(&file, "0:/HZK16", FA_READ); if (res != FR_OK) { return -2; // 打开文件失败 } // 3. 定位并读取 res = f_lseek(&file, offset); if (res != FR_OK) { f_close(&file); return -3; // 定位失败 } res = f_read(&file, buffer, 32, &br); f_close(&file); if (res != FR_OK || br != 32) { return -4; // 读取失败 } return 0; // 成功 }

第二步:将点阵数据绘制到屏幕获取到32字节的点阵数据后,我们需要解析每一位,并将其绘制到屏幕上。

// 函数:在指定位置显示一个16x16的汉字 // 参数:x, y - 汉字左上角的坐标 // chinese_code - 汉字的GBK内码 // color - 文字颜色 // bg_color - 背景颜色(用于透明或覆盖显示) void LCD_ShowChinese16x16(uint16_t x, uint16_t y, uint16_t chinese_code, uint16_t color, uint16_t bg_color) { uint8_t buffer[32]; // 存放点阵数据 uint8_t i, j, k; uint8_t dat; // 每个字节的数据 // 1. 获取点阵数据 if (Get_HzMat(&chinese_code, buffer) != 0) { // 获取失败,可以在这里画一个错误标记或直接返回 return; } // 2. 逐行逐列绘制 for (i = 0; i < 16; i++) { // 共16行 // 每行有2个字节(16列) for (j = 0; j < 2; j++) { dat = buffer[i * 2 + j]; // 取出一行的两个字节之一 for (k = 0; k < 8; k++) { // 每个字节8位 // 判断最高位(MSB)是否为1。注意扫描顺序,这里假设数据是左高右低。 if (dat & 0x80) { LCD_DrawPoint(x + j * 8 + k, y + i, color); } else { // 如果想显示透明效果,可以不画背景色 // 如果想显示不透明效果,则画背景色 if (bg_color != TRANSPARENT) { // TRANSPARENT可定义为特定值如0xFFFF LCD_DrawPoint(x + j * 8 + k, y + i, bg_color); } } dat <<= 1; // 左移一位,检查下一位 } } } }

第三步:显示字符串函数单个汉字显示成功后,就可以组合显示字符串了。这里的关键是区分中英文。GBK汉字由两个大于0xA0的字节组成,而ASCII码(英文、数字、符号)小于0x80

// 函数:显示一个中英文混合字符串 // 参数:x, y - 起始坐标 // str - 字符串指针(GBK编码) // color - 文字颜色 // bg_color - 背景色 void LCD_ShowString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color) { uint16_t x0 = x; while (*str) { // 判断是否为汉字内码的第一个字节 (GBK: 第一个字节 > 0xA0) if ((uint8_t)(*str) > 0xA0) { // 是汉字,组合两个字节 uint16_t ch_code = ((uint16_t)((uint8_t)*str) << 8) | (uint8_t)*(str + 1); LCD_ShowChinese16x16(x0, y, ch_code, color, bg_color); str += 2; // 跳过两个字节 x0 += 16; // 汉字宽度为16像素 } else { // 是ASCII字符,调用显示英文字符的函数(需要另外实现8x16或更宽的点阵) LCD_ShowChar(x0, y, *str, color, bg_color); // 假设英文字符宽8像素 str += 1; x0 += 8; } } }

至此,一个完整的、支持动态从字库读取并显示中文的底层框架就搭建好了。

5. 项目实战:构建一个交互式汉字显示测试程序

现在,我们来实现你规划中的第一个测试程序:通过串口接收字符,并实时显示在LCD上。

5.1 系统架构与流程设计

程序的核心是一个简单的状态机,运行在MCU的主循环或一个独立任务中:

  1. 初始化:初始化系统时钟、串口、SPI Flash、FATFS、LCD。
  2. 命令监听:在串口接收中断或主循环中解析命令。当收到mctest命令时,进入测试模式。
  3. 测试模式循环: a. 通过串口发送提示信息,如“请输入字符(按ESC退出):”。 b. 等待串口输入。由于汉字是双字节,需要缓存足够的数据(例如一个环形缓冲区)。 c. 解析缓冲区数据。如果是ASCII字符(如‘A’,‘1’,‘ESC’),直接处理。如果检测到连续两个字节都大于0xA0,则将其组合为一个汉字内码。 d. 调用LCD_ShowString函数,将解析出的字符显示在LCD的当前光标位置。 e. 更新LCD光标位置(x坐标增加字符宽度)。 f. 如果收到‘ESC’键(ASCII码0x1B),则清屏并退出测试模式,返回命令监听状态。

5.2 关键代码实现与注释

这里给出测试模式核心部分的简化代码逻辑:

void mctest_command_handler(void) { uint8_t rx_buffer[128]; uint8_t rx_index = 0; uint16_t cursor_x = 0, cursor_y = 50; // 显示起始位置 uint8_t ch; LCD_Clear(WHITE); // 清屏为白色背景 printf("Enter Chinese/English characters (ESC to quit):\r\n"); while (1) { if (USART_Receive_Byte(&ch)) { // 从串口读取一个字节 if (ch == 0x1B) { // ESC键 printf("\r\nExited.\r\n"); LCD_Clear(WHITE); break; } // 将收到的字节存入缓冲区 rx_buffer[rx_index++] = ch; // 简单处理:每次收到字节都尝试解析并显示缓冲区内容 // 更健壮的做法是判断是否收到一个完整字符(ASCII或GBK)后再显示 if (rx_index >= 2) { // 检查是否可能是一个GBK汉字的前两个字节 if (rx_buffer[0] > 0xA0 && rx_buffer[1] > 0xA0) { uint16_t ch_code = (rx_buffer[0] << 8) | rx_buffer[1]; LCD_ShowChinese16x16(cursor_x, cursor_y, ch_code, BLACK, WHITE); cursor_x += 16; // 清空缓冲区,准备接收下一个字符 rx_index = 0; } else { // 处理ASCII字符 LCD_ShowChar(cursor_x, cursor_y, rx_buffer[0], BLACK, WHITE); cursor_x += 8; // 将第二个字节移到第一个位置,继续判断 rx_buffer[0] = rx_buffer[1]; rx_index = 1; } } else if (rx_index == 1) { // 只有一个字节,可能是ASCII或汉字的第一半 // 如果是ASCII控制字符(如回车换行)或可打印字符,立即显示 if (rx_buffer[0] == '\r' || rx_buffer[0] == '\n') { // 处理换行 cursor_x = 0; cursor_y += 16; rx_index = 0; } else if (rx_buffer[0] < 0x80) { // 标准ASCII LCD_ShowChar(cursor_x, cursor_y, rx_buffer[0], BLACK, WHITE); cursor_x += 8; rx_index = 0; } // 如果字节>0xA0,则等待第二个字节,不做处理 } // 光标越界处理 if (cursor_x > LCD_WIDTH - 16) { cursor_x = 0; cursor_y += 16; } if (cursor_y > LCD_HEIGHT - 16) { LCD_Clear(WHITE); // 滚屏或清屏 cursor_x = 0; cursor_y = 50; } } } }

5.3 效果验证与调试

将程序编译下载到STM32开发板,连接好LCD和串口。在串口终端(如SecureCRT、Putty,设置为GBK编码)中输入mctest命令。然后尝试输入:

  • 英文字母和数字:应能正确显示。
  • 中文“测试”:应能正确显示两个汉字。
  • 中英文混合“Hello世界”:应能正确区分并显示。

如果显示乱码,请按以下步骤排查:

  1. 检查字库文件:确认HZK16文件已正确烧录到SPI Flash或SD卡的根目录,并且文件没有损坏。
  2. 检查编码:确认你的串口终端发送数据的编码是GBK。如果终端设置为UTF-8,发送“测试”两个字,MCU收到的将是6个字节的UTF-8编码,而非2个字节的GBK编码,必然导致寻址错误和乱码。
  3. 检查偏移量计算:在Get_HzMat函数中打印出计算出的offset值,并与PC上用工具查看的“测”字在HZK16中的实际偏移进行对比。
  4. 检查点阵数据:在成功读取32字节后,通过串口将这32个字节的十六进制形式打印出来,与你已知的某个汉字(如“小”)的点阵数据进行对比,看是否一致。
  5. 检查画点函数:确保LCD_DrawPoint的坐标逻辑正确,特别是xy的方向是否与你的屏幕扫描方向一致。

6. 进阶优化与常见问题深度解析

6.1 性能瓶颈分析与优化策略

在实时性要求高的场景(如快速刷新菜单),频繁读文件可能成为瓶颈。以下是一些优化思路:

  1. 字库缓存:将最常用的几十到几百个汉字(如一级菜单项)的点阵数据在启动时加载到RAM或MCU内部Flash的数组中。显示时直接从内存读取,速度极快。
  2. 使用SPI Flash的XIP模式(如果支持):将整个字库文件映射到MCU的地址空间,像读取内部Flash一样直接通过地址访问,省去文件系统开销。但这需要MCU支持QSPI内存映射模式,且字库需存放在特定地址。
  3. 优化文件读取:不要为每个汉字都执行f_open,f_lseek,f_read,f_close。可以在程序初始化时f_open字库文件,并保持打开状态(使用全局FIL变量),显示汉字时只调用f_lseekf_read,最后在程序退出时f_close。这能大幅减少文件系统操作的开销。
  4. 使用更高效的字库格式:除了原始的二进制点阵文件,还可以考虑将字库转换为C语言数组文件,直接编译进代码,但这又回到了内置字库的路径,适用于固定内容。

6.2 显示效果提升技巧

  1. 抗锯齿与平滑字体:16×16点阵汉字在放大时锯齿感明显。如果需要显示大字号,应直接使用24×24或32×32的点阵字库,而不是将16×16的进行软件放大。有开源库如u8g2LVGL内置了抗锯齿字体渲染引擎,但需要更多资源。
  2. 字体混排与对齐:实现一个完善的LCD_ShowString函数,需要处理好中英文宽度不同导致的对齐问题。可以预先计算字符串的像素宽度,再决定显示起始位置,以实现居中或右对齐。
  3. 透明背景与叠加显示:在LCD_ShowChinese16x16函数中,我提供了一个bg_color参数。如果设置为TRANSPARENT(用一个特殊颜色值表示),则在绘制“0”像素点时就不做任何操作,保留屏幕原有内容,实现透明叠加效果,这在制作复杂UI时非常有用。

6.3 典型问题排查速查表

问题现象可能原因排查方法
显示全黑块或错乱方块1. 点阵数据全部为0xFF或0x00。
2. 偏移量计算错误,读到了字库文件的其他位置。
3. 画点函数的坐标计算错误,像素点全部画在同一个位置。
1. 打印读取到的32字节点阵数据,检查是否全0或全1。
2. 核对内码计算偏移量的公式,特别是-0xA1这一步。
3. 单步调试画点函数,观察x, y坐标变化是否按(0,0), (1,0)...(15,0), (0,1)...规律进行。
显示汉字上下或左右颠倒点阵数据的扫描顺序与绘制顺序不匹配。检查字库文件的扫描顺序(通常是先行后列,每行从左到右)。调整LCD_ShowChinese16x16函数中i(行)和j/k(列)的循环顺序,以及dat字节中位的解析顺序(0x80对应最左像素还是最右像素)。
能显示英文,不能显示中文1. 串口接收编码错误(如终端发UTF-8,程序按GBK解析)。
2. 中文字符判断逻辑有误。
3. 字库文件路径错误或读取失败。
1.最可能的原因:将串口终端编码设置为GBK。
2. 在接收中断中打印收到的每个字节的十六进制值,确认中文是两字节且值大于0xA0
3. 检查f_open返回值,确认文件是否存在。
显示速度非常慢1. 为每个汉字都执行完整的文件打开关闭操作。
2. SPI Flash时钟频率设置过低。
3. 文件系统缓冲区太小。
1. 采用“打开-多次寻址读取-关闭”的模式。
2. 提高SPI时钟频率到器件允许的最大值。
3. 增大FATFS的缓冲区。
部分汉字显示为空白或错误1. 使用的字库文件不包含该汉字(如用了GB2312字库显示GBK扩展字)。
2. 该汉字在字库中的点阵数据损坏。
1. 确认字库文件是完整的GBK字库(如HZK16文件大小约为256KB)。
2. 尝试显示一些非常用字,或在PC上用字库查看工具确认该汉字点阵。

6.4 从点到面:构建完整的GUI文本显示层

实现了单个字符串显示后,可以在此基础上构建更高级的功能:

  • 文本区域管理:实现一个文本控件,支持自动换行、滚动、对齐(左、中、右)。
  • 多字体与字号支持:管理多个字库文件(HZK16,HZK24),根据属性选择不同的显示函数。
  • 格式化输出:类似printf,实现一个LCD_Printf函数,支持%d,%s,%f等格式符,并能自动处理其中的中文字符。

这个过程虽然从基础的点阵操作开始,但贯穿了编码理论、文件系统、外设驱动和图形显示等多个嵌入式核心知识点。通过亲手实现一遍,你对“字符如何从代码变成屏幕上的图形”这一过程的理解将会非常透彻。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 17:46:17

GHelper终极指南:华硕笔记本轻量级控制工具的完整解决方案

GHelper终极指南&#xff1a;华硕笔记本轻量级控制工具的完整解决方案 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook…

作者头像 李华
网站建设 2026/6/6 17:44:29

从A*到JPS:机器人路径规划算法演进史,以及为什么你该关注跳点搜索

从A*到JPS&#xff1a;路径规划算法的效率革命与技术选型指南 在仓储机器人以3m/s速度穿行货架时&#xff0c;每毫秒的路径计算延迟都可能导致碰撞风险&#xff1b;当自动驾驶汽车在复杂城市场景中需要每秒重新规划10次路线时&#xff0c;传统A*算法突然显得力不从心——这正是…

作者头像 李华
网站建设 2026/6/6 17:42:31

保姆级教程:手把手配置华为防火墙USG6309E的SNMP v2c/v3网管监控

华为USG6309E防火墙SNMP网管配置实战指南在网络安全运维中&#xff0c;将防火墙纳入统一监控体系是保障业务连续性的关键环节。作为华为旗舰级安全设备&#xff0c;USG6309E防火墙支持通过SNMP协议实现设备状态、流量统计、会话数等关键指标的实时采集。不同于普通交换机的配置…

作者头像 李华