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

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

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

Евгений
Евгений
Ответ на комментарий  Aveal
2 месяцев назад

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)

Евгений
Евгений
Ответ на комментарий  Евгений
2 месяцев назад

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

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