12 KiB
#linux #GPIO #i2c #spi #hardware #userspase #libgpiod
Контроль и управление периферией в Linux https://habr.com/ru/articles/520488/
Устройства в Linux представлены в виде файлов и живут в системной директории: /dev
Например :
- Устройства i2c
/dev/i2c-0/dev/i2c-1 - Последовательные порты (Виртуальная консоль)
/dev/ttyUSB0 - SPI устройства
/dev/spidev*
GPIO Driver
Полезная информация: https://devdotnet.org/post/rabota-s-gpio-v-linux-chast-6-biblioteka-libgpiod/
Способы управления:
- Userspace driver -прямое управление
- Libgpiod - управление через библиотеку (API для Python и C/C++)
- Python library
User Space
Прямой доступ к регистрам, минуя медленные системные вызовы.
Посредством открытия /dev/mem и мапирование базового адреса GPIO.
==Подводные камни:==
1. Обход GPIO Subsystem
2. Нет контроля конкуренции (может управлять несколько процессов)
3. Можно сломать драйвер
4. Нет безопасности
GPIO Output from userspace
#include <iostream> // вывод сообщений
#include <fcntl.h> // открытие файлов
#include <sys/mman.h> // mmap и munmap
#include <unistd.h> // usleep и close
#include <cstring> // perror
#include <cstdint> // для uint32_t
/*
1. Userspace драйвер GPIO.
Управление GPIO Raspberry Pi через mmap обеспечивает прямой доступ к регистрам периферии,
минуя медленные системные вызовы. Это реализуется через открытие /dev/mem,
мапирование базового адреса GPIO
*/
#define GPIO_BASE 0x3F200000 // физический адрес начала блока регистров GPIO на SoC BCM2835
#define BLOCK_SIZE 4096 // Размер отображ. памяти Регистры GPIO занимают 41 байт, но мы отображаем целую страницу для простоты.
#define TIME_SLEEP 500000 // Время задержки в микросекундах
int main() {
// открытие спец.файла физической памяти. Флаги - чтение и запись + отключение кэширования
int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
// Если не удалось открыть - вывод ошибки
if (mem_fd < 0) {
perror("open");
return 1;
}
/*
Отображение памяти регистров в виртуальное адресное пространство
mmap создаёт отображение физической памяти, начиная с адреса GPIO_BASE, размером BLOCK_SIZE, в виртуальную память процесса.
NULL – ядро само выбирает адрес для отображения.
BLOCK_SIZE – количество байт.
PROT_READ | PROT_WRITE – разрешено чтение и запись.
MAP_SHARED – изменения видны другим процессам и синхронизируются с файлом (важно для доступа к реальной памяти).
mem_fd – файловый дескриптор /dev/mem.
GPIO_BASE – смещение в файле (физический адрес).
*/
void* gpio_map = mmap(
NULL,
BLOCK_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
mem_fd,
GPIO_BASE
);
if (gpio_map == MAP_FAILED) {
perror("mmap");
return 1;
}
// Указатель приводится к типу volatile uint32_t*, чтобы:
// Обращаться к памяти 32-битными словами.
// volatile запрещает компилятору оптимизировать обращения, т.к. значения регистров могут меняться аппаратно.
volatile uint32_t* gpio = (volatile uint32_t*)gpio_map;
// GPIO17 = бит 21 в GPFSEL1
gpio[1] &= ~(7 << 21); // очистить
gpio[1] |= (1 << 21); // output
while (true) {
gpio[7] = (1 << 17); // set
usleep(TIME_SLEEP);
gpio[10] = (1 << 17); // clear
usleep(TIME_SLEEP);
}
// освобождение ресурсов, при выходе
munmap(gpio_map, BLOCK_SIZE);
close(mem_fd);
}
Libgpiod - library control
Library General Purpose Input/Output device - Библиотека и набор утилит в Linux для управления пинами GPIO (ввод/вывод общего назначения) через символьные устройства (/dev/gpiochip*) или контроллеры GPIO
Состав библиотеки:
- gpiodetect
- gpioinfo
- gpioset
- gpioget
Установка:
sudo apt-get update
sudo apt-get install -y libgpiod-dev libgpiod-doc gpiod
Проверка корректности установки:
pkg-config --cflags --libs libgpiod
Компиляция:
g++ -o gpio_control gpio_control.cpp -lgpiod
Example GPIO Output
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define GPIO_CHIP "gpiochip0"
#define GPIO_PIN 17
int main(int argc, char *argv[]) {
struct gpiod_chip *chip;
struct gpiod_line *line;
int ret;
// Open the GPIO chip
chip = gpiod_chip_open_by_name(GPIO_CHIP);
if (!chip) {
perror("gpiod_chip_open_by_name");
return EXIT_FAILURE;
}
// Get the GPIO line
line = gpiod_chip_get_line(chip, GPIO_PIN);
if (!line) {
perror("gpiod_chip_get_line");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
// Request the line as output
ret = gpiod_line_request_output(line, "gpio-control", 0);
if (ret < 0) {
perror("gpiod_line_request_output");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
for(int i = 0; i < 50; i++){
// Set GPIO to HIGH
ret = gpiod_line_set_value(line, 1);
if (ret < 0) {
perror("gpiod_line_set_value");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
printf("GPIO 17 set to HIGH\n");
sleep(2);
// Set GPIO to LOW
ret = gpiod_line_set_value(line, 0);
if (ret < 0) {
perror("gpiod_line_set_value");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
printf("GPIO 17 set to LOW\n");
sleep(2);
}
// Release resources
gpiod_chip_close(chip);
return EXIT_SUCCESS;
}
Запуск программы (находясь в папке)
./gpio_control
Example GPIO Input
#include <gpiod.h>
#include <iostream>
#include <unistd.h>
int main() {
const char* chipname = "gpiochip0";
unsigned int line_num = 27;
gpiod_chip* chip = gpiod_chip_open_by_name(chipname);
if (!chip) {
perror("chip open");
return 1;
}
gpiod_line* line = gpiod_chip_get_line(chip, line_num);
if (!line) {
perror("get line");
return 1;
}
// Запрос линии как input с событием
if(gpiod_line_request_falling_edge_events(line,"button") < 0) {
perror("request events");
return 1;
}
std::cout << "Waiting for button press..." << std::endl;
while (true) {
int ret = gpiod_line_event_wait(line, NULL);
if (ret < 0) {
perror("event wait");
break;
}
if (ret == 1) {
struct gpiod_line_event event;
gpiod_line_event_read(line, &event);
std::cout << "Button pressed!" << std::endl;
}
}
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}
Запуск компиляции
g++ -std=c++11 gpio_button.cpp -lgpiod -o gpio_button
Example GPIO Led Button Contol
Пример управления светодиодом по кнопке.
#include <gpiod.h>
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define GPIO_CHIP "gpiochip0"
#define LED_PIN 17
#define BUTTON_PIN 27
// Function Prototype
int system_init();
int button_control();
// int led_controller();
// General Function
int main() {
button_control();
}
int button_control (){
// value`s to led control
int led;
bool led_state = false;
gpiod_chip* chip = gpiod_chip_open_by_name(GPIO_CHIP);
if (!chip) {
perror("chip open");
return 1;
}
gpiod_line* button_line = gpiod_chip_get_line(chip, BUTTON_PIN);
if (!button_line) {
perror("get line");
return 1;
}
gpiod_line* led_line = gpiod_chip_get_line(chip, LED_PIN);
if (!led_line) {
perror("get led line");
return 1;
}
// Request the line as output
led = gpiod_line_request_output(led_line, "gpio-control", 0);
if (led < 0) {
perror("gpiod_line_request_output");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
// Запрос линии как input с событием
if (gpiod_line_request_falling_edge_events(button_line, "button") < 0) {
perror("request events");
return 1;
}
std::cout << "Waiting for button press..." << std::endl;
while (true) {
int ret = gpiod_line_event_wait(button_line, NULL);
if (ret < 0) {
perror("event wait");
break;
}
if (ret == 1) {
struct gpiod_line_event event;
gpiod_line_event_read(button_line, &event);
std::cout << "Button pressed!" << std::endl;
led_state = !led_state;
if(led_state){
// Set GPIO LED to HIGH
led = gpiod_line_set_value(led_line, 1);
if (led < 0) {
perror("gpiod_line_set_value");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
printf("GPIO 17 set to HIGH\n");
}
else{
// Set GPIO to LOW
led = gpiod_line_set_value(led_line, 0);
if (led < 0) {
perror("gpiod_line_set_value");
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
printf("GPIO 17 set to LOW\n");
}
}
}
gpiod_line_release(button_line);
gpiod_chip_close(chip);
return 0;
}
Запуск компиляции
g++ -std=c++11 gpio_button.cpp -lgpiod -o gpio_button