在C语言中的位设置逻辑

3

我很难理解成功设置32位寄存器中的一位的逻辑。以下是该函数的伪代码:

读取主寄存器,
如果第29位 CREG_CLK_CTRL_I2C0 未设置,则设置它

uint32_t creg;

//read the CREG Master register   
creg = READ_ARC_REG((volatile uint32_t)AR_IO_CREG_MST0_CTRL);

if((creg & (1 << CREG_CLK_CTRL_I2C0)) == 0){
     creg |= ( 1 << CREG_CLK_CTRL_I2C0);
     WRITE_ARC_REG(creg, (volatile uint32_t)(AR_IO_CREG_MST0_CTRL));
}

如果CREG主寄存器最初为空,则逻辑不按预期工作。但是,如果我用所有零和第31位上的1(1000 ... 0)填充它,那么逻辑会起作用。 我不确定我的测试条件是否不正确,或者可能是其他原因。

有人能帮忙吗?


2
creg的类型是什么? - dbush
如果第29位未设置 - 为什么不直接设置它呢? - Weather Vane
你的代码期望 CREG_CLK_CTRL_I2C0 是 29,代表第 29 位。你确定这是正确的吗?CREG_CLK_CTRL_I2C0 的二进制表示中,第 29 位不是 0b00100000000000000000000000000000 吗? - acwaters
@yano:WRITE_ARC_REG 的额外调用可能会有影响,具体取决于 API。 - Markus Laire
3
除非你能证明必须使用转换并且理解所有含义,否则请勿使用转换!你使用的宏应该已经被正确定义(如果没有,请获取另一个工具链,它很糟糕)。并且要小心有符号整数的位移操作。除非你确实知道自己在做什么而且必须使用它们,否则避免使用它们! - too honest for this site
显示剩余3条评论
3个回答

3

就我个人而言,我会使用给定的数据类型:uint32_t。这将保证无论上下文如何,都能保持对齐。这是一个事实(为了清晰起见进行了因素分解,并假设移位不产生不同大小类型):

uint32_t mask = ((uint32_t)1) << CREG_CLK_CTRL_I2C0;
if((creg & mask) == 0){
     creg |= mask;
     WRITE_ARC_REG(creg, (volatile uint32_t)(AR_IO_CREG_MST0_CTRL));
}

1
以下是几种解决问题的想法:
第一步:确保您实际上了解该寄存器的工作原理。请记住,微控制器寄存器可能与内存的行为不同。在某些情况下,寄存器可能会忽略您对位的写入尝试,直到满足其他条件为止。也许这就是为什么如果您首先将1写入31位,则会起作用。第31位是做什么的?
第二步:我在网上搜索了一下,并发现定义READ_ARC_REG()和WRITE_ARC_REG()的相同标头也可以包括SET_ARC_BIT()的定义。看看能否找到并使用它。
第三步:确保您尝试写入的内容是有意义的。通过调试器逐步执行函数和/或添加某种形式的输出来显示您尝试写入寄存器的值。然后在这样做之后读取寄存器并重复该过程。查看是否尝试写入了正确的值,然后查看该写入是否实际发生。如果您的代码尝试将所需值写入寄存器,但随后的读取显示您的写入未更改位,则返回上面的第1步。

0

只是猜测(我不做嵌入式编程),但根据C标准,数字文字1的类型为int。在嵌入式编程中,int可以是16位,这种情况下,左移将具有未定义的行为,因为右操作数太大。

因此,请尝试使用1L使其成为long类型。

另外,正如Olaf在评论中提到的那样,除非您确定需要它,否则不要使用(volatile uint32_t)转换。一些搜索表明,这与Arduino 101有关,我找到的源代码同时使用了READ_ARC_REGWRITE_ARC_REG而没有任何转换。

creg = READ_ARC_REG(AR_IO_CREG_MST0_CTRL);

if((creg & (1L << CREG_CLK_CTRL_I2C0)) == 0){
     creg |= (1L << CREG_CLK_CTRL_I2C0);
     WRITE_ARC_REG(creg, AR_IO_CREG_MST0_CTRL);
}

我已经移除了我的volatile转换,将我的"1"整数转换为uint32_t并使用掩码,但如果寄存器最初为空,则该位仍然不会被设置... - claudia
1
使用 1L 的弱点是它是有符号的,将 1 移入符号位置会导致问题,因为 CREG_CLK_CTRL_I2C0 可能是 31。代码可以使用 1UL 来避免这种情况。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接