我正在C语言中的微控制器上工作。这部分涉及更改寄存器中的位值。我提出了一些宏来简化操作:
#define BIT_SET(addr, shift) (addr | (1 << shift))
#define BIT_RESET(addr, shift) (addr & ~(1 << shift))
#define BIT_GET(addr, shift) (addr & (1 << shift))
...
reg1 = BIT_SET(reg1, 3); //set bit 3
...
但是,现在我想创建一个宏,该宏将一次更改几个位。例如,可能想将10101010
的前3位更改为110
,从而得到数字11001010
。
我可以用C语言吗?还是我走错路了?
答案 0 :(得分:2)
我的经验是拥有多个位的位掩码要容易得多。因此,使用特定的定义来标识各个位,其定义表示该位的功能,如
#define ENABLE_T1 0x0001
#define ENABLE_T2 0x0002
然后将它们与按位运算符一起使用以打开和关闭位。
unsigned short usReg1 = GetRegister(REG1);
// disable T1 and enable T2
usReg1 &= ~ ENABLE_T1;
usReg1 |= ENABLE_T2;
或
// enable both T1 and T2
usReg1 |= (ENABLE_T1 | ENABLE_T2);
或
// disable both T1 and T2
usReg1 &= ~(ENABLE_T1 | ENABLE_T2);
或测试位
if (usReg1 & ENABLE_T1) { // is T1 turned on
if (usReg1 & (ENABLE_T1 | ENABLE_T2)) { // is either T1 or T2 turned on
if ((usReg1 & (ENABLE_T1 | ENABLE_T2)) == ENABLE_T1) { // is T1 turned on and T2 turned off
if ((usReg1 & (ENABLE_T1 | ENABLE_T2)) && ((usReg1 & (ENABLE_T1 | ENABLE_T2)) != (ENABLE_T1 | ENABLE_T2))) { // is either T1 or T2 turned on but not both
宏说明
通过这种方式,您需要确保在宏定义中使用括号来尽可能地隔离参数,以确保安全。所以
#define BIT_SET(addr, shift) (addr | (1 << shift))
真的应该写成
#define BIT_SET(addr, shift) ((addr) | (1 << (shift)))
答案 1 :(得分:2)
您可以引入一个全角掩码,该掩码定义要设置的位。 例如。要设置前3位,请使用0xe0(二进制11100000)。
连同该掩码一起,提供还要以全角写入的值。
例如。前三个位中所需的二进制110为0xc0。
然后只需要写一点所需的位,就需要一点点操作魔术。
#define MULTIBIT_SET(addr, mask, value) (((addr)&((0xff)^(mask)))|((value)&(mask)))
此处((0xff)^(mask))
会反转掩码,因此与此进行“与”运算会从值中删除现有位。
((value)&(mask))
是该值,但仅在所需位置设置了位,即,这防止了在其他位置进行不必要的位设置。
总的来说,将这两个部分相加,即可获得所需的结果。
这里有一个设计选择,我要提到:
称之为偏执狂。如果有人告诉我“我只想将前三位设置为该值,该值在其他位置有位”,那么我宁愿以这种方式确保安全,即删除“其他位”。
是的,我假设无意设置位比没有设置位的风险更大,这是一个问题。
答案 2 :(得分:1)
您始终可以在C语言中完成它:-)
如果您使用的是C99兼容的编译器,并且假设它是2019年,我想您可以使用,则可以使用可变参数宏使它工作,更具体地说,使用P99。
我有一个示例,其中我提取了P99的相关组件,但是它变得一团糟,因为这是对C预处理器作为图灵完备语言的一种严重使用。
这也滥用括号;特别是EXTRACT_VALUE x
扩展为EXTRACT_VALUE (a, b)
,因为我们传递了一个元组列表。
#include "p99.h"
#define EXTRACT_SHIFT(shift, value) (shift)
#define EXTRACT_VALUE(shift, value) (value)
#define MERGE(NAME, I, A, B) (A), (B)
#define SET_OR_CLEAR(target, x, i) \
((EXTRACT_VALUE x) ? \
((target) |= (1 << EXTRACT_SHIFT x)) : \
((target) &= ~(1 << EXTRACT_SHIFT x)))
#define SET_AND_CLEAR(target, pairs...) \
P99_FOR(target, P99_NARG(pairs), MERGE, SET_OR_CLEAR, pairs)
// ...
reg1 = BIT_SET(reg3, 3); //set bit 3
// ...
SET_AND_CLEAR(reg3, (7, 1), (6, 1), (5, 0)); // set bits 6 and 7, clear bit 5
您可能需要使用-std=c99
进行编译。请注意,我使用了变体词pairs
而不是__VA_ARGS__
,以明确表明我们希望使用2元组作为参数。
请注意,这不会掩盖任何未设置或清除的输入寄存器。