C-macro:将位掩码定义的寄存器字段设置为给定值

ges*_*ema 3 c

我有 32 位寄存器,其字段定义为位掩码,例如

#define BM_TEST_FIELD 0x000F0000
Run Code Online (Sandbox Code Playgroud)

我需要一个宏,它允许我将寄存器(由其地址定义)的字段(由其位掩码定义)设置为给定值。这是我想出的:

#include <stdio.h>
#include <assert.h>

typedef unsigned int u32;

/* 
 * Set a given field defined by a bit-mask MASK of a 32-bit register at address
 * ADDR to a value VALUE.
 */
#define SET_REGISTER_FIELD(ADDR, MASK, VALUE)                                      \
{                                                                                  \
  u32 mask=(MASK); u32 value=(VALUE);                                              \
  u32 mem_reg = *(volatile u32*)(ADDR); /* Get current register value           */ \
  assert((MASK) != 0);                  /* Null masks are not supported         */ \
  while(0 == (mask & 0x01))             /* Shift the value to the left until    */ \
  {                                     /* it aligns with the bit field         */ \
    mask = mask >> 1; value = value << 1;                                          \
  }                                                                                \
  mem_reg &= ~(MASK);                   /* Clear previous register field value  */ \
  mem_reg |= value;                     /* Update register field with new value */ \
  *(volatile u32*)(ADDR) = mem_reg;     /* Update actual register               */ \
}

/* Test case */
#define BM_TEST_FIELD 0x000F0000
int main()
{
  u32 reg = 0x12345678;
  printf("Register before: 0x%.8X\n", reg);/* should be 0x12345678 */
  SET_REGISTER_FIELD(&reg, BM_TEST_FIELD, 0xA);
  printf("Register after: 0x%.8X\n", reg); /* should be 0x123A5678 */
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

有没有更简单的方法来做到这一点?

编辑:特别是,我正在寻找一种方法来减少运行时计算要求。有没有办法让预处理器计算值所需的左移次数?

Mat*_*ery 5

编辑:特别是,我正在寻找一种方法来减少运行时计算要求。有没有办法让预处理器计算值所需的左移次数?

是的:

value *= ((MASK) & ~((MASK) << 1))
Run Code Online (Sandbox Code Playgroud)

这乘以 中value的最低设置位MASK。已知乘数在编译时是 2 的常数幂,因此这将被任何远程理智的编译器编译为简单的左移。