Продолжаем осваивать различные модули Raspberry Pi, так вот сегодня пришла очередь разобрать работу с шиной I2C. Не будем ходить вокруг да около, а сразу же перейдем к делу. И начнем с постановки задачи, которую будем решать в этой статье.
Итак, возьмем какое-нибудь устройство, работающее по I2C и подключим его к Raspberry Pi. У меня под рукой обнаружилась такая плата с 9-осевым датчиком MPU-9250:
Вполне подойдет для экспериментов. Естественно, выбор именно этого устройства не накладывает никаких ограничений на работу с шиной. То есть все, что мы сегодня разберем на примере MPU-9250 актуально и для любых других устройств, подключенных по I2C.
Схема подключения выглядит следующим образом:
При работе с I2C обязательно нужны подтягивающие резисторы, которые подтягивают линии SDA и SCL к напряжению питания:
В моем случае эти резисторы уже распаяны на плате с MPU-9250, так что дополнительных действий не требуется. На этом с подключением заканчиваем и переходим к программной части статьи.
Включение I2C на Raspberry Pi.
Первым делом нам следует включить интерфейс I2C. Для этого воспользуемся утилитой для конфигурации Raspberry Pi - raspi-config. Вводим в терминале команду:
sudo raspi-config
Далее переходим в пункт Interfacing options и включаем I2C:
После этого необходимо перезапустить плату:
sudo reboot
Альтернативным способом включения I2C является редактирование файла /boot/config.txt. В файле находим и раскомментируем строку:
dtparam=i2c_arm=on
После чего аналогичным образом перезапускаем плату. При следующем включении модуль I2C будет активирован. Оба этих способа можно использовать, подключившись к плате по SSH.
Работа с I2C через консоль.
Для I2C, как и для других модулей платы, существуют разные варианты и методы использования. И начнем мы с применения команд непосредственно в консоли, в чем нам поможет пакет i2c-tools. Устанавливаем командой:
sudo apt-get install -y i2c-tools
После успешной установки мы можем использовать команду i2cdetect для просмотра доступных шин I2C:
i2cdetect -l
Результат выполнения в моем случае выглядит так:
Из вывода команды следует, что у нас активирована шина i2c-1, которую мы и будем использовать. Для новых версий плат Raspberry Pi всегда по умолчанию используется именно i2c-1. Для более старых же модификаций, например Raspberry Pi Model B с оперативной памятью 256 МБ, будет использована шина i2c-0.
Двигаемся дальше, следующая команда, которую мы рассмотрим:
sudo i2cdetect -y 1
Цифра 1 здесь как раз и относится к тому, что мы будем работать с шиной i2c-1. Кроме того, используем ключ -y. По умолчанию команда i2cdetect ожидает подтверждения от пользователя прежде, чем вмешиваться в работу шины, а ключом -y мы даем ей добро на работу с I2C без дополнительного разрешения.
Итак, вывод этой команды может быть таким:
Здесь мы видим все подключенные к I2C устройства и их адреса. Поскольку я подключил датчик MPU-9250, адрес которого - 0x68, то его мы тут и обнаружили:
Кроме того, пакет i2c-tools включает в себя ряд других команд для работы с шиной, в частности:
- i2cdump - позволяет прочитать значения всех регистров подключенного к шине устройства. Например:
i2cdump -y 1 0x68
Здесь мы снова указываем номер шины (1), а также адрес устройства, регистры которого хотим проанализировать (0x68). Здесь выделено одно значение, а именно 0x71, соответствующее регистру с адресом 0x75. Из документации на датчик мы можем определить, что именно такое значение и должно быть у этого регистра.
- i2cget - чтение значения конкретного регистра определенного устройства. Конечно, же рассмотрим пример:
i2cget -y 1 0x68 0x75
Считываем значение все того же регистра с адресом 0x75 и проверяем, верное ли значение выдает команда:
- i2cset - запись определенного значения в регистр устройства. И снова не обходимся без примера:
i2cset -y 1 0x68 0x1A 0x03
Здесь мы последовательно указываем - номер шины, адрес устройства, адрес регистра и значение, которое будет записано в этот регистр. В данном случае записываем в регистр с адресом 0x1A значение 0x03.
На этом завершаем работу с I2C через консоль, мы рассмотрели все основные и необходимые операции и переходим к работе с I2C при помощи Python.
Работа с I2C на Python.
Для работы с шиной I2C необходимо установить пакет python-smbus:
sudo apt-get install -y python-smbus
По традиционной схеме создаем файл, в котором и будем писать код для работы с I2C, назовем файл, к примеру, i2c_test.py. Ну и для наглядной демонстрации прочитаем значение все того же регистра с адресом 0x75 и сравним результаты. Собственно, пишем минимально необходимый для этого код:
from smbus import SMBus bus = SMBus(1) data = bus.read_byte_data(0x68, 0x75) print(hex(data)) bus.close()
Давайте разберем, что тут происходит, поэтапно. Делаем import библиотеки:
from smbus import SMBus
Открываем использующуюся у нас шину i2c-1:
bus = SMBus(1)
И, наконец, читаем значение регистра, выводим его на экран и заканчиваем работу с I2C:
data = bus.read_byte_data(0x68, 0x75) print(hex(data)) bus.close()
Первый аргумент функции - адрес устройства, второй - адрес регистра. Запускаем выполнение кода:
python i2c_test.py
И в результате видим верное значение регистра:
А это говорит нам о том, что код сработал абсолютно верно. Напоследок, рассмотрим некоторые другие функции библиотеки smbus, которые можно использовать по своему усмотрению:
Функция | Описание |
---|---|
long read_byte(int addr) | Чтение байта данных |
long write_byte(int addr, char val) | Запись байта данных |
long read_byte_data(int addr, char cmd) | Чтение значения регистра |
long write_byte_data(int addr, char cmd, char val) | Запись значения в регистр |
long[] read_block_data(int addr, char cmd) | Чтение блока данных (до 32-х байт) из регистра |
write_block_data(int addr, char cmd, long vals[]) | Запись блока данных в регистр |
long read_word_data(int addr, char cmd) | Чтение слова (2-х байт) из регистра |
long write_word_data(int addr, char cmd, int val) | Запись слова (2-х байт) в регистр |
На этом заканчиваем обзор работы с шиной I2C для Raspberry Pi, на очереди - интерфейс SPI, который будем запускать в следующей статье.
Здравствуйте. Первое что хотелось бы сказать это слова блогодарности. Я смог найти на Вашем сайте ответы на некоторые вопросы. Но вот есть один вопрос на который я никак не могу найти ответ. А вопрос мой такой, как в micropython продолжить выполнение программы после того как произойдет ошибка по шине i2c. Например. По шине i2c подключен oled дисплей и выводится на него какая-то информация. При его физисеском отключении программа python выдаст ошибку и выполнение программы остановится. Как продолжить выполнение программы?
Добрый день, благодарю за отзыв!
Можешь код скинуть, я посмотрю, как организована работа с I2C в целом.
from ssd1306 import SSD1306_I2C
from machine import Pin, I2C, ADC
from utime import sleep
from micropython import const
def read_ADC(num, reads = 0):
bat = ADC(26+num)
for i in range(1000):
r = bat.read_u16()
reads = reads + r
sleep(0.001)
return int(reads/1000)
def read_to_volts(read):
return ((10071*read)/100000000)
led_b = Pin(25, Pin.OUT, value=True)
led_r = Pin(17, Pin.OUT, value=True)
led_g = Pin(16, Pin.OUT, value=True)
rele_1 = Pin(1, Pin.OUT, value=1)
rele_2 = Pin(2, Pin.OUT, value=1)
rele_3 = Pin(4, Pin.OUT, value=1)
rele_4 = Pin(3, Pin.OUT, value=1)
i2c = I2C(1, scl=Pin(7), sda=Pin(6), freq=200000)#Grove - OLED Display 0.96" (SSD1315)
oled = SSD1306_I2C(128, 64, i2c)
while True:
read_bat_1 = read_ADC(0)
read_bat_2 = read_ADC(1)
read_bat_3 = read_ADC(2)
read_bat_4 = read_ADC(3)
print(read_to_volts(read_bat_1), read_to_volts(read_bat_2), read_to_volts(read_bat_3), read_to_volts(read_bat_4), )
#print(volts(read_bat_1, ADC_0), volts(read_bat_2, ADC_1), volts(read_bat_3, ADC_2), volts(read_bat_4, ADC_3))
oled.fill(0)#clear
oled.text("Bat 1:", 0,0)
oled.text( str(read_to_volts(read_bat_1)), 50,0)
oled.text("Bat 2:", 0,15)
oled.text( str(read_to_volts(read_bat_2)), 50,15)
oled.text("Bat 3:", 0,30)
oled.text( str(read_to_volts(read_bat_3)), 50,30)
oled.text("Bat 4:", 0,45)
oled.text( str(read_to_volts(read_bat_4)), 50,45)
oled.show()
if (read_to_volts(read_bat_1)<3.0):
rele_1.value(0)
if (read_to_volts(read_bat_1)>3.2):
rele_1.value(1)
if (read_to_volts(read_bat_2)<2.0):
rele_2.value(0)
if (read_to_volts(read_bat_2)>3.2):
rele_2.value(1)
if (read_to_volts(read_bat_3)<3.0):
rele_3.value(0)
if (read_to_volts(read_bat_3)>3.2):
rele_3.value(1)
if (read_to_volts(read_bat_4)<3.0):
rele_4.value(0)
if (read_to_volts(read_bat_4)>3.2):
rele_4.value(1)
Использую seeedxiao RP2040 и OLED SSD1306. Суть простая. Считать показания с аналоговых входов и вывести на экран и в порт. И по результатам включить или выключить выход.
Аналогично, не могу включить дисплей OLED в программе MMDVMHost на Orange pI zero 3 - пишет что мол дисплей не найден, хотя данные все пингуются. Там в программе шина 1, а у меня 3. Как настроить что бы показывал хоть что-то ХЗ!
-------------
root@orangepizero3:~# i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:~# i2cdetect -y 2
Error: Could not open file
/dev/i2c-2' or
/dev/i2c/2': No such file or directoryroot@orangepizero3:~# i2cdetect -y 3
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:~# i2cget -y 1 0x3c
Error: Read failed
root@orangepizero3:~# i2cget -y 3 0x3c
0x43
root@orangepizero3:~# i2cget -y 3 0x3c
^C
root@orangepizero3:~#
root@orangepizero3:~# mc
root@orangepizero3:/etc# i2cdetect -l
i2c-3 i2c twi3 I2C adapter
i2c-1 i2c twi1 I2C adapter
i2c-4 i2c twi4 I2C adapter
i2c-5 i2c twi5 I2C adapter
root@orangepizero3:/etc# i2cdetect -2
Error: Unsupported option "-2"!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.
root@orangepizero3:/etc# i2cdetect -3
Error: Unsupported option "-3"!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.
root@orangepizero3:/etc# i2cdetect -4
Error: Unsupported option "-4"!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.
root@orangepizero3:/etc# i2cdetect -5
Error: Unsupported option "-5"!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.
root@orangepizero3:/etc# i2cdetect 0
Error: Could not open file
/dev/i2c-0' or
/dev/i2c/0': No such file or directoryroot@orangepizero3:/etc# i2cdetect -3
Error: Unsupported option "-3"!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.
root@orangepizero3:/etc# i2cdetect y -3
Error: I2C bus name doesn't match any bus present!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.
root@orangepizero3:/etc# sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:/etc# sudo i2cdetect -y 3
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:/etc# sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:/etc# sudo i2cdetect -y 4
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:/etc# sudo i2cdetect -y 5
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- UU -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@orangepizero3:/etc# i2cdump -y 3 0x3c
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: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
10: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
20: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
30: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
40: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
50: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
60: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
70: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
80: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
90: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
a0: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
b0: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
c0: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
d0: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
e0: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
f0: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
root@orangepizero3:/etc# i2cdump -y 5 0xUU
Error: Chip address is not a number!
Usage: i2cdump [-f] [-y] [-r first-last] [-a] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x08 - 0x77, or 0x00 - 0x7f if -a is given)
MODE is one of:
b (byte, default)
w (word)
W (word on even register addresses)
s (SMBus block)
i (I2C block)
c (consecutive byte)
Append p for SMBus PEC
root@orangepizero3:/etc# i2cget -y 3 0x3c 0x43
0x43
root@orangepizero3:/etc#
-------
root@orangepizero3:/etc# gpio readall
+------+-----+----------+--------+---+ H616 +---+--------+----------+-----+------+
| GPIO | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | GPIO |
+------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+
| | | 3.3V | | | 1 || 2 | | | 5V | | |
| 229 | 0 | SDA.3 | ALT5 | 0 | 3 || 4 | | | 5V | | |
| 228 | 1 | SCL.3 | ALT5 | 0 | 5 || 6 | | | GND | | |
| 73 | 2 | PC9 | OFF | 0 | 7 || 8 | 0 | OUT | TXD.5 | 3 | 226 |
| | | GND | | | 9 || 10 | 1 | IN | RXD.5 | 4 | 227 |
| 70 | 5 | PC6 | ALT5 | 0 | 11 || 12 | 0 | OFF | PC11 | 6 | 75 |
| 69 | 7 | PC5 | ALT5 | 0 | 13 || 14 | | | GND | | |
| 72 | 8 | PC8 | OFF | 0 | 15 || 16 | 0 | OFF | PC15 | 9 | 79 |
| | | 3.3V | | | 17 || 18 | 0 | OFF | PC14 | 10 | 78 |
| 231 | 11 | MOSI.1 | IN | 1 | 19 || 20 | | | GND | | |
| 232 | 12 | MISO.1 | OFF | 0 | 21 || 22 | 0 | OFF | PC7 | 13 | 71 |
| 230 | 14 | SCLK.1 | IN | 1 | 23 || 24 | 0 | ALT3 | CE.1 | 15 | 233 |
| | | GND | | | 25 || 26 | 0 | OFF | PC10 | 16 | 74 |
| 65 | 17 | PC1 | OFF | 0 | 27 || 28 | | | | | |
| 272 | 18 | PI16 | OFF | 0 | 29 || 30 | | | | | |
| 262 | 19 | PI6 | ALT3 | 0 | 31 || 32 | | | | | |
| 234 | 20 | PH10 | ALT3 | 0 | 33 || 34 | | | | | |
+------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+
| GPIO | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | GPIO |
+------+-----+----------+--------+---+ H616 +---+--------+----------+-----+------+
root@orangepizero3:/etc#
----------------