获取宏:
GPIOxMODE(gpio,mode,port) ( GPIO##gpio->MODER = ((GPIO##gpio->MODER & ~((uint32_t)GPIO2BITMASK << (port*2))) | (mode << (port * 2))) )
假设寄存器的重置值为0xFFFF.FFFF,我想将2位宽度设置为任意值。这是为STM32编写的 每个端口有15个引脚的MCU。 GPIO2BITMASK定义为0x3。有没有一种更好的方法可以清除和设置 32位宽的寄存器。
端口0-15的有效范围
模式0-3的有效范围
我想出的方法是对掩码进行位移位,将其反转,然后将其与现有寄存器值进行逻辑与,然后对结果与位移位后的新值进行逻辑或。
我希望将掩码和新值结合起来以减少逻辑运算的位数。目的还在于使该过程具有足够的通用性,以便我可以用于1,2,3或4位宽度的位操作。
有更好的方法吗?
从长远来看,还有没有更好的方法确实是一个悬而未决的问题。我正在专门寻找一种方法,它可以减少逻辑运算和移位运算的数量,而只是一种简单的单行语句。
答案是否定的。
您必须必须进行重置/设置,以确保要写入的位字段具有所需的值。
收到的答案可能更好(就观点/偏好/哲学/实践而言),因为它们不是必需的宏,并且具有参数检查功能。评论和回应中也指出了这种风格的坑坑洼洼。
答案 0 :(得分:1)
我想出的方法是将遮罩移位,将其反转, 逻辑上将其与现有寄存器值进行逻辑与,或 新值稍有移位的结果。
做到这一点或等效的方法。
我希望将蒙版和新值结合起来以减少数量 逻辑运算移位运算。目标也是保持 足够通用的过程,以便可以用于1,2,3的位操作 或4位宽度。
有更好的方法吗?
您必须完成两个基本目标:
在一般情况下,这些操作需要两个单独的操作:按位与以强制关闭位,以及按位与(或XOR,如果先清除位,则将其打开)。对于特定情况下的原始值和目标值,可能会有捷径,但是如果您想要通用的东西,正如您所说的,那么您的选择将受到限制。
不过,个人而言,我认为我倾向于从多个部分构建它,将GPIO选择与实际计算分开。至少,您可以分离出用于设置位范围的通用宏:
#define SETBITS32(x,bits,offset,mask) ((((uint32_t)(x)) & ~(((uint32_t)(mask)) << (offset))) | (((uint32_t)(bits)) << (offset)))
#define GPIOxMODE(gpio,mode,port) (GPIO##gpio->MODER = SETBITS32(GPIO##gpio->MODER, mode, port * 2, GPIO2BITMASK)
但是请注意,似乎没有避免这种宏多次评估其某些参数的好方法。因此,将SETBITS32
编写为函数可能更安全。在任何情况下,编译器都可能内联这样的函数,但是您可以通过声明static
和inline
来最大程度地发挥这种作用:
static inline uint32_t SETBITS32(uint32_t x, uint32_t bits, unsigned offset, uint32_t mask) {
return x & ~(mask << offset) | (bits << offset);
}
尽管它像宏一样,确实假设bits
的掩码区域之外没有设置位,但这也更易于阅读。
当然还有其他类似的表述。例如,如果您不需要支持不连续的位范围,则可以指定位计数而不是位掩码。这种替代方法可以防止用户提供超出指定范围的位,并且还可以进行一些参数验证:
static inline uint32_t set_bitrange_32(uint32_t x, uint32_t bits, unsigned width,
unsigned offset) {
if (width + offset > 32) {
// error: invalid parameters
return x;
} else if (width == 0) {
return x;
}
uint32_t mask = ~(uint32_t)0 >> (32 - width);
return x & ~(mask << offset) | ((bits & mask) << offset);
}
答案 1 :(得分:1)
出于多种原因,应避免将此类宏作为斑块:
和许多其他原因
使用内联函数可以存档的结果相同。产生的代码将是相同的有效
static inline __attribute__((always_inline)) void GPIOMODE(GPIO_TypeDef *gpio, unsigned mode, unsigned pin)
{
gpio -> MODER &= ~(GPIO_MODER_MODE0_Msk << (pin * 2));
gpio -> MODER |= mode << (pin * 2);
}
但是如果您喜欢宏
#define GPIOxMODE(gpio,mode,port) {volatile uint32_t *mdr = &GPIO##gpio->MODER; *mdr &= ~(GPIO_MODER_MODE0_Msk << (port*2)); *mdr |= mode << (port * 2);}
我希望将蒙版和新值结合起来以减少数量 逻辑运算移位运算。
你不能。您需要重置然后设置位。