Категории публикаций
Подписка на рассылку по Email
новости (подписчиков: 3)
комментарии (подписчиков: 2)

Отменить подписку
Популярные публикации
Интересный опрос
Какую платежную систему вы предпочетаете?

Счет в Банке
Webmoney
PayPal
BitCoin и другие криптовалюты
okPay
ePayServices
AdvCash
Ее нет в списке
Поблагодарить автора
donate
1B4ZZ2PXUd9E7AqEB82AqFnkpk2bt5hQG7

Знакомство с шиной 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 кбит/с.

Существует достаточно много различных модулей и микросхем, которые управляются через шину 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 кОм.

В случае подключения нескольких таких модулей к одной шине 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 в 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 с компонентами для экспериментов

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

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

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

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

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

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

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

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

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

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

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

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

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

Рис. 6. Модуль 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

Рис. 7. Режимы работы входов преобразователя 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 http://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 в консоли

Рис. 8. Вывод тестовой программы для 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В

Рис. 9. Принципиальная схема резистивного делителя напряжения 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В.

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

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

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

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

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

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

Рис. 11. Принципиальная схема подключения делителя напряжения к 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 http://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.

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

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

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

При разных напряжениях на входе 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 не составляет больших трудностей, ее можно применять в самых разных областях где нужен анализ и вывод значений аналогового сигнала со средней точностью.

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

 (5/5) голосов: 1   просмотров: 251


Тематика:  Raspberry Pi  Linux  I2C  PCF8591  ADC  DAC  Python


Добавить комментарий captcha