第二章:数制与编码——二进制、八进制、十六进制及其转换,原码、反码、补码,格雷码与BCD码
各位同学,欢迎来到第二章。这一章的内容,说白了就是数字电路的“语言基础”。你想想看,我们平时用的十进制,在FPGA的世界里根本行不通。FPGA只认0和1,也就是二进制。所以,搞懂数制转换和编码,是咱们入行的第一道门槛。
我个人习惯,在开始讲新项目前,一定会先确认数据格式。是二进制?还是BCD码?搞错了,整个逻辑都得重来。今天我们就把这些基础打牢。
2.1 二进制、八进制、十六进制
先说说为什么要有这么多进制。二进制是机器语言,但写起来太长了。比如一个8位二进制数“10110010”,你看着不晕吗?所以工程师们发明了八进制和十六进制,作为二进制的“缩写”。
二进制:基数为2,只有0和1。逢二进一。
八进制:基数为8,数字0~7。逢八进一。一个八进制位正好对应3个二进制位。
十六进制:基数为16,数字0~9和A~F(A=10, B=11, C=12, D=13, E=14, F=15)。逢十六进一。一个十六进制位对应4个二进制位。
我在项目中遇到过,用十六进制写寄存器地址,比二进制方便太多了。比如地址0x3F,一看就知道是二进制的0011 1111,省事。
2.2 进制转换方法
转换其实不难,记住两个方向:
- 其他进制转十进制:按权展开相加。比如二进制1011 = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 8+0+2+1=11。
- 十进制转其他进制:除基取余法。比如十进制13转二进制:13÷2=6余1,6÷2=3余0,3÷2=1余1,1÷2=0余1,从下往上读余数:1101。
嗯,这里要注意:二进制转八进制,从右向左每3位一组,不够补0。二进制转十六进制,每4位一组。反过来也一样。
快速记忆口诀:
二进制转八进制,三位一组要记牢;
二进制转十六进制,四位一组别混淆;
十进制转二进制,除二取余倒着读。
2.3 原码、反码、补码
这部分是很多初学者的噩梦。但说白了,就是计算机怎么表示正数和负数的问题。
原码:最高位是符号位(0正1负),其余位是数值的绝对值。比如+3的原码是0011,-3的原码是1011。
反码:正数的反码等于原码;负数的反码是符号位不变,数值位按位取反。比如-3的反码是1100。
补码:正数的补码等于原码;负数的补码是反码加1。比如-3的补码是1101。
为什么要有补码?因为CPU做减法太麻烦,用补码可以把减法变成加法。比如3 + (-3) = 0,用补码算:0011 + 1101 = 10000,丢掉最高位的进位,就是0000,完美。
避坑指南:我曾经在写一个状态机时,把负数直接当原码用了,结果比较器死活不对。后来发现,FPGA里的有符号数默认是补码形式。所以,写代码时一定要搞清楚数据是有符号还是无符号。
2.4 格雷码
格雷码是一种循环二进制码,特点是相邻两个数之间只有一位不同。这个特性在异步时钟域传输中特别有用。
比如二进制:000→001→010→011,从001到010变了两位。而格雷码:000→001→011→010,每次只变一位。
我在项目中用过格雷码做FIFO的读写指针同步,避免了多比特信号跨时钟域时的亚稳态问题。嗯,这个后面讲跨时钟域时会细说。
二进制转格雷码:最高位不变,其余位是当前位与上一位的异或。比如二进制1011,格雷码就是1110。
格雷码转二进制:最高位不变,其余位是当前格雷码位与上一位二进制位的异或。
2.5 BCD码
BCD码(Binary-Coded Decimal)是用4位二进制数表示1位十进制数。比如十进制25,BCD码就是0010 0101。
BCD码的好处是方便人机交互,比如数码管显示、按键输入。但缺点是浪费比特位,因为4位二进制本来可以表示0~15,但BCD只用到0~9。
个人经验:我建议在做数字时钟或计数器时,优先用BCD码。因为显示的时候不需要再做二进制到十进制的转换,省一个译码模块。但如果是做大量运算,比如滤波算法,还是用纯二进制更高效。
2.6 知识体系结构图
下面这张图,是我自己总结的数制与编码的知识脉络。你把它记在脑子里,后面学什么都不会乱。
2.7 实战小练习
光说不练假把式。给你留几个小题目,自己动手算一算:
- 将十进制数 47 转换为二进制、八进制、十六进制。
- 写出 -15 的8位原码、反码、补码。
- 将二进制 11010110 转换为格雷码。
- 将十进制数 89 用BCD码表示。
答案我就不写了,你自己算完对照一下。嗯,这里提醒一句:补码的符号位扩展要小心,8位补码扩展到16位,高位全部补符号位,不是补0。
好了,这一章就到这里。数制与编码是数字电路的基石,你把它吃透了,后面学组合逻辑、时序逻辑都会轻松很多。
公众号:蓝海资料掘金营,微信deep3321