29

Beaglebone の LED を点滅させるための C プログラムを作成しようとしています。sysfs の方法を使用できることはわかっていますが、/dev/mem を使用して物理アドレス空間をマッピングしても同じ結果が得られるかどうかを確認したいと思います。

次の内容のヘッダー ファイル 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

そして、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;
}

出力は次のとおりです。

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

しかし、LEDの点滅が見えません。

プログラムの出力からわかるように、構成は正しく、FE1FFFFF は、GPIO1_21、GPIO1_22、GPIO1_23、および GPIO1_24 が出力として構成され、それぞれが LED を駆動するため、コヒーレントです。

理由について何か考えはありますか?

4

7 に答える 7

12

気をつけて。これは一見機能しますが、GPIO コントローラードライバーが所有していると認識しているレジスターに直接書き込みます。この GPIO ラインまたは同じバンクにある GPIO のいずれかで、奇妙で追跡が困難な副作用が発生します。これが確実に機能するためには、カーネル GPIO ドライバーからバンク全体を無効にする必要があります。

于 2013-12-07T21:29:47.820 に答える
9

修正は次のとおりです。

pio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR);
于 2013-08-01T18:22:55.213 に答える
8

元の投稿に示されているコードは、最新の 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;
}

mmap-ed アクセスが 3.8 カーネル周辺で機能しなくなったようで、それ以降、誰も有効な解決策を投稿していないため、ここに投稿します。/sys/class/gpio インターフェイスを使用して、制御レジスタのオフセットをリバース エンジニアリングする必要がありました。この回答により、新しいカーネルで BeagleBone GPIO を使用することに伴うフラストレーションが軽減されることを願っています。

コードは BSD ライセンスの下でライセンスされています。どこでも自由に使用してください。

編集: user3078565 は上記の回答で正しいです。トリガーを none に設定するか、デバイス ツリーを編集してカーネルから完全に隠して、デフォルトのユーザー LED GPIO ドライバーを無効にする必要があります。これを行わないと、LED が想定どおりに点滅しますが、カーネル GPIO ドライバーによって状態がオーバーライドされることもあります。

これは、カーネル GPIO ドライバーによってほとんど無視される GPIO バンク 0 を使用するため、元のアプリケーションでは問題ではありませんでした。

于 2014-01-02T00:53:28.547 に答える
4

ユーザー空間で制御しようとしているハードウェアのクロックを有効にする必要がある場合もあります。幸いなことに、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

このコード フラグメントを実行すると、別の mmap() ポインターを使用して SPI0 レジスターにアクセスできます。最初に SPI0 モジュール クロックを有効にしないと、これらの SPI レジスタにアクセスしようとするとバス エラーが発生します。クロックの有効化は永続的です: この方法で有効にすると、無効にするまで、または spidev を使用してから閉じるか、再起動するまでオンのままになります。そのため、有効にしたハードウェアでアプリケーションが完成したら、電力を節約するために無効にすることをお勧めします。

于 2014-02-08T03:00:03.237 に答える
1

参照:マッドサイエンティスト159

// 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 char アドレスから派生した unsigned int オフセット アドレス

于 2014-10-23T11:37:24.577 に答える
1

この異常は、AM335x チップの不完全なアドレス デコードによるアーティファクトのようです。0x4D、0x4E、および 0x4F が、これらのレジスタにアクセスするためのベース アドレスからのオフセットとして機能することは理にかなっています。C/C++ ポインター演算は、これらのオフセットを 4 倍して、0x134、0x138、および 0x13C の真のオフセットを生成します。ただし、これらのレジスタの「シャドウ」コピーには、0x14D、0x14E、および 0x14F を介してアクセスできます。オフセットの両方のセットが機能することを確認しました。わざわざ 0x24D などを試すことはありませんでした。

GPIO_CLEARDATAOUT レジスタにはオフセット 0x64 を使用してアクセスでき、GPIO_SETDATAOUT レジスタにはオフセット 0x65 を使用してアクセスできます。

于 2014-10-30T16:39:10.027 に答える