HAL_Linux: change to mem access method from sysfs access method

This step is taken to reduce the time for gpio access substantially
This commit is contained in:
bugobliterator 2014-05-31 19:54:12 +05:30 committed by Andrew Tridgell
parent eb95130441
commit fd0685cc01
2 changed files with 202 additions and 161 deletions

View File

@ -10,85 +10,110 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#include <sys/mman.h>
#include <sys/stat.h>
using namespace Linux; using namespace Linux;
struct GPIO{
volatile unsigned *base;
volatile unsigned *oe;
volatile unsigned *in;
volatile unsigned *out;
}gpio0,gpio1,gpio2,gpio3;
LinuxGPIO::LinuxGPIO() LinuxGPIO::LinuxGPIO()
{} {}
void LinuxGPIO::init() void LinuxGPIO::init()
{ {
int fd, len; int mem_fd;
char buf[64]; // 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");
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); /* open /dev/mem */
if (fd < 0) { if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
perror("LinuxGPIO::init"); printf("can't open /dev/mem \n");
exit (-1);
} }
len = snprintf(buf, sizeof(buf), "%d", LED_AMBER); /* mmap GPIO */
::write(fd, buf, len); gpio0.base = (volatile unsigned *)mmap(0, GPIO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO0_BASE);
close(fd); if ((char *)gpio0.base == MAP_FAILED) {
printf("mmap error %d\n", (int)gpio0.base);
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); exit (-1);
if (fd < 0) { }
perror("LinuxGPIO::init"); gpio1.base = (volatile unsigned *)mmap(0, GPIO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO1_BASE);
if ((char *)gpio1.base == MAP_FAILED) {
printf("mmap error %d\n", (int)gpio1.base);
exit (-1);
}
gpio2.base = (volatile unsigned *)mmap(0, GPIO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO2_BASE);
if ((char *)gpio2.base == MAP_FAILED) {
printf("mmap error %d\n", (int)gpio2.base);
exit (-1);
}
gpio3.base = (volatile unsigned *)mmap(0, GPIO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO3_BASE);
if ((char *)gpio3.base == MAP_FAILED) {
printf("mmap error %d\n", (int)gpio3.base);
exit (-1);
} }
len = snprintf(buf, sizeof(buf), "%d", LED_BLUE);
::write(fd, buf, len);
close(fd);
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); gpio0.oe = gpio0.base + GPIO_OE;
if (fd < 0) { gpio0.in = gpio0.base + GPIO_IN;
perror("LinuxGPIO::init"); gpio0.out = gpio0.base + GPIO_OUT;
}
len = snprintf(buf, sizeof(buf), "%d", LED_SAFETY); gpio1.oe = gpio1.base + GPIO_OE;
::write(fd, buf, len); gpio1.in = gpio1.base + GPIO_IN;
close(fd); gpio1.out = gpio1.base + GPIO_OUT;
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); gpio2.oe = gpio2.base + GPIO_OE;
if (fd < 0) { gpio2.in = gpio2.base + GPIO_IN;
perror("LinuxGPIO::init"); gpio2.out = gpio2.base + GPIO_OUT;
}
len = snprintf(buf, sizeof(buf), "%d", SAFETY_SWITCH); gpio3.oe = gpio3.base + GPIO_OE;
::write(fd, buf, len); gpio3.in = gpio3.base + GPIO_IN;
close(fd); gpio3.out = gpio3.base + GPIO_OUT;
close(mem_fd);
} }
void LinuxGPIO::pinMode(uint8_t pin, uint8_t output) void LinuxGPIO::pinMode(uint8_t pin, uint8_t output)
{ {
int fd,len; if(output == GPIO_INPUT){
char buf[64]; if(pin/32 < 1){
*gpio0.oe = *gpio0.oe | (1<<pin);
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", pin);
fd = ::open(buf, O_WRONLY);
if (fd < 0) {
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); //try exporting GPIO pin
if (fd < 0) {
perror("LinuxGPIO::direction");
} }
else if(pin/32 < 2){
len = snprintf(buf, sizeof(buf), "%d", pin); *gpio1.oe = *gpio1.oe | (1<<(pin-32));
::write(fd, buf, len); }
close(fd); else if(pin/32 < 3){
*gpio2.oe = *gpio2.oe | (1<<(pin-64));
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", pin); //retry writing direction }
fd = ::open(buf, O_WRONLY); else if(pin/32 < 4){
if (fd < 0) { *gpio3.oe = *gpio3.oe | (1<<(pin-96));
perror("LinuxGPIO::direction"); //report faillure }
}
else{
if(pin/32 < 1){
*gpio0.oe = *gpio0.oe & (~(1<<pin));
}
else if(pin/32 < 2){
*gpio1.oe = *gpio1.oe & (~(1<<(pin-32)));
}
else if(pin/32 < 3){
*gpio2.oe = *gpio2.oe & (~(1<<(pin-64)));
}
else if(pin/32 < 4){
*gpio3.oe = *gpio3.oe & (~(1<<(pin-96)));
} }
} }
if (output == GPIO_OUTPUT)
::write(fd, "out", 4);
else
::write(fd, "in", 3);
close(fd);
} }
int8_t LinuxGPIO::analogPinToDigitalPin(uint8_t pin) int8_t LinuxGPIO::analogPinToDigitalPin(uint8_t pin)
@ -98,49 +123,55 @@ int8_t LinuxGPIO::analogPinToDigitalPin(uint8_t pin)
uint8_t LinuxGPIO::read(uint8_t pin) { uint8_t LinuxGPIO::read(uint8_t pin) {
int fd;
char buf[64];
char ch;
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", pin); uint8_t value;
fd = open(buf, O_RDONLY); if(pin/32 < 1){
if (fd < 0) { value = (bool)(*gpio0.in & (1<<pin));
perror("LinuxGPIO::read"); }
else if(pin/32 < 2){
value = (bool)(*gpio1.in & (1<<(pin-32)));
}
else if(pin/32 < 3){
value = (bool)(*gpio2.in & (1<<(pin-64)));
}
else if(pin/32 < 4){
value = (bool)(*gpio3.in & (1<<(pin-96)));
} }
::read(fd, &ch, 1); return value;
close(fd);
if (ch == '0') {
return 0;
}
else if (ch == '1'){
return 1;
}
return -1;
} }
void LinuxGPIO::write(uint8_t pin, uint8_t value) void LinuxGPIO::write(uint8_t pin, uint8_t value)
{ {
int fd; if(value == HIGH){
char buf[64]; if(pin/32 < 1){
*gpio0.out = *gpio0.out | (1<<pin);
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", pin); }
else if(pin/32 < 2){
fd = open(buf, O_WRONLY); *gpio1.out = *gpio1.out | (1<<(pin-32));
if (fd < 0) { }
perror("LinuxGPIO::write"); else if(pin/32 < 3){
*gpio2.out = *gpio2.out | (1<<(pin-64));
}
else if(pin/32 < 4){
*gpio3.out = *gpio3.out | (1<<(pin-96));
}
}
else{
if(pin/32 < 1){
*gpio0.out = *gpio0.out & (~(1<<pin));
}
else if(pin/32 < 2){
*gpio1.out = *gpio1.out & (~(1<<(pin-32)));
}
else if(pin/32 < 3){
*gpio2.out = *gpio2.out & (~(1<<(pin-64)));
}
else if(pin/32 < 4){
*gpio3.out = *gpio3.out & (~(1<<(pin-96)));
}
} }
if (value==LOW)
::write(fd, "0", 2);
else
::write(fd, "1", 2);
close(fd);
} }
void LinuxGPIO::toggle(uint8_t pin) void LinuxGPIO::toggle(uint8_t pin)
@ -167,98 +198,95 @@ bool LinuxGPIO::usb_connected(void)
LinuxDigitalSource::LinuxDigitalSource(uint8_t v) : LinuxDigitalSource::LinuxDigitalSource(uint8_t v) :
_v(v) _v(v)
{ {
int fd, len;
char buf[64];
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
if (fd < 0) {
perror("LinuxGPIO::init");
}
len = snprintf(buf, sizeof(buf), "%d", v);
::write(fd, buf, len);
close(fd);
} }
void LinuxDigitalSource::mode(uint8_t output) void LinuxDigitalSource::mode(uint8_t output)
{ {
uint8_t pin = _v;
int fd,len; if(output == GPIO_INPUT){
char buf[64]; if(pin/32 < 1){
*gpio0.oe = *gpio0.oe | (1<<pin);
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction",_v);
fd = ::open(buf, O_WRONLY);
if (fd < 0) {
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); //try exporting GPIO pin
if (fd < 0) {
perror("LinuxGPIO::direction");
} }
else if(pin/32 < 2){
len = snprintf(buf, sizeof(buf), "%d",_v); *gpio1.oe = *gpio1.oe | (1<<(pin-32));
::write(fd, buf, len); }
close(fd); else if(pin/32 < 3){
*gpio2.oe = *gpio2.oe | (1<<(pin-64));
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction",_v); //retry writing direction }
fd = ::open(buf, O_WRONLY); else if(pin/32 < 4){
if (fd < 0) { *gpio3.oe = *gpio3.oe | (1<<(pin-96));
perror("LinuxGPIO::direction"); //report faillure }
}
else{
if(pin/32 < 1){
*gpio0.oe = *gpio0.oe & (~(1<<pin));
}
else if(pin/32 < 2){
*gpio1.oe = *gpio1.oe & (~(1<<(pin-32)));
}
else if(pin/32 < 3){
*gpio2.oe = *gpio2.oe & (~(1<<(pin-64)));
}
else if(pin/32 < 4){
*gpio3.oe = *gpio3.oe & (~(1<<(pin-96)));
} }
} }
if (output == GPIO_OUTPUT)
::write(fd, "out", 4);
else
::write(fd, "in", 3);
close(fd);
} }
uint8_t LinuxDigitalSource::read() { uint8_t LinuxDigitalSource::read() {
int fd; uint8_t value, pin = _v;
char buf[64];
char ch;
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value",_v); if(pin/32 < 1){
value = (bool)(*gpio0.in & (1<<pin));
fd = open(buf, O_RDONLY); }
if (fd < 0) { else if(pin/32 < 2){
perror("LinuxGPIO::read"); value = (bool)(*gpio1.in & (1<<(pin-32)));
}
else if(pin/32 < 3){
value = (bool)(*gpio2.in & (1<<(pin-64)));
}
else if(pin/32 < 4){
value = (bool)(*gpio3.in & (1<<(pin-96)));
} }
::read(fd, &ch, 1); return value;
close(fd);
if (ch == '0') {
return 0;
}
else if (ch == '1'){
return 1;
}
return -1;
} }
void LinuxDigitalSource::write(uint8_t value) { void LinuxDigitalSource::write(uint8_t value) {
int fd;
char buf[64];
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value",_v); uint8_t pin = _v;
fd = open(buf, O_WRONLY); if(value == HIGH){
if (fd < 0) { if(pin/32 < 1){
perror("LinuxGPIO::write"); *gpio0.out = *gpio0.out | (1<<pin);
}
else if(pin/32 < 2){
*gpio1.out = *gpio1.out | (1<<(pin-32));
}
else if(pin/32 < 3){
*gpio2.out = *gpio2.out | (1<<(pin-64));
}
else if(pin/32 < 4){
*gpio3.out = *gpio3.out | (1<<(pin-96));
}
}
else{
if(pin/32 < 1){
*gpio0.out = *gpio0.out & (~(1<<pin));
}
else if(pin/32 < 2){
*gpio1.out = *gpio1.out & (~(1<<(pin-32)));
}
else if(pin/32 < 3){
*gpio2.out = *gpio2.out & (~(1<<(pin-64)));
}
else if(pin/32 < 4){
*gpio3.out = *gpio3.out & (~(1<<(pin-96)));
}
} }
if (value==LOW)
::write(fd, "0", 2);
else
::write(fd, "1", 2);
close(fd);
} }
void LinuxDigitalSource::toggle() { void LinuxDigitalSource::toggle() {

View File

@ -5,6 +5,19 @@
#include <AP_HAL_Linux.h> #include <AP_HAL_Linux.h>
#define SYSFS_GPIO_DIR "/sys/class/gpio" #define SYSFS_GPIO_DIR "/sys/class/gpio"
#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 LED_AMBER 117 #define LED_AMBER 117
#define LED_BLUE 48 #define LED_BLUE 48
#define LED_SAFETY 61 #define LED_SAFETY 61