第四章 编码与解码技术:NRZ、曼彻斯特编码、汉明码、CRC校验
各位工程师朋友,大家好。我是老张,干遥测这行快十五年了。今天咱们聊聊编码与解码技术。说实话,这章内容看着基础,但我在项目里见过太多人在这上面栽跟头。你想想看,信号发出去收不回来,十有八九是编码没选对,或者校验没做好。
咱们先看一张图,把今天要讲的知识体系理清楚。
4.1 NRZ编码:最简单的,也是最容易出问题的
NRZ,全称是Non-Return-to-Zero,不归零码。说白了,就是高电平代表1,低电平代表0,就这么简单。我刚开始做遥测时,第一个项目用的就是NRZ。当时觉得这玩意儿太简单了,结果调试时发现接收端老是丢数据。
为什么会这样?因为NRZ有个致命弱点——没有时钟同步。你想想看,如果发送一串连续的1,电平一直保持高,接收端怎么知道发了多少个1?它只能靠自己的时钟去猜。一旦收发双方的时钟有偏差,数据就全乱了。
NRZ的优缺点我列个表,大家看得清楚些:
| 特性 | 优点 | 缺点 |
|---|---|---|
| 带宽效率 | 高,每个码元携带1比特信息 | — |
| 时钟同步 | — | 长串连续比特时丢失同步 |
| 直流分量 | — | 有直流分量,不利于变压器耦合 |
| 实现复杂度 | 极低,硬件实现简单 | — |
NRZ的编码规则很简单,看代码就明白了:
// NRZ编码示例
// 输入:data[] = {1, 0, 1, 1, 0, 0, 1}
// 输出:高电平表示1,低电平表示0
void nrz_encode(uint8_t *data, int len) {
for (int i = 0; i < len; i++) {
if (data[i] == 1) {
set_pin_high(); // 输出高电平
} else {
set_pin_low(); // 输出低电平
}
}
}
4.2 曼彻斯特编码:自带时钟的聪明方案
曼彻斯特编码解决了NRZ的同步问题。它的思路很巧妙:每个比特中间都有一次电平跳变。从高到低表示1,从低到高表示0。这样接收端就能从跳变中提取时钟信号。
我记得有一次做无人机遥测链路,信道干扰特别大,NRZ完全没法用。换成曼彻斯特编码后,问题迎刃而解。虽然带宽翻了一倍,但可靠性上去了,值!
// 曼彻斯特编码示例
// 规则:1 -> 高到低跳变,0 -> 低到高跳变
void manchester_encode(uint8_t *data, int len) {
for (int i = 0; i < len; i++) {
if (data[i] == 1) {
set_pin_high(); // 前半周期高电平
delay_half_bit();
set_pin_low(); // 后半周期低电平
delay_half_bit();
} else {
set_pin_low(); // 前半周期低电平
delay_half_bit();
set_pin_high(); // 后半周期高电平
delay_half_bit();
}
}
}
关键点: 曼彻斯特编码每个比特都有跳变,所以接收端可以轻松恢复时钟。但代价是带宽需求翻倍——原来1Hz能传1比特,现在需要2Hz。
曼彻斯特编码还有个变种叫差分曼彻斯特编码。它看的是比特起始处有没有跳变:有跳变表示0,没有跳变表示1。这种编码抗干扰能力更强,我在一些工业遥测设备上见过。
4.3 汉明码:能纠错的"聪明码"
前面讲的都是线路编码,解决的是信号同步问题。但数据在传输过程中还可能出错,这就需要纠错编码了。汉明码就是其中经典的一种。
汉明码的核心思想是:在数据位中插入校验位,这些校验位能定位出错的位置。比如(7,4)汉明码,4位数据加3位校验位,能纠正1位错误。
我曾经在一个卫星遥测项目中用过汉明码。卫星信号经过大气层时会有闪烁,偶尔丢几个比特。用了汉明码后,大部分单比特错误都能自动纠正,省去了重传的麻烦。
// (7,4)汉明码编码示例
// 输入:4位数据 d1 d2 d3 d4
// 输出:7位码字 p1 p2 d1 p3 d2 d3 d4
void hamming_74_encode(uint8_t *data, uint8_t *code) {
uint8_t d1 = data[0], d2 = data[1], d3 = data[2], d4 = data[3];
// 计算校验位
uint8_t p1 = d1 ^ d2 ^ d4; // 校验位1
uint8_t p2 = d1 ^ d3 ^ d4; // 校验位2
uint8_t p3 = d2 ^ d3 ^ d4; // 校验位3
// 组装码字
code[0] = p1;
code[1] = p2;
code[2] = d1;
code[3] = p3;
code[4] = d2;
code[5] = d3;
code[6] = d4;
}
4.4 CRC校验:数据完整性的最后防线
CRC,循环冗余校验。这玩意儿在遥测系统里太常见了,几乎每个数据帧末尾都会带CRC校验码。它的原理是把数据看作一个多项式,用约定的生成多项式去除,余数就是CRC码。
CRC的检错能力很强,能检测出所有单比特错误、双比特错误、奇数个错误,以及长度不超过生成多项式阶数的突发错误。我习惯用CRC-16或CRC-32,具体看项目对误检率的要求。
// CRC-16计算示例(多项式 x^16 + x^15 + x^2 + 1)
uint16_t crc16_calculate(uint8_t *data, int len) {
uint16_t crc = 0xFFFF;
for (int i = 0; i < len; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001; // 0xA001是反转后的多项式
} else {
crc = crc >> 1;
}
}
}
return crc;
}
CRC的生成多项式选择也有讲究。常用的有:
- CRC-8:多项式0x07,用于小数据包
- CRC-16:多项式0x8005,用于一般遥测帧
- CRC-32:多项式0x04C11DB7,用于大数据块
我个人建议,遥测链路至少用CRC-16。CRC-8的检错能力在长数据帧面前有点不够看。
4.5 实际应用中的选择策略
讲了这么多,到底怎么选?我给大家一个参考:
- 信道质量好、带宽紧张:用NRZ + 扰码,配合CRC校验
- 信道有干扰、需要时钟同步:用曼彻斯特编码 + CRC
- 信道误码率较高、能容忍带宽开销:用汉明码 + 曼彻斯特编码
- 高速遥测、硬件资源充足:用NRZ + 硬件CRC + 卷积码
嗯,编码与解码技术就讲到这里。这些内容看着基础,但每个都是实战中绕不开的坎。下次调试遥测链路时,如果发现数据对不上,先检查编码方式选对了没有,再查CRC计算有没有问题。很多时候,问题就出在这些"简单"的地方。