Files
wikipedia/Linux/Embedded Linux/Linux - GPIO.md
T
2026-04-22 19:55:16 +03:00

374 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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/
Способы управления:
1. Userspace driver -прямое управление
2. Libgpiod - управление через библиотеку (API для Python и C/C++)
3. Python library
#### User Space
Прямой доступ к регистрам, минуя медленные системные вызовы.
Посредством открытия `/dev/mem` и мапирование базового адреса GPIO.
==Подводные камни:==
1. Обход GPIO Subsystem
2. Нет контроля конкуренции (может управлять несколько процессов)
3. Можно сломать драйвер
4. Нет безопасности
##### GPIO Output from userspace
```cpp title:userspace_gpio_control.cpp
#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**
Установка:
``` bash
sudo apt-get update
sudo apt-get install -y libgpiod-dev libgpiod-doc gpiod
```
Проверка корректности установки:
``` bash
pkg-config --cflags --libs libgpiod
```
Компиляция:
``` bash
g++ -o gpio_control gpio_control.cpp -lgpiod
```
##### Example GPIO Output
``` cpp title:libgpiod_control.cpp
#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;
}
```
Запуск программы (находясь в папке)
``` bash
./gpio_control
```
##### Example GPIO Input
```c
#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;
}
```
Запуск компиляции
```bash
g++ -std=c++11 gpio_button.cpp -lgpiod -o gpio_button
```
##### Example GPIO Led Button Contol
Пример управления светодиодом по кнопке.
```c++
#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;
}
```
Запуск компиляции
```bash
g++ -std=c++11 gpio_button.cpp -lgpiod -o gpio_button
```