vault backup: 2026-05-01 17:07:28

This commit is contained in:
2026-05-01 17:07:28 +03:00
parent 805249c94e
commit 5e338653a9
6 changed files with 18 additions and 8 deletions
+373
View File
@@ -0,0 +1,373 @@
#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
```
@@ -0,0 +1,52 @@
#DeviceTree #Boot #Linux
## Hardware starting. Процесс запуска и инициализации
Далее описан процесс запуска и инициализации оборудования следующих уровней:
Firmware layer
Hardware descriptional layer
kernal initialization stage
1. Power On
2. Starting GPU on SoC
3. ROM внутри Video Core начинает выполнение.
4. GPU читает SD карту (загружаются)
Файлы расположены в `/boot или /firmware/boot`
1. ==bootcode.bin== - FirstStage Bootloader.
Он инициализирует SDRAM и загружает прошивку start.elf
2. ==start.elf== - Second Stage Bootloader
Инициализирует систему и загружает ядро линукс, управляет памятью
3. ==config.txt== - файл конфигурации
Основной конфигурационный файл системы, считываемый GPU используемый для настройки оборудования
4. ==cmdline.txt==
файл конфигурации, передаваемый ядру линукс при загрузке
5. ==device tree== - подгружается дерево устройств. (файлы *dtb)
Передается в kernel. Хранятся в каталоге boot / firmware / overlays
5. Только после этого GPU включает ARM CPU
6. ARM начинает выполнение linux kernal
## Device tree - Описание аппаратной конфигурации.
[Device Tree wiki](https://www.altlinux.org/Device_Tree)
Device Tree — это **описание аппаратной конфигурации конкретной платы**.
Применяется GPU до запуска ядра.
Device Tree Overlays - фрагмент device tree, или слой абстракции который:
добавляет новое устройство
включает отключенный контроллер
меняет параметры существующего узла
Пример изменения конфигурации DT без перекомпиляции ядра
Открыть config.txt и добавить `dtoverlay=i2c-rtc,ds3231`
Что происходит:
1. GPU загрузит основной DTB
2. GPU применит overlay
3. GPU передаст модифицированный DT ядру
4. Ядро увидит новое устройство
5. Драйвер RTC автоматически загрузится
Если удалить узел устройства (например SPI) из Device Tree то ядро просто не создаст SPI контроллер.
[[Linux - Commands]]
+306
View File
@@ -0,0 +1,306 @@
#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
```
---
## I2C Bus. Integrates
Представление:
- `/sys/bus/i2c/devices/` - управление конфигурацией устройств через драйверы ядра
- `/dev/i2c-0` - прямой доступ к шине через userspace
По умолчанию шина отключена.
Включить можно следующими способами:
1 - Через raspi - config
``` bash
sudo raspi-config
```
2 - корректировкой `/boot/firmware/config.txt`
раскомментировать параметр `dtparam=i2c_arm=on`
Затем в устройство должно появиться в `/dev` как `i2c-1`
Набор утилит для работы с шиной i2c: `i2c-tools`
Установка:
```bash
sudo apt-get install i2c-tools
```
Состав библиотеки:
- **i2cdetect**
- **i2cdump**
- **i2cget**
- **i2cset**
## SPI
Команды для работы:
```bash
```
## Commands
Система:
`vcgencmd get_config int` - параметры, которые передает GPU
`cat /etc/os-release` - просмотр версии системы