☰
8421/5421/2421/余三码BCD编码原理与工程转换实战
2026/10/31 17:18:47 网站建设 项目流程

做数字电路和嵌入式开发的人,对8421码、5421码、2421码、余三码这四个名字多半不陌生。它们几乎总是成组出现:课本上摆一张对照表,考试让你默写,做设计时又要在译码器、计数器、加法器之间来回换算。真正让人栽跟头的往往不是记不住码表,而是没搞清这几种编码的“出身”——为什么权重偏偏是这几组数,谁具备自补性,余三码做加法为什么一会儿加 3 一会儿减 3,5421 码为什么明明权重加起来是 12 却被很多人误当成自补码。这些问题在教材里往往一句话带过,落到实际调电路、写代码、做题目的时候就全暴露出来了。

这篇内容面向三类人:正在学数字逻辑、需要把编码表彻底吃透的学生;做 FPGA 或单片机项目,需要在七段数码管、拨码输入、十进制运算之间做转换的工程师;以及想给团队做一次内部技术分享、需要一份能直接讲的材料的人。我会把这四种编码的权重结构、编码表、转换规则、自补性判定、加法修正逻辑、冗余码处理、选型思路和常见排查点全部拆开讲,并且给出可以直接跑的验证脚本和一段可综合的硬件描述代码,方便你抄作业。读完之后,你至少应该能做到:看到任意一个 4 位编码,三秒内判断它属于哪种编码体系、是合法码还是冗余码、能不能自补、做加法要不要修正。

1. 先把四种编码的底层结构看清楚

1.1 BCD 编码到底在解决什么问题

十进制是人类习惯的计数方式,二进制是电路能可靠表达的方式,中间必须有一层“翻译约定”,这就是 BCD 编码存在的理由。四位二进制能组合出 16 种状态,而一位十进制只需要 10 种,必然有 6 种状态被闲置。怎么挑出这 10 种、按什么顺序挑、挑完之后对运算和显示有什么影响,就是各种 BCD 编码之间的全部差异所在。

最直觉的做法当然是照搬自然二进制:0 到 9 就用 0000 到 1001,这就是 8421 码。它的问题也很明显——自然二进制的进位门槛是 16,十进制的进位门槛是 10,两者相差 6,所以一旦做加法就必须额外做修正。这个“差 6”是所有 BCD 加法器设计绕不开的核心矛盾,后面讲 8421 码加法修正的时候会详细推导。

另一种思路是“重新分配权重”。既然四位二进制可以自定义每一位代表多少,那就没必要死守 8、4、2、1 这套递增权重,改成 5、4、2、1 或者 2、4、2、1 都能凑出 0 到 9。这么一改,某些在 8421 码里很别扭的操作会突然变得简单,代价是编码本身不再等同于二进制数值,转换时要多一步。这就是 5421 码和 2421 码的由来。

第三种思路更彻底:干脆不用权重,直接在 8421 码的基础上整体平移 3,得到余三码。它牺牲了“按权重直接读数”的便利,换来两个非常实用的性质:自补性,以及没有全 0 状态。

1.2 权重决定一切:四种编码的关键属性对照

理解这四种编码,最快的入口不是背表,而是看权重。把权重列出来,很多性质几乎是自动浮现的。

编码名称各位权重(高位到低位)权重之和是否有权码是否自补码加法是否需要修正
8421 码8、4、2、115是否需要,加 6 修正
5421 码5、4、2、112是否需要,规则较特殊
2421 码(A 码)2、4、2、19是是可借助自补性化简
2421 码(B 码)2、4、2、19是否视具体定义而定
余三码不固定(8421 平移 3)无意义否是需要,有进位加 3、无进位减 3

这张表里藏着一条很值得记住的规律:有权码想要具备自补性,各位权重之和必须等于 9。8421 码权重和是 15,5421 码是 12,都不满足,所以它们注定不是自补码;2421 码权重和正好是 9,所以它能自补;同理 5211 码(权重 5、2、1、1,和为 9)也是自补码。这条规律不是巧合,而是数学上的必然,我在 4.1 节会给出直观解释。

1.3 有权码的“冗余状态”和“非法码”不是一回事

这里有个特别容易混淆的点,值得单独拎出来说。8421 码只用了 0000 到 1001,剩下 1010 到 1111 六个状态,这六个状态按权重算出来的值是 10 到 15,全都超出了十进制的表示范围,所以它们是天然的非法码,不存在歧义。

5421 码就不一样了。它规定 0 到 4 用 0000 到 0100,5 到 9 用 1000 到 1100。可是如果你拿 0101 去按权重算,0×5 + 1×4 + 0×2 + 1×1 = 5,这个值明明在 0 到 9 的合法范围内,但按照约定它就是一个不被采用的码。也就是说,5421 码的冗余状态里有几个“按权重能算出合法数值,但约定不用”的码。2421 码(A 码)也是同样情况,0101 按权重算是 5,但标准 2421A 码用 1011 表示 5。

这个区别在实际工程里很重要:做非法码检测电路的时候,8421 码的检测逻辑是“值大于 9”,而 5421 码和 2421 码必须用“查表匹配”的方式,把六个约定不用的码逐个列出来做比较,不能简单用数值比较器一刀切。我见过有人图省事,直接用大于 9 来判断 5421 码的非法状态,结果 1101、1110、1111 这三个码漏检了,因为它们按权重算出来的值分别是 8、9、10,都“看起来合法”。

1.4 四种编码共同的隐藏优点:单调性

还有一个几乎所有教材都不提、但在实际做比较器时非常有用的性质:这四种编码的码值都随十进制数值单调递增。8421 码不用说,0 到 9 对应 0000 到 1001;5421 码对应 0 到 4、8 到 12,也是递增;2421A 码对应 0 到 4、11 到 15,仍是递增;余三码对应 3 到 12,依然是递增。

这意味着,如果你只是要比较两个一位十进制数的大小,完全可以直接把它们的 4 位编码送进数值比较器,不需要先转回 8421 码。这个结论能省掉一级转换逻辑,在资源紧张的场合挺实用。但要注意,这个性质只在“同一种编码内部比较”时成立,跨编码比较必须先统一。

2. 四种编码逐个拆解

2.1 8421 码:最顺理成章,也最“笨重”的一种

8421 码就是自然二进制的前十个状态,权重从高到低是 8、4、2、1。它的好处几乎不用解释:十进制转编码就是把数字写成二进制,编码转十进制就是把二进制读成十进制,中间没有任何额外的心算负担。硬件上更省事,七段数码管的译码芯片、拨码开关、十进制拨轮、大部分数字仪表的内部表示,默认都是 8421 码。你几乎可以认为它是 BCD 领域的“通用语”。

但它的短板在运算环节暴露得很彻底。四位二进制的进位门槛是 16,十进制的进位门槛是 10,中间差了 6。所以两个 8421 码相加,只要结果超过 9,就必须补上这 6 的差额,否则得到的就是一个错误的二进制数而不是 BCD 数。举个例子,5 + 8:0101 + 1000 = 1101,这个 1101 是二进制的 13,但它不是一个合法的 BCD 码(BCD 里 1101 属于冗余状态)。正确的做法是再加 0110,得到 1 0011,低位 0011 表示 3,进位 1 表示十位的 1,合起来就是 13。这个“加 6 修正”是 8421 码加法器的标准动作,我在 4.3 节会把推导过程写清楚。

另一个短板是不具备自补性。所谓自补,就是对编码按位取反,能直接得到 9 的补数。8421 码做不到这一点,比如 0 的编码是 0000,取反得 1111,而 9 的编码是 1001,两者对不上。这意味着做十进制减法时,必须老老实实算 9 减 x,或者额外加一级求补逻辑,电路面积和延迟都要多花一点。

2.2 5421 码:为分频和高位判断而生

5421 码把最高位的权重从 8 改成 5,其余三位保持 4、2、1。这个改动看起来只是换了个数,实际上改变了整个码表的分布形态:0 到 4 用 0000 到 0100,最高位是 0;5 到 9 用 1000 到 1100,最高位是 1。也就是说,最高位直接就是“这个数是否大于等于 5”的判据,不需要任何比较逻辑。

这个性质在计数器设计里非常好用。假设你要从一个十进制计数单元里取出一个 5 分频的方波信号,用 8421 码的话,最高位在 0 到 7 时是 0、8 到 9 时是 1,一个十状态周期里高电平只占两个状态,占空比只有 20%,而且不是对称的。用 5421 码就不一样了,最高位在五个状态里为 1、五个状态里为 0,天然就是 50% 占空比的 5 分频方波,直接引出来就能用,省掉一级额外的分频或整形电路。这是 5421 码在数字钟、频率计、计时器一类电路里长期存在的主要原因。

但 5421 码有一个流传很广的误解:不少人把它当成自补码。验证一下就清楚了。5421 码里 0 的编码是 0000,按位取反得到 1111;而 9 的编码是 1100。1111 不等于 1100,所以它不自补。为什么会有这个误解?我猜是因为 5421 码的权重和是 12,看起来“接近 9”,加上有些资料在讲自补码时把 5421 和 5211 放在一起提,读者容易串行。记住一条就行:权重和是 9 的才有资格自补,5421 的权重和是 12,直接排除。

2.3 2421 码:自补码里的实用派,但有 A、B 两个版本

2421 码的权重是 2、4、2、1,注意它不是递减的,第二位权重比第一位还大,这在有权码里并不违规,只要各位权重组合起来能唯一(或按约定)表示 0 到 9 就行。2421 码最核心的价值是权重和恰好等于 9,因此它具备自补性:把任意一个合法码按位取反,得到的就是 9 减去原值后的编码。

它的编码分布是这样的:0 到 4 用 0000 到 0100,5 到 9 用 1011 到 1111。这里出现了一个必须警惕的问题——2421 码有两种常见定义,通常称为 A 码和 B 码。A 码的 5 到 9 是 1011、1100、1101、1110、1111,具备自补性;B 码的 5 到 7 是 0101、0110、0111,8 和 9 才是 1110、1111,这种版本不具备完整的自补性。两种版本在 0 到 4 段完全一致,从 5 开始分道扬镳。

如果题目或器件手册里只写了“2421 码”没写版本,默认按 A 码处理,因为 A 码是教材里的主流定义。但如果你在维护一份老设计文档,或者对接别人给的码表,一定要先确认版本,否则从 5 开始全部译码错误,而且这种错误很隐蔽——0 到 4 是对的,看起来像是“高位译码坏了”,很容易把人带到错误的排查方向上去。

2.4 余三码:平移 3 带来的两个漂亮性质

余三码的做法非常直接:在 8421 码的基础上,每个编码都加 3(二进制 0011)。0 变成 0011,9 变成 1100,整个码表从 0011 排到 1100。为什么偏偏加 3?因为四位二进制有 16 个状态,十进制只用 10 个,多出来 6 个。要让这 10 个状态在 16 个位置里居中排列,上下各留 3 个,正好就是加 3。这个解释比死记“加 3”要直观得多。

居中排列带来两个直接好处。第一是自补性:0 的编码是 0011,取反得 1100,正好是 9 的编码;1 的编码是 0100,取反得 1011,正好是 8 的编码。整张表两两对称,取反就等于求 9 的补。第二是没有全 0 状态:最小的合法码是 0011,0000 不在合法码集合里。这在可靠性设计上有实际意义,如果一根数据线断了或者存储单元出了故障,读出全 0,系统能立刻判定为无效数据而不是误认为数值 0。早期的某些串行运算结构和存储校验方案里,这个特性是被认真利用过的。

余三码的代价是不能按权重直接读数。你看到 1010,不能像 8421 码那样直接算 8+2=10,得先减 3 才知道是 7。所以它很少用于显示和输入,主要出现在运算单元、特定计数器以及教学演示里。

2.5 横向对比:四种编码的完整编码表

把四张表并排放在一起,很多规律会自己跳出来。

十进制8421 码5421 码2421 码(A)2421 码(B)余三码
000000000000000000011
100010001000100010100
200100010001000100101
300110011001100110110
401000100010001000111
501011000101101011000
601101001110001101001
701111010110101111010
810001011111011101011
910011100111111111100

盯住 5 和 8 这两行,你会发现一个容易被误判的巧合:5421 码的 5 是 1000、8 是 1011,余三码的 5 也是 1000、8 也是 1011,四个格子完全一样。这纯粹是数值上的巧合,不代表两种编码在 5 和 8 处等价。我见过有人在转换 58 这个两位数时,发现两种编码结果一致,就下结论说“5421 和余三码在高位数字上相同”,换个数字比如 47 立刻翻车。判断编码是否相同,永远要逐位验算,不能靠个别样本归纳。

3. 转换实操:从一张表到一段代码

3.1 8421 码转其他三种:本质就是加常数

从 8421 码出发做转换是最省心的,因为其他三种编码都可以看成 8421 码的分段线性变换。规则可以总结成一句话:小于 5 的部分原样保留,大于等于 5 的部分加上一个固定偏移。

  • 转余三码:不分段,每一位都加 0011。
  • 转 5421 码:值小于 5 不变,值大于等于 5 时加 0011,得到 1000 到 1100。
  • 转 2421 码(A 码):值小于 5 不变,值大于等于 5 时加 0110,得到 1011 到 1111。

拿 47 这个两位数完整走一遍。47 的 8421 码是 0100 0111。

转余三码,两位分别加 0011:0100 加 0011 得 0111,0111 加 0011 得 1010,结果是 0111 1010。

转 5421 码,4 小于 5 保持不变得 0100,7 大于等于 5 加 0011 得 1010,结果是 0100 1010。

转 2421A 码,4 保持不变得 0100,7 加大 0110 得 1101,结果是 0100 1101。

三个结果拿回码表核对,全部正确。这里要强调的是,多位转换必须每一位独立处理,绝对不能把整个字节当成一个二进制数去加 3。47 的 8421 码是 01000111,整体加 3 得到 01001010,这个结果对应的是 4 和 10,完全错误。这个坑我在带新人的时候见过不止一次,尤其是在写 C 语言代码用+ 0x03处理整个字节的时候。

3.2 反向转换:先用最高位判断区间,再减偏移

从 5421 码或 2421A 码转回 8421 码,关键在于先判断这一位落在哪个区间。5421 码的 0 到 4 最高位是 0,5 到 9 最高位是 1,所以直接看最高位就行。2421A 码同样如此,0 到 4 是 0000 到 0100,最高位为 0;5 到 9 是 1011 到 1111,最高位为 1。所以这两种编码的反向转换可以写成同一套逻辑:最高位为 0 就原样输出,最高位为 1 就减去对应的偏移(5421 减 0011,2421A 减 0110)。

余三码的反向转换更简单,不分段,每一位统一减 0011 就行。但要注意减法的借位问题:余三码的 0 是 0011,减 0011 得 0000,刚好;余三码的 9 是 1100,减 0011 得 1001,也刚好。因为余三码的合法区间是 0011 到 1100,减去 0011 之后必然落在 0000 到 1001 之间,不会出现借位,这也是余三码设计巧妙的地方。

反向转换还有一个必须处理的环节:非法码判定。如果你收到一个 5421 码 0101,最高位是 0,按规则原样输出,得到 0101,这在 8421 码里是合法的 5,但原码 0101 在 5421 体系里是非法的。所以反向转换之前必须先做一次合法性检查,把约定不用的六个码挑出来报错,否则非法码会被悄悄转成合法值,故障就被掩盖了。

3.3 用脚本自动生成码表并校验,避免手抄出错

码表这东西,手抄一遍错一个比特,后面所有推导都跟着错。我现在的习惯是写几行脚本自动生成并做性质校验,跑一遍只要几毫秒,比人眼核对可靠得多。下面这段 Python 可以直接复制执行,它会打印四种编码的完整码表,并自动判断每一种是否具备自补性。

# 四种常见 BCD 编码的生成与自补性校验 def code_8421(v): return v def code_5421(v): # 0-4 用 0000-0100,5-9 用 1000-1100,相当于后半段整体加 3 return v if v < 5 else v + 3 def code_2421a(v): # A 码:0-4 用 0000-0100,5-9 用 1011-1111,相当于后半段整体加 6 return v if v < 5 else v + 6 def code_excess3(v): # 余三码:8421 码整体平移 3 return v + 3 CODES = { "8421 ": code_8421, "5421 ": code

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询