1、Nano I2C 硬件
Nano 七组硬件I2C 总线及对应关系: (分别有1.8V和3.3V)
2、Jetson Nano + IMU (MPU6050)
惯性测量单元(IMU)是一种电子设备,它使用加速度计和陀螺仪(有些搭配磁场传感器件)组合来测量和报告当前设备的速度、方向和重力信息。
硬件连接 (Nano+MPU6050)
1.VCC ---接Nano引脚第17脚; (接 5V 非3.3V)
2.GND ---接Nano 引脚第25脚;
3.SCL ---接Nano 引脚第5脚(GEN2_I2C_SCL),电平已转换成3.3V电平;
4.SDA ---接Nano 引脚第3脚(GEN2_I2C_SDA),电平已转换成3.3V电平;
软件配置
默认Nano L4T 的内核Kernel Image已包含了MPU6050 I2C Driver,不需要修改Kernel 代码, 但需要修改DTS 以支持MPU6050对应I2C和寄存器配置!
hardware\nvidia\platform\t210\porg\kernel-dts\tegra210-porg-p3448-common.dtsi
根据硬件连接修改如下:
1、去掉/*#include "porg-platforms/tegra210-porg-super-module-e2614.dtsi"*/
2、添加
i2c@7000c400 { /*bus1 */
tw_icm20628: icm20628@68 {
compatible = "invensense,mpu6xxx";
reg = <0x68>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(Z, 0) 0x01>;
accelerometer_matrix= [01 00 00 00 01 00 00 00 01];
gyroscope_matrix = [01 00 00 00 01 00 00 00 01];
geomagnetic_rotation_vector_disable = <1>;
gyroscope_uncalibrated_disable = <1>;
quaternion_disable = <1>;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
vcc-supply = <&p3448_vdd_3v3_sys>;
vcc-pullup-supply = <&p3448_vdd_3v3_sys>;
};
};
3、烧录dtb 文件
sudo ./flash.sh -r -k DTB jetson-nano-qspi-sd mmcblk0p1
4、检查I2C 设备 (可查看dmesg log), 若显示UU,则参考附录中的说明,检查I2C 的DTS 配置是否有地址相同设备!
nvidia@tw-nano:~$ sudo i2cdetect -y -r 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
5、i2c-tools 读写是否成功以验证设备连接
sudo i2cdump -f -y 1 0x86
sudo i2cget -y 1 0x68 0x41 (TEMPERATURE-OUT0)
PS: MPU6050 特性及应用
- MPU-6000(6050)的角速度测量范围为±250、±500、±1000与±2000°/sec (dps),可准确追踪快速与慢速动作;
- MPU-6000(6050)的加速器测量范围为±2g、±4g±8g与±16g;
- 以数字输出6轴或9轴的旋转矩阵、四元数(quaternion)、欧拉角格式(Euler Angle forma)的融合演算数据
- 数字运动处理(DMP: Digital Motion Processing)引擎可减少复杂的融合演算数据、感测器同步化、姿势感应等的负荷
- 通讯采用400kHz的IIC或最高达20MHz的SPI(MPU-6050没有SPI);
- MPU-6000(6050)可在不同电压下工作,VDD供电为2.5V±5%、3.0V±5%或3.3V±5%,逻辑接口VDDIO供电为1.8V± 5%(MPU6000仅用VDD);
- 应用场景:姿势识别、遥控拍摄、画面放大缩小、滚动、快速下降中断、high-G中断、零动作感应、触击感应、摇动感应功能等等
3、 Python + I2C 读取陀螺仪数据
Step1. 获取用户对I2C总线操作权限 (ex. nvidia as the login name)
$sudo usermod -aG i2c $USER
sudo usermod -aG i2c nvidia
--- 重启系统确保更改I2C等有效 ---
Step2. MPU6050 运行结果如下:
jetbot@jetbot:~/work/mpu-6050-Python$ python3 example.py
Accelerometer data
x: -7.570465649414062
y: 6.232106921386718
z: 0.7589619262695312
Gyroscope data
x: 1.5801526717557253
y: 3.2900763358778624
z: 0.6793893129770993
Temp: 44.906470588235294oC
Accelerometer data
x: -7.615955480957031
y: 6.241683728027343
z: 0.6344634399414062
Gyroscope data
x: 1.6870229007633588
y: 3.3969465648854964
z: 1.1984732824427482
Temp: 45.00058823529412oC
Step3. Python example案例代码
"""This program handles the communication over I2C
between a Jetson Nano and a MPU-6050 Gyroscope / Accelerometer combo.
Made by: Dennis/TW
Released under the MIT License
Copyright 2019
"""
import smbus
class mpu6050:
# Global Variables
GRAVITIY_MS2 = 9.80665
address = None
bus = smbus.SMBus(1)
# Scale Modifiers
ACCEL_SCALE_MODIFIER_2G = 16384.0
ACCEL_SCALE_MODIFIER_4G = 8192.0
ACCEL_SCALE_MODIFIER_8G = 4096.0
ACCEL_SCALE_MODIFIER_16G = 2048.0
GYRO_SCALE_MODIFIER_250DEG = 131.0
GYRO_SCALE_MODIFIER_500DEG = 65.5
GYRO_SCALE_MODIFIER_1000DEG = 32.8
GYRO_SCALE_MODIFIER_2000DEG = 16.4
# Pre-defined ranges
ACCEL_RANGE_2G = 0x00
ACCEL_RANGE_4G = 0x08
ACCEL_RANGE_8G = 0x10
ACCEL_RANGE_16G = 0x18
GYRO_RANGE_250DEG = 0x00
GYRO_RANGE_500DEG = 0x08
GYRO_RANGE_1000DEG = 0x10
GYRO_RANGE_2000DEG = 0x18
# MPU-6050 Registers
PWR_MGMT_1 = 0x6B
PWR_MGMT_2 = 0x6C
SELF_TEST_X = 0x0D
SELF_TEST_Y = 0x0E
SELF_TEST_Z = 0x0F
SELF_TEST_A = 0x10
ACCEL_XOUT0 = 0x3B
ACCEL_XOUT1 = 0x3C
ACCEL_YOUT0 = 0x3D
ACCEL_YOUT1 = 0x3E
ACCEL_ZOUT0 = 0x3F
ACCEL_ZOUT1 = 0x40
TEMP_OUT0 = 0x41
TEMP_OUT1 = 0x42
GYRO_XOUT0 = 0x43
GYRO_XOUT1 = 0x44
GYRO_YOUT0 = 0x45
GYRO_YOUT1 = 0x46
GYRO_ZOUT0 = 0x47
GYRO_ZOUT1 = 0x48
ACCEL_CONFIG = 0x1C
GYRO_CONFIG = 0x1B
def __init__(self, address):
self.address = address
# Wake up the MPU-6050 since it starts in sleep mode
self.bus.write_byte_data(self.address, self.PWR_MGMT_1, 0x00)
# I2C communication methods
def read_i2c_word(self, register):
"""Read two i2c registers and combine them.
register -- the first register to read from.
Returns the combined read results.
"""
# Read the data from the registers
high = self.bus.read_byte_data(self.address, register)
low = self.bus.read_byte_data(self.address, register + 1)
value = (high << 8) + low
if (value >= 0x8000):
return -((65535 - value) + 1)
else:
return value
# MPU-6050 Methods
def get_temp(self):
"""Reads the temperature from the onboard temperature sensor of the MPU-6050.
Returns the temperature in degrees Celcius.
"""
# Get the raw data
raw_temp = self.read_i2c_word(self.TEMP_OUT0)
# Get the actual temperature using the formule given in the
# MPU-6050 Register Map and Descriptions revision 4.2, page 30
actual_temp = (raw_temp / 340) + 36.53
# Return the temperature
return actual_temp
def set_accel_range(self, accel_range):
"""Sets the range of the accelerometer to range.
accel_range -- the range to set the accelerometer to. Using a
pre-defined range is advised.
"""
# First change it to 0x00 to make sure we write the correct value later
self.bus.write_byte_data(self.address, self.ACCEL_CONFIG, 0x00)
# Write the new range to the ACCEL_CONFIG register
self.bus.write_byte_data(self.address, self.ACCEL_CONFIG, accel_range)
def read_accel_range(self, raw = False):
"""Reads the range the accelerometer is set to.
If raw is True, it will return the raw value from the ACCEL_CONFIG
register
If raw is False, it will return an integer: -1, 2, 4, 8 or 16. When it
returns -1 something went wrong.
"""
# Get the raw value
raw_data = self.bus.read_byte_data(self.address, self.ACCEL_CONFIG)
if raw is True:
return raw_data
elif raw is False:
if raw_data == self.ACCEL_RANGE_2G:
return 2
elif raw_data == self.ACCEL_RANGE_4G:
return 4
elif raw_data == self.ACCEL_RANGE_8G:
return 8
elif raw_data == self.ACCEL_RANGE_16G:
return 16
else:
return -1
def get_accel_data(self, g = False):
"""Gets and returns the X, Y and Z values from the accelerometer.
If g is True, it will return the data in g
If g is False, it will return the data in m/s^2
Returns a dictionary with the measurement results.
"""
# Read the data from the MPU-6050
x = self.read_i2c_word(self.ACCEL_XOUT0)
y = self.read_i2c_word(self.ACCEL_YOUT0)
z = self.read_i2c_word(self.ACCEL_ZOUT0)
accel_scale_modifier = None
accel_range = self.read_accel_range(True)
if accel_range == self.ACCEL_RANGE_2G:
accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_2G
elif accel_range == self.ACCEL_RANGE_4G:
accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_4G
elif accel_range == self.ACCEL_RANGE_8G:
accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_8G
elif accel_range == self.ACCEL_RANGE_16G:
accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_16G
else:
print("Unkown range - accel_scale_modifier set to self.ACCEL_SCALE_MODIFIER_2G")
accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_2G
x = x / accel_scale_modifier
y = y / accel_scale_modifier
z = z / accel_scale_modifier
if g is True:
return {'x': x, 'y': y, 'z': z}
elif g is False:
x = x * self.GRAVITIY_MS2
y = y * self.GRAVITIY_MS2
z = z * self.GRAVITIY_MS2
return {'x': x, 'y': y, 'z': z}
def set_gyro_range(self, gyro_range):
"""Sets the range of the gyroscope to range.
gyro_range -- the range to set the gyroscope to. Using a pre-defined
range is advised.
"""
# First change it to 0x00 to make sure we write the correct value later
self.bus.write_byte_data(self.address, self.GYRO_CONFIG, 0x00)
# Write the new range to the ACCEL_CONFIG register
self.bus.write_byte_data(self.address, self.GYRO_CONFIG, gyro_range)
def read_gyro_range(self, raw = False):
"""Reads the range the gyroscope is set to.
If raw is True, it will return the raw value from the GYRO_CONFIG
register.
If raw is False, it will return 250, 500, 1000, 2000 or -1. If the
returned value is equal to -1 something went wrong.
"""
# Get the raw value
raw_data = self.bus.read_byte_data(self.address, self.GYRO_CONFIG)
if raw is True:
return raw_data
elif raw is False:
if raw_data == self.GYRO_RANGE_250DEG:
return 250
elif raw_data == self.GYRO_RANGE_500DEG:
return 500
elif raw_data == self.GYRO_RANGE_1000DEG:
return 1000
elif raw_data == self.GYRO_RANGE_2000DEG:
return 2000
else:
return -1
def get_gyro_data(self):
"""Gets and returns the X, Y and Z values from the gyroscope.
Returns the read values in a dictionary.
"""
# Read the raw data from the MPU-6050
x = self.read_i2c_word(self.GYRO_XOUT0)
y = self.read_i2c_word(self.GYRO_YOUT0)
z = self.read_i2c_word(self.GYRO_ZOUT0)
gyro_scale_modifier = None
gyro_range = self.read_gyro_range(True)
if gyro_range == self.GYRO_RANGE_250DEG:
gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_250DEG
elif gyro_range == self.GYRO_RANGE_500DEG:
gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_500DEG
elif gyro_range == self.GYRO_RANGE_1000DEG:
gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_1000DEG
elif gyro_range == self.GYRO_RANGE_2000DEG:
gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_2000DEG
else:
print("Unkown range - gyro_scale_modifier set to self.GYRO_SCALE_MODIFIER_250DEG")
gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_250DEG
x = x / gyro_scale_modifier
y = y / gyro_scale_modifier
z = z / gyro_scale_modifier
return {'x': x, 'y': y, 'z': z}
def get_all_data(self):
"""Reads and returns all the available data."""
temp = get_temp()
accel = get_accel_data()
gyro = get_gyro_data()
return [accel, gyro, temp]
if __name__ == "__main__":
mpu = mpu6050(0x68)
print(mpu.get_temp())
accel_data = mpu.get_accel_data()
print(accel_data['x'])
print(accel_data['y'])
print(accel_data['z'])
gyro_data = mpu.get_gyro_data()
print(gyro_data['x'])
print(gyro_data['y'])
print(gyro_data['z'])
I2C Python 方法可以参考:
MPU-6050-Python
Raspberry-Pi-I2C-Python
NV Forum TX2-MPU6050
附录:
- I2C 相关
nvidia@tw-nano:~$ sudo i2cdetect -F 2
Functionalities implemented by /dev/i2c-2:
I2C yes
SMBus Quick Command no
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read no
SMBus Block Process Call no
SMBus PEC yes
I2C Block Write yes
案例:
GEN3_I2C_SDA/SCL (PIN232/234) 连接的是EEPROM(AT24C02D)
nvidia@tw-nano:~$ sudo i2cdetect -y -r 2
Warning: Can't use SMBus Quick Write command, will skip some addresses
0 1 2 3 4 5 6 7 8 9 a b c d e f
00:
10:
20:
30: -- -- -- -- -- -- -- --
40:
50: 50 -- -- -- -- -- -- 57 -- -- -- -- -- -- -- --
60:
70:
From the i2cdetect man page:
INTERPRETING THE OUTPUT
Each cell in the output table will contain one of the following
symbols:
· "--". The address was probed but no chip answered.
· "UU". Probing was skipped, because this address is currently in use
by a driver. This strongly suggests that there is a chip at this
address.
· An address number in hexadecimal, e.g. "2d" or "4e". A chip was found
at this address.
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-0/name
7000c000.i2c
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-1/name
7000c400.i2c
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-2/name
7000c500.i2c
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-3/name
7000c700.i2c
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-4/name
7000d000.i2c
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-5/name
7000d100.i2c
nvidia@tw-nano:~$ cat /sys/bus/i2c/devices/i2c-6/name
Tegra I2C adapter
nvidia@tw-nano:~$ ls /sys/firmware/devicetree/base/i2c@7000*
/sys/firmware/devicetree/base/i2c@7000c000:
'#address-cells' clock-names compatible dmas iommus name reg resets status
clock-frequency clocks dma-names interrupts linux,phandle phandle reset-names '#size-cells' temp-sensor@4c
/sys/firmware/devicetree/base/i2c@7000c400:
'#address-cells' clock-frequency clocks compatible dmas i2cmux@70 interrupts iqs263@44 name reg resets '#size-cells'
ak8963@0d clock-names cm32180@48 dma-names gpio@20 icm20628@68 iommus linux,phandle phandle reset-names rt5659.1-001a@1a status
/sys/firmware/devicetree/base/i2c@7000c500:
'#address-cells' battery-gauge@55 clock-names compatible dmas iommus name reg resets status
battery-charger@6b clock-frequency clocks dma-names interrupts linux,phandle phandle reset-names '#size-cells'
/sys/firmware/devicetree/base/i2c@7000c700:
'#address-cells' clock-names compatible dmas iommus name phandle reg resets status
clock-frequency clocks dma-names interrupts linux,phandle nvidia,restrict-clk-change print-rate-limit reset-names '#size-cells'
/sys/firmware/devicetree/base/i2c@7000d000:
'#address-cells' clocks dmas linux,phandle max77621@1c nvidia,require-cldvfs-clock reset-names sda-gpio
clock-frequency compatible interrupts max77620@3c name phandle resets '#size-cells'
clock-names dma-names iommus max77621@1b nvidia,bit-bang-after-shutdown reg scl-gpio status
/sys/firmware/devicetree/base/i2c@7000d100:
'#address-cells' clock-names compatible dmas iommus name reg resets status
clock-frequency clocks dma-names interrupts linux,phandle phandle reset-names '#size-cells'
root@jetbot:~/work/mpu-6050-Python# i2cdump -y 1 0x68
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 83 01 00 4d d4 e1 02 ce 0d 61 04 0c 28 52 52 b7 ??.M?????a??(RR?
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 01 f1 0c 1c cc c6 ..........??????
40: 74 f5 50 00 00 00 00 00 00 00 00 00 00 00 00 00 t?P.............
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 3f 00 00 72 ............?..r
70: 00 00 00 00 00 68 00 00 00 00 00 00 00 00 00 00 .....h..........
80: 83 01 00 4d d4 e1 02 ce 0d 61 04 0c 28 52 52 b7 ??.M?????a??(RR?
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 01 f1 0c 1c cc c6 ..........??????
c0: 74 f5 60 00 00 00 00 00 00 00 00 00 00 00 00 00 t?`.............
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 3f 00 03 df ............?.??
f0: 00 00 00 00 00 68 00 00 00 00 00 00 00 00 00 00 .....h..........