#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 // вывод сообщений #include // открытие файлов #include // mmap и munmap #include // usleep и close #include // perror #include // для 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 #include #include #include #include #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 #include #include 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` - просмотр версии системы