树莓派5模拟I2C通信读取MPU6050数据
树莓派有I2C通信接口,需要开启并使用特定引脚,然后使用smbus进行通信使用。
为了加深对于I2C通信过程中的信号的处理,通过使用GPIO模拟I2C通信,完成MPU6050的数据读取。
from gpiozero import DigitalOutputDevice, DigitalInputDevice from time import sleep class I2C: def __init__(self, scl_pin, sda_pin): self.scl_pin = scl_pin self.sda_pin = sda_pin # init output default high self.scl = DigitalOutputDevice(self.scl_pin) self.sda = DigitalOutputDevice(self.sda_pin) def start(self): ''' 每一次完成通信过程,需要以start方式开始 ''' self.chang_write_mode() self.scl.value = 1 self.sda.value = 1 self.sda.value = 0 self.scl.value = 0 def stop(self): ''' 每一次完成通信过程,需要以stop方式结束 ''' self.chang_write_mode() self.sda.value = 0 self.scl.value = 1 self.sda.value = 1 def send_data(self, address, reg_add, *datas): ''' 向指定i2c设备、以及i2c设备的寄存器地址,发送数据 ''' address_w = (address << 1) | 0x00 address_r = address << 1 | 0x01 self.start() self.send_byte(address_w) self.send_byte(reg_add) for data in datas: self.send_byte(data) i2c.stop() def read_data(self, address, reg_add, data_len=1): ''' 从指定i2c设备、以及i2c设备的寄存器地址,读取数据 ''' address_w = (address << 1) | 0x00 address_r = address << 1 | 0x01 self.start() self.send_byte(address_w) self.send_byte(reg_add) self.start() self.send_byte(address_r) datas = self.read_bytes(data_len) self.stop() if len(datas) == 1: return datas[0] else: return datas def read_ack(self, func=None): self.chang_read_mode() ack = self.read_bit() if ack != 0: self.stop() if func is None: raise Exception("ack is not 0") else: func() def send_ack(self): self.chang_write_mode() self.send_bit(0) def send_nack(self): self.chang_write_mode() self.send_bit(1) def send_byte(self, value): for i in range(0, 8): bit = value & (0x80 >> i) self.send_bit(bit) self.read_ack() def send_bit(self, value): self.chang_write_mode() self.sda.value = value sleep(0.001) self.scl.value = 1 self.scl.value = 0 def read_bytes(self, len=1): values = [] for index in range(0, len): value = 0 for i in range(0, 8): bit = self.read_bit() value = (value << 1) | bit values.append(value) if index < len - 1: self.send_ack() self.send_nack() return values def read_byte(self): value = 0 for i in range(0, 8): bit = self.read_bit() value = (value << 1) | bit self.send_nack() return value def read_bit(self): self.chang_read_mode() self.scl.value = 1 sleep(0.001) value = self.sda.value self.scl.value = 0 return value def chang_read_mode(self): if type(self.sda) != DigitalInputDevice: self.sda.close() self.sda = DigitalInputDevice(self.sda_pin) def chang_write_mode(self): if type(self.sda) != DigitalOutputDevice: self.sda.close() self.sda = DigitalOutputDevice(self.sda_pin) # mpu6050的地址 address = 0x68 # 电源管理寄存器地址 power_mgmt_1 = 0x6B power_mgmt_2 = 0x6C i2c = I2C(21, 20) i2c.send_data(address, power_mgmt_1, 0) # 获取温度数据 word = i2c.read_data(address, 0x41, 2) value = (word[0] << 8) + word[1] if value >= 0x8000: value = -((65535 - value) + 1) temperature = value / 340 + 36.53
因为是树莓派5,RPi.GPIO无法使用,最后使用的是gpiozero。
如果要读取角速度和加速度信息,read_data中的寄存器地址修改为对应的即可,别忘了比例转换得到实际值。
I2C具体交互过程参考了MPU-6000-Datasheet1.pdf (tdk.com)其中的时序图内容。