I2C同步串行通信
SPI通信虽然功能更强大了,但是需要4根线做连接,还是有点复杂,接下来I2C的连线就简单很多了。
I2C通信原理
I2C也是一种常用的串行通信方式,和SPI一样可以连接多个设备,重点是它只需要两根线就可以完成。
不过他的两根线和UART不同,不全是传输数据用的,I2C中的一根线是时钟线,另一根才是传输数据的,这根线可以双向的传输数据。
I2C通信中可以有多个主设备或者从设备,只要能通过地址找到彼此的位置即可。
主器件用于启动总线传送数据,并产生时钟给各种从机,此时任何被寻址的器件均被认为是从机。在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从机,主机首先得找到从机的地址,然后主动发送数据过去,最后也得由主机终止数据传送;如果主机要接收从机的数据,同样由主机得找到从机的地址,然后主机接收从器件发送的数据,最后由主机终止接收过程。
因为I2C的特性,使用I2C的设备比SPI多很多,比如图中的紫外线传感器,陀螺仪之类的,都是使用I2C进行通信的。
硬件接线
我们找来一个常用的陀螺仪模块,按照这里的接线图连接到旭日X3派的40PIN接口上,这里除了I2C通信的两根线之外,另外两根是电源线,负责给这个模块供电的,让它正常工作起来。
运行示例程序
接线是挺简单的,我们继续来运行这个例程,看下能否收到数据。
在终端启动例程,很快就可以看到通过I2C读取到了大量传感器的数据,这些就是陀螺仪模块的原始数据,收到之后我们就可以进行结算处理啦,这就是我们后续机器人开发的需要解决的问题了。
代码解析
mpu6500_i2c.py:
#!/usr/bin/env python3
import smbus,time
def MPU6050_start():
# alter sample rate (stability)
samp_rate_div = 0 # sample rate = 8 kHz/(1+samp_rate_div)
bus.write_byte_data(MPU6050_ADDR, SMPLRT_DIV, samp_rate_div)
time.sleep(0.1)
# reset all sensors
bus.write_byte_data(MPU6050_ADDR,PWR_MGMT_1,0x00)
time.sleep(0.1)
# power management and crystal settings
bus.write_byte_data(MPU6050_ADDR, PWR_MGMT_1, 0x01)
time.sleep(0.1)
#Write to Configuration register
bus.write_byte_data(MPU6050_ADDR, CONFIG, 0)
time.sleep(0.1)
#Write to Gyro configuration register
gyro_config_sel = [0b00000,0b010000,0b10000,0b11000] # byte registers
gyro_config_vals = [250.0,500.0,1000.0,2000.0] # degrees/sec
gyro_indx = 0
bus.write_byte_data(MPU6050_ADDR, GYRO_CONFIG, int(gyro_config_sel[gyro_indx]))
time.sleep(0.1)
#Write to Accel configuration register
accel_config_sel = [0b00000,0b01000,0b10000,0b11000] # byte registers
accel_config_vals = [2.0,4.0,8.0,16.0] # g (g = 9.81 m/s^2)
accel_indx = 0
bus.write_byte_data(MPU6050_ADDR, ACCEL_CONFIG, int(accel_config_sel[accel_indx]))
time.sleep(0.1)
# interrupt register (related to overflow of data [FIFO])
bus.write_byte_data(MPU6050_ADDR, INT_ENABLE, 1)
time.sleep(0.1)
return gyro_config_vals[gyro_indx],accel_config_vals[accel_indx]
def read_raw_bits(register):
# read accel and gyro values
high = bus.read_byte_data(MPU6050_ADDR, register)
low = bus.read_byte_data(MPU6050_ADDR, register+1)
# combine higha and low for unsigned bit value
value = ((high << 8) | low)
# convert to +- value
if(value > 32768):
value -= 65536
return value
def mpu6050_conv():
# raw acceleration bits
acc_x = read_raw_bits(ACCEL_XOUT_H)
acc_y = read_raw_bits(ACCEL_YOUT_H)
acc_z = read_raw_bits(ACCEL_ZOUT_H)
# raw temp bits
## t_val = read_raw_bits(TEMP_OUT_H) # uncomment to read temp
# raw gyroscope bits
gyro_x = read_raw_bits(GYRO_XOUT_H)
gyro_y = read_raw_bits(GYRO_YOUT_H)
gyro_z = read_raw_bits(GYRO_ZOUT_H)
#convert to acceleration in g and gyro dps
a_x = (acc_x/(2.0**15.0))*accel_sens
a_y = (acc_y/(2.0**15.0))*accel_sens
a_z = (acc_z/(2.0**15.0))*accel_sens
w_x = (gyro_x/(2.0**15.0))*gyro_sens
w_y = (gyro_y/(2.0**15.0))*gyro_sens
w_z = (gyro_z/(2.0**15.0))*gyro_sens
## temp = ((t_val)/333.87)+21.0 # uncomment and add below in return
return a_x,a_y,a_z,w_x,w_y,w_z
# MPU6050 Registers
MPU6050_ADDR = 0x68
PWR_MGMT_1 = 0x6B
SMPLRT_DIV = 0x19
CONFIG = 0x1A
GYRO_CONFIG = 0x1B
ACCEL_CONFIG = 0x1C
INT_ENABLE = 0x38
ACCEL_XOUT_H = 0x3B
ACCEL_YOUT_H = 0x3D
ACCEL_ZOUT_H = 0x3F
TEMP_OUT_H = 0x41
GYRO_XOUT_H = 0x43
GYRO_YOUT_H = 0x45
GYRO_ZOUT_H = 0x47
# start I2C driver
bus = smbus.SMBus(0) # start comm with i2c bus
gyro_sens,accel_sens = MPU6050_start() # instantiate gyro/accel
while True:
print(mpu6050_conv())
这里我们看一下怎么使用I2C和陀螺仪通信来获取信息。主体上有三个大的函数,而这三个函数的功能都比较单一,第一个函数通过给陀螺仪发送数据来进行初始化的设置,比如配置陀螺仪的电源寄存器,加速度寄存器等;第二个函数则是从陀螺仪读取数据;第三个函数则是将读取到的函数进行计算,从而变成真正的加速度等信息。
具体的读写操作可以看下面的代码片段,也是直接调用函数指定地址和数据就好了。
这里我们也能看到几个关键的参数,比如陀螺仪MPU6050的地址是0x68,通过这个地址才能确认到陀螺仪的存在,然后进行初始化,初始化完成之后进入循环,不停的将数据打印出来。