【问题标题】:logic operators & bit separation calculation in C (PIC programming)C中的逻辑运算符和位分离计算(PIC编程)
【发布时间】:2014-02-20 15:19:04
【问题描述】:

我正在对 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 新手,这是我第一次涉足电子领域,所以我非常感谢任何帮助。谢谢。

【问题讨论】:

  • 我认为这个问题不适合这里。只需阅读 C 位运算符(|、&、
  • 你有没有推荐更合适的地方?对不起,如果我放错地方了。
  • 您要查看的位的屏蔽是使用 & 运算符完成的,并且您要分隔的位设置为 1。因此,如果您从我的读取秒数中查看,可能会简单地转换为 ( ( ( regdata & 0x7 ) * 10 ) + ( regdata & 0xf ); 我认为
  • 这样的问题可能会在electronics.stackexchange.com 得到更好的接待。不幸的是,PC 程序员过多地使用了 SO。不过,您需要缩小实际问题的范围,不要一次问多个问题。
  • @MartinJames 不,永远不要使用位域进行位映射。事实上,不要将它们用于任何事情See this.

标签: c embedded pic microchip mplab


【解决方案1】:

要删除(零)位,您可以将值与设置了所有其他位的掩码进行“与”运算,即您希望归零的位的补码,例如:

value_without_bit_6 = value & ~(1<<6);

要隔离整数中的位,您可以将值与仅设置了这些位的掩码进行“与”运算。要检查标志,这就是您需要做的所有事情,例如,

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;

对于更具可读性的代码,您应该使用常量或#defined 宏来表示您需要的各种位掩码,例如:

#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;

位域的一个潜在问题是编译器生成的代码可能大得无法预测或效率低下,这有时是微控制器的一个问题,但显然它更好地读写。 (另一个经常提到的问题是位字段的组织,例如字节顺序,在很大程度上没有被 C 标准指定,因此不能保证跨编译器和平台的可移植性。但是,我认为微控制器的低级开发往往是本质上是不可移植的,所以如果你找到了正确的位布局,我不会考虑使用“错误”的位域,尤其是对于业余爱好者项目。)

然而,你可以用宏来完成类似的可读语法;只是宏本身的可读性较差:

#define GET_SECONDS(r) ( ((r) & 0x0F) + (((r) & 0x70) >> 4) * 10 )
uint8_t sr = READ_ADDRESS(0x00);
unsigned int seconds = GET_SECONDS(sr);

【讨论】:

    【解决方案2】:

    关于位掩码本身,您需要在微控制器中制作该内存映射的模型。最简单、最有趣的方法是 #define 一些位掩码,如下所示:

    #define REG1_ST          0x80u
    #define REG1_10_SECONDS  0x70u
    #define REG1_SECONDS     0x0Fu
    
    #define REG2_10_MINUTES  0x70u
    ...
    

    然后在读取每个字节的时候,屏蔽掉你感兴趣的数据。例如:

    bool    st          = (data & REG1_ST) != 0;
    uint8_t ten_seconds = (data & REG1_10_SECONDS) >> 4;
    uint8_t seconds     = (data & REG1_SECONDS);
    

    重要的部分是尽量减少源代码中“幻数”的数量。

    写入数据:

    reg1 = 0;
    reg1 |= st ? REG1_ST : 0;
    reg1 |= (ten_seconds << 4) & REG1_10_SECONDS;
    reg1 |= seconds & REG1_SECONDS;
    

    请注意,我省略了 I2C 通信。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-23
      • 1970-01-01
      • 2017-06-30
      • 1970-01-01
      • 2012-09-02
      相关资源
      最近更新 更多