我正在对 PIC18F94K20 进行编程,使其与 MCP7941X I2C RTCC 船和 24AA128 I2C CMOS 串行 EEPROM 设备一起工作。目前我有代码可以成功地初始化 RTCC 的秒/天/等值并启动定时器,在每一秒的周转时切换 LED。
我正在尝试扩充代码以读回这些值的正确数据,但是当我尝试解释值中的各种“额外”位时遇到了麻烦。内存映射可能有助于在某种程度上阐明我的问题:
例如,小时列或 02h 地址。位 6 设置为 1 以切换 12 小时时间,将 01000000 添加到小时位。我可以在这个地址读回字节的全部内容,但我想使用一个 if 语句来检测是 12 小时还是 24 小时时间,并相应地进行调整。我不担心 10 小时的位,因为我可以使用 BCD 转换循环(我认为)很容易地计算出它。
我之前在 C 中使用按位或运算符将原始小时数据增加到 24。我将这个特定情况下的小时初始化为 0x11,并将 12 小时控制位设置为 0x64。设置时间时:
WriteI2C(0x11|0x64);
如您所见,它使用了按位或。
在回读时间时,如何将运算符合并到我的代码中以将多余的位与实际时间位分开?我试着做这样的事情:
current_seconds = ReadI2C();
current_seconds = ST & current_seconds;
但这完全毁了一切。它可以编译,但设备会“卡在”这个序列上。
我如何将 ST/AMPM/VBATEN 位与我需要的实际数据分开,以及针对它们出现的各种情况实现 for 循环的好方法是什么(例如,如果位读回 12 小时时间6 = 0 和 24 小时时间(如果 bit6 = 1,依此类推)。
我是一个 C 新手,这是我第一次涉足电子领域,所以我非常感谢任何帮助。谢谢。
最佳答案
要删除(零)位,您可以将值与设置了所有其他位的掩码进行 AND 运算,即您希望清零的位的补码,例如:
value_without_bit_6 = value & ~(1<<6);
要隔离整数中的某个位,您可以使用仅设置了这些位的掩码对值进行 AND。对于检查标志,这就是您需要做的所有事情,例如,
if (value & (1<<6)) {
// bit 6 is set
} else {
// bit 6 is not set
}
要读取较大整数偏移量内的小整数值,首先隔离这些位,然后将它们右移最低位的索引(以使最低有效位进入正确位置),例如:
value_in_bits_4_and_5 = (value & ((1<<4)|(1<<5))) >> 4;
为了更易读的代码,您应该使用常量或#define
d 宏来表示您需要的各种位掩码,例如:
#define BIT_VBAT_EN (1<<3)
if (value & BIT_VBAT_EN) {
// VBAT is enabled
}
另一种方法是使用位域来定义位的组织,例如:
typedef union {
struct {
unsigned ones:4;
unsigned tens:3;
unsigned st:1;
} seconds;
uint8_t byte;
} seconds_register_t;
seconds_register_t sr;
sr.byte = READ_ADDRESS(0x00);
unsigned int seconds = sr.seconds.ones + sr.seconds.tens * 10;
位域的一个潜在问题是编译器生成的代码可能会异常庞大或效率低下,这有时是微 Controller 的一个问题,但显然它更易于读写。 (另一个经常被引用的问题是位字段的组织,例如字节顺序,在很大程度上没有被 C 标准指定,因此不能保证跨编译器和平台的可移植性。但是,我认为微 Controller 的低级开发往往是本质上是不可移植的,所以如果你找到了正确的位布局,我不会认为使用位域是“错误的”,尤其是对于爱好者项目。)
然而,您可以使用宏实现类似的可读语法;只是宏本身的可读性较差:
#define GET_SECONDS(r) ( ((r) & 0x0F) + (((r) & 0x70) >> 4) * 10 )
uint8_t sr = READ_ADDRESS(0x00);
unsigned int seconds = GET_SECONDS(sr);
关于c - C中的逻辑运算符和位分离计算(PIC编程),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21911979/