Top.Mail.Ru

Raspberry Pi. Обмен данными по интерфейсу SPI.

Продвигаемся по нашему учебному курсу, посвященному работе с Raspberry Pi! Героем сегодняшней статьи будет интерфейс SPI. Разберем его включение, настройку и, конечно же, напишем реальный пример использования. Приступаем...

Как и в статье по работе с шиной I2C, для полноценности примера возьмем какое-нибудь устройство и подключим к Raspberry Pi по SPI. Из имеющегося у меня в наличии идеально подойдет датчик L3GD20. Схема подключения будет такой:

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

Raspberry будет играть роль ведущего устройства SPI (master), а гироскоп - подчиненного (slave). Здесь мы используем сигналы:

  • SPI0_CE0_N - chip select
  • SPI0_MOSI - master output slave input - для передачи данных от ведущего к подчиненному
  • SPI0_MISO - master input slave output - для приема данных от slave
  • SPI0_SCLK - тактовый сигнал - его также генерирует master

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

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

Для того, чтобы активировать интерфейс SPI платы Raspberry Pi можно воспользоваться утилитой raspi-config. Вводим команду:

sudo raspi-config

После запуска утилиты идем по пути Interfacing options - SPI и включаем нужный нам интерфейс:

Утилита raspi-config
Raspberry Pi включение SPI

Другим способом активации SPI является непосредственное редактирование файла /boot/config.txt. В нем нужно раскомментировать строку:

dtparam=spi=on

В обоих случаях после изменения конфигурации необходимо перезапустить плату:

sudo reboot

Не важно, каким именно способом пользоваться, результат будет одинаков. Для проверки вводим команду:

ls /dev

И в выводе команды в консоли видим такие строки:

Устройства SPI

Наличие в списке spidev0.0 и spidev0.1 сигнализирует нам о том, что интерфейс SPI успешно активирован. Первая цифра (0) в наименовании указывает, что используется SPI0, к которому и подключен датчик на нашей схеме.

А вторая цифра (0 или 1) определяет, какой вывод платы будет использован в качестве Chip Select'а. Здесь есть два варианта:

  • SPI0_CE0_N
  • SPI0_CE1_N

У нас использован SPI0_CE0_N, поэтому нас интересует spidev0.0. Два вывода Chip Select нужны для того, чтобы реализовать возможность подключения двух разных устройств к SPI0, каждому из которых будет соответствовать свой сигнал Chip Select (CS), а линии данных и тактирования (MOSI, MISO, SCLK) будут общими.

При таком подключении, если мы собираемся обмениваться данными с одним из устройств, то следует подать низкий уровень (0) на его вывод CS. А на CS второго устройства - высокий уровень (1). Таким образом и осуществляется переключение между устройствами, использующими одни и те же линии данных.

Работа с SPI на Python.

Здесь нам понадобится модуль python-spidev, который можно установить командой:

sudo apt-get install python-spidev

Если при запуске написанного кода будут возникать ошибки, то поможет принудительная установка версии 3.4 пакета:

pip3 install spidev==3.4 --force-reinstall

Теперь уже можно переходить к написанию программы. Первый делом для использования SPI делаем import:

import spidev

Далее нужно создать объект для работы с SPI и произвести подключение:

spi = spidev.SpiDev()
spi.open(0, 0)

Аргументы функции open() как раз и позволяют выбрать использующийся модуль и сигнал Chip Select, который мы обсудили ранее. В нашем примере - SPI0 и SPI0_CE0_N. В целом, после вызова этих функций уже можно начинать работу с SPI, но давайте рассмотрим помимо прочего доступные варианты конфигурации:

  • bits_per_word - количество битов в слове.
  • cshigh - в активном режиме сигнал на выходе Chip Select может быть либо высокого, либо низкого уровня. Для первого случая ставим cshigh = True, для второго, соответственно - cshigh = False.
  • loop - включение/отключение loopback режима.
  • no_cs - записываем в параметр True, если не используем Chip Select.
  • lsbfirst - выбираем, наиболее или наименее значимый бит будет первым в данных.
  • max_speed_hz - максимальная частота интерфейса.
  • mode - в этот параметр записываем два бита, например 0b01:
Бит Сигнал Описание Значения
Старший CPOL Исходный уровень сигнала синхронизации 0: низкий уровень
1: высокий уровень
Младший CPHA Фаза синхронизации 0: по переднему фронту - выборка данных, по заднему - установка
1: по переднему фронту - установка данных, по заднему - выборка

К примеру, если mode = 0b10, то исходный уровень сигнала синхронизации - высокий, по переднему фронту происходит выборка данных, по заднему - установка.

  • threewire - режим, при котором вместо двух линий MOSI и MISO используется одна, объединяющая в себе их функции.

В коде установка параметров может происходить следующим образом:

spi.lsbfirst = False
spi.cshigh = False
spi.mode = 0b01
spi.bits_per_word = 8

Пришло время, наконец, рассмотреть и основные функции для приема и передачи данных:

  • xfer(list of values[, speed_hz, delay_usec, bits_per_word])

Функция осуществления SPI транзакции. В квадратных скобках указаны необязательные аргументы, среди которых последовательно: частота, величина задержки между данными в микросекундах и количество битов в слове. Обязательный аргумент list of values - непосредственно передаваемые данные. Возвращает же функция данные, принятые от подчиненного устройства.

  • xfer2(list of values[, speed_hz, delay_usec, bits_per_word])

Аналогичная функция, отличие от xfer() заключается в том, что сигнал Chip Select остается активным между транзакциями. В случае же с xfer() после каждой отправки CS переходит в неактивное состояние, а затем снова активируется.

  • xfer3(list of values[, speed_hz, delay_usec, bits_per_word])

И еще одна функция из этой группы. Ее отличие заключается в том, что если размер передаваемых данных превышает максимально допустимый, то данные будут разбиты на порции и отправлены по частям. Максимальный размер передаваемых данных задается через /sys/module/spidev/parameters/bufsiz.

  • readbytes(n)

Функция приема данных, аргумент - количество байт, которые необходимо прочитать.

  • writebytes(list of values)

Передача данных, задаваемых аргументом функции.

  • writebytes2(list of values)

И похожая на предыдущую функция, отличающаяся тем, что разбивает данные на части в случае превышения максимального размера. В общем, аналогично функции xfer3().

Итак, именно при помощи этих функций и организуется обмен данными по интерфейсу SPI.

А теперь вспомним про наш подключенный датчик L3GD20 и напишем небольшой пример для чтения значений его регистров. В частности, есть один регистр (адрес 0x0F), значение которого строго фиксировано и равно 0xD4. Его значение и прочитаем, а затем сравним с заведомо известной верной величиной 0xD4 (11010100):

Регистры L3GD20

Но нужно, в двух словах, особо не углубляясь, рассмотреть процесс обмена данными с L3GD20. В целом выглядит это так:

Чтение данных по SPI

В рамках рассматриваемой нами сегодня задачи это значит следующее. Для того, чтобы запросить значение регистра с адресом 0x0F, нам нужно отправить подчиненному (датчику) команду 0x8F. В ответ на это гироскоп отправит значение регистра. Нюанс заключается в том, что, отправив команду, ведущий (Raspberry Pi) должен обеспечивать тактирование на линии в то время как идет отправка данных подчиненным. Для того, чтобы это осуществить, отправим 2 байта - 0x8F и 0x00. Второй байт не несет никакой смысловой нагрузки, он нужен только для того, чтобы master продолжал выдавать сигнал тактирования в то время, пока slave отправляет данные.

Создаем файл spi_test.py. Итоговый код будет выглядеть так:

import spidev

spi = spidev.SpiDev()
spi.open(0, 0)

spi.max_speed_hz = 4000000

txData = [0x8F, 0x00]
rxData = spi.xfer(txData)

for i in range(len(rxData)):
    print (hex(rxData[i]))
    
spi.close()

Запускаем:

python spi_test.py

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

Работа с SPI на Python для Raspberry Pi

Отправляем два байта, как и планировали, и в ответе от slave получаем также два байта, первый из которых не несет полезной информации. Он соответствует тому периоду времени, когда master отправлял запрос (0x8F), соответственно на шине было тактирование, а на MISO в этот момент был низкий уровень, который и был прочитан как 0x00.

Вот на успешном запуске программы и закончим сегодняшнюю статью, всем спасибо за внимание и до скорого 🤝

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

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