Remote gpio raspberry что это
Перейти к содержимому

Remote gpio raspberry что это

  • автор:

Удаленное управление.

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

Ну а так как не хотелось пользовать всякие node.js и прочие готовые инструменты, решил немного повелосипедить. Так что про то, что «Есть же вот такая штука!» или на худой конец «ssh», я вкурсе. Велосипедил я Just For Fun

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

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

Проект состоит из 2 частей: agent — часть, работающая на стороне железки (малина, или другая). Агент собирается только под системы Linux;

Client — часть, которая соединяется с агентом и может получить доступ к ресурсам (fs, gpio, i2c. ) вышеозначенной железки. Клиент, вообще, это библиотека, на основе которой можно написать что-то свое. Вполне собирается под linux, windows и не вижу причин, почему бы ей не собраться под какую-нибудь MacOS. Для примера использования я начинал делать lua-client, который оказался полезен не только как пример, но и как вполне рабочий инструмент. На это примере я и собираюсь показать что можно сделать при помощи lua и ferro-remote

Агент.

Агент это один исполняемый бинарник, который запускается на стороне RPi (раз уж на сайте raspberrypi пишу), открывает один или несколько серверных точек и предоставляет доступ к:

  • os: запуск консольной команды (вызов system)
  • fs: доступ к файловой системе. Итерация содержимого директорий. Доступ к содержимому файлов (чтение/запись)
  • gpio: доступ к пинам. Возвожность получения нотификаций об изменении состояния пина (разумеется для пинов, которые поддерживают данный механизм).
  • i2c: доступ к шине

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

А например. тут напишу пару примеров работы

Кто не хочет сам собирать агента и клиента, если готовая сборка тут. Там агент для малины, клиент для малины и клиент для windows. Сборки статические. Для малины нужна glibc 2.6, так что на старой прошивке работать не будет, сборка для windows не будет работать на XP, скорее всего, потому как собиралась на VS2013, которая не поддерживает XP. Скрипты будет одинаково работать как на клиенте Windows, так и на клиенте Linux.

Запустим агента на стороне малины:

 $ ./ferro_remote_agent -s 0.0.0.0:12345 -l- 2015-Aug-31 13:19:07.734228 [INF] [log] Started. 2015-Aug-31 13:19:07.734344 [INF] [lua] Started. 2015-Aug-31 13:19:07.734842 [INF] [fs] Started. 2015-Aug-31 13:19:07.735115 [INF] [gpio] Started 2015-Aug-31 13:19:07.735427 [INF] [i2c] Started. 2015-Aug-31 13:19:07.735619 [INF] [listener] tcp://0.0.0.0:12345 started 2015-Aug-31 13:19:07.735628 [INF] [listener] Started. 2015-Aug-31 13:19:07.735633 [INF] [main] Agent started. 

агент откроет 1 сервер и будет выводить логи в текущую консоль.

А теперь я напишу скрипт, который покажет содержимое, например директории /home/data/pi

fs = fr.client.fs -- алиасы iter = fs.iterator function main(argv) local dir = "/home/data/pi" local i, err = iter.begin( dir ) if not err then while i:has_next( ) do local path = i:get( ) local stat = fs.stat( path ) print( path, stat.size, "bytes" ) i:next( ) end else print( err ) end end 

Сохраню этот файл рядом с lua_client в dir.lua, например и выполню (малинка висит на 192.168.1.11):

$ ./lua_client -s 192.168.1.11:12345 -e dir.lua /home/data/pi/r.txt 0 bytes /home/data/pi/hello.txt 15 bytes /home/data/pi/img1.jpg 90540 bytes /home/data/pi/diff1.txt 15746 bytes 

Все просто. Клиент подключился, экспортировал таблицы с вызовами в lua, исполнил скрипт. В скрипте работа происходит с удаленной системой, как с локальной.

Еще один пример — скачивание файла, чтоб показать что работа с удаленным файлом не отличается от работы с локальным:

function main( argv ) local path = argv[1] -- параметр первый - что скачать local out = argv[2] -- параметр второй - куда скачать if not path then return end if not out then out = "pulled.file" end local fs = fr.client.fs -- локальные local file = fs.file -- алиасы чтоб удобнее было local f, e = file.open( path, "rb" ) -- откроем удаленный файл (на малинке) local out, eout = io.open( out, "wb" ) -- локальный файл, куда будет писать if f and out then f:seek( 0, "end" ) -- определение размера print( "file size:", f:tell( ) ) -- просто для примера работы f:seek( 0 ) local d = f:read( ) -- читаем удаленный файл while d do -- пока он не кончился out:write( d ) -- пишем в локальный d = f:read( ) -- снова читаем удаленный end else print( "remote error:", e, "local error:", eout ) end end 

исполним: (параметры в скрипт передаются ключами -p клиента)

$ ./lua_client -s 192.168.1.11:12345 -e file-pull.lua -p /home/data/pi/img1.jpg -p img.jpg file size: 90540 $ ls -la total 13224 drwxrwxr-x 3 nwnclv nwnclv 4096 авг. 31 14:14 . drwxrwxr-x 10 nwnclv nwnclv 4096 авг. 31 11:01 .. . -rw-rw-r-- 1 nwnclv nwnclv 1205 авг. 31 14:14 dir.lua -rw-rw-r-- 1 nwnclv nwnclv 90540 авг. 31 14:14 img.jpg -rwxrwxr-x 1 nwnclv nwnclv 3775064 авг. 31 13:46 lua_client . 

Вот как-то так. Все довольно просто получилось.

Ну и раз уж в тегах стоит GPIO, пример с GPIO: Возмем схему из топика GPIO для чайников (часть 1)

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

function main( ) local g, e = fr.client.gpio.export( 3, "out" ) -- получим доступ к GPIO3 if not e then g:set( 0 ) -- установим значение в 0. диодик горит. else print( "export error:", e ) end end 

Помимо этого можно отлавливать события с GPIO (изменения значения с 1 на 0 и наоборот) тут описал как

А еще клиент умеет таймер, умеет внутренние события, умеет работать с I2c пример работы с акселерометром mma7660fc. Умеет работать с консолью

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

Веб-контроль Raspberry Pi GPIO

Доброго времени суток! В этом посте я хочу показать, как управлять электроникой через Интернет используя Raspberry Pi. Выглядеть это будет примерно вот так.

  • Серверная программа, которая работает на Raspberry Pi, для того чтобы читать переменные
  • Базы данных MySQL для хранения переменных
  • Веб-страница Apache2, для того чтобы контролировать переменные
Шаг 1. База знаний
  • Веб-сервер Apache
  • PHP5
  • Сервер MySQL
  • phpMyAdmin

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

Шаг 2. Создание root аккаунта

Пропустите это, если у вас уже есть аккаунт, а если нет — продолжайте прочтение 🙂

Войдите в Raspberry Pi используя следующую команду:
sudo -i
passwd root

Теперь введите и подтвердите пароль для учётной записи.
Затем нужно закрыть SSH сессии и перезапустить его, войдя в систему как root.

Шаг 3. Настройка баз данных и phpMyAdmin

Это решение основано на базах данных MySQL, сейчас мы их и настроем.

Итак, для начала скачайте этот файл.

Залогиньтесь в панели управления phpMyAdmin и нажмите кнопку «Import» в верхнем меню.
Теперь, в пункте «File to Import«, нажмите кнопку «Choose File» и выберите файл, который скачали до этого (gpio.sql).
И в завершении нажмите кнопку «Go» внизу страницы.
Это позволит создать все необходимые таблицы.


  • Кликнуть на «Users» вверху страницы.
  • Теперь жмите на ссылку «Add User«.
  • В поле «User name» введите нужное вам имя пользователя. У ввёл «gpio«.
  • В поле «Host» введите «localhost«.
  • Затем в двух полях введите подходящие пароли. (Без пробелов, переносов или специальных символов). Я ввёл «pr03ND2«.
  • Все остальное остальное оставьте по умолчанию, а затем нажмите кнопку «Add User» кнопку в правом нижнем углу.

  • Нажмите на кнопку «Users» в верхнем меню, затем пролистывайте вниз до тех пор, пока вы не увидете только что добавленного вами пользователя в таблице «Users Overview«.
  • Напротив имени пользователя нажмите на ссылку «Edit Privileges«.
  • Листайте внизу к пункту «Database-specific privileges» и выберите «gpio» из выпадающего списка, нажмите кнопку «Go«.
  • Отметьте ВСЕ флажки и нажмите кнопку «Go» слева снизу.



Шаг 4. Shell Script

Это та часть, которая запускает проверку значений в базе данных MySQL на Raspberry Pi.
Этот сценарий довольно прост, но требует настройки.

  • sudo -i и нажмите Return / Enter
  • wget raspberrypi-gpio.googlecode.com/files/GPIOServer.sh и нажмите Return / Enter
  • chmod +x GPIOServer.sh и нажмите Return / Enter
  • nano GPIOServer.sh и нажмите Return / Enter

  • mysqlusername=»ЗДЕСЬ ИМЯ ПОЛЬЗОВАТЕЛЯ»
  • mysqlpassword=»ЗДЕСЬ ПАРОЛЬ»

После того как всё изменили, удерживайте клавишу Ctrl и нажмите X, затем отпустите Ctrl и нажмите Y. Нажмите Return / Enter.

Шаг 5. Настройка веб-страницы

Финальный шаг, перед тем как вы сможете использовать это, — настройка веб-страницы.

  • wget raspberrypi-gpio.googlecode.com/files/control.php и нажмите Return / Enter
  • wget raspberrypi-gpio.googlecode.com/files/off.jpg и нажмите Return / Enter
  • wget raspberrypi-gpio.googlecode.com/files/on.jpg и нажмите Return / Enter

  • mv control.php /var/www/control.php и нажмите Return / Enter.
  • chmod 755 /var/www/control.php и нажмите Return / Enter.
  • mv off.jpg /var/www/off.jpg и нажмитеReturn / Enter.
  • chmod 755 /var/www/off.jpg и нажмите Return / Enter.
  • mv on.jpg /var/www/on.jpg и нажмите Return / Enter.
  • chmod 755 /var/www/on.jpg и нажмите Return / Enter.

Теперь вам нужно изменить пару переменных перед использованием файла, поэтому напишите nano /var/www/control.php и нажмите Return / Enter.

  • $MySQLUsername = «ЗДЕСЬ ИМЯ ПОЛЬЗОВАТЕЛЯ»;
  • $MySQLPassword = «ЗДЕСЬ ПАРОЛЬ»;

Сейчас через браузер зайдите на страницу control.php (у меня raspberryPi/control.php, где raspberryPi — имя хоста.)

Там потребуется ввести следующие данные:
Имя пользователя: admin
Пароль: gpio

Я рекомендую нажать на ссылку «Change Password» сверху страницы и сменить пароль по очевидным причинам.


Шаг 6. Использование

Чтобы запустить программы GPIO, сделайте следующее:
Начните SSH сессию на Raspberry Pi и залогиньтесь как root, затем напечатайте ./GPIOServer.sh и нажмите Return / Enter.

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

Готово! Теперь войдите в панель управления (http://ИМЯ ХОСТА/control.php) и наслаждайтесь!

Для безопасности следует удалить root аккаунт.

Raspberry Pi для домашней автоматизации. Установка Webiopi

webiopi

В прошлом обзоре мы разобрались, как установить на Raspberry Pi операционную систему Raspbian, выполнить её основные настройки и включить возможность удаленного управления с помощью программы Putty. А сегодня попробуем поработать с внешним интерфейсом нашего миникомпьютера.

Raspberry Pi имеет довольно неплохой набор портов GPIO, которые можно задействовать как аналоговые или цифровые входы/выходы, интерфейсы UART , SPI , I 2 C . В начале рассмотрим использование портов в качестве цифровых выходов и входов.

Для этого воспользуемся готовым фреймворком WebIOPi . Он был специально разработан для работы с портами GPIO Raspberry Pi и позволяет создавать на своей основе различные пользовательские приложения. Как заявлено на официальном сайте поддержки этого проекта http s ://code.google.com/p/webiopi, WebIOPi имеет следующие возможности:

Встроенный Web — c ервер, реализованный на языке Python

Встроенная поддержка более чем 30 устройств с интерфейсами UART , SPI , I 2 C , 1- Wire

Библиотеки Javascript / HTML для создания Web -интерфейса

Библиотеки Python / Java для создания приложений под Android

Совместимость с языком Python версий 2 и 3

Возможность адаптации под нужды пользователей

Структура фреймворка WebIOPi приведена на рис.1:

структура webiopi

Рис. 1

Чтобы выполнить установку WebIOPi , заходим в Raspberry Pi через программу Putty (логин – pi , пароль — raspberry ) и в терминале вводим поочередно следующие команды:

$ wget http://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz

$ tar xvzf WebIOPi-0.7.1.tar.gz

$ cd WebIOPi-0.7.1

$ sudo ./setup.sh

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

$ sudo update-rc.d webiopi defaults

После чего перезапускаем Raspberry Pi :

sudo reboot

Теперь проверяем работу WebIOPi . С любого компьютера в локальной сети набираем сетевой адрес, присвоенный Raspberry Pi с указанием порта 8000. Например:

http ://192.168.1.8:8000

Для доступа к WebIOPi необходимо в открывшейся форме ввести логин и пароль. По умолчанию логин « webiopi », пароль – « raspberry ». Их, как и номер порта, затем можно будет изменить, но об этом поговорим отдельно, а для наших экспериментов воспользуемся стандартными параметрами.

Итак, если вы все сделали правильно при установке WebIOPi , ввели верный логин и пароль, то в браузере должна открыться страница (рис.2)

webiopi main menu

Рис. 2

На этой странице есть четыре пункта меню. GPIO Header и GPIO List открывают два варианта графических интерфейсов работы с портами ввода/вывода, Serial Monitor – терминал для работы с последовательным портом UART , Devise Monitor – подключенные к GPIO датчики.

Выбираем в меню пункт GPIO Header и попадаем на страницу с графическим интерфейсом управления портами (рис.3)

webiopi gpio

Рис. 3

Как видно из приведенного рисунка, графический интерфейс представляет собой мнемоническую интерактивную схему портов GPIO Raspberry Pi . По умолчанию, все они настроены как входы ( IN ) . Для использования порта как выхода, нужно кликнуть по IN мышкой и надпись измениться на OUT .

Черный цвет порта указывает, что на выходе низкий уровень (0В), желтый – высокий уровень (3,3В). Для изменения состояния определенного порта кликнем по нему мышкой и уровень на его выходе изменится на инверсный.

Для работы с WebIOPi через Web -интерфейс, рекомендуется использовать браузер Chrome , с другими браузерами корректная работа фреймворка не гарантируется. Однако, я проверял его работу в Opera , Internet Explorer под Windows , Safari под iOS и особых проблем не наблюдалось. Но, тем не менее, лучше все же воспользоваться рекомендацией разработчика и во избежание дополнительных проблем на начальном этапе пользоваться только Chrome .

Как уже отмечалось, при включении все порты GPIO в Raspberry Pi настраиваются как входы. Но, допустим, требуется, что бы при включении определенные порты не только были выходами по умолчанию, но и имели, в случае необходимости, высокий или низкий «стартовый» уровень. Никаких проблем, это довольно просто реализовать в WebIOPi . Остановимся на этом вопросе более подробно.

Подключаемся к Raspberry Pi через терминальную программу Putty (логин – pi , пароль — raspberry ) и открываем для редактирования конфигурационный файл config :

sudo nano / etc / webiopi / config

Допустим, мы хотим что бы порты GPIO 2, 3, 4, 17 были сконфигурированы как выходы с низким стартовым уровнем, порты GPIO 9, 10, 11, 22, 27 – как выходы с высоким стартовым уровнем, а оставшиеся GPIO 7, 8, 18, 23, 24, 25 как входы . Находим в открывшемся конфигурационном файле заголовок:

[ GPIO ] # Initialize following GPIOs with given function and optional value # This is used during WebIOPi start process

Редактируем в соответствии с нашими требованиями (рис.4)

webiopi config

Рис. 4

Надеюсь, что здесь все понятно и без комментариев: IN – это вход, OUT 1выход с высоким уровнем, OUT 0выход с низким уровнем. Назначение входов IN можно не прописывать – не назначенные в конфигурационном файле порты по умолчанию будут входами.

Сохраняем настройки сочетанием клавиш Ctrl и O , затем нажимаем Enter . Настройки сохранены, делаем перезагрузку Raspberry Pi командой sudo reboot и после перезагрузки заходим на страницу WebIOPi , как это описывалось выше. Мы увидим, что назначение портов GPIO изменилось в соответствии с заданными настройками в конфигурационном файле (рис.5)

remote GPIO Raspberry Pi

Рис. 5

В принципе, даже с помощью рассмотренного «базового» варианта WebIOPi , уже можно организовать систему удаленного управления нагрузками через локальную сеть или интернет. Для этого необходимо подключить к портам GPIO Raspberry Pi транзисторные ключи с электромагнитными реле или симисторы. Однако, нужно предусмотреть некоторые меры предосторожности во избежание выхода из строя портов GPIO . Рекомендуется сделать гальваническую развязку портов входов и выходов, например, через оптроны. Кстати, входы можно задействовать для организации сигнализации переключения исполнительного устройства.

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

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

Устройство управления и контроля по GSM

Управление нагрузками по телефону

Управление нагрузками по радио

Категория: | Просмотров: 39996 | Добавил: Admin | Теги: Raspberry Pi | Рейтинг: 5.0/1 |
Всего комментариев: 13

Использование портов GPIO в Raspberry Pi. Часть 2

С момента выхода первой части этой статьи разработчики Rasbperry Pi анонсировали и начали продавать обновленную модель своего дивайса, Raspberry B+. В анонсе говорится, что это не новая, а именно обновленная модель. Несмотря на это, изменения очень приятные:

  • вместо двух USB-портов теперь четыре;
  • слот для SD-карты заменен слотом микро-SD (они называют это USD, видимо выбрав традиционное ASCII-написание греческой буквы mu );
  • разъем видеовыхода совмещен со звуковым (кому вообще может потребоваться видеовыход при наличии HDMI?);
  • всесто 26-контактного разъема с портами GPIO P1 теперь установлен 40-контактный;
  • вместо двух отверстий для крепления платы теперь четыре 🙂

В рамках темы этой статьи самое интересное изменение для нас — увеличение числа выводов GPIO. В новом 40-контактном разъеме J8, который теперь стоит на месте бывшего P1, доступны 26 портов GPIO вместо прежних 17. При этом с платы исчезли контакты для подключения разъема P5, хотя даже с учетом этой потери общее число доступных выводов все равно увеличилось.

Верхняя часть разъема полностью совпадает с разъемом P1 модели B, что позволяет без проблем заменить старое устройство и поставить на его место плату новой модели B+. Разводка нового разъема J8 опубликована на сайте производителя, а новые доступные порты GPIO соответствуют выводам следующим образом:

  1. GPIO05 — 29
  2. GPIO06 — 31
  3. GPIO12 — 32
  4. GPIO13 — 33
  5. GPIO16 — 36
  6. GPIO19 — 35
  7. GPIO20 — 38
  8. GPIO21 — 40
  9. GPIO26 — 37

Все перечисленные GPIO абсолютно новые, а выводы GPIO28—GPIO31, которые были выведены на исчезнувший разъем P5, теперь недоступны.

B+ и libbcm2835

Библиотека libbcm2835 и модуль Device::BCM2835, о которых рассказано в предыдущей части статьи, пользуются константами типа RPI_V2_GPIO_P1_12 , содержащих в своих именах номер разъема (P1 или P5) и номер физического контакта (последние две цифры) соответствующего разъема. При программировании для Raspberry Pi B+ можно по-прежнему применять эти константы, но это неудобно.

Во-первых, они не позволяют обратиться к новым выводам. Констант типа RPI_V2_GPIO_P1_37 не существует, а библиотека по-прежнему не обновлена, и на сегодня доступна версия 1.36, а на официальном форуме не видно никаких обсуждений по поводу того, будет ли это сделано.

Во-вторых, константы для раъема P5 более неактуальны.

Такое положение дел — не причина не использовать новые выводы, а наоборот хороший повод разобраться, что делать. Все константы определены в заголовочном файле bcm2835.h следующим образом:

typedef enum < . . . // RPi Version 2 RPI_V2_GPIO_P1_03 = 2, ///< Version 2, Pin P1-03 RPI_V2_GPIO_P1_05 = 3, ///< Version 2, Pin P1-05 RPI_V2_GPIO_P1_07 = 4, ///< Version 2, Pin P1-07 . . . >RPiGPIOPin;

Числовое значение констант — это не что иное как номер GPIO, поэтому вместо громоздких имен можно безболезненно подставлять числовые значения. Это не только облагородит код, но и немного уменьшит необходимость во время программирования бесконечного пересчета номеров GPIO в номера физических выводов на плате (хотя от этого не скрыться, когда дойдет дело до подключения реальных устройств).

Например, вместо строк (из которых непонятно, с каким GPIO ведется работа)

Device::BCM2835::gpio_set(RPI_V2_GPIO_P1_12); # Установка в 1 Device::BCM2835::gpio_clr(RPI_V2_GPIO_P1_12); # Сброс в 0

полностью безопасно и корректно писать так (где номер GPIO указан явно):

Device::BCM2835::gpio_set(18); # Установка в 1 Device::BCM2835::gpio_clr(18); # Сброс в 0

При таком подходе появляется возможность программно добраться до всех 26 GPIO, доступных в модели B+. Однако надо проявлять повышенную осторожность, поскольку теперь компилятор (что C, что Perl) не сможет проконтролировать вас, сверяя имена констант с предопределенным списком, и если указать номер несуществующего GPIO (или порта, который имеется в процессоре BCM2835, но не выведен на разъем Raspberry Pi) могут произойти непредвиденные побочные эффекты. Функции библиотеки libbcm2835 не делают никакой проверки переданных аргументов. В моем случаи после попытки записи в порты с неверными номерами Raspberry Pi просто отключалась, к счастью, без физических повреждений.

Работа в реальном времени

В проекте, где я использую Raspberry Pi, мне потребовалось плавно изменять яркость лампы накаливания, причем такой регулятор должен управляться полностью программно и быть многоканальным. В интернетах такого толком ничего не описано: есть много ссылок о том, как регулировать яркость светодиода, парочка видео, где разработчик меняет яркость, передвигая ползунок на экране, но нет никакого описания, наконец, есть одна схема, работающая скорее с Arduino, чем с Raspberry, и одно смешное решение, где компьютер управляет моторчиком, который вращает регулятор яркости.

Кратко регуляторы яркости ламп накаливания (питающихся от сетевого переменного напряжения 220 В) работают так. Одна часть устройства следит за сетевым напряжением и формирует импульс в тот момент, когда оно переходит через нуль. Начиная с этого момента необходимо отсчитать определенное число миллисекунд и подать другой импульс, который включит симистор, который в свою очередь подаст на лампу напряжение. Как только напряжение вновь приблизится к нулю, симистор закроется (сам по себе), и лампа обесточится до следующего программного импульса. Все это происходит дважды за период сетевого напряжения. Его частота 50 Гц, так что управляющий цикл повторяется 100 раз в секунду, а его длина составляет 10 мс. Например, если задержку сделать в 3 мс, то лампа будет светиться примерно на две трети, а если 7 мс, то на четверть.

Казалось бы, при тактовой частоте Raspberry Pi 700 МГц проблем с программным формированием в 700 000 раз более низкочастотных интервалов быть не должно. На практике, однако, все не так. Регулятор яркости работает, но раз в одну-две секунды, а иногда и чаще, лампа помаргивает. Не помогает даже оверклокинг, максимально возможный до 1 ГГц. Я пробовал писать программу и на перле, и на С, но результат остался тем же.

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

Дальше можно пойти тремя путями. Во-первых, попробовать поставить иную операционную систему. Например, среди рекомендованных для Raspberry есть интересная система RISC OS, которая изначально проектировалась с учетом ограниченных возможностей оборудования, но она совсем не Unix-подобная, и как с ней работать, мне осталось неясным. Во-вторых, никто не мешает перейти на Arduino (хоть это и не спортивно) или на UDOO (это более интересно, и есть смысл попробовать). В-третьих, остается разобраться с тем, как избавиться от прерываний.

Регистры контроля прерываний

Контроль за работой прерываний в Raspberry Pi, а точнее, в процессоре BCM2835 описано в седьмой главе «Interrupts» мануала по периферии. Если не вдаваться в подробности, то имеется десяток регистров, доступных по адресам, начиная с 0x2000B000, со следующими смещениями:

  1. 0x200 — IRQ basic pending
  2. 0x204 — IRQ pending 1
  3. 0x208 — IRQ pending 2
  4. 0x20C — FIQ control
  5. 0x210 — Enable IRQs 1
  6. 0x214 — Enable IRQs 2
  7. 0x218 — Enable Basic IRQs
  8. 0x21C — Disable IRQs 1
  9. 0x220 — Disable IRQs 2
  10. 0x224 — Disable Basic IRQs

На странице Accurate timing for real time control показан пример кода на C, который отключив прерывания, формирует на одном из GPIO сигнал частотой 50 кГц. Этот обнадеживающий код я взял за основу и написал небольшую библиотеку libraspio, которая позволяет программно запрещать и разрешать прерывания. Как только прерывания перестали мешать, удается формировать интервалы времени с точностью до единиц микросекунд, что для моей исходной задачи более чем достаточно.

Вот так выглядит фрагмент функции main() моей итоговой программы, управляющей светом:

#ifndef DEBUG disable_interrupts(); #endif dimmer_loop(); #ifndef DEBUG enable_interrupts(); #endif

Да, это не перл, но если воспользоваться XS или Inline::C или просто обернуть библиотеку по типу того, как сделан модуль Device::BCM2835, все получится и на перле.

Отдельной строкой замечу, что позже я узнал и про то, что существует пара функций local_irq_disable() и local_irq_disable() , доступных практически из коробки, и наверняка есть смысл в следующий раз поэкспериментировать именно с ними.

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

Поскольку прерывания более не доступны, временные задержки придется отсчитывать самостоятельно. Полагаться на функции типа sleep или Device::BCM2835::delay более нельзя: если их вызвать, то обратно не вернуться, и программа зависнет.

Как работать с регистрами на перле

Процессор BCM2835 содержит внутренний таймер, работающий на частоте 1 МГц, который доступен через регистры, отображаемые на память. В мануале его работа описана в главе 12 «System Timer». В отличие от регистров контроля прерываний, здесь все проще, поэтому я остановлюсь на этом подробнее.

Системное время (время, отсчитываемое с момента подачи питания), хранится в двух 32-разрядных регистрах CLO и CHI, видных, соответственно, по адресам 0x20003004 и 0x20003008. Два беззнаковых регистра обеспечивают 64 бита, что дает возможность отсчитывать время до 2^64 микросекунд, то есть на пятьсот с лишним тысяч лет (32 бит не хватит и на полтора часа).

Прочитать значения системных регистров относительно просто. Для этого в перле удобно воспользоваться функциями модуля Sys::Mmap (он, очевидно, использует POSIX-фукнцию mmap(2)):

my $devmemfh; my $timer_CLO_reg; my $timer_CHI_reg; sysopen($devmemfh, "/dev/mem", O_RDWR|O_SYNC, 0666); mmap($timer_CLO_reg, 4, PROT_READ, MAP_SHARED, $devmemfh, 0x20003004); mmap($timer_CHI_reg, 4, PROT_READ, MAP_SHARED, $devmemfh, 0x20003008); close($devmemfh);

Теперь переменные $timer_CLO_reg и $timer_CHI_reg всегда содержат значения из соответствующих регистров системного таймера (эти значения, изменяются без вмешательства программиста, поэтому последовательные чтения могут вернуть разные результаты — поведение, описываемое ключевым словом volatile в C, C# и Java).

Несмотря на кажущуюся избыточность, я использую все 64 бита, чтобы не получить непрятных сюрпризов в вычислениях после того, как заполнится весь младший регистр. Распаковать число типа unsigned long , сдвинуть и сложить, на перле это делается так:

my $timer_lo = unpack 'L', $timer_CLO_reg; my $timer_hi = unpack 'L', $timer_CHI_reg; my $timer = $timer_lo + ($timer_hi 

Описанная функция реализована в модуле Device::BCM2835::Timer, его использование сводится к вызову функции timer() :

use Device::BCM2835::Timer; say Device::BCM2835::Timer::timer();

Другие возможности

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

  1. Формирование широтно-модулированного сигнала (PWM, pulse-width modulation) на выводе GPIO18. Но, к сожалению, такой выход только один, и сделать многоканальное устройство не получится.
  2. Использование прямого доступа к памяти (DMA, direct memory access), что позволяет с высокой точностью пересылать на выход (опять же, один-единственный) данные, предварительно записанные в некоторую область памяти. В частности, так можно реализовать проигрывание звукового файла в обход процессора.
  3. Самый необычный проект, использующий DMA, — pifm. Это FM-передатчик на Raspberry Pi. Программным путем генерируется сигнал на частоте до 200 МГц, который принимается обычным УКВ-приемником: sudo ./pifm sound.wav 100.0 Одно из последних обновлений этого проекта (сайт у них давно сломан) позволяло выводить в эфир стереозвук (сам по себе аудиовыход у Raspberry Pi монофонический).

Комментарии к статье

Показать комментарии

© pragmaticperl.com
Все материалы принадлежат их авторам. Перепечатывать статьи без спросу вредно.
По вопросам рекламы и размещения вакансий, пишите на editor эт pragmaticperl дот com .
Работает на Tu и Perl

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *