I'm trying to set the Pin G11 on a NanoPi Neo, previously configured as input, to output in C++ by mapping the PIO function register into the virtual memory with mmap
and setting a single bit.
Per Allwinner H3 Datasheet the following hardware addresses are specified:
PIO Base Address: 0x01C20800 (p. 316)
PG Configure Register 1 offset: 0xDC (p. 338)
PG11 bits: 14:12
Similar to the Raspberry Pi's function register, the Pin knows at least two states:
000: input
001: output
In my Script, I'm therefore trying to set the 12th bit of the PG Configure Register 1. Here's my C++ code:
struct peripheral {
unsigned long addr_hardware;
int map_size;
int mem_fd;
void *mem_map;
volatile unsigned long *addr_virtual;
int map() {
if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) return -1;
// map addr_hardware=0x01C20000 into /dev/mem
if ((mem_map = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, addr_hardware)) == MAP_FAILED) return -1;
// store virtual address with offset of 0x800, which was rounded down before
addr_virtual = (volatile unsigned long *) mem_map + 0x800;
return 0;
};
void unmap() {
munmap(mem_map, map_size);
close(mem_fd);
};
};
int main() {
// initialize peripheral at rounded down PIO base address
peripheral gpio {0x01C20000, 4096 * 10};
// map PIOs into virtual memory
if (gpio.map() == -1) return -1;
// output current value of PG Configure Register 1
cout << bitset<32>(*(gpio.addr_virtual + 0xDC)) << endl;
// set 12th bit
*(gpio.addr_virtual + 0xDC) |= (1 << 12);
// output value of register after setting the 12th bit
cout << bitset<32>(*(gpio.addr_virtual + 0xDC)) << endl;
gpio.unmap();
return 0;
}
Unfortunately, my code does not work. When I run the script again, the content of the PIO Configure Register has changed to its previous state. When I configure the Pin G11 as Output (via WiringNP), the displayed value doesn't change. I'm checking the proper functionality of my code with an LED (it should turn on when state is changed from IN to OUT because it is set to HIGH).
When I alter the map_size
, I get the following results (spaces added for better readability):
> gpio {0x01C20000, 4096 * 2}
< 00000000 00000000 00000000 00000000
< 00000000 00000000 00010000 00000000
> gpio {0x01C20000, 4096 * 10}
< 00000000 00000000 00000000 00110011
< 00000000 00000000 00000000 00110011
I am expecting the following result:
> gpio {0x01C20000, 4096 * 10}
< 00000000 00000000 00000000 00110011
< 00000000 00000000 00010000 00110011
To solve my problem, I had to change the virtual address used to access the PIO hardware in the following way:
addr_virtual = (volatile unsigned long *) mem_map + (0x800 >> 2);
which equals the shortened statement:
addr_virtual = (volatile unsigned long *) mem_map + 0x200;
but is more readable, as the PIO hardware address is 0x01C20000 + 0x800
. I also went with a map size of 4096 * 10
bytes.
I am using these macros for I/O-operations:
#define GPIO_IN(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + ((((g) - ((g) & ~31)) / 8) * 4)) >> 2)) &= ~(7 << ((((g) - (((g) / 32) * 32)) - ((((g) - (((g) / 32) * 32)) / 8) * 8)) * 4))
#define GPIO_OUT(g) GPIO_IN(g); *(gpio.addr_virtual + (((((g) / 32) * 36) + ((((g) - ((g) & ~31)) / 8) * 4)) >> 2)) |= 1 << ((((g) - (((g) / 32) * 32)) - ((((g) - (((g) / 32) * 32)) / 8) * 8)) * 4)
#define GPIO_SET(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + 0x10) >> 2)) |= 1 << ((g) - (((g) / 32) * 32))
#define GPIO_CLR(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + 0x10) >> 2)) &= ~(1 << ((g) - (((g) / 32) * 32)))