2018年10月14日 星期日

[OpenWRT] 透過Direct Acess存取AR9331 GPIO

在OpenWRT上要控制GPIO有二種方式

1.透過修改/sys/class/gpio/gpioxx/Value來變更,此種方法好處是可以在多種程式語言,

單純透過讀寫檔案的方式就可以改變GPIO,亦不需要知道實際GPIO所對應的記憶體位置。

缺點則是速度較慢。

Ex:切換系統LED

echo "27" > /sys/class/gpio/export

echo "out" > /sys/class/gpio/gpio27/direction
echo "1" > /sys/class/gpio/gpio27/value
or
echo "0" > /sys/class/gpio/gpio27/value
2. Direct Access GPIO Register,這種方式需要知道GPIO所對應的暫存器位置 ,適合以C語言開發,並對切換GPIO腳位速度有需求的使用者,例如RS 485應用。


以AR9331為例,參考它的DataSheet ,可以得知GPIO的位置為18040000。


讓GPIO 27 閃爍的範例程碼


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <time.h>


#define GPIO_ADDR 0x18040000 // base address
#define GPIO_BLOCK 48     // memory block size

volatile unsigned long *gpioAddress;
//Read GPIO Status
//return 0 -->Low
//return >0 -->High
int gpioRead(int gpio)
{
    unsigned long value = *(gpioAddress + 1);
    return (value & (1 << gpio));
}

// "1" (output) or "0" (input) 
void gpioDirection(int gpio, int direction)
{
    // obtain current GPIO settings
    unsigned long value = *(gpioAddress + 0); 
    if (direction == 1)
    {
        value |= (1 << gpio); // set bit to 1 (output)
    }
    else
    {
        value &= ~(1 << gpio); // clear bit (input)
    }
    *(gpioAddress + 0) = value;
}
//Set GPIO
void gpioSet(int gpio, int data)
{
    // obtain current GPIO settings
    unsigned long value = *(gpioAddress + 2); 
    if (data == 1)
    {
        value |= (1 << gpio); // set bit to 1 (High)
    }
    else
    {
        value &= ~(1 << gpio); // clear bit  (Low)
    }
    *(gpioAddress + 2) = value;
}
//Init GPIO Address, if /dev/mem not foud, you need to rebuild Image and add this module
int gpioSetup()
{
    int  m_mfd;
    if ((m_mfd = open("/dev/mem", O_RDWR)) < 0)
    {
        return -1;
    }    
    gpioAddress = (unsigned long*)mmap(NULL, GPIO_BLOCK, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, GPIO_ADDR);
    close(m_mfd);

    if (gpioAddress == MAP_FAILED)
    {
        return -2;
    }
    return 0;
}

int main(int argc, char **argv)
{
    int pin=27;
    int ms=500;
    
    gpioSetup();
        
 sscanf(argv[1], "%d", &pin);

    gpioDirection(pin,1);//set pin output    

    for (int i=0;i<100;i++)  //blink system led
    {
        gpioSet(pin,1);  //set syteme led high
        usleep(1000*ms); //sleep 500ms
        gpioSet(pin,0);  //set system led low
        usleep(1000*ms); //sleep 500ms
    }  
}

參考來源:

Working with GPIOs (C/C++)

AR9331 Datasheet