python

Генерируем произвольные последовательности на выводах платы Raspberry Pi

  • пятница, 9 июня 2017 г. в 03:14:41
https://habrahabr.ru/company/dataart/blog/330536/
  • Разработка робототехники
  • Разработка под Linux
  • Алгоритмы
  • Python
  • Блог компании DataArt




Автор: Николай Хабаров, Embedded Expert DataArt, евангелист технологий умного дома.

В этой статье я расскажу, как написать обычное user space-приложение на Python для современного ARM-процессора с ОС Linux для генерирования сложных последовательностей импульсов на выводах платы. Суть идеи — использовать DMA-модуль процессора для копирования из предварительно подготовленного буфера в памяти в GPIO с высокой точностью по времени.

Когда речь заходит о необходимости сгенерировать сложную последовательность импульсов, например, для шаговых двигателей, обычно используют старые добрые простенькие микроконтроллеры с установленной специальной операционной системой реального времени или вообще без операционной системы. Реализация при этом, в лучшем случае, написана на C++. Сейчас процессоры шагнули далеко вперед и имеют массу преимуществ: производительность, возможность использования операционной системы Linux со всей инфраструктурой и ПО, а также высокоуровневых языков программирования, таких как Python. И все же современные микроконтроллеры для генерирования сложных последовательностей на выводах GPIO, как правило, не используют.

Я реализовал генерацию импульсов для управления шаговыми двигателями проекта PyCNC — проекта контроллера машин с ЧПУ, станков, 3D-принтеров, полностью написанного на Python и запускаемого на современном ARM-процессоре на плате Raspberry Pi.

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

GPIO


General Purpose Input Output (GPIO) — модуль процессора, который отвечает за установку логических уровней на физических выводах. Как известно, в цифровом мире на выводе может быть «0», т. е. ножка притянута к «земле», либо «1», т. е. ножка притянута к питанию.

Наверняка многие из вас зажигали светодиод, управля одной из ножек микроконтроллера. Например, у микроконтроллеров AVR это делалось установкой соответствующего бита в переменную, или, как ее еще часто называют, регистр PORTx. Что же это за переменная? Если посмотреть заголовочные файлы, там будет нечто наподобие:

#define PORT (*(volatile uint8_t *)(0x12345678))

Иначе говоря, запись состояния вывода — запись этого значения по заранее известному адресу. Обратите внимание, что это — не адрес в оперативной памяти, тем более, не адрес в виртуальной памяти процесса, это адрес в адресном пространстве самого процессора. Физический, где-то внутри чипа, к этому адресу подключен реальный модуль GPIO, к которому мы и обращаемся по этому адресу через ядро процессора, передавая байт информации для установки состояния вывода. И такие модули есть практически у любого процессора. ARM-процессор Raspberry Pi — не исключение. Чтобы узнать, где именно в адресном пространстве расположен каждый модуль, нужно взглянуть на документацию интересующего вас процессора.

Для Raspberry Pi этот документ находится здесь.

В нем по адресу шины 0x7E200000 распологаются регистры GPIO, другими словами, записывая данные в соответствующие адреса, можно управлять состоянием выводов. Сразу отметим, что вся периферия у процессоров Raspberry Pi 1, 2 и 3 одинаковая, отличается только физические адреса, в которые мапятся адреса шины периферии, начиная с адреса 0x7E000000. Для версии 1 Raspberry Pi это будет 0x20000000, для версий 2 и 3 — 0x3F000000. Т. е. для процессора RPi3, чтобы обратиться к адресу шины 0x7E200000, нужно писать по физическому адресу 0x3F200000. В документации по ссылке выше все адреса — адреса шины.

В подавляющем большинстве случаев, на ARM-процессоре будет установлена ОС Linux. Сразу возникает вопрос, как получить доступ к физической памяти. Доступ к ней есть в самом ядре ОС. Мы же хотим сделать обычное приложение, запускаемое в виртуальном адресном пространстве. К счастью, ядро ОС Linux предоставляет доступ к физической памяти через виртуальное устройство '/dev/mem', открыв которое (нам потребуются права суперпользователя), мы можем писать в физическую память. Справедливости ради отметим, что в официальной для Raspberry Pi ОС Raspbian существует еще устройство '/dev/gpiomem,' предоставляемое драйвером 'bcm2835_gpiomem', доступ к которому есть даже без прав суперпользователя — по нулевому отступу сразу идет доступ к первому регистру GPIO.

Давайте слегка развеемся практикой, думаю, так будет проще понять все написанное выше. Напишем простое приложение на Python, которое будет зажигать светодиод, подключенный к выводу GPIO21, работающее на Raspberry Pi 2 и 3 (для RPi1 поправьте адрес в листинге). Вместо Python здесь можно использовать любой другой язык программирования, способный вызывать системные функции из libc.so. Вот, собственно, код: