Driving Beaglebone GPIO through /dev/mem

前端 未结 7 1658
温柔的废话
温柔的废话 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:09

    Be careful. This works at first blush, but it directly writes to a register that the GPIO controller driver believes it owns. It will cause odd and hard to track down side effects, either on this GPIO line or on a GPIO that is in the same bank. For this to work reliably you need to disable the entire bank from the kernel GPIO driver.

    0 讨论(0)
  • 2020-12-04 09:11

    The fix is:

    pio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR);
    
    0 讨论(0)
  • 2020-12-04 09:18

    REF: madscientist159

    // OE: 0 is output, 1 is input
    #define GPIO_OE 0x14d
    #define GPIO_IN 0x14e
    #define GPIO_OUT 0x14f
    should be
    // OE: 0 is output, 1 is input
    #define GPIO_OE 0x4d
    #define GPIO_IN 0x4e
    #define GPIO_OUT 0x4f
    

    unsigned int offset address derived from the unsigned char address

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2020-12-04 09:22

    This anomaly appears to be an artifact of incomplete address decoding in the AM335x chip. It makes sense that 0x4D, 0x4E, and 0x4F work as offsets from the base address for accessing these registers. C/C++ pointer arithmetic multiplies these offsets by 4 to produce true offsets of 0x134, 0x138, and 0x13C. However a 'shadow' copy of these registers can be accessed through 0x14D, 0x14E, and 0x14F. I have verified that both sets of offsets work. I didn't bother trying 0x24D etc.

    The GPIO_CLEARDATAOUT register can be accessed using offset 0x64 and the GPIO_SETDATAOUT register can be accessed using offset 0x65.

    0 讨论(0)
  • 2020-12-04 09:24

    You might also need to enable the clock for any piece of hardware you are trying to control in user-space. Fortunately, you can use dev/mem and mmap() to fiddle with the clock control register for your particular piece of hardware, like this code I wrote to enable SPI0: (define values are all from spruh73i.pdf register descriptions)

    #define CM_PER_BASE     0x44E00000  /* base address of clock control regs */
    #define CM_PER_SPI0_CLKCTRL     0x4C        /* offset of SPI0 clock control reg */
    
    #define SPIO_CLKCTRL_MODE_ENABLE 2          /* value to enable SPI0 clock */
    
    int mem;            // handle for /dev/mem
    
    int  InitSlaveSPI(void) // maps the SPI hardware into user space
    {
        char *pClockControl;    // pointer to clock controlregister block (virtualized by OS)
        unsigned int value;
    
        // Open /dev/mem:
        if ((mem = open ("/dev/mem", O_RDWR | O_SYNC)) < 0)
        {
            printf("Cannot open /dev/mem\n");
            return 1;
        }
        printf("Opened /dev/mem\n");
    
        // map a pointer to the clock control block:
        pClockControl = (char *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, mem, CM_PER_BASE);
    
        if(pClockControl == (char *)0xFFFFFFFF) 
        {
            printf("Memory map failed. error %i\n", (uint32_t)pClockControl);
            close( mem );
            return 2;
        }
    
        value = *(uint32_t *)(pClockControl + CM_PER_SPI0_CLKCTRL);
        printf("CM_PER_SPI0_CLKCTRL was 0x%08X\n", value);
    
        *(uint32_t *)(pClockControl + CM_PER_SPI0_CLKCTRL) = SPIO_CLKCTRL_MODE_ENABLE;
    
        value = *(uint32_t *)(pClockControl + CM_PER_SPI0_CLKCTRL);
        printf("CM_PER_SPI0_CLKCTRL now 0x%08X\n", value);
    
        munmap( pClockControl, 4096 );              // free this memory map element
    

    Once I have executed this code fragment, I can access SPI0 registers using another mmap() pointer. If I don't enable the SPI0 module clock first, then I get a bus error when I try to access those SPI registers. Enabling the clock is persistent: Once enabled this way it stays on until you disable it, or maybe until you use the spidev and then close it, or reboot. So if your application is finished with the hardware you enabled, you might want to disable it to save power.

    0 讨论(0)
提交回复
热议问题