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

3次阅读

共计 3424 个字符,预计需要花费 9 分钟才能阅读完成。

前言

在嵌入式开发中,不可避免要和驱动打交道。很多外设的寄存器都是应用 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 进制的数字,各位数据如下:

// 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

利用

随便关上一个传感器的数据手册,外面对寄存器的配置阐明长这样:

用上方才的代码,做如下配置:
陀螺仪输入速率: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 位;二是存在字节序的问题。有趣味的敌人能够本人扩大实现一下。

End

本篇文章只有一行代码,但却是嵌入式开发中一个十分有用的工具,通过这个工具,能够节俭很多工夫。
c 语言的开发,本身的语法糖并不多,而嵌入式的开发中,IDE 带来的辅助成果更匮乏。所以为了晋升效率,倡议大家收集本人罕用的工具代码,整顿成模块,屡次复用起来,这也是题目 utils 库的由来。
打造 utils 库会是一系列的文章,会跟大家分享有用的代码。少数会以宏定义的形式实现,也会有 c 代码级别的实现。期待咱们下一次见面。

正文完
 0