I’m attempting to port a simple LED matrix driver from Arduino to a Rock Pi S HW revision 1.3. I first used the available device files under /sys/class/gpio and that worked but the speed was much too slow. The fastest I could get with this was around 5fps which is not enough to remove flicker. But I know it is at least possible, because I proved that it did work (but slowly).
I am trying to instead directly control the GPIO through mmap registers and reading the application note for the RK3308. I’ve also studied what I could from the pinctrl and gpio drivers in the linux kernel. I am having no luck, maybe I am missing something simple? I have done a fair amount of embedded programming but never directly under linux so perhaps I am doing something stupid?
If I run the following under root, I can see Pin #3 toggling state on an Oscilloscope:
root@rockpis:/sys/class/gpio# echo 11 > export
root@rockpis:/sys/class/gpio# cd gpio11
root@rockpis:/sys/class/gpio/gpio11# echo out > direction
root@rockpis:/sys/class/gpio/gpio11# echo 1 > value
root@rockpis:/sys/class/gpio/gpio11# echo 0 > value
If I attempt to compile and run this test program as root, I see no effect. As if the registers weren’t mapped properly? I noticed that wiringX does not have a port to Rock Pi S or I would simply use that.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define INPUT 1
#define OUTPUT 2
#define HIGH 1
#define LOW 0
void *GRU_base = NULL;
void *GPIO0_base = NULL;
void *GPIO1_base = NULL;
void *GPIO2_base = NULL;
void *GPIO3_base = NULL;
void *GPIO4_base = NULL;
static inline unsigned int raw_read(const volatile void *addr) {
return *(const volatile unsigned int *) addr;
}
static inline void raw_write(unsigned int data, volatile void *addr) {
*(volatile unsigned int *) addr = data;
}
#define GPIO_SWPORT_DR 0x00
#define GPIO_SWPORT_DDR 0x04
void *getControlReg(int base, int pin) {
switch (base) {
case 0:
return GPIO0_base;
case 1:
return GPIO1_base;
case 2:
return GPIO2_base;
case 3:
return GPIO3_base;
case 4:
return GPIO4_base;
default:
return 0;
}
}
unsigned int getGRUMuxOffset(int base, int pin) {
switch (base) {
case 0: {
switch(pin / 8) {
case 0:
return 0x0;
case 1:
return 0x8;
case 2:
return 0x10;
}
break;
}
}
// Unmapped for now.
return 0;
}
unsigned int getGRUMuxMask(int base, int pin) {
switch (base) {
case 0: {
switch (pin % 8) {
case 0:
return 0x00030000;
case 1:
return 0x000C0000;
case 2:
return 0x00300000;
case 3:
return 0x00C00000;
case 4:
return 0x03000000;
case 5:
return 0x0C000000;
case 6:
return 0x30000000;
case 7:
return 0xC0000000;
}
break;
}
}
// Unmapped for now.
return 0;
}
unsigned int getGRUEnableOffset(int base, int pin) {
switch (base) {
case 0: {
switch(pin / 8) {
case 0:
return 0xa0;
case 1:
return 0xa4;
case 2:
return 0xa8;
}
break;
}
}
// Unmapped for now.
return 0;
}
unsigned int getGRUEnableMask(int base, int pin) {
switch (base) {
case 0: {
switch (pin % 8) {
case 0:
return 0x00030000;
case 1:
return 0x000C0000;
case 2:
return 0x00300000;
case 3:
return 0x00C00000;
case 4:
return 0x03000000;
case 5:
return 0x0C000000;
case 6:
return 0x30000000;
case 7:
return 0xC0000000;
}
break;
}
}
// Unmapped for now.
return 0;
}
int pinMode(unsigned int gpio, unsigned int direction) {
unsigned int base = gpio / 32;
unsigned int pin = gpio % 32;
unsigned int offset = 1UL << pin;
// Mux settings for raw GPIO.
unsigned int gru_off = getGRUMuxOffset(base, pin);
unsigned int gru_mask = getGRUMuxMask(base, pin);
raw_write(gru_mask, GRU_base + gru_off);
// Enable settinsg to make sure its not a weak pull-up/pull-down.
gru_off = getGRUEnableOffset(base, pin);
gru_mask = getGRUEnableMask(base, pin);
raw_write(gru_mask, GRU_base + gru_off);
void *reg = getControlReg(base, pin);
if (reg == 0) {
return -1;
}
unsigned int val = raw_read(reg + GPIO_SWPORT_DDR);
if (direction == INPUT) {
val &= ~offset;
} else {
val |= offset;
}
printf("pinMode: %08X\n", val);
raw_write(val, reg + GPIO_SWPORT_DDR);
return 0;
}
int digitalWrite(int gpio, int value) {
unsigned int base = gpio / 32;
unsigned int pin = gpio % 32;
unsigned int offset = 1UL << pin;
void *reg = getControlReg(base, pin);
if (reg == 0) {
return -1;
}
unsigned int val = raw_read(reg + GPIO_SWPORT_DR);
if (value == 0) {
val &= ~offset;
} else {
val |= offset;
}
printf("digitalWrite: %08X\n", val);
raw_write(val, reg + GPIO_SWPORT_DR);
return 0;
}
int mapReg(void *reg, void **reg_mapped) {
int fd;
unsigned int addr_start, addr_offset;
unsigned int pagesize, pagemask;
void *pc;
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
return -1;
}
pagesize = sysconf(_SC_PAGESIZE);
pagemask = (~(pagesize - 1));
addr_start = (unsigned int)reg & pagemask;
addr_offset = (unsigned int)reg & ~pagemask;
pc = (void *)mmap(0, pagesize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_start);
if(pc == MAP_FAILED) {
return -2;
}
close(fd);
*reg_mapped = (pc + addr_offset);
return 0;
}
int gpioInit() {
// Map GPIO0-4 for bit twiddling.
if (mapReg((void *)0xff220000, &GPIO0_base) != 0) {
return -1;
}
if (mapReg((void *)0xff230000, &GPIO1_base) != 0) {
return -1;
}
if (mapReg((void *)0xff240000, &GPIO2_base) != 0) {
return -1;
}
if (mapReg((void *)0xff250000, &GPIO3_base) != 0) {
return -1;
}
if (mapReg((void *)0xff260000, &GPIO4_base) != 0) {
return -1;
}
// Map GRU for MUX settings.
if (mapReg((void *)0xff000000, &GRU_base) != 0) {
return -2;
}
return 0;
}
void main() {
printf("Initializing GPIO...\n");
if (gpioInit() != 0) {
printf("Failed to init GPIO!\n");
return;
}
pinMode(11, OUTPUT);
printf("Toggling pin...\n");
while( 1 ) {
printf("On\n");
digitalWrite(11, HIGH);
sleep(1);
printf("Off\n");
digitalWrite(11, LOW);
sleep(1);
}
}