Программирование, радиоэлектроника,
саморазвитие и частичка из моей жизни здесь...

Знакомство с шиной I2C в Raspberry Pi, работаем с ADC-DAC PCF8591 на Python

Знакомство с шиной I2C в Raspberry Pi, базовая настройки и консольные инструменты. Основы применения и программирования для цифро-аналогового преобразователя PCF8591, напишем простые программы на Python, научимся считывать значения напряжений с аналоговых входов, а также устанавливать значение на аналоговом выходе. Соберем простой делитель для измерения напряжения батареи 12В (0-14,8В).

Постараюсь подробно рассказать как работать с шиной I²C в Linux (Raspbian), как узнать номер шины, адреса устройств и т.п. Приведу примеры практического применения модуля-конвертера PCF8591, распишу что в нем есть и как этим управлять.

Содержание:

Двухпроводная шина обмена данными I2C

I²C (I2C) - двунаправленная шина передачи данных, разработанная еще в 1980х годах компанией Philips для осуществления связи между разными схемами и устройствами.

Передача данных осуществляется по двум проводам - SDA (Serial Data) и SCL (Serial Clock). На одной такой двухпроводной линии связи можно держать до 127 различных устройств и модулей которые умеют работать с шиной I²C.

Подключение устройств к шине I2C

Рис. 1. Подключение устройств к шине I2C.

Режимы работы шины:

  • Скоростной - 400 кбит/с;
  • Обычный - 100 кбит/с;
  • С пониженной скоростью - 10 кбит/с.

В самом начале своего становления спецификация шины I2C была сфокусирована на логике, которая питается от напряжения +5V. В следующей своей спецификации 2.0 (1998 год) уровень опорного напряжения был уменьшен до 2V.

Линии SCL и SDA шины I2C являются двунаправленными, что делает невозможным подключение устройств с разными напряжениями питания. Для выхода из такой ситуации нужно использовать конвертеры уровней напряжения!

Существует достаточно много различных модулей и микросхем, которые управляются через шину I²C, приведу несколько примеров:

  • Микросхема TDA7448 - 6-ти канальный регулятор громкости для аудио-аппаратуры;
  • Микросхема TDA7313 - цифровой стерео аудио-процессор (регулировка громкости по каналам, эффект пространственного звучания, предусилитель, регулировка высоких и низких частот, приглушение, тонкомпенсация);
  • Микросхема TEA6420 - электронный переключатель аудио-сигналов (5 стерео-входов и 4 стереовыхода);
  • Микросхема TEA5767 - FM радиоприемник на диапазон частот 76...108 МГц;
  • Микросхема DS1307 - часы реального времени;
  • Микросхема PCF8591 - аналогово<->цифровой преобразователь (4 аналоговых входа, 1 аналоговый выход);
  • и другие...

Подобные микросхемы и модули применяются в автомагнитолах, музыкальных центрах, компьютерной электронике и автоматике, в электронных игрушках (квадрокоптеры, радиоуправляемые автомобили и другие), в системах контроля и охраны, а также во многих других промышленных и бытовых устройствах.

Работа с шиной I²C поддерживается в таких операционных системах как GNU Linux, FreeBSD, OpenBSD. Данная шина отлично подходит для связывания модулей в различных встраиваемых устройствах, как самодельных, так и промышленных.

Особенности подключения к шине I2C

Как уже было сказано выше, к шине I²C можно подключить до 127 устройств. Устройство которое будет управлять этим набором периферии является ведущим (master), а модули подключенные к нему - ведомыми (slaves).

Генерация сигнала осуществляется "прижиманием" линии к нулю (низкий уровень), высокий уровень присутствует по умолчанию за счет подключенных подтягивающих резисторов к каждой из линий (SCL, SDA) и шине питания (VCC). От сопротивления этих резисторов зависит скорость восстановления на линиях высокого уровня, а заодно и скорость работы шины I²C.

Оптимальное сопротивление подтягивающих резисторов - 10 кОм, минимальное - примерно 1,5 кОм. Как правило, на многих готовых модулях уже установлены подтягивающие резисторы сопротивлением 3,3 - 4,7 кОм.

Напряжения высокого (HIGH) и низкого (LOW) уровней на линиях I²C не фиксированы! Они напрямую зависят от напряжения питания подключаемого модуля.

В документе UM10204 от NXP Semiconductors указаны следующие уровни на линиях рассматриваемой нами шины:

  • LOW = 0.3 * Vdd (30% от напряжения питания);
  • HIGH = 0.7 * Vdd (70% от напряжения питания).

В случае подключения нескольких модулей к одной шине I²C подтягивающие сопротивления окажутся включенными параллельно и результирующее сопротивление такого себе, комплексного подтягивающего резистора, окажется достаточно низким (менее 1,5 кОм), что в свою очередь может негативно сказаться на работе модулей и шины в целом.

Поэтому, при подключении нескольких готовых модулей к одной шине I²C нужно осмотреть модули и оставить подтягивающие резисторы только на одном из модулей, а остальные - отпаять!

Схема подключения нескольких устройств к одной шине I2C, подтягивающие резисторы

Рис. 2. Схема подключения нескольких устройств к одной шине I2C, подтягивающие резисторы.

В свое время, не зная о данной особенности и не вникая в подробности подключил 4 разных модуля к одной шине I²C и заметил что одно из устройств (датчик температуры и влажности) при сканировании шины то появляется, то пропадает - думал уже выбросить его и заказать новый модуль.

Более детально изучил информацию по работе с шиной, осмотрел всем модули и их принципиальные схемы, выпаял лишние подтягивающие резисторы - все заработало стабильно!

Если нет схемы модуля - не страшно, ставим тестер в режим "прозвонки" и ищем на плате резисторы, выводы которых подключены вот так:

  • Первая нога - к выводу SCL или SDA;
  • Вторая - к линии питания VCC (или внутреннему стабилизатору напряжения 3,3В).

Подключение двух модулуй, каждый с подтягивающими резисторами на борту, еще вполне допустимо. К примеру, если на платах стоят 2 резистора по 3.3к и два по 4,7к, считаем их результирующее сопротивление, используя для этого формулы для расчета сопротивления параллельно-соединенных резисторов:

1 / Rсум = (1/R1) + (1/R2) ... (1/Rn).

Для двух соединенных в параллель резисторов: Rсум2 = (R1*R2) / (R1+R2).

В нашем случае: Rсум = (3,3*4,7) / (3,3+4,7) = 1,93875 (кОм). Почти по два килоОма, что вполне еще допустимо.

Подключение по I2C нескольких устройств с разным напряжением питания

Если посмотреть на схему (рисунок 2) то там по шине i2C соединены 4 ведомых устройства и одно ведущее, предполагается что все они вместе с подтягивающими резисторами R1, R2 питаются от одного и того же источника напряжения (VCC).

В случае когда одно из устройств рассчитано на работу от +5V, а остальные - от +3,3V, потребуется двунаправленный конвертер уровней напряжения. Подключать напрямую к одной шине i2C устройства с разным напряжением питания не безопасно!

На рисунке ниже показан принцип подключения нескольких устройств с разным напряжением питания к одной общей шине i2C.

Подключение трех устройств с разным напряжением питания к общей шине I2C

Рис. 3. Подключение трех устройств с разным напряжением питания к общей шине I2C.

Как видим, здесь линии SCL и SDA для каждой стороны со своим напряжениями питания подтянуты двумя парами резисторов для обеспечения надежного высокого уровня на шине I2C.

Двунаправленный конвертер уровней напряжения можно собрать на основе транзисторов MOSFET (Metal-Oxide Semiconductor Field-Effect Transistor). Схема такого решения приведена в документе "AN10441 - Level shifting techniques in I2C-bus design" (NXP Semiconductors, Philips).

Вот как будет выглядеть схема соединений для двух устройств, одно из которых "ведущее" и питается от +3,3V, а второе - "ведомое" и с питанием от +5V:

 Принципиальная схема двунаправленного конвертера уровней напряжения для шины I2C, собранного на MOSFET

Рис. 4. Принципиальная схема двунаправленного конвертера уровней напряжения для шины I2C, собранного на MOSFET.

Вот как работает эта простая схема (на основе описания из документа AN10441), три варианта состояний для линии SDA:

  1. Ни одно из устройств не установило на линии SDA логический 0. Линия SDA в левой части схемы ('master', с питанием от +3,3V) подтянута к высокому уровню с помощью резисторов R1, R2. На шлюзах (G, Gate) и истоках (S, Source) транзистора Q1 у нас также +3,3V. Значение напряжения между S-G не достаточно для открытия транзистора. Это позволяет линии SDA с правой стороны схемы ('slave') быть подтянутой к +5V резисторами R3, R4. Таким образом, линия SDA с каждой из сторон подтянута к высокому уровню, при разном значении питающих напряжений.
  2. Устройство 'master' с питанием +3,3V установило на линии SDA низкий уровень. Вывод S транзистора теперь подключен к земле (минусу питания через электронный ключ внутри устройства 'master'), в то же время на выводе G у нас +3,3V. Напряжение между S-G достигло нужного значения и открыло транзистор Q1. Вывод SDA для устройства 'slave' с питанием +5V также подтянут к земле, через транзистор, на нем виставлен низкий уровень. Таким образом, мы имеем одинаковые низкие уровни напряжения на каждой из сторон.
  3. Устройство 'slave' с питанием +5V установило на линии SDA низкий уровень. Низкий уровень напряжения на линии SDA для устройства 'master' будет установлен через диод внутри транзистора Q1, но до тех пор пока напряжение S-G не станет достаточным для открытия этого транзистора. Когда это произойдет, транзистор откроется и через его выводы D-S низкий уровень будет установлен на линиях SDA для каждого из устройств. Таким образом, мы имеем одинаковые низкие уровни напряжения на каждой из сторон.

Аналогичным образом все происходит и с линией SCL. Состояние (1) здесь отвечает за нормализацию уровней напряжения для каждого из устройств.

Сопротивления резисторов в сравнении со схемой на рисунке 2 увеличены не спроста - когда любое из устройств 'master' или 'slave' выставит низкий уровень на любой из линий данных, то ему придется держать ток в нагрузке через два резистора: для SDA - R2 и R3, а для SCL - R1, R4.

Полевые транзисторы в этой хитрой схеме нужно использовать низковольтные быстродействующие N-канальные, с диодом внутри, например:

  1. BSS138 в корпусе SOT-23;
  2. TN2501 в корпусе SOT-89;
  3. 2N7002A в корпусе SOT-23 (TO-236AB);
  4. и другие...

Джим Хегирмен (Jim Hagerman, www.hagtech.com) предложил также вариант конвертера уровней напряжения для шины I2C на основе биполярных транзисторов структуры N-P-N.

Пример схемы такого конвертера показан ниже:

Принципиальная схема двунаправленного конвертера уровней напряжения для шины I2C, собранного на биполярных N-P-N транзисторах

Рис. 5. Принципиальная схема двунаправленного конвертера уровней напряжения для шины I2C, собранного на биполярных N-P-N транзисторах.

Такое решение вполне может подойти для несложных самодельных устройств, блоки которых взаимодействуют через шину I2C.

Из готовых решений также можно рассмотреть микросхему PCA9306 (2-bit bidirectional I2C bus and SMBus voltage-level shifter).

Пример сопряжения двух устройств по шине I2C с питающими напряжениями +3,3В и +5В используя микросхему PCA9306

Рис. 6. Пример сопряжения двух устройств по шине I2C с питающими напряжениями +3,3В и +5В используя микросхему PCA9306.

Два устройства с разными напряжениями питания можно попробовать соединить по шине I2C, подтянув резисторами высокий уровень до наименьшего из напряжений, но это не всегда сработает. Как уже было сказано выше, высокий уровень на линии шины будет получен при напряжении равному 70% от питающего, поэтому напряжения подтяжки к +3V(и даже +3,3V) может быть не достаточно: 5V * 70% = 3,5V.

Если одно устройство питается от +3,3V, а второе - от +5V, то подтянув резисторами выводы SCL и SDA к линии питания +3,3V мы обеспечим надежную работу первого устройства, но не второго, для которого высокий уровень (HIGH) на линиях шины I2C может быть и не достигнут.

Поэтому, ответ на вопрос "можно ли соединить два устройства с питанием +3,3В и +5В по шине I2C без схемы согласования уровней?" - можно, если подтягивающие резисторы подключить к питанию +3,3В, это будет безопасно для двух устройств, но стабильность работы такой связки будет низкой, зависящей от внутреннего строения устройства с питанием +5В, от того сможет ли оно толерантно воспринимать уровень напряжения +3,3В как высокий (HIGH).

Поэтому корректные пути решения проблемы здесь следующие, и их не много:

  1. Использовать преобразователь уровней напряжения;
  2. Рассмотреть возможность питания все устройств с шиной I2C одним напряжением (изучить даташиты, попробовать найти простое решение).

Одним из примеров подключения модулей с разным питанием к малинке может служить схема моего самодельного роутера на Raspberry Pi. Там к шине I2C подключены два модуля: дисплей SSD1306 (+3,3В) и часы реального времени DS1307 (+5В).

Настройка и инструменты для работы с I2C в Raspberry Pi

Предполагается что у вас уже установлена и настроена Raspbian (GNU Debian Linux for Raspberry Pi), о том как настроить Raspbian в Raspberry Pi я писал в одной из публикаций.

Первым делом нужно включить поддержку I2C в ядре операционной системы (ОС). Для этого запустим конфигуратор Raspbian при помощи следующей команды в консоли:

sudo raspi-config

Идем по пунктам “Advanced Options” - “I2C” - "Yes" - жмем "Finish". Перезагружаем операционную систему:

sudo reboot

Теперь модуль ядра для шины I²C должен быть активирован, можно приступать к работе. Полезно знать что проделанная операция в настройках конфигуратора равносильна добавлению или раскомментированию строчки "dtparam=i2c_arm=on" в файле "/boot/config.txt".

При помощи нехитрой связки из команд посмотрим загружены ли модули ядра для поддержки I2C:

lsmod | grep i2c

В выводе должны присутствовать примерно такие строчки:

i2c_bcm2708 4920 0
i2c_dev 5671 0

Если они есть - значит все в норме и можем продолжать дальше.

Установим модуль для работы с шиной I2C в Python, а также пакет с полезными консольными инструментами для I2C:

sudo apt-get update
sudo apt-get install -y python-smbus i2c-tools

После установки нам будут доступны несколько полезных утилит: i2cdetect, i2cdump, i2cget, i2cset. Отобразим список доступных в системе шин I²C, этой командой можно проверить есть (+включена) ли поддержка шины в устройстве или нет:

i2cdetect -l

У меня вывод команды имеет следующий вид:

i2c-1  i2c   3f804000.i2c      I2C adapter

В начале строчки "i2c-1" можно увидеть что номер шины к которой подключен адаптер I²C = 1, эта информация нам позже пригодится.

Для просмотра адресов подключенных устройств можно вызвать команду i2cdetect с указанным номером шины, также по умолчанию она запустится в интерактивном режиме, который мы отключим при помощи параметра "-y", это нужно чтобы просто вывести состояние шины на консоль в текстовом виде.

Какой номер шины указывать для Rasperry Pi:

  • 1 - для Model A, B Rev 2 or B+
  • 0 - для более старых моделей с Rev 1.

В одной из статей я уже приводил рисунок о том как отличаются распиновки GPIO Raspberry Pi в ревизиях Revision 1 и Revision 2.

В моем случае используется Raspberry Pi2 model B, также раньше при помощи команды i2cdetect я узнал что номер шины адаптера - 1. Давайте посмотрим что у нас подключено к шине под номером 1:

sudo i2cdetect -y 1

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

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

Контроллер PCF8591 на шине I2C найден, его адрес - 0x48. Зная адрес устройства и принимая во внимание тот факт, что оно обнаружилось, можно переходить к изучению документации и выполнению первых экспериментов.

Обзор АЦП-ЦАП (ADC-DAC) PCF8591

PCF8591 - это микросхема, которая содержит в себе 4 входа для Аналогово-Цифрового Преобразователя (АЦП, ADC) и 1 выход для Цифро-Аналоговый Преобразователя (ЦАП, DAC).

Как известно, в Raspberry Pi нет встроенного АЦП и ЦАП, данная микросхема позволит исправить эту проблему не только для Raspberry Pi, но и на других платформах.

На входы ADC микросхемы могут быть подключены четыре различных аналоговых сигнала для наблюдения и анализа их величины (термодатчик, фотодатчик, делитель напряжения я другие). Также в микросхеме есть возможность сконфигурировать четыре входа для для сравнения напряжений из различных источников.

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

Работает микросхема в диапазоне питающих напряжений 2,5 - 6В, поэтому ее смело можно питать от линий с напряжением 3,3В или 5В.

Разрешающая способность данных для входов и выходов микросхемы - 8 Бит. Это значит что минимальный уровень у нас будет соответствовать 0, а максимальный - 255 (2^8-1). Такой разрешающей способности вполне достаточно для измерения напряжений источников питания и батарей, сигналов с разных датчиков и модулей где не нужна высокая точность измерений.

Для расчета значения на входе АЦП можно применить следующую формулу:

V = Uin / Uadc * 255

где:

  • V - значение (value), полученное с ADC программным путем;
  • Uin, В - напряжение, полученное на выходе датчика что подключен к ADC;
  • Uadc, В - напряжение питания микросхемы (ADC).

Пример расчета: питаем PCF8591 от линии 3,3В и к нему на один из входов подключен аккумулятор с напряжением 1,2В.

V = 1,2 / 3,3 * 255 = 92.

Важно заметить, что напряжение питания микросхемы должно быть хорошо стабилизировано - его можно взять из GPIO линий питания 3,3В или 5В в малинке, а также от внешнего стабилизатора напряжения с хорошей помехозащищенностью.

Подключаем PCF8591 к Raspberry Pi

Для экспериментов был приобретен готовый модуль с микросхемой PCF8591 на котором также установлены: переменный резистор, фото-резистор, термо-резистор и два светодиода (1 для питания, 2й для индикации выхода DAC).

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

Модуль PCF8591 с компонентами для экспериментов

Рис. 7. Модуль PCF8591 с компонентами для экспериментов.

Принципиальная схема модуля PCF8591

Рис. 8. Принципиальная схема модуля PCF8591.

Как видим из схемы, на плате модуля уже установлены подтягивающие резисторы - R7 и R8. Также есть переменный резистор (потенциометр), терморезистор, фоторезистор и два светодиода.

Сенсоры, подключенные к входам на плате и назначение джамперов приведено в табличке ниже:

Вход Описание
AIN0 Джампер P5 подключает фото-резистор
AIN1 Джампер P4 подключает термо-резистор
AIN2 Ничего не подключено
AIN3 Джампер P6 подключает переменный резистор

Для подключения модуля к плате с малинкой нужно выполнить соединение выводов:

Raspberry Pi Модуль PCF8591
SDA SDA
SCL SCL
3V3 VCC
GND GND

В данном случае модуль будет питаться от напряжения 3,3В и нам не потребуется дополнительный конвертер для нормализации уровней напряжений по линиям шины I2C для каждого из устройств (малинка и PCF8591).

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

В принципе светодиод можно и не подключать, поскольку на модуле уже установлен светодиод зеленого цвета свечения, он подключен к AOUT.

подключение модуля PCF8591 к Raspberry Pi схема

Рис. 9. Схема эксперимента - подключение модуля PCF8591 к Raspberry Pi.

Модуль PCF8591 подключен к Raspberry Pi

Рис. 10. Модуль PCF8591 подключен к Raspberry Pi.

Работа со входами и выходом PCF8591

Чтобы работать с I²C на языке Python в системе должен быть установлен модуль python-smbus, его мы уже установили на этапе конфигурации.

Для программного управления контроллером PCF8591 по шине I²C нужно отправить два байта:

  • Байт адресации (адрес устройства на шине I²C);
  • Байт управления (выбор входов, выхода и их режимов работы).

Структура командного байта:

7 6 5 4 3 2 1 0
0 X X X 0 X X X
  | | |   | | |
  A B B   C D D

A - состояние ЦАП:

  • 0 - не активный;
  • 1 - активный.

BB - конфигурация входов АЦП:

  • 00 - четыре прямых входа;
  • 01 - три дифференциальных входа;
  • 10 - смешанный режим (два прямых и один дифференциальный вход);
  • 11 - два дифференциальных входа.

C - режим инкрементации:

  • 0 - не автоматический;
  • 1 - автоматический.

DD - выбор канала:

  • 00 - канал 0, AIN0;
  • 01 - канал 1, AIN1;
  • 10 - канал 2, AIN2;
  • 11 - канал 3, AIN3.

Чтобы лучше было понятно как работают режимы переключения входов приведу небольшой рисунок:

Режимы работы входов преобразователя PCF8591

Рис. 11. Режимы работы входов преобразователя PCF8591.

Простая программа на Python

Ниже приведена простая программа на Python, которая будет делать следующее:

  • Считывать значения на всех входах ADC (AIN0...AIN3) и отображать на экране в консоли;
  • Изменять значение на выходе DAC (AOUT) в соответствии со значением на входе AIN3 (переменный резистор).

Создадим файл для тестовой программы и откроем его в редакторе:

nano /tmp/pcf8591p_test_channels.py

Поместим в него следующий код:

#!/usr/bin/env python
# Test program for ADC-DAC PCF8591P
# 2016 https://ph0en1x.net

import os
import time
from smbus import SMBus

DEV_ADDR = 0x48
adc_channels = {
    'AIN0':0b1000000, # 0x40 (foto-resistor)
    'AIN1':0b1000001, # 0x41 (thermistor)
    'AIN2':0b1000010, # 0x42 (not connected)
    'AIN3':0b1000011, # 0x43 (variable resistor)
}
dac_channel = 0b1000000 # 0x40

bus = SMBus(1)          # 1 for RPi model B rev.2
tmp = 0

while(1):
    os.system('clear')
    print("Press Ctrl C to stop...\n")
    for channel in adc_channels:
        # read value from ADC input
        bus.write_byte(DEV_ADDR, adc_channels[channel])
        bus.read_byte(DEV_ADDR) # read last value
        bus.read_byte(DEV_ADDR) # repeat reading last value
        value = bus.read_byte(DEV_ADDR)
        if channel == 'AIN3':
            tmp = value
        print 'Channel ' + channel + ' ---> ' + str(value)
    # set value in DAC
    bus.write_byte_data(DEV_ADDR, dac_channel, tmp)
    time.sleep(0.1)

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

python /tmp/pcf8591p_test_channels.py

После запуска программа начнет выводить на экран состояния всех четырех входов, вращая ползунок переменного резистора будет меняться значение на входе AIN3 и соответственно яркость свечения светодиода что подключен к выходу AOUT.

Вывод тестовой программы для PCF8591 в консоли

Рис. 12. Вывод тестовой программы для PCF8591 в консоли.

Для того чтобы яркость свечения светодиода изменялась при изменении  освещения фото-резистора нужно изменить в программе проверку канала AIN3 на AIN0 - заменить "if channel == 'AIN3':" на "if channel == 'AIN0':".

Кратко о структуре программы. Сперва мы подключаем нужные библиотеки (os, time, smbus), затем переменной DEV_ADDR присваиваем адресс нашего контроллера в шестнадцатеричном виде (HEX).

Дальше мы создаем словарь adc_channels в котором перечисляем значения байта управления для установки нужного входа ADC. Для наглядности и понимания, все значения записаны в двоичной системе исчисления (Binary). В переменной dac_channel будет храниться значение байта управления для доступа к DAC.

Переменная bus содержит экземпляр объекта для работы с шиной I²C которая доступна в системе под номером 1. Также инициализируем временную переменную tmp, она нам понадобится для хранения значений из выбранного входа ADC и установки значения на выходе DAC.

Дальше идет вечный цикл в котором:

  • Очищаем экран консоли;
  • Выводим сообщение о том что прекратить выполнение можно нажав CTRL+C;
  • Перебираем из словаря adc_channels адреса всех каналов и считываем их значения на входах, значения входа AIN3 записываем во временную переменную, выводим на экран значение для каждого канала;
  • Устанавливаем значение на выходе AOUT равным значению временной переменной tmp;
  • Выполняем задержку в 100 миллисекунд (0,1с).

Здесь есть важный нюанс: при считывании значения из канала первые два вызова bus.read_byte после установки бита управления содержат последнее значение и повторение последнего значения которое присутствовало на входе, третий вызов даст нам текущее значение на выбранном входе. При написании программ это нужно учитывать.

Краткое видео работы данного эксперимента:

Простой резистивный делитель напряжения

Применяя преобразователь PCF8591 мы упираемся в максимально возможный измеряемый уровень напряжения равный питающему напряжению ADC. Если же на вход ADC подать большее напряжение то канал (а то и вся микросхема) может выйти из строя.

Для того чтобы иметь возможность измерять напряжения более 5В (к примеру 12В) можно собрать несложный резистивный делитель напряжения.

Принципиальная схема резистивного делителя напряжения 14,8В - 3В

Рис. 13. Принципиальная схема резистивного делителя напряжения 14,8В - 3В.

Расчет параметров подобного делителя напряжения можно выполнить применяя закон Ома, я же здесь приведу готовые формулы. Для начала нужно было определиться с сопротивлением R1, оно будет в разы больше чем сопротивление R2, к тому же сумма сопротивлений должна получиться такой чтобы через них протекал небольшой ток (зачем нам лишние траты энергии) и его было достаточно для измерения при помощи ADC.

Было выбрано значение 62 КОм. Теперь зная входное напряжение, выходное напряжение и сопротивление резистора R1 можем перейти к расчету сопротивления R2.

R2 = R1 / (Vin/Vout - 1) = 62000 / (14,8/3 - 1) = 62000 / 3,933 = 15764 Ом

Сопротивление резистора R2 равняется примерно 16 КОм.

Такой делитель можно собрать за несколько минут и при помощи тестера убедиться в том что на выходе будет примерно 3В при входном напряжении 14,8В.

Проверка резистивного делителя напряжения

Рис. 14. Проверка резистивного делителя напряжения.

Ток через делитель без нагрузки на выходе: I = U/R = 14,8V / (62000 + 16000) = 0,00019A = 0,19мА. При таком токе можно использовать резисторы на любую мощность.

Программа для измерения напряжения батареи 12В

Первым делом приведу дополненную схему эксперимента с делителем напряжения:

Принципиальная схема подключения делителя напряжения к PCF8591

Рис. 15. Принципиальная схема подключения делителя напряжения к PCF8591.

Теперь напишем программу, которая будет управлять яркостью свечения светодиода, который подключен к VOUT, в зависимости от входного напряжения на входе AIN2, к которому подключен резистивный делитель напряжения.

Создадим новый файл для программы и откроем его в редакторе:

nano /tmp/pcf8591p_test_12v.py

Поместим в файл следующий код:

#!/usr/bin/env python
# Test program for ADC-DAC PCF8591P and voltage divider 14,8-3В
# 2016 https://ph0en1x.net

import os
import time
from smbus import SMBus

DEV_ADDR = 0x48
adc_channel = 0b1000010 # 0x42 (вход AIN2 + включенный DAC)
dac_channel = 0b1000000 # 0x40

bus = SMBus(1)          # 1 for RPi model B rev.2
tmp = 0

while(1):
    os.system('clear')
    print("Press Ctrl C to stop...\n")
    # read ADC value
    bus.write_byte(DEV_ADDR, adc_channel)
    bus.read_byte(DEV_ADDR)
    bus.read_byte(DEV_ADDR)
    value = bus.read_byte(DEV_ADDR)
    print 'AIN value = ' + str(value)
    # set value in DAC
    bus.write_byte_data(DEV_ADDR, dac_channel, value)
    time.sleep(0.1)

Запускаем программу командой:

python /tmp/pcf8591p_test_12v.py

После запуска программы в консоли будет обновляться и отображаться значение на входе AIN2.

Вывод выполняющейся программы в окне консоли

Рис. 16. Вывод выполняющейся программы в окне консоли.

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

При разных напряжениях на входе AIN2 я получил следующие значения:

Напряжение, В Значение AIN2
1,1 20
2 35
4 67
5 83
6 100
8 131
9 147
10 164
10,3 168
11 180
12 198
13 214
14 228
14,5 238

Немного изменив программу можно заставить светодиод засвечиваться при достижении некоторого порога напряжения, например чтобы сигнализировать о разряде батареи до 10,5В.

Также для этого события можно запрограммировать и другие действия, например корректно завершить работу Raspberry Pi, которая питается от аккумулятора 12В через преобразователь DC-DC 5В и другие...

Краткая видео-демонстрация работы данного эксперимента:

Заключение

Работать с микросхемой (модулем) PCF8591P не составляет больших трудностей, ее можно применять в самых разных областях где нужен анализ и вывод значений аналогового сигнала со средней точностью.

Полезные источники:

  1. Википедия - I²C (RU);
  2. Википедия - I²C (EN);
  3. I2C Bus - What is that?
  4. Sunfounder - Sensor Kit V2.0 for Raspberry Pi B+ (уроки и примеры);
  5. Даташит (datasheet) на PCF8591 (PDF);
  6. AN10441 -  Level shifting techniques in I2C-bus design, by NXP Semiconductors (PDF);
  7. UM10204 - I2C-bus specification and user manual, 2014, by NXP Semiconductors (PDF);
  8. Jim Hagerman - "Two transistor circuit replaces IC" (PDF);
1 2160 Железо
Комментарии к публикации (5):
Ассистент #1Ассистент
27 Февраль 2019 21:34

При подключении нескольких PCF8591 на одну шину, как определить их адрес автоматически? Например нужно произвести замену или добавить новый датчик.

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

0
ph0en1x #2ph0en1x
28 Февраль 2019 11:05

Здравствуйте.

К одной шине I2C можно подключить до восьми микросхем PCF8591. Данное число ограничено количеством возможных вариантов адресов, которые настраиваются подачей высокого или низкого уровня напряжения на пины 5, 6, 7 (A0, A1, A2).

Возможные комбинации подключения пинов 5, 6 и 7 (1-высокий уровень, 0-низкий уровень): 001, 011, 111, 110, 100, 000, 101, 010 (всего 8 комбинаций). Более подробная информация есть в даташие на микросхему PCF8591 (страницы 12-14).

В готовых модулях на основе микросхемы PCF8591, адрес устройства выставлен в одном и том же значении - выводы 5-7 подключены к минусу питания (смотрите схему на рисунке 4). Если отпаять вывод 5 (A0) и подключить его к плюсу питания, то модуль будет запрограммирован уже на другой адрес. Аналогично на смену адреса будут влиять подключения остальных двух выводов 6 (A1) и 7 (A2).

Адрес каждого сконфигурированного модуля лучше всего проверить экспериментально, с помощью утилиты i2cdetect, а потом уже прописать его в программном обеспечении и привязать к какому-то функционалу.

Шина I2C была спроектирована для внутрисхемной коммуникации между устройствами, зачастую длина линий передачи данных может варьироваться от нескольких сантиметров до метра или более. Данная шина не является помехоустойчивым интерфейсом для осуществления связи между устройствами на больших расстояниях.

Скорость и стабильность передачи данных по шине I2C будет изменяться в зависимости от длины проводников (SDL, SDA, GND) и их емкости, а также от влияния на них близко расположенных источников помех. Дополнительно нужно будет поэкспериментировать с сопротивлением подтягивающих резисторов.

Если скорость передачи данных по этой шине в вашей конструкции не критична (например нужно периодично "дергать" датчики температуры), то можно поэкспериментировать подключение на расстояния 50м и более.

Есть люди, которые пишут что соединяли по шине I2C устройства на расстоянии более 100 метров, используя для подключения кабель (витую пару) STP - это нужно проверять экспериментально и с учетом места где будет проложен кабель (учитывая влияние возможных источников помех в виде силовых кабелей и т.п.).

Также не помешает добавить дополнительные проверки правильности полученных данных в коде программы - например получать значение температуры 5-10 раз подряд с задержкой 100мс и потом высчитывать результирующее среднее значение.

Еще нашел одну интересную микросхему - P82B715, представляющую из себя такой себе буфер для возможности удлинения линий передачи данных SDA и SCL, соединяющих два устройства (нужно две такие микросхемы).

0
Ассистент #3Ассистент
28 Февраль 2019 17:34

P82B715 интересная штука, в принципе можно ставить перед каждым модулем и у малины.

Адрес на модулях выставлять перемычками будет удобно.

Жалко, что всего 8 устройств...

Огромная благодарность за подробный ответ, и за ваш ресурс, приятно читать хорошие и грамотные статьи, которые редко встречаются.

+1
ph0en1x #4ph0en1x
01 Март 2019 14:40

Пожалуйста!
Могу еще добавить: если нужно собирать информацию с большого количества термодатчиков, то можно воспользоваться шиной 1-Wire (она есть в Raspberry Pi), подключив к ней до 128 датчиков DS18B20. Эти датчики есть в корпусе TO-92 (по виду как маломощный транзистор), а также в защищенных металлических корпусах для установки на разные поверхности.

Ограничение в 8 модулей PCF8591 на одной шине I2C можно попробовать обойти, коммутируя ее выводы SCL+SDA на насколько отдельных пар линий, к каждой из которых подключены по 8 микросхем PCF8591. Для этого можно применить расширители портов на сдвиговых регистрах (например SN74HC595D)  или же несколько пинов GPIO + электронные ключи на КМОП (например микросхема 4066 - 4 ключа внутри).

Также в качестве расширителя портов можно использовать микросхему PCF8574, которая сама управляется по шине I2C и добавляет 8 дополнительных управляемых пинов. Еще более интересная микросхема - PCA9555, содержит 16 двунаправленных коммутируемых каналов, управляется также через шину I2C.

0
Дима #5Дима
07 Март 2019 08:45

Спасибо за статью. Очень грамотно написана.

+1