#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 ``` ##### Example GPIO Led Button Contol Пример управления светодиодом по кнопке. ```c++ #include #include #include #include #include #include #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 ```