Files
wikipedia/Linux Embedded/Linux SBC - Hardware.md

9.7 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/

Способы управления:

  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

#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

I2C Bus. Integrates

Представление:

  • /sys/bus/i2c/devices/ - управление конфигурацией устройств через драйверы ядра
  • /dev/i2c-0 - прямой доступ к шине через userspace По умолчанию шина отключена. Включить можно следующими способами: 1 - Через raspi - config
sudo raspi-config

2 - корректировкой /boot/firmware/config.txt раскомментировать параметр dtparam=i2c_arm=on Затем в устройство должно появиться в /dev как i2c-1

Набор утилит для работы с шиной i2c: i2c-tools Установка:

sudo apt-get install i2c-tools

Состав библиотеки:

  • i2cdetect
  • i2cdump
  • i2cget
  • i2cset

SPI

Команды для работы:


Commands

Система: vcgencmd get_config int - параметры, которые передает GPU cat /etc/os-release - просмотр версии системы