GPIO напрямую, без библиотек.

Тема в разделе "Raspberry Pi", создана пользователем NR55RU, 29 апр 2014.

  1. NR55RU

    NR55RU Оракул

    Изучал вопрос доступа к GPIO без библиотек, разбирался.
    Хотел бы высказать описание и получить ответ верно ли я понял.
    И так:
    Как я понял GPIO проецируется на физическую память, физическая память отведенная под GPIO начинается с адреса 0x20000000 + 0x200000
    Но у меня всегда был вопрос, если процесс работает в рамках виртуальной памяти, то как ему обратится к реальному физическому адресу в памяти.
    На вики нашел такой код:

    Код (Text):

    // Access from ARM Running Linux

    #define BCM2708_PERI_BASE 0x20000000
    #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */


    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <unistd.h>

    #define PAGE_SIZE (4*1024)
    #define BLOCK_SIZE (4*1024)

    int mem_fd;
    void *gpio_map;

    // I/O access
    volatile unsigned *gpio;


    // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
    #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
    #define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
    #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

    #define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0
    #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

    void setup_io();

    int main(int argc, char **argv)
    {
    int g,rep;

    // Set up gpi pointer for direct register access
    setup_io();

    // Switch GPIO 7..11 to output mode

    /************************************************************************\
    * You are about to change the GPIO settings of your computer. *
    * Mess this up and it will stop working! *
    * It might be a good idea to 'sync' before running this program *
    * so at least you still have your code changes written to the SD-card! *
    \************************************************************************/

    // Set GPIO pins 7-11 to output
    for (g=7; g<=11; g++)
    {
    INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO
    OUT_GPIO(g);
    }

    for (rep=0; rep<10; rep++)
    {
    for (g=7; g<=11; g++)
    {
    GPIO_SET = 1<<g;
    sleep(1);
    }
    for (g=7; g<=11; g++)
    {
    GPIO_CLR = 1<<g;
    sleep(1);
    }
    }

    return 0;

    } // main


    //
    // Set up a memory regions to access GPIO
    //
    void setup_io()
    {
    /* open /dev/mem */
    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
    printf("can't open /dev/mem \n");
    exit(-1);
    }

    /* mmap GPIO */
    gpio_map = mmap(
    NULL, //Any adddress in our space will do
    BLOCK_SIZE, //Map length
    PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
    MAP_SHARED, //Shared with other processes
    mem_fd, //File to map
    GPIO_BASE //Offset to GPIO peripheral
    );

    close(mem_fd); //No need to keep mem_fd open after mmap

    if (gpio_map == MAP_FAILED) {
    printf("mmap error %d\n", (int)gpio_map);//errno also set!
    exit(-1);
    }

    // Always use volatile pointer!
    gpio = (volatile unsigned *)gpio_map;


    } // setup_io
     
    После изучения понял что файл /dev/mem как раз таки и есть файл через который можно получить доступ к физической памяти системы, в данном примере с помощью системного вызова mmap() часть файла со смещением от начала 0x20000000 + 0x200000 отображена на виртуальную память процесса, что по сути позволяло обращаясь к "копии" в виртуальной памяти и тем самым менять значения в физической памяти за пределами виртуальной памяти процесса.

    Таким образом получается что при установки требуемых бит в определенные места физической памяти напрямую. мы управляем наличием и отсутствием напряжения на GPIO выводах.
    Установив единицу в требуемый бит мы получаем OUT а установив ноль IN.
    Я прав или где то что-то напутал ?

    P.s. Я правда пока не разобрался с вычислениями которые загнаны в макросы, но думаю тут просто datasheet надо покурить чтобы понять какие ячейки памяти в этом пространстве за что отвечают и какие у них размеры и значащие биты.
     
  2. ALev

    ALev Оракул

    Идея правильная: /dev/mem - это файл-устройство. Доступ к нему равноценен доступу непосредственно к оперативной памяти. Вопрос состоит только в том, во всю ли оперативную память отображается /dev/mem или в её часть. Для решения этой головоломки можно поступить следующим образом.

    В даташите на процессор найти карту памяти (memory map). Обычно в процессоре есть области, в которых всегда единицы (reserved) или какие-то определённые значения (например, область, служащая для идентификации процессора). Считываем данные по таким адресам и проверяем: если считалось не то, что ожидали - значит отображение RAM в /dev/mem нетривиально.