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

В прошлой статье мы рассмотрели пример управления светодиодами с помощью виртуальной файловой системы. Таким же образом можно управлять портами GPIO.
В этом случае используется директория /sys/class/gpio .

Но сначала разберемся с использованием GPIO в виртуальной файловой системе из командной строки.
Перейдите в директорию /sys/class/gpio и просмотрите содержимое этого каталога :

Результат будет выглядеть приблизительно так :

Обратите внимание на названия портов gpiochip0, gpiochip32, gpiochip64, gpiochip96.
Это все те-же доступные для устройства порты GPIO0 – GPIO3, только нумерация линий портов у них идет от 0 до 127.

Не все выводы портов будут нам доступны, поскольку некоторые задействованы в схеме BeagleBone Black. Какие именно порты GPIO можно использовать в своих целях вы можете прочитать в документе Beagle Bone Black System Reference Manual соответствующей версии платы. Собственно это выводы микропроцессора AM3359, выведенные на два боковых разъема платы.

Делаем экспорт используемых линий GPIO . Например линия 6 порта GPIO1, которая в моей версии платы выведена на контакт 3 разъема P8.
Посчитаем ее номер , учитывая сквозную нумерацию линий : 1 * 32 + 6 = 38

После этого появиться новый подкаталог gpio38 с файловой структурой :

Будем использовать линию gpio38 как выход :

Установка логического уровня на линии порта :

Оформим все выше изложенное в виде программы на языке С :

Компилируем программу и проверяем как она работает :

Управление портами через виртуальную файловую систему (ВФС) имеет ограничения по скорости, поэтому написать программу на основе ВФС для высокоскоростного переключения портов (например вывод на TFT LCD экран ) вряд ли получиться.
Для более серьезной работы с платой необходимо иметь под рукой документацию на используемый процессор AM3359.

Загрузить Reference Manual на семейство процессоров AM335x можно с сайта производителя данного устройства Texas Instruments.

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

Базовые адреса портов GPIO можно узнать в разделе 2 справочного руководства , где описана карта памяти микропроцессора (лично для меня более приемлемо называть его микроконтроллером, но раз на сайте производителя называют микропроцессором, то как говорится «жираф большой , ему видней»(С) В.Высоцкий).

Обращаться к регистрам GPIO будем через указатели языка С .
Доступ к карте памяти мы получим благодаря использованию драйвера, расположенного в /dev/mem . Для этого нам понадобиться всего два системных вызова Linux — open() и mmap() .

Системный вызов open() традиционно для ОС Linux открывает устройство. Что касается системного вызова mmap() , то с его помощью можно выполнить отображение устройства или файла на память, говоря на жаргоне — «замапить» устройство или файл.

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

Прототип системного вызова mmap() выглядит следующим образом :

  • start – начальный адрес
  • length – размер отображаемых данных
  • prot – режим защиты
  • flags – тип отображаемого объекта
  • fd – дескриптор файла
  • offset – смещение внутри файла

При возникновении ошибки традиционно для системных вызовов Linux mmap() возвращает значение ошибки -1 . Код ошибки можно прочитать из глобальной переменной errno сразу после вызова mmap() .
В случае успешного завершения системный вызов mmap() возвращает адрес отображенных данных.
Теперь выполним моргание светодиодом , используя прямое управление портом GPIO , к которому подключен соответствующий светодиод.

Смотрим в принципиальную электрическую схему платы BeagleBone Black.

Нужно сначала посмотреть ревизию платы, для каждой ревизии принципиальная схема отличается. У моей платы BeagleBone Black была старая ревизия A5B.
Из принципиальной схемы мы видим, что пользовательский светодиод usr0 подключен к линии 21 порта GPIO1.

Из руководства пользователя определяем базовый адрес порта GPIO1, который равен 0x4804C000. В описании регистров GPIO находим базовый адрес регистра GPIO_DATAOUT, который равен 0x13C.

Теперь настало время написать простую программу, выполняющую мерцание светодиодом usr0 непосредственно через управление линией GPIO1_21. Инициализацию порта в этой простой программе мы выполнять не станем, поскольку она уже выполнена для светодиода usr0 .

Скомпилируем программу, затем поочередно включим и выключим светодиод usr0 :

В сети интернет можно найти библиотеки на языках С и C++ для удобного обращения к портам GPIO . Вы можете использовать одну из этих библиотек, например BBBIOlib.

Viewed 133773 times by 16111 viewers

Last modified: 26/10/2020

Author

Comments

Здрасти! Извените если задам не совсем умный вопрос, но в статье написано в пояснении ко второму листингу на С, что инициализация GPIO1 выполнена в первой программе… имеются ввиду строки с 9 по 33 ? (я так понимаю что там выполнена настройка направления порта) Так?
И ещё. Во второй программе есть такие строки » gpio1[ GPIO_DATAOUT/4 ]…. » Почему базовый адрес регистра GPIO_DATAOUT делится на 4 ?
Я совсем недавно занялся освоением линукса на BBB и для меня некоторые вещи в диковинку )))
Спасибо!

…в статье написано в пояснении ко второму листингу на С, что инициализация GPIO1 выполнена в первой программе… имеются ввиду строки с 9 по 33 ?

Вы имеете ввиду вот эти строки ?

Теперь настало время написать простую программу, выполняющую мерцание светодиодом usr0 непосредственно через управление линией GPIO1_21. Инициализацию порта в этой простой программе мы выполнять не станем, поскольку она уже выполнена для светодиода usr0 .

Если да, то инициализация GPIO1_21 выполняется при запуске ОС , поскольку на этой линии находится светодиод, линия настраивается как выход GPIO.

(я так понимаю что там выполнена настройка направления порта) Так?

Совершенно верно

Почему базовый адрес регистра GPIO_DATAOUT делится на 4 ?

Потому что регистры 32-битные ( 4 байта ). Например , адреса первых трех регистров 0x00, 0x04, 0x08 , соответственно это 0-вой , 1-ый и 2-ой элементы массива uint32_t

регистр да, 32-разрядный… . сагласно мануалу на камень 0x13C — это не базовый адрес регистра , а его смещение относительно базового адреса порта… и именно его надо использовать для записи. а не делёное на 4…. да, регистры порта можно рассматривать как массив, расположенный по адресу порта, но это ни как не объясняет деление на 4 я не понимаю причём тут разрядность. половину жизни пишу код для контроллеров и впервые вижу такое.

аааа…. кажется понял…. gpio1 это int указатель, а GPIO_DATAOUT фактически адрес первого байта регистра, соответственно индекс регистра в массиве будет в 4 раза меньше… так?
и ещё вопрос… почему при вызове mmap первый и второй параметры соответственно NULL и 0x1000 ? спасибо!

упс. всё, вопрос снят )))) нарыл в мануале…

Write a Reply or Comment