单片机实战指南:串口通信中的汉字编码与发送技巧

发布时间:2026/5/20 8:40:01

单片机实战指南:串口通信中的汉字编码与发送技巧 1. 串口通信中的汉字编码基础在单片机开发中串口通信是最常用的调试和数据显示方式之一。但很多初学者在尝试发送汉字时都会遇到乱码问题这其实涉及到字符编码的基础知识。ASCII码只能表示128个字符显然无法覆盖汉字这时候就需要了解更复杂的编码方式。常见的汉字编码主要有GB2312、GBK和UTF-8三种。GB2312是我国最早的中文字符集标准包含6763个汉字GBK是GB2312的扩展支持更多汉字而UTF-8则是国际通用的Unicode编码实现。在Keil环境下默认使用的是GB2312编码这也是为什么我们直接使用printf输出汉字时在串口助手中能够正确显示的原因。我刚开始学习时也踩过坑以为只要在代码里写上汉字就能发送。实际上Keil编译器在编译过程中会自动将源代码中的汉字转换为对应的GB2312编码。比如早上好这三个字在内存中实际存储的是6个字节的编码数据每个汉字占2个字节。理解这一点很重要因为后续如果涉及到从外部存储读取汉字发送就需要自己处理编码转换了。2. Keil环境下汉字发送的两种实现方式2.1 使用标准库函数puts和printf在Keil的标准库中puts和printf是最常用的字符串输出函数。它们底层都调用了putchar函数来实现单个字符的发送。使用它们发送汉字非常简单只需要包含stdio.h头文件然后像发送普通字符串一样调用即可。#include reg52.h #include stdio.h void main() { UARTInit(); // 串口初始化 while(1) { TI 1; // 关键步骤 puts(嵌入式开发); // 自动换行 printf(当前温度25℃); // 不会自动换行 while(!TI); // 等待发送完成 TI 0; delay(1000); } }这里有个重要细节调用puts或printf前必须手动置位TI标志位。因为putchar函数内部会等待TI置位后才发送数据而第一次调用时TI默认是0如果不手动置1就会死等。这个坑我踩过好几次调试时程序卡住不动最后发现是忘了设置TI。2.2 直接操作串口发送汉字编码有时候我们可能需要更灵活地控制汉字发送比如从EEPROM或Flash中读取汉字数据发送。这时候就需要了解如何直接发送汉字编码。unsigned char hz[] {0xD4,0xE7,0xC9,0xCF,0xBA,0xC3}; // 早上好的GB2312编码 void sendGB2312(unsigned char *str, int len) { int i; for(i0; ilen; i) { SBUF str[i]; while(!TI); TI 0; } } void main() { UARTInit(); while(1) { sendGB2312(hz, sizeof(hz)); delay(1000); } }这种方法虽然麻烦但在需要动态组合汉字内容时非常有用。我曾经做过一个项目需要根据传感器数据动态生成包含数值的汉字语句就是采用这种方式实现的。3. 串口助手配置与调试技巧3.1 正确设置串口参数发送端和接收端的串口参数必须完全一致这是保证汉字正常显示的基础。主要参数包括波特率常用9600、115200等数据位8位停止位1位校验位无在Keil中初始化串口时TH1和TL1的值决定了波特率。对于11.0592MHz晶振9600波特率对应的值是0xFD。如果使用其他晶振频率需要重新计算这两个寄存器的值。3.2 串口助手的选择与设置市面上串口助手软件很多但并非所有都支持汉字显示。我推荐使用SecureCRT、Xshell或者MobaXterm它们对中文支持都很好。使用时需要注意选择正确的COM端口波特率等参数与单片机设置一致显示模式选择文本模式或字符模式编码格式选择与单片机一致的GB2312或GBK调试时如果遇到乱码可以先用ASCII字符测试确认基本通信正常后再尝试发送汉字。我曾经遇到过因为串口助手编码设置错误导致所有汉字都显示为问号的情况。4. 常见问题与解决方案4.1 汉字显示为乱码这是最常见的问题可能的原因有串口初始化不正确检查波特率设置忘记包含stdio.h头文件调用printf/puts前未置位TI标志串口助手编码设置错误单片机晶振频率与波特率计算不匹配4.2 发送内容不完整有时候会发现发送的汉字少了一半这通常是因为发送过程中被中断打断未等待前一个字符发送完成就发送下一个缓冲区溢出解决方法是在每个字符发送后都检查TI标志确保发送完成后再继续。4.3 多字节汉字处理在需要截取或处理汉字字符串时要特别注意因为一个汉字由两个字节组成。如果错误地截断会导致后续所有汉字都显示异常。我曾经在做一个滚动显示项目时就因为这个问题调试了一整天。// 错误的截取方式 char *str 嵌入式系统; char subStr[3]; memcpy(subStr, str2, 2); // 可能截断汉字 // 正确的处理方式 int pos 0; while(pos strlen(str)) { if(str[pos] 0x80) { // 是汉字首字节 // 处理完整汉字 processChinese(strpos, 2); pos 2; } else { // 处理ASCII字符 processASCII(strpos, 1); pos 1; } }5. 进阶应用汉字点阵显示与存储虽然本文主要讨论串口发送汉字但了解汉字在嵌入式系统中的其他应用也很有必要。比如LED点阵或LCD显示汉字时都需要用到汉字编码和字模数据。在资源有限的单片机中通常需要自定义字库。这时就需要根据GB2312的编码规则来组织字库。比如我们可以将常用汉字做成一个数组每个汉字对应一个编码和字模数据typedef struct { unsigned char code[2]; // GB2312编码 unsigned char matrix[32]; // 16x16点阵数据 } ChineseChar; ChineseChar fontLib[] { {{0xD4,0xE7}, {0x04,0x40,0x04,0x40,...}}, // 早 {{0xC9,0xCF}, {0x10,0x40,0x10,0x40,...}}, // 上 // 更多汉字... };这种方式的优点是节省存储空间只包含实际用到的汉字。我在一个智能家居项目中就采用这种方法将产品界面用到的200多个汉字存储在Flash中大大节省了内存空间。6. 实际项目经验分享在工业控制项目中我们经常需要通过串口发送包含汉字的状态信息。这里分享几个实用技巧使用环形缓冲区避免在中断服务程序中直接调用printf而是将数据存入缓冲区在主循环中发送添加帧头帧尾特别是在无线通信中建议给每条汉字消息添加固定的帧头帧尾便于接收方识别错误重传机制对于重要信息可以实现简单的ACK确认和重传机制数据压缩当需要发送大量汉字时可以考虑使用简单的压缩算法我曾经参与开发过一个环境监测系统需要定时发送包含汉字的环境数据。最初版本直接使用printf发送后来发现当传感器数据变化快时会出现丢数据的情况。最终解决方案是采用双缓冲机制一个缓冲用于组装数据另一个用于发送通过标志位来同步。这样既保证了数据完整性又不会阻塞主程序运行。

相关新闻