Top.Mail.Ru

Raspberry Pi. Настройка и использование шины I2C.

Продолжаем осваивать различные модули Raspberry Pi, так вот сегодня пришла очередь разобрать работу с шиной I2C. Не будем ходить вокруг да около, а сразу же перейдем к делу. И начнем с постановки задачи, которую будем решать в этой статье.

Итак, возьмем какое-нибудь устройство, работающее по I2C и подключим его к Raspberry Pi. У меня под рукой обнаружилась такая плата с 9-осевым датчиком MPU-9250:

MPU-9250

Вполне подойдет для экспериментов. Естественно, выбор именно этого устройства не накладывает никаких ограничений на работу с шиной. То есть все, что мы сегодня разберем на примере MPU-9250 актуально и для любых других устройств, подключенных по I2C.

Схема подключения выглядит следующим образом:

Подключение к Raspberry Pi по I2C

При работе с I2C обязательно нужны подтягивающие резисторы, которые подтягивают линии SDA и SCL к напряжению питания:

Подтягивающие резисторы

В моем случае эти резисторы уже распаяны на плате с MPU-9250, так что дополнительных действий не требуется. На этом с подключением заканчиваем и переходим к программной части статьи.

Включение I2C на Raspberry Pi.

Первым делом нам следует включить интерфейс I2C. Для этого воспользуемся утилитой для конфигурации Raspberry Pi - raspi-config. Вводим в терминале команду:

sudo raspi-config

Далее переходим в пункт Interfacing options и включаем I2C:

Настройка Raspberry Pi
Включение I2C для Raspberry Pi

После этого необходимо перезапустить плату:

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

Результат выполнения в моем случае выглядит так:

Команда i2cdetect

Из вывода команды следует, что у нас активирована шина i2c-1, которую мы и будем использовать. Для новых версий плат Raspberry Pi всегда по умолчанию используется именно i2c-1. Для более старых же модификаций, например Raspberry Pi Model B с оперативной памятью 256 МБ, будет использована шина i2c-0.

Двигаемся дальше, следующая команда, которую мы рассмотрим:

sudo i2cdetect -y 1

Цифра 1 здесь как раз и относится к тому, что мы будем работать с шиной i2c-1. Кроме того, используем ключ -y. По умолчанию команда i2cdetect ожидает подтверждения от пользователя прежде, чем вмешиваться в работу шины, а ключом -y мы даем ей добро на работу с I2C без дополнительного разрешения.

Итак, вывод этой команды может быть таким:

Команда i2cdetect для Raspberry Pi

Здесь мы видим все подключенные к I2C устройства и их адреса. Поскольку я подключил датчик MPU-9250, адрес которого - 0x68, то его мы тут и обнаружили:

Пример использования i2cdetect

Кроме того, пакет i2c-tools включает в себя ряд других команд для работы с шиной, в частности:

  • i2cdump - позволяет прочитать значения всех регистров подключенного к шине устройства. Например:
i2cdump -y 1 0x68
Команда i2cdump

Здесь мы снова указываем номер шины (1), а также адрес устройства, регистры которого хотим проанализировать (0x68). Здесь выделено одно значение, а именно 0x71, соответствующее регистру с адресом 0x75. Из документации на датчик мы можем определить, что именно такое значение и должно быть у этого регистра.

  • i2cget - чтение значения конкретного регистра определенного устройства. Конечно, же рассмотрим пример:
i2cget -y 1 0x68 0x75

Считываем значение все того же регистра с адресом 0x75 и проверяем, верное ли значение выдает команда:

Команда i2cget
  • 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

И в результате видим верное значение регистра:

Работа с I2C на Python

А это говорит нам о том, что код сработал абсолютно верно. Напоследок, рассмотрим некоторые другие функции библиотеки 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, который будем запускать в следующей статье.

Подписаться
Уведомить о
guest

5 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Евгений
Евгений
1 год назад

Здравствуйте. Первое что хотелось бы сказать это слова блогодарности. Я смог найти на Вашем сайте ответы на некоторые вопросы. Но вот есть один вопрос на который я никак не могу найти ответ. А вопрос мой такой, как в micropython продолжить выполнение программы после того как произойдет ошибка по шине i2c. Например. По шине i2c подключен oled дисплей и выводится на него какая-то информация. При его физисеском отключении программа python выдаст ошибку и выполнение программы остановится. Как продолжить выполнение программы?

Евгений
Евгений
Ответ на комментарий  Aveal
1 год назад

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)

Евгений
Евгений
Ответ на комментарий  Евгений
1 год назад

Использую seeedxiao RP2040 и OLED SSD1306. Суть простая. Считать показания с аналоговых входов и вывести на экран и в порт. И по результатам включить или выключить выход.

Иван
Иван
Ответ на комментарий  Евгений
1 год назад

Аналогично, не могу включить дисплей 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 directory
root@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 directory
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 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#

----------------

5
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x