驾驶小猎犬号GPIO通过/dev/mem

时间:2022-04-27 10:42:05

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 the physical address space with /dev/mem.

我在写一个C程序,用来在Beaglebone上闪烁LED。我知道我可以用sysfs的方法……但是我想看看是否有可能用/dev/ mem/来映射物理地址空间。

I have a header file, beaglebone_gpio.h wit the following contents:

我有一个头文件,beaglebone_gpio。h包含以下内容:

#ifndef _BEAGLEBONE_GPIO_H_
#define _BEAGLEBONE_GPIO_H_

#define GPIO1_START_ADDR 0x4804C000
#define GPIO1_END_ADDR 0x4804DFFF
#define GPIO1_SIZE (GPIO1_END_ADDR - GPIO1_START_ADDR)
#define GPIO_OE 0x134
#define GPIO_SETDATAOUT 0x194
#define GPIO_CLEARDATAOUT 0x190

#define USR0_LED (1<<21)
#define USR1_LED (1<<22)
#define USR2_LED (1<<23)
#define USR3_LED (1<<24)

#endif

and then I have my C program, gpiotest.c

然后我有一个C程序,gpiotest。C

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h> 
#include "beaglebone_gpio.h"

int main(int argc, char *argv[]) {
    volatile void *gpio_addr = NULL;
    volatile unsigned int *gpio_oe_addr = NULL;
    volatile unsigned int *gpio_setdataout_addr = NULL;
    volatile unsigned int *gpio_cleardataout_addr = NULL;
    unsigned int reg;
    int fd = open("/dev/mem", O_RDWR);

    printf("Mapping %X - %X (size: %X)\n", GPIO1_START_ADDR, GPIO1_END_ADDR, GPIO1_SIZE);

    gpio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR);

    gpio_oe_addr = gpio_addr + GPIO_OE;
    gpio_setdataout_addr = gpio_addr + GPIO_SETDATAOUT;
    gpio_cleardataout_addr = gpio_addr + GPIO_CLEARDATAOUT;

    if(gpio_addr == MAP_FAILED) {
        printf("Unable to map GPIO\n");
        exit(1);
    }
    printf("GPIO mapped to %p\n", gpio_addr);
    printf("GPIO OE mapped to %p\n", gpio_oe_addr);
    printf("GPIO SETDATAOUTADDR mapped to %p\n", gpio_setdataout_addr);
    printf("GPIO CLEARDATAOUT mapped to %p\n", gpio_cleardataout_addr);

    reg = *gpio_oe_addr;
    printf("GPIO1 configuration: %X\n", reg);
    reg = reg & (0xFFFFFFFF - USR1_LED);
    *gpio_oe_addr = reg;
    printf("GPIO1 configuration: %X\n", reg);

    printf("Start blinking LED USR1\n");
    while(1) {
        printf("ON\n");
        *gpio_setdataout_addr= USR1_LED;
        sleep(1);
        printf("OFF\n");
        *gpio_cleardataout_addr = USR1_LED;
        sleep(1);
    }

    close(fd);
    return 0;
}

The output is:

的输出是:

Mapping 4804C000 - 4804DFFF (size: 1FFF)
GPIO mapped to 0x40225000
GPIO OE mapped to 40225134
GPIO SEDATAOUTADDR mapped to 0x40225194
GPIO CLEARDATAOUTADDR mapped to 0x40225190
GPIO1 configuration: FE1FFFFF
GPIO1 configuratino: FE1FFFFF
Start blinking LED USR1
ON
OFF
ON
OFF
...

but I can't see the led blinking.

但是我看不到led闪烁。

As you can see from the output of the program the configuration is correct, FE1FFFFF, is coherent since GPIO1_21, GPIO1_22, GPIO1_23 and GPIO1_24 are configured as outputs, each one driving a LED.

从程序的输出可以看出,配置是正确的,FE1FFFFF是一致的,因为GPIO1_21、GPIO1_22、GPIO1_23和GPIO1_24被配置为输出,每个都驱动一个LED。

Any idea about the reason?

你知道原因吗?

7 个解决方案

#1


12  

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.

小心些而已。乍一看,这是可行的,但它直接写入GPIO控制器驱动程序认为它拥有的寄存器。它将导致奇怪和难以追踪副作用,无论是在这个GPIO线还是在同一个银行的GPIO。要使其可靠地工作,您需要从内核GPIO驱动程序禁用整个银行。

#2


8  

The fix is:

解决办法是:

pio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR);

#3


8  

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:

原始post中显示的代码与最新的Beaglebone Black及其相关的3.12内核不兼容。控制寄存器的偏移量似乎已经改变;以下代码经验证可以正常工作:

#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.

我在这里发布了这个消息,因为mmap-ed访问停止了围绕3.8内核的工作,并且从那时起没有人发布一个工作解决方案。我必须使用/sys/类/gpio接口反向设计控制寄存器偏移量;我希望这个答案能减少在使用比格尔波恩GPIOs和新内核时遇到的一些挫折。

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

该代码是在BSD许可下授权的——您可以在任何地方使用它。

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.

编辑:user3078565在他的回答中是正确的。您将需要禁用默认的用户LED GPIO驱动程序,方法是将它们的触发器设置为none,或者通过编辑设备树将它们完全隐藏到内核中。如果做不到这一点,将导致led按预期闪烁,但偶尔也会被内核GPIO驱动程序覆盖它们的状态。

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

对于我最初的应用程序来说,这不是一个问题,因为它使用了GPIO bank 0,这在很大程度上被内核GPIO驱动程序忽略了。

#4


4  

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)

您可能还需要为您试图在用户空间中控制的任何硬件启用时钟。幸运的是,您可以使用dev/mem和mmap()来修改您的特定硬件的时钟控制寄存器,比如我编写的这个代码来启用SPI0:(定义值都来自spruh73i。pdf寄存器描述)

#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.

一旦执行了这个代码片段,我就可以使用另一个mmap()指针访问SPI0寄存器。如果我没有首先启用SPI0模块时钟,那么当我试图访问这些SPI寄存器时,我将会得到一个总线错误。启用时钟是持久的:一旦以这种方式启用它,它将一直保持到您禁用它,或者可能直到您使用spidev然后关闭它,或者重新启动它。因此,如果您的应用程序已经完成了您所启用的硬件,您可能想要禁用它以节省电力。

#5


2  

for enable GPIO banks....

为使GPIO银行....

enableClockModules () {
    // Enable disabled GPIO module clocks.
    if (mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
    if (mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
    if (mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
    if (mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
}

Where...

在那里……

MMAP_OFFSET = 0x44C00000

MMAP_OFFSET = 0 x44c00000

MMAP_SIZE = 0x481AEFFF - MMAP_OFFSET

MMAP_SIZE = 0x481AEFFF - MMAP_OFFSET。

GPIO_REGISTER_SIZE = 4

GPIO_REGISTER_SIZE = 4

MODULEMODE_ENABLE = 0x02

MODULEMODE_ENABLE = 0 x02

IDLEST_MASK = (0x03 << 16)

IDLEST_MASK = (0x03 < 16)

CM_WKUP = 0x44E00400

CM_WKUP = 0 x44e00400

CM_PER = 0x44E00000

CM_PER = 0 x44e00000

CM_WKUP_GPIO0_CLKCTRL = (CM_WKUP + 0x8)

CM_WKUP_GPIO0_CLKCTRL = (CM_WKUP + 0x8)

CM_PER_GPIO1_CLKCTRL = (CM_PER + 0xAC)

CM_PER_GPIO1_CLKCTRL = (CM_PER + 0xAC)

CM_PER_GPIO2_CLKCTRL = (CM_PER + 0xB0)

CM_PER_GPIO2_CLKCTRL = (CM_PER + 0xB0)

CM_PER_GPIO3_CLKCTRL = (CM_PER + 0xB4)

CM_PER_GPIO3_CLKCTRL = (CM_PER + 0xB4)

I have written a small library that perhaps you might be interested. At the moment only works with digital pins.

我写了一个小图书馆,也许你会感兴趣。目前只适用于数码管脚。

Regards

问候

#6


1  

REF: madscientist159

裁判: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

从无符号字符地址派生的无符号int偏移地址

#7


1  

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.

这种异常现象似乎是AM335x芯片中一个不完整地址解码的伪迹。0x4D、0x4E和0x4F作为访问这些寄存器的基本地址的偏移量是有意义的。C/C+指针算法将这些偏移量乘以4,生成0x134、0x138和0x13C的真实偏移量。但是,可以通过0x14D、0x14E和0x14F访问这些寄存器的“影子”副本。我已经验证了这两组抵消工作。我没有麻烦尝试0x24D等等。

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

可以使用offset 0x64访问GPIO_CLEARDATAOUT寄存器,可以使用offset 0x65访问GPIO_SETDATAOUT寄存器。

#1


12  

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.

小心些而已。乍一看,这是可行的,但它直接写入GPIO控制器驱动程序认为它拥有的寄存器。它将导致奇怪和难以追踪副作用,无论是在这个GPIO线还是在同一个银行的GPIO。要使其可靠地工作,您需要从内核GPIO驱动程序禁用整个银行。

#2


8  

The fix is:

解决办法是:

pio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR);

#3


8  

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:

原始post中显示的代码与最新的Beaglebone Black及其相关的3.12内核不兼容。控制寄存器的偏移量似乎已经改变;以下代码经验证可以正常工作:

#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.

我在这里发布了这个消息,因为mmap-ed访问停止了围绕3.8内核的工作,并且从那时起没有人发布一个工作解决方案。我必须使用/sys/类/gpio接口反向设计控制寄存器偏移量;我希望这个答案能减少在使用比格尔波恩GPIOs和新内核时遇到的一些挫折。

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

该代码是在BSD许可下授权的——您可以在任何地方使用它。

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.

编辑:user3078565在他的回答中是正确的。您将需要禁用默认的用户LED GPIO驱动程序,方法是将它们的触发器设置为none,或者通过编辑设备树将它们完全隐藏到内核中。如果做不到这一点,将导致led按预期闪烁,但偶尔也会被内核GPIO驱动程序覆盖它们的状态。

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

对于我最初的应用程序来说,这不是一个问题,因为它使用了GPIO bank 0,这在很大程度上被内核GPIO驱动程序忽略了。

#4


4  

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)

您可能还需要为您试图在用户空间中控制的任何硬件启用时钟。幸运的是,您可以使用dev/mem和mmap()来修改您的特定硬件的时钟控制寄存器,比如我编写的这个代码来启用SPI0:(定义值都来自spruh73i。pdf寄存器描述)

#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.

一旦执行了这个代码片段,我就可以使用另一个mmap()指针访问SPI0寄存器。如果我没有首先启用SPI0模块时钟,那么当我试图访问这些SPI寄存器时,我将会得到一个总线错误。启用时钟是持久的:一旦以这种方式启用它,它将一直保持到您禁用它,或者可能直到您使用spidev然后关闭它,或者重新启动它。因此,如果您的应用程序已经完成了您所启用的硬件,您可能想要禁用它以节省电力。

#5


2  

for enable GPIO banks....

为使GPIO银行....

enableClockModules () {
    // Enable disabled GPIO module clocks.
    if (mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
    if (mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
    if (mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
    if (mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK) {
      mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE;
      // Wait for the enable complete.
      while (mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET) / GPIO_REGISTER_SIZE] & IDLEST_MASK);
    }
}

Where...

在那里……

MMAP_OFFSET = 0x44C00000

MMAP_OFFSET = 0 x44c00000

MMAP_SIZE = 0x481AEFFF - MMAP_OFFSET

MMAP_SIZE = 0x481AEFFF - MMAP_OFFSET。

GPIO_REGISTER_SIZE = 4

GPIO_REGISTER_SIZE = 4

MODULEMODE_ENABLE = 0x02

MODULEMODE_ENABLE = 0 x02

IDLEST_MASK = (0x03 << 16)

IDLEST_MASK = (0x03 < 16)

CM_WKUP = 0x44E00400

CM_WKUP = 0 x44e00400

CM_PER = 0x44E00000

CM_PER = 0 x44e00000

CM_WKUP_GPIO0_CLKCTRL = (CM_WKUP + 0x8)

CM_WKUP_GPIO0_CLKCTRL = (CM_WKUP + 0x8)

CM_PER_GPIO1_CLKCTRL = (CM_PER + 0xAC)

CM_PER_GPIO1_CLKCTRL = (CM_PER + 0xAC)

CM_PER_GPIO2_CLKCTRL = (CM_PER + 0xB0)

CM_PER_GPIO2_CLKCTRL = (CM_PER + 0xB0)

CM_PER_GPIO3_CLKCTRL = (CM_PER + 0xB4)

CM_PER_GPIO3_CLKCTRL = (CM_PER + 0xB4)

I have written a small library that perhaps you might be interested. At the moment only works with digital pins.

我写了一个小图书馆,也许你会感兴趣。目前只适用于数码管脚。

Regards

问候

#6


1  

REF: madscientist159

裁判: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

从无符号字符地址派生的无符号int偏移地址

#7


1  

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.

这种异常现象似乎是AM335x芯片中一个不完整地址解码的伪迹。0x4D、0x4E和0x4F作为访问这些寄存器的基本地址的偏移量是有意义的。C/C+指针算法将这些偏移量乘以4,生成0x134、0x138和0x13C的真实偏移量。但是,可以通过0x14D、0x14E和0x14F访问这些寄存器的“影子”副本。我已经验证了这两组抵消工作。我没有麻烦尝试0x24D等等。

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

可以使用offset 0x64访问GPIO_CLEARDATAOUT寄存器,可以使用offset 0x65访问GPIO_SETDATAOUT寄存器。