/dev/mem access

PB thru PH series gpios control begins at memory address 0x01C20800

PL series gpios control begins at memory address 0x01F02C00

These are the equivalents of the PERI_BASE address seen with Raspberry Pis, and there is a porting effort of the Pi-Gpio library, where the programming side is mapping to the PI-2-Bus connectors as similar as possible to what is happening on the Raspberry Pi.

The following example program shows how to flash a LED connected to port PC4 (pin P32)


#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

#define GPIO_BASE_ADDR_PB 0x01c20800
#define GPIO_BASE_ADDR_PL 0x01f02c00

int main()
{
	int fd;
	void *p_mem;
	volatile uint32_t *gpio;    /* The registers in gpio-space are 32-bits wide. */
	int j;

	uint64_t addr_offset; 
	uint64_t addr_start;      /* page-aligned start-address of mmapped space. */
	uint64_t page_size;       /* Page-size from system, it reports 0x1000 here. */
	uint64_t page_mask;       /* Mask to separate out page-alignment bits */
	uint64_t port_io_base;    /* input GPIOs base-address, doesn't have to be page-aligned */
	uint64_t offset; 

	port_io_base = GPIO_BASE_ADDR_PB;          /* For example for ports PB-PH */

	page_size = sysconf(_SC_PAGESIZE);         /* 0x1000 */
	page_mask = (~(page_size-1));              /* 0xFFFFFFFFFFFFF000 */
	addr_start = port_io_base & page_mask;     /* gives the page-aligned start address 0x01c20000, last 24 bits are 0 */
	addr_offset = port_io_base & ~page_mask;   /* mask with 0FFF so we get the 0800 offset into the page */

	fd = open("/dev/mem", O_RDWR | O_SYNC);
	if(fd == -1)
	{
	   /* Very likely if you forget to run this as root ... */
	   perror("/dev/mem");
	   return(0);
	}

	p_mem = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_start);
	if(p_mem == NULL)
	{
	   perror("mmap");
	   close(fd);
	   return(0);
	}

	close(fd);

	offset = addr_offset + 0x48;       /* Will aim at the first of the PC control registers, which controls PC7-PC0 */
									   /* Page 377 in the manual */

	gpio = (volatile uint32_t *) ( (char *) (p_mem) + offset);
	printf("GPIOs at %p\n", gpio);

	*(gpio) &= ~(7 << 16);  /* Set SELECT to 000 for enable or input */
	*(gpio) |= (1 << 16);   /* Set SELECT to 001 for output */

	offset = addr_offset + 0x58;  /* Will aim at the the PC data register, which controls state of PC18-PC0 wires */
									   /* Page 380 in the manual. */
	gpio = (volatile uint32_t *) ( (char *) (p_mem) + offset);
	printf("GPIOs at %p\n", gpio);

	/* flash on and off 20 times */
	for(j = 0; j<20; j++)
	{
	   *(gpio) &= ~(0x10);  /* Set bit 4 for PC4 to 0 */
	   usleep(500000);
	   *(gpio) |= (0x10);   /* Set bit 4 for PC4 to 1 */
	   usleep(500000);
	}

	munmap(p_mem, page_size*2);

	return(0);
}


The GPIO Control registers each consist of 8 3-bit values for each of the gpio lines, which gives a function-number for that gpio line. Function number 0 (000) means this is a general-purpose input, function number 1 (001) means it is a general-purpose output and function number 7 (111) means that this line is disabled. Function numbers 2 through 6 refer to various special functions, which will be different for each gpio line. Many of these are marked as reserved. The info on which are which is in the Allwinner_A64_User_Manual_V1.0.pdf sections 3.21 and 3.22 (starting at page 376).

To Pine 64+ main page


Powered by Apache

Valid XHTML 1.0!