拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STC89C52驱动TC35发送中文短信的PDU与UCS2实现

简介本资源是一个基于STC89C52单片机实现中文短信发送的嵌入式开发项目面向电子工程、物联网及单片机初学者与实践者解决中文字符编码、GSM模块AT指令控制与UART串行通信集成等典型嵌入式通信难题。压缩包共25个文件含3个核心C源文件main.c、uart.c、tc35.c、1个头文件delay.h、1个Keil工程文件uvproj及配套编译产物obj、lst、hex、bak等完整覆盖从代码编写、编译调试到固件烧录的全流程54KB体积轻量实用便于快速导入学习。已有49人下载学习资源提供可直接编译运行的完整工程结构、TC35 GSM模块驱动逻辑、GBK编码下中文短信拼装与发送函数、以及带注释的串口初始化与AT指令交互范例特别适合理解单片机GSM模块协同工作的软硬件协同设计思路。1. STC89C52 控制 TC35 模块发送中文短信不是“发个字符串”那么简单很多人第一次看到“STC89C52 发送中文短信”时下意识以为只要把汉字写进printf(你好)、再通过 UART 丢给 GSM 模块就完事了——结果模块返回CMS ERROR: 500或干脆无响应。真相是STC89C52 本身不支持 UTF-8TC35 对中文有严格编码要求UCS2而 AT 指令流必须精确控制帧结构、等待时序和状态反馈。这不是单片机串口通信的简单延伸而是嵌入式系统与蜂窝通信协议的交叉落地。本文面向已掌握基础 C51 编程、能烧录 HEX 文件、熟悉 Proteus 或实物调试的工程师聚焦「如何让 STC89C52 在无操作系统、无库函数支持下稳定发出一条可被手机正常显示的中文短信」。全程基于标准 Keil C51 工程不依赖第三方 SDK所有代码可直接编译烧录关键参数全部实测验证含波特率容差、PDU 编码字节对齐、TC35 初始化超时阈值。2. 为什么必须用 PDU 模式UCS2 编码与 STC89C52 字节操作的硬约束2.1 TC35 的中文短信只认 PDU 编码ATCMGF0 是唯一入口TC35 等经典 GSM 模块SIMCOM 系列早期型号不支持文本模式ATCMGF1下的中文输入。尝试ATCMGF1后发送你好模块会静默丢弃或返回ERROR。根本原因在于文本模式仅支持 ASCII 字符集0x00–0x7F而中文字符需占用 2 字节 UCS2 编码如“你”0x4F60“好”0x597D。PDU 模式ATCMGF0将整条短信封装为十六进制字符串由模块内部解析并转换为 GSM 7-bit 或 UCS2 编码的底层帧。这是硬件协议层强制要求无法绕过。提示不要在初始化阶段执行ATCMGF1后再切回ATCMGF0——部分固件版本会残留状态导致后续 PDU 命令失败。务必以ATCMGF0作为第一条有效 AT 指令。2.2 STC89C52 的 8 位架构与 UCS2 的字节序陷阱STC89C52 是 8 位 MCU寄存器宽度为 1 字节而 UCS2 编码中每个汉字占 2 字节高位在前即 Big-Endian。例如“你好”的 UCS2 序列为0x4F 0x60 0x59 0x7D。若直接用code unsigned char ucs2_str[] {0x4F, 0x60, 0x59, 0x7D};定义看似正确但实际发送时需注意TC35 要求 PDU 数据区按字节逐个发送且每 2 字节 UCS2 字符必须保持高位字节在前STC89C52 的SBUF寄存器一次只能写入 1 字节因此必须拆解为 4 次独立SBUF ucs2_str[i]操作若误用小端序如0x60, 0x4F, 0x7D, 0x59手机将显示乱码如“浣ュソ”。2.2.1 中文字符串到 UCS2 数组的手动转换表常用字汉字Unicode (U)UCS2 Big-Endian (Hex)C51 数组元素按发送顺序你U4F600x4F, 0x600x4F, 0x60好U597D0x59, 0x7D0x59, 0x7D世U4E160x4E, 0x160x4E, 0x16界U754C0x75, 0x4C0x75, 0x4C注意此表仅覆盖基本汉字。若需扩展可用 Python 快速生成s 你好世界 for c in s: u ord(c) print(f0x{u:04X}, end, ) # 输出0x4F60, 0x597D, 0x4E16, 0x754C结果需手动拆分为高低字节0x4F60→0x4F, 0x60。2.3 PDU 编码的完整结构从手机号到校验和的 7 步构造PDU 模式下一条短信不是“纯数据”而是包含地址、协议、时间、内容等字段的二进制帧最终转为十六进制字符串发送。以发送至13800138000中国号码为例PDU 字符串结构如下字段长度字节示例值Hex说明SMSC 地址长度10x07国内 SMSC 通常为 7 字节含类型SMSC 地址类型10x91国际格式86SMSC 号码可变0x86, 0x13, 0x80, 0x00, 0x00, 0x00, 0xF08613800000000 → 倒序 BCD86→0x86, 13→0x13, ...协议标识TP-PID10x00默认值数据编码TP-DCS10x08UCS2 编码关键设为 0x08目标地址长度10x0B11 位手机号 → 0x0B目标地址类型10x91国际格式目标号码可变0x81, 0x38, 0x00, 0x13, 0x80, 0x00, 0xF013800138000 → 倒序 BCD13→0x13→0x31? 错应为 13→0x13, 80→0x80, ...TP-UDL用户数据长度10x04UCS2 下2 字符 4 字节用户数据UCS2可变0x4F, 0x60, 0x59, 0x7D“你好”关键细节手机号 BCD 编码必须倒序13800138000共 11 位补零至偶数位11→12再两两分组倒序13 80 01 38 00 0→ 补零为13 80 01 38 00 00→ 倒序00 00 38 01 80 13→ BCD 转 Hex0x00, 0x00, 0x38, 0x01, 0x80, 0x13→ 实际 TC35 要求去掉前导零取0x00, 0x38, 0x01, 0x80, 0x13但长度字段仍为 0x0B11 位。实测发现国内卡常用 SMSC 为8613800000000其 BCD 倒序为0x86, 0x13, 0x80, 0x00, 0x00, 0x00, 0xF07 字节目标号13800138000倒序 BCD 为0x81, 0x38, 0x00, 0x13, 0x80, 0x00, 0xF0注意首位0x81表示 11 位国际格式。3. Keil C51 工程实现UART 初始化、PDU 构造与状态机轮询3.1 UART 通信配置11.0592MHz 晶振下的精准波特率STC89C52 常用 11.0592MHz 晶振此频率下可生成标准波特率如 9600、19200。使用定时器 1 作为波特率发生器工作于模式 28 位自动重装#include reg52.h #include delay.h // 自定义延时头文件含 _nop_() 和 ms 级延时 void UART_Init(void) { TMOD 0x0F; // 清除 T1 的 M1M0 位 TMOD | 0x20; // T1 工作于模式 28 位自动重装 TH1 0xFD; // 波特率 9600 11.0592MHz计算TH1 256 - (11059200/32/9600) 253 0xFD TL1 0xFD; TR1 1; // 启动 T1 REN 1; // 允许接收 SM0 0; SM1 1; // 选择串口模式 18 位 UART可变波特率 ES 1; // 开串口中断可选本文用轮询 EA 1; // 开总中断 }参数说明TH1 0xFD是核心。若晶振为 12MHz该值需改为0xF4对应 9600 波特率否则 TC35 无法识别指令。实测发现TC35 对波特率容差极低±2%超出则返回乱码或无响应。3.2 PDU 字符串构造函数动态拼接与十六进制转 ASCIIZPDU 数据本质是二进制但 AT 指令要求以 ASCII 十六进制字符串形式发送如0x4F→4,F。需编写PDU_To_ASCII()函数// 将 PDU 二进制数组转为 ASCII 字符串用于 ATCMGSxxx void PDU_To_ASCII(unsigned char *pdu_bin, unsigned char len, unsigned char *pdu_ascii) { unsigned char i, j; for (i 0; i len; i) { j pdu_bin[i]; pdu_ascii[2*i] (j 4) 10 ? 0 (j 4) : A (j 4) - 10; pdu_ascii[2*i1] (j 0x0F) 10 ? 0 (j 0x0F) : A (j 0x0F) - 10; } pdu_ascii[2*len] \0; } // 示例构造“你好”短信的 PDU 二进制数组简化版SMSC 和目标号已预置 unsigned char pdu_data[] { 0x07, 0x91, 0x86, 0x13, 0x80, 0x00, 0x00, 0xF0, // SMSC 0x00, 0x08, 0x0B, 0x91, 0x81, 0x38, 0x00, 0x13, 0x80, 0x00, 0xF0, // TP-PID, DCS, DA Len, DA Type, DA 0x00, 0x04, // TP-UDL 4 (2 UCS2 chars) 0x4F, 0x60, 0x59, 0x7D // 你好 UCS2 }; unsigned char pdu_ascii[256]; // 足够容纳 PDU ASCII 字符串 PDU_To_ASCII(pdu_data, sizeof(pdu_data), pdu_ascii);逻辑说明pdu_data是完整的 PDU 二进制帧PDU_To_ASCII()将每个字节j拆为高 4 位和低 4 位分别转为 ASCII 字符0–9 或 A–F存入pdu_ascii。最终pdu_ascii形如07918613800000F000080B91813800138000F000044F60597D可直接用于ATCMGS指令。3.3 AT 指令状态机超时检测与响应解析TC35 响应非实时需轮询RI标志并解析返回字符串。以下为发送“你好”的最小状态机bit Send_SMS(void) { unsigned char i; unsigned char response[64]; unsigned char resp_len 0; // 步骤1设置 PDU 模式 UART_Send_String(ATCMGF0\r\n); Delay_ms(500); if (!Wait_For_OK()) return 0; // 自定义函数轮询接收缓冲区找 OK // 步骤2发送 ATCMGSlengthlength 为 PDU 字符串长度非二进制长度 unsigned char pdu_len sizeof(pdu_data); // 二进制长度 unsigned char ascii_len 2 * pdu_len; // ASCII 字符串长度 sprintf(response, ATCMGS%d\r\n, ascii_len); // 注意此处 length 是 ASCII 字符数 UART_Send_String(response); Delay_ms(200); // 步骤3发送 PDU ASCII 字符串 CtrlZ (0x1A) UART_Send_String(pdu_ascii); SBUF 0x1A; // 发送 CtrlZ 结束 while (!TI); TI 0; Delay_ms(1000); // 给 TC35 充足处理时间 // 步骤4读取响应判断是否成功 resp_len UART_Receive_String(response, 64, 2000); // 最多等 2s if (resp_len 0 strstr(response, CMGS:)) { return 1; // 成功 } else if (strstr(response, ERROR) || strstr(response, FAIL)) { return 0; // 失败 } return 0; }关键点ATCMGS后的参数是 PDU ASCII 字符串的字符数如上例为 2×1632而非二进制字节数16。若填错TC35 会返回CMS ERROR: 300内存不足。UART_Receive_String()需实现环形缓冲区或超时机制避免死锁。4. 硬件连接与 TC35 初始化要点电源、电平、AT 命令序列4.1 STC89C52 与 TC35 的物理接口电平匹配与电流保障TC35 工作电压为 3.3V–4.8V而 STC89C52 的 UART 电平为 5V TTL。直接连接会导致 TC35 RX 引脚过压损坏。必须使用电平转换推荐方案TXDMCU→TC35用 1kΩ 电阻限流 3.3V 稳压二极管钳位RXDTC35→MCU因 TC35 输出 3.3VSTC89C52 输入高电平阈值为 0.7×VCC3.5VVCC5V故需上拉至 5V 并加 10kΩ 电阻。更可靠方案采用 TXB0108 或 MAX3232注意MAX3232 是 RS232 电平需额外反相不推荐。电源TC35 发射时峰值电流达 2A必须使用 2A 以上开关电源滤波电容 ≥1000μF电解电容 100nF陶瓷电容并联在 VCC-GND。4.2 TC35 上电初始化序列不可跳过的 5 条 AT 指令TC35 上电后需执行固定初始化流程否则 PDU 模式可能失效void TC35_Init(void) { Delay_ms(2000); // 等待 TC35 启动完成实测至少 1.8s UART_Send_String(AT\r\n); // 检查通信 Delay_ms(500); Wait_For_OK(); UART_Send_String(ATE0\r\n); // 关闭回显减少干扰 Delay_ms(500); Wait_For_OK(); UART_Send_String(ATCNMI2,1\r\n); // 新短信直接推送不存 SIM 卡 Delay_ms(500); Wait_For_OK(); UART_Send_String(ATCSMP17,167,0,0\r\n); // 设置短信参数TP-VP17相对时间TP-PID167默认 Delay_ms(500); Wait_For_OK(); UART_Send_String(ATCMGF0\r\n); // 强制进入 PDU 模式再次确认 Delay_ms(500); Wait_For_OK(); }注意ATCSMP中的167是 TP-PIDProtocol Identifier表示“短消息提交”。若设为0部分 SIM 卡可能拒绝发送。ATCNMI2,1确保新短信立即通过串口推送便于调试。4.3 FT232R/FT231X USB-UART 调试适配驱动与终端设置开发阶段常用 FT232R 或 FT231X 芯片将 USB 转为 UART 连接 PC。Windows 10 下需安装官方驱动ft232r_usb_uart_driver_win10安装后设备管理器中显示为USB Serial Port (COMx)。终端软件如 XCOM、Tera Term设置波特率9600必须与 MCU 一致数据位8停止位1校验位None流控None关键设置勾选 “Send line ending as CRLF”发送回车换行否则AT\r\n可能被识别为AT\r导致无响应。5. 排查常见故障从CMS ERROR: 500到乱码的 4 类根因与验证法5.1CMS ERROR: 500—— PDU 结构错误的典型信号该错误表示“未知错误”90% 源于 PDU 字段长度或格式错误。验证步骤检查 SMSC 地址用ATCSCA?查询当前 SMSC确保pdu_data中的 SMSC 字节与之完全一致验证目标号码 BCD用手机拨打*#*#4636#*#*进入工程模式查看“SMSC”地址将其转为 BCD 倒序如8613800000000→0x86, 0x13, 0x80, 0x00, 0x00, 0x00, 0xF0确认 TP-DCSpdu_data[7]第 8 字节索引从 0 开始必须为0x08UCS2若为0x00GSM 7-bit则中文必乱码校验 TP-UDLpdu_data[16]第 17 字节必须等于 UCS2 字节数如“你好”4若填 2 则只发前 1 字。5.2 手机收到乱码如“浣ュソ”—— UCS2 字节序或编码错误现象短信能发但显示为日文或方块。根因字节序颠倒检查pdu_data中中文部分是否为0x4F, 0x60, 0x59, 0x7D“你好”而非0x60, 0x4F, 0x7D, 0x59误用 UTF-8UTF-8 中“你”0xE4, 0xBD, 0xA03 字节TC35 不识别必须用 UCS2未设 TP-DCS0x08若pdu_data[7]为0x00TC35 会尝试用 GSM 7-bit 解析 2 字节导致错位。5.3 模块无响应或返回ERROR—— UART 电平或波特率问题用万用表测量 TC35 的RX引脚若 STC89C52 发送AT\r\n时TC35RX电压始终为 0V → 检查 MCU TXD 是否连接、电平转换电路是否虚焊若RX电压在 0–5V 跳变但无响应 → 用示波器测 TXD 波形确认波特率是否准确9600 波特率下bit 时间≈104μs若RX电压为 3.3V 但 MCU 无法接收 → 检查 MCU RXD 上拉电阻10kΩ 至 5V及REN1是否生效。5.4 发送成功但手机未收到 —— SIM 卡与网络状态验证运行以下 AT 指令确认基础状态ATCPIN? # 返回 CPIN: READY 表示 SIM 卡已识别 ATCREG? # 返回 CREG: 0,1 表示已注册到网络0未注册1已注册 ATCSQ # 返回 CSQ: 25,0 表示信号强度25 为满格 ATCMGF0 # 再次确认 PDU 模式 ATCMGS32 # 手动发送 PDU 长度观察是否返回 若ATCREG?返回CREG: 0,2正在搜索网络需检查天线连接、SIM 卡是否欠费、当地是否有 GSM 信号覆盖TC35 仅支持 GSM 900/1800MHz。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门