Embedded 通过/dev/mem驱动Beaglebone GPIO

Embedded 通过/dev/mem驱动Beaglebone GPIO,embedded,embedded-linux,mmap,gpio,Embedded,Embedded Linux,Mmap,Gpio,我正试图写一个C程序来让Beaglebone上的LED灯闪烁。我知道我可以使用sysfs方法……但是我想看看是否有可能得到与/dev/mem映射物理地址空间相同的结果 我有一个头文件beaglebonegpio.h,包含以下内容: #ifndef _BEAGLEBONE_GPIO_H_ #define _BEAGLEBONE_GPIO_H_ #define GPIO1_START_ADDR 0x4804C000 #define GPIO1_END_ADDR 0x4804DFFF #define

我正试图写一个C程序来让Beaglebone上的LED灯闪烁。我知道我可以使用sysfs方法……但是我想看看是否有可能得到与/dev/mem映射物理地址空间相同的结果

我有一个头文件beaglebonegpio.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
但我看不到led灯在闪烁

从程序的输出可以看出,配置是正确的,FE1FFFF, 是一致的,因为GPIO1_21、GPIO1_22、GPIO1_23和GPIO1_24配置为输出, 每个人驾驶一个LED

知道原因吗?

解决方案是:

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

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

原始帖子中显示的代码不适用于最新的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;
}
#定义GPIO0_基0x44E07000
#定义GPIO1_基0x4804C000
#定义GPIO2_基0x481AC000
#定义GPIO3_基0x481AE000
#定义GPIO_大小0x00000FFF
//OE:0为输出,1为输入
#定义GPIO_OE 0x14d
#在0x14e中定义GPIO_
#定义GPIO_输出0x14f

#定义USR0_LED(1您可能还需要为您试图在用户空间中控制的任何硬件启用时钟。幸运的是,您可以使用dev/mem和mmap()来处理特定硬件的时钟控制寄存器,如我为启用SPI0而编写的代码: (定义值均来自spruh73i.pdf寄存器说明)

#定义时钟控制寄存器的每个基址0x44E00000/*的CM*/
#根据SPI0时钟控制寄存器的偏移量定义CM_PER_SPI0_CLKCTRL 0x4C/**/
#定义SPIO_CLKCTRL_MODE_ENABLE 2/*值以启用SPI0时钟*/
int mem;//用于/dev/mem的句柄
int InitSlaveSPI(void)//将SPI硬件映射到用户空间
{
char*pClockControl;//指向时钟控制寄存器块的指针(由操作系统虚拟化)
无符号整数值;
//打开/dev/mem:
如果((mem=open(“/dev/mem”,O|RDWR | O|u SYNC))<0)
{
printf(“无法打开/dev/mem\n”);
返回1;
}
printf(“Opened/dev/mem\n”);
//将指针映射到时钟控制块:
pClockControl=(char*)mmap(0,4096,PROT_READ | PROT_WRITE,MAP_SHARED,mem,CM_PER_BASE);
if(pClockControl==(char*)0xFFFFFFFF)
{
printf(“内存映射失败。错误%i\n”,(uint32\u t)pClockControl);
关闭(mem);
返回2;
}
值=*(uint32\u t*)(pClockControl+CM\u/u SPI0\u CLKCTRL);
printf(“CM\u PER\u SPI0\u CLKCTRL为0x%08X\n”,值);
*(uint32_t*)(pClockControl+CM_PER_SPI0_CLKCTRL)=SPIO_CLKCTRL_MODE_ENABLE;
值=*(uint32\u t*)(pClockControl+CM\u/u SPI0\u CLKCTRL);
printf(“CM\u PER\u SPI0\u CLKCTRL现在为0x%08X\n”,值);
munmap(pClockControl,4096);//释放此内存映射元素
一旦我执行了这个代码片段,我就可以使用另一个mmap()访问SPI0寄存器指针。如果我没有首先启用SPI0模块时钟,那么当我尝试访问这些SPI寄存器时会出现总线错误。启用时钟是持久性的:一旦以这种方式启用,它将一直保持,直到您禁用它,或者可能直到您使用spidev并关闭它,或者重新启动。因此,如果您的应用程序使用了您启用的硬件,您可以可能要禁用它以节省电源。

REF:madscienceist159

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

从无符号字符地址派生的无符号整数偏移地址这一异常现象似乎是AM335x芯片中地址解码不完整的产物。0x4D、0x4E和0x4F作为访问这些寄存器的基址偏移量工作是有意义的。C/C++指针算术将这些偏移量乘以4,得到真值0x134、0x138和0x13C的偏移量。但是,这些寄存器的“影子”副本可以通过0x14D、0x14E和0x14F访问。我已经验证了这两组偏移量都有效。我没有尝试0x24D等


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

用于启用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);
    }
}
在哪里

MMAP_偏移量=0x44C00000

MMAP_大小=0x481AEFFF-MMAP_偏移量

GPIO_寄存器_大小=4

MODULEMODE_ENABLE=0x02


IDLEST_掩码=(0x03我找到了解决方案…只需要在mmap中使用MAP_SHARED,而不是MAP_PRIVATE。无论如何,我保留该问题。也许它对其他人有用。回答自己的问题是一种完美的做法,只要其他人也有公平的机会回答。你确定这就是我认为你需要的修复方法吗d在mmap中使用MAP_SHARED而不是MAP_PRIVATE。您能解释为什么需要MAP_SHARED吗?@RicoRico MAP_SHARED意味着对mmap区域的写入会提交回映射内存区域…而MAP_PRIVATE不会发生这种情况,这会导致由于写入时复制而将数据复制到不同的内存区域。您好,谢谢代码。只是想知道为什么不使用类似于
close(mem_fd)的东西来关闭它作为一个新手,我只想知道是否有必要关闭它?偏移地址错误。将其更正为0x4d,0x4e,0x4f而不是0x14d,0x14e,0x14f谢谢关于在访问银行之前导出的提示,这是一个很难调试的提示。有没有办法禁用GPIO控制器驱动程序以避免这一问题?您不需要调用析构函数在C++中,当对象被破坏时(它是通过为堆栈分配的对象留下范围,或者当为堆分配对象使用DELASE)自动完成的。同样,不需要将枚举提取到未签名的字符,它实际上并不快,SI。
#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
// 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
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);
    }
}