ARM裸机开发-I2C-LM75 一.背景LM75,温度传感器.工作电压2.7V-5.5V精度– 25°C to 100°C: ±2°C (Max)– 55°C to 125°C: ±3°C (Max)量程– 55°C to 125°C分辨率9位0.5°COS中断引脚可在传感器的寄存器里配置温度的阈值当温度到达阈值可引发中断。设备地址0x48低三位可通过焊接置0或1表示温度十六进制值乘以分辨率0.5负温度值用补码保存先转为原码再计算通过配置寄存器低三位的值选择不同寄存器本次配置000即读取温度。读取2字节温度舍弃低7位。float get_lm75_temp(void) { short temp; unsigned char s[2]; i2c_read(I2C1, 0x48, 0, s, 2); temp s[0] 8; temp | s[1]; temp 7; return temp * 0.5; }此外对read和write的传输寄存器地址代码进行改进由于寄存器地址可能不止一位AT24C02能存储256字节数据所以char刚好可以表示LMA75从P0-P7也为一字节长度这两都可用char表示但后续操作显示屏时char则不够所以用char类型定义寄存器地址会出现问题用unsigned short reg_address定义地址, int reg_len定义寄存器地址长度改进如下int i 0; for (i 0; i reg_len; i) { // 中断标记位清零 base-I2SR ~(1 IIF); // 发送寄存器地址 base-I2DR reg_address (reg_len - i - 1) * 8; // 等待从机ACK while ((base-I2SR (1 IIF)) 0); }二.底层最下面的硬件层即上篇及本篇使用的传感器AT24C02和LMA75等连接的上面I2C_adapter即SOC芯片内部的I2C控制器具体实现就是适配器驱动这一层最近对I2C的学习所实现的代码其实写的就是控制器这块的代码显然偏底层这部分代码通常为芯片厂商负责写比如使用imx6ull为NXP公司生产所以由他们负责写控制器底层代码而不同芯片厂商生产的芯片对应的寄存器自然不相同。再往上则可以由Linux系统进行调用这些封装好的代码可适配不同芯片屏蔽底层芯片直接面向已经被芯片厂商封装好的函数。图中核心层的i2c_transfer是灵魂函数。当设备驱动调用i2c_transfer时核心层会打包好i2c_msg包含设备地址、读写标志、数据长度然后去调用底层适配器驱动注册好的master_xfer函数指针。底层再根据这个指针去操作真正的 i.MX6ULL 寄存器。master_xfer每个芯片厂商都会写的不管使用哪个芯片想使用I2C都可调用master_xfer。以下为该函数的实现// I2C传输方向 enum I2C_Direction { I2C_Write 0, I2C_Read 1 }; // I2C信息 typedef struct { unsigned char dev_address; unsigned short reg_address; int reg_len; unsigned char *data; int len; enum I2C_Direction direction; }I2C_Msg; void master_xfer(I2C_Type *base, I2C_Msg *msg) //主机_发送/接收不管是哪个芯片厂商生产的在linux系统下都可以调用此函数 { if (msg-direction I2C_Write) { i2c_write(base, msg-dev_address, msg-reg_address, msg-reg_len, msg-data, msg-len); } else if (msg-direction I2C_Read) { i2c_read(base, msg-dev_address, msg-reg_address, msg-reg_len, msg-data, msg-len); } }调用该封装函数实现LM75传感器读取温度float get_lm75_temp(void) { short temp; unsigned char s[2]; I2C_Msg msg { .direction I2C_Read, .dev_address 0x48, .reg_address 0, .reg_len 1, .data s, .len 2 }; master_xfer(I2C1, msg); temp s[0] 8; temp | s[1]; temp 7; return temp * 0.5; }总结Linux社区不管每一款芯片在底层怎么操作自己的寄存器只是提供芯片厂商一个函数比如master_xfer告诉厂商你们负责实现你们自己芯片对应的这个函数而不同厂商则在master_xfer中实现如何操纵自己的寄存器实现好了之后由Linux封装到自己的函数接口i2c_transfer内部再把这个函数接口提供给用户层使用用户使用该i2c_transfer函数也不用考虑底层是哪个芯片厂商生产的底层寄存器是怎么操作的都由芯片厂商实现了。