c++ - i2c 字节写函数,这段代码是如何工作的?我完全不能理解

标签 c++ i2c

谁能解释一下这条线是如何工作的

template <class T>
...const T& value)...
.
.
. 
const uint8_t* p = (const uint8_t*)(const void*)&value;

关于此代码(为 eeprom 写入 i2c 字节)

template <class T>
uint16_t writeObjectSimple(uint8_t i2cAddr, uint16_t addr, const T& value){

      const uint8_t* p = (const uint8_t*)(const void*)&value;    
      uint16_t i;
      for (i = 0; i < sizeof(value); i++){
            Wire.beginTransmission(i2cAddr);
                  Wire.write((uint16_t)(addr >> 8));  // MSB
                  Wire.write((uint16_t)(addr & 0xFF));// LSB
                  Wire.write(*p++);
            Wire.endTransmission();
            addr++;
            delay(5);  //max time for writing in 24LC256
      }
      return i;
}

template <class T>
uint16_t readObjectSimple(uint8_t i2cAddr, uint16_t addr, T& value){

            uint8_t* p = (uint8_t*)(void*)&value;
            uint8_t objSize = sizeof(value);
            uint16_t i;      
            for (i = 0; i < objSize; i++){
                  Wire.beginTransmission (i2cAddr);
                        Wire.write((uint16_t)(addr >> 8));  // MSB
                        Wire.write((uint16_t)(addr & 0xFF));// LSB
                  Wire.endTransmission();
                  Wire.requestFrom(i2cAddr, (uint8_t)1);         
                  if(Wire.available()){
                        *p++ = Wire.read();
                  }
                  addr++;
            }
            return i;
}

我认为这些线条就像指针一样? 当我这样做时,我无法理解代码如何正确存储每种类型的数据

struct data{
    uint16_t yr;
    uint8_t mont;
    uint8_t dy;
    uint8_t hr;
    uint8_t mn;
    uint8_t ss; 
};
.
.
.
data myString;
writeObjectSimple(0x50,0,myString);

然后使用正确的方法恢复值

data myStringRead;
readObjectSimple(0x50,0,myStringRead)

函数 i2c 字节写入检测每种数据类型之间的一些特殊字符以存储在正确的位置?

谢谢

最佳答案

首先我必须声明,这段代码是由一个不完全熟悉 C++ 和 C 处理指针类型的差异的人编写的。我的印象是这个人有很强的 C 背景,只是想关闭 C++ 编译器以抛出警告。

我们来分解一下这行代码的作用

const uint8_t* p = (const uint8_t*)(const void*)&value;

这里的目的是获取一个任意类型的缓冲区——我们在这里甚至不知道,因为它是一个模板类型——并将其视为一个无符号 8 位整数缓冲区。这样做的原因是,此缓冲区的内容稍后将通过线路逐位发送(这称为“位碰撞”)。

在 C 中,执行此操作的方法是编写

const uint8_t* p = (const void*)&value;

这是有效的,因为在 C 中将 void* 类型的指针分配给非 void 指针是完全有效的,反之亦然。然而,C 语言设定的重要规则是,从技术上讲,当您将 void* 指针转换为非 void 类型时,void* 指针必须具有通过获取同一类型对象的地址(& 运算符)获得。然而在实践中,实现允许将 void* 类型指针转换为与原始对象对齐兼容的任何类型,并且对于大多数 - 但不是全部! – 体系结构 uint8_t 缓冲区可以与任何地址对齐。

但是在 C++ 中,这种 void* 指针的来回赋值是不允许隐式的。 C++ 需要一个显式 转换(这也是为什么您经常可以看到 C++ 程序员在 C 代码中编写类似 struct foo *p = (struct foo*)malloc(...) 的原因).

所以你用 C++ 写的是

const uint8_t* p = (const uint8_t*)&value;

这确实有效并且不会引发任何警告。然而,一些static linter 工具会反对它。所以第一个强制转换(你必须从右到左阅读强制转换)首先通过强制转换为 void* 来丢弃原始类型以满足 linter,然后第二个强制转换为目标类型以满足编译器.

然而,正确的 C++ 习惯用法应该是使用大多数 linters 也会接受的 reinterpret_cast

const uint8_t* p = reinterpret_cast<const uint8_t*>(&value);

然而,所有这些转换仍然会调用实现定义的行为,并且当涉及到位碰撞时,您会遇到字节顺序问题(至少)。

Bit banging 本身的工作原理是一个接一个地提取值的每一位,并相应地连接进出处理器端口的电线。此处使用的运算符是 >>> 来移动位和二进制 & 来“选择”特定位。

例如,当您看到这样的语句时

(v & (1<<x))

接下来要做的是检查位号 x 是否设置在变量 v 中。您还可以通过屏蔽 (= 应用 binary & 运算符 - 不要与 unary 产生指针的“地址”运算符)。

同样,您可以使用 | 运算符将多个变量的位相互“叠加”。结合移位运算符,您可以使用它来逐位构建变量的内容(位来自端口)。

关于c++ - i2c 字节写函数,这段代码是如何工作的?我完全不能理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57241590/

相关文章:

c++ - 我应该如何使用新的 C++11 标准进行 C++ 编程?

c++ - 复杂/ double 类型的运算符重载 "+"

c++ - boost hana 将两个序列压缩到 map 中

c++ - 修复 TinyWireS 库的 I2C 地址问题

c++ - 在 Windows 上获取 boost::filesystem::path 作为 UTF-8 编码的 std::string

c++ - 如何专门化 vector 类模板?

c# - 在 Window 10 IoT Core 的多线程应用程序中访问 I2C 设备的正确方法是什么?

c - 用于 i2c 设备的 linux 驱动程序——读取两个字节

c - 数据请求仅返回地址值(I2C)

c - Linux C 编程 : Concurrent reads/writes to same file descriptor