Driving Beaglebone GPIO through /dev/mem

前端 未结 7 1699
温柔的废话
温柔的废话 2020-12-04 08:34

I\'m trying to write a C program for blinking a LED on the Beaglebone. I know I can use the sysfs way...but I\'d like to see if it is possible to get the same result mapping

7条回答
  •  南笙
    南笙 (楼主)
    2020-12-04 09:19

    The code shown in the original post does not work with the latest Beaglebone Black and its associated 3.12 kernel. The control register offsets appear to have changed; the following code is verified to work properly:

    #define GPIO0_BASE 0x44E07000
    #define GPIO1_BASE 0x4804C000
    #define GPIO2_BASE 0x481AC000
    #define GPIO3_BASE 0x481AE000
    
    #define GPIO_SIZE  0x00000FFF
    
    // OE: 0 is output, 1 is input
    #define GPIO_OE 0x14d
    #define GPIO_IN 0x14e
    #define GPIO_OUT 0x14f
    
    #define USR0_LED (1<<21)
    #define USR1_LED (1<<22)
    #define USR2_LED (1<<23)
    #define USR3_LED (1<<24)
    
    int mem_fd;
    char *gpio_mem, *gpio_map;
    
    // I/O access
    volatile unsigned *gpio;
    
    static void io_setup(void)
    {
        // Enable all GPIO banks
        // Without this, access to deactivated banks (i.e. those with no clock source set up) will (logically) fail with SIGBUS
        // Idea taken from https://groups.google.com/forum/#!msg/beagleboard/OYFp4EXawiI/Mq6s3sg14HoJ
        system("echo 5 > /sys/class/gpio/export");
        system("echo 65 > /sys/class/gpio/export");
        system("echo 105 > /sys/class/gpio/export");
    
        /* 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 = (char *)mmap(
                0,
                GPIO_SIZE,
                PROT_READ|PROT_WRITE,
                MAP_SHARED,
                mem_fd,
                GPIO1_BASE
        );
    
        if (gpio_map == MAP_FAILED) {
                printf("mmap error %d\n", (int)gpio_map);
                exit (-1);
        }
    
        // Always use the volatile pointer!
        gpio = (volatile unsigned *)gpio_map;
    
        // Get direction control register contents
        unsigned int creg = *(gpio + GPIO_OE);
    
        // Set outputs
        creg = creg & (~USR0_LED);
        creg = creg & (~USR1_LED);
        creg = creg & (~USR2_LED);
        creg = creg & (~USR3_LED);
    
        // Set new direction control register contents
        *(gpio + GPIO_OE) = creg;
    }
    
    int main(int argc, char **argv)
    {
        io_setup();
        while (1) {
            // Set LEDs
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR0_LED;
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR1_LED;
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR2_LED;
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR3_LED;
    
            sleep(1);
    
            // Clear LEDs
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR0_LED);
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR1_LED);
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR2_LED);
            *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR3_LED);
    
            sleep(1);
        }
    
        return 0;
    }
    

    I post this here as it appears that mmap-ed access stopped working around the 3.8 kernel, and no one has posted a working solution since then. I had to reverse-engineer the control register offsets using the /sys/class/gpio interface; I hope this answer reduces some of the frustration associated with using the BeagleBone GPIOs with the newer kernels.

    The code is licensed under a BSD license--feel free to use it wherever.

    EDIT: user3078565 is correct in his answer above. You will need to disable the default user LED GPIO drivers either by setting their triggers to none or by completely hiding them from the kernel via editing the device tree. Failure to do this will result in the LEDs flashing as they are supposed to, but also occasionally having their states overridden by the kernel GPIO driver.

    This was not an issue for my original application as it uses GPIO bank 0, which is largely ignored by the kernel GPIO drivers.

提交回复
热议问题