0

嵌入式项目中打造自己的utils库-二进制转换

 2 years ago
source link: https://segmentfault.com/a/1190000040709867
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

嵌入式项目中打造自己的utils库-二进制转换

发布于 9 月 19 日

在嵌入式开发中,不可避免要和驱动打交道。很多外设的寄存器都是使用2进制形式进行配置的。如果每次配寄存器,或回顾以前代码,对着16进制凭借大脑或者计算器来做2进制转换,就会非常麻烦。那么何不写一些代码,让2进制看起来更直观呢。
虽然GCC是支持0b开头的语法的(参考0x),但过于依赖会降低可移植性,不如自己手写一个吧。

下面直接贴代码:

#define BIN(n)              ((0x##n>>21 & 0x80) | \
                             (0x##n>>18 & 0x40) | \
                             (0x##n>>15 & 0x20) | \
                             (0x##n>>12 & 0x10) | \
                              (0x##n>>9 & 0x08) | \
                              (0x##n>>6 & 0x04) | \
                              (0x##n>>3 & 0x02) | \
                                 (0x##n & 0x01) )
uint8_t val = BIN(10100101);
printf("val:%X\n", val);
val:A5
uint8_t val = BIN(10100101)

// 宏展开
((0x10100101>>21 & 0x80) | \
 (0x10100101>>18 & 0x40) | \
 (0x10100101>>15 & 0x20) | \
 (0x10100101>>12 & 0x10) | \
  (0x10100101>>9 & 0x08) | \
  (0x10100101>>6 & 0x04) | \
  (0x10100101>>3 & 0x02) | \
     (0x10100101 & 0x01) )

// 用2进制表示
((0x10100101>>21 & 0b10000000) | \
 (0x10100101>>18 & 0b01000000) | \
 (0x10100101>>15 & 0b00100000) | \
 (0x10100101>>12 & 0b00010000) | \
  (0x10100101>>9 & 0b00001000) | \
  (0x10100101>>6 & 0b00000100) | \
  (0x10100101>>3 & 0b00000010) | \
     (0x10100101 & 0b00000001) )

注意:此时10100101是一个16进制的数字,各位数据如下:
image.png

// 2进制表示和移位过程
0b00010000000100000000000100000001 >> 21 & 0b10000000 = 0b00010000000 & 0b10000000                      = 0b10000000
0b00010000000100000000000100000001 >> 18 & 0b01000000 = 0b00010000000100 & 0b01000000                   = 0b00000000
0b00010000000100000000000100000001 >> 15 & 0b00100000 = 0b00010000000100000 & 0b00100000                = 0b00100000
0b00010000000100000000000100000001 >> 12 & 0b00010000 = 0b00010000000100000000 & 0b00010000             = 0b00000000
0b00010000000100000000000100000001 >>  9 & 0b00001000 = 0b00010000000100000000000 & 0b00001000          = 0b00000000
0b00010000000100000000000100000001 >>  6 & 0b00000100 = 0b00010000000100000000000100 & 0b00000100       = 0b00000100
0b00010000000100000000000100000001 >>  3 & 0b00000010 = 0b00010000000100000000000100000 & 0b00000010    = 0b00000000
0b00010000000100000000000100000001       & 0b00000001 = 0b00010000000100000000000100000001 & 0b00000001 = 0b00000001

最后各位相加(与),是不是组合得到0b10100101(0xA5)了?

课外小知识

不知道大家对宏定义的#号语法是否还有印象,这里一起复习一下:

/*
  单个#
  作用是将#后面的参数转成字符串
*/
#define PRINT_VAR(a) printf("%s:%d\n", #a, a)
uint8_t length = 123;
PRINT_VAR(length);
// 输出:length:123

/*
  两个##
  作用是连接##前后两部分内容
*/
#define VAR_DEF(a) int _var_##a
#define VAR(a) _var_##a

VAR_DEF(length);
VAR(length) = 123;
printf("length:%d\n", VAR(length));
// 输出:length:123

随意打开一个传感器的数据手册,里面对寄存器的配置说明长这样:
image.png
用上刚才的代码,做如下配置:
陀螺仪输出速率:104Hz(0100)
陀螺仪量程:1000dps(10)
另外一个不管它,保持默认为0。

uint8_t ctrl2_g_config = BIN(01001000);
i2c_reg_write(&dev, 0x11, ctrl2_g_config);

是不是比写0x48直观多了,同时也方便下次修改配置。

uint8_t ctrl2_g_config = 0x48;
i2c_reg_write(&dev, 0x11, ctrl2_g_config);

再把寄存器注释写在代码上,下次维护代码一目了然。

/*
  ODR_G [7:4] Gyroscope output data rate selection. Default value: 0000
           ODR_G0 ODR [Hz] when G_HM_MODE = 1   ODR [Hz] when G_HM_MODE = 0
  0 0 0 0: Power down Power down
  0 0 0 1: 12.5 Hz (low power)                  12.5 Hz (high performance)
  0 0 1 0: 26 Hz (low power)                    26 Hz (high performance)
  0 0 1 1: 52 Hz (low power)                    52 Hz (high performance)
  0 1 0 0: 104 Hz (normal mode)                 104 Hz (high performance)
  0 1 0 1: 208 Hz (normal mode)                 208 Hz (high performance)
  0 1 1 0: 416 Hz (high performance)            416 Hz (high performance)
  0 1 1 1: 833 Hz (high performance)            833 Hz (high performance)
  1 0 0 0: 1.66 kHz (high performance)          1.66 kHz (high performance)
  1 0 0 1: 3.33 kHz (high performance           3.33 kHz (high performance)
  1 0 1 0: 6.66 kHz (high performance           6.66 kHz (high performance)
  1 0 1 1: Not available                        Not available
  
  FS_G [3:2] Gyroscope full-scale selection. Default value: 00
  (00: 245 dps; 01: 500 dps; 10: 1000 dps; 11: 2000 dps)
  
  FS_125[1] Gyroscope full-scale at 125 dps. Default value: 0
  (0: disabled; 1: enabled)
  
  Reserve[0] 0
*/
uint8_t ctrl2_g_config = BIN(01001000);
i2c_reg_write(&dev, 0x11, ctrl2_g_config);

但需要注意的是,此方法仅支持8位2进制的表示,要支持16位、32位的2进制,一是参数存储字节会超32位;二是存在字节序的问题。有兴趣的朋友可以自己扩展实现一下。

本篇文章只有一行代码,但却是嵌入式开发中一个非常有用的工具,通过这个工具,可以节省很多时间。
c语言的开发,自身的语法糖并不多,而嵌入式的开发中,IDE带来的辅助效果更匮乏。所以为了提升效率,建议大家收集自己常用的工具代码,整理成模块,多次复用起来,这也是标题utils库的由来。
打造utils库会是一系列的文章,会跟大家分享有用的代码。多数会以宏定义的方式实现,也会有c代码级别的实现。期待我们下一次见面。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK