GPIO programming


7 posts
by doetoe » Wed Jun 20, 2012 9:54 pm
I tried hard to understand the code examples of driving the gpio pins at http://elinux.org/RPi_Low-level_peripherals#Code_examples. I must admit that I only have a vague idea of what (e.g.) a driver is. I would really appreciate it if someone could clarify and correct some things:

I think I understood the shell example: essentially there is a driver that makes the gpio pins accessible through a pseudo file system /sys/class/gpio/. Moreover this is a standard linux driver, not specific to Raspberry Pi.

The difficult part is the C example. Combining information from the datasheet, this code, Gordon Henderson's wiringPi code and some man pages, I ended up with some superficial understanding.
The SoC contains several control and access registers to the peripherals, that can be accessed as if they were in physical memory starting at address 0x20000000 (BCM2708_PERI_BASE), virtual memory address 0xF2000000, or bus address 0x7E000000. Starting from this base address, the GPIO registers start at location 0x2000000. The datasheet lists bus addresses, but the code example uses physical memory addresses. Physical memory is accessed through the file /dev/mem, but this is accessed through an mmap-ed piece of heap memory.
Why is that? Is that the only way, or is it for performance or convenience? Also it looks like what is mapped is a chunk of size BLOCK_SIZE that starts at an address that is a multiple of PAGE_SIZE. Where do the values of BLOCK_SIZE and PAGE_SIZE come from? Why is a chunk of size BLOCK_SIZE mapped? It seems to be much more than needed. Why does the pointer have to be a multiple of PAGE_SIZE? For performance?
Many thanks in advance!
Posts: 7
Joined: Wed Nov 02, 2011 1:09 pm
by Gert van Loo » Thu Jun 21, 2012 7:07 am
In the old days there was just one big flat memory and if you want to do a peek or poke to an arbitrarily address: go ahead! In a modern OS you can not do that anymore. And it is a good thing otherwise any user/task could peek and poke in to other user/task memory.
The MMAP is mostly to do with the MMU. I can't fully explain an MMU here as that is a very long story and I strongly suggest you read up on that. The MMU handles the memory and it does so in chunks of 4K. (That 4K is a pseudo standard these days and that is where your BLOCK_SIZE and PAGE_SIZE come from).The MMU makes that although you have many tasks running in parallel, the memory looks the same to each tasks. It also make sure that the different task can not trample on each others memory. The latter makes that you can no longer write to an arbitrarily address.

Then with mmap you tell the MMU that you want to access a specific piece of the memory and it HAS to be at that specific address. After that you have access to the peripheral and just like in the good 'ol days you can start peeking and poking.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 2081
Joined: Tue Aug 02, 2011 7:27 am
by doetoe » Thu Jun 21, 2012 9:56 am
Thanks a lot Gert, this is very helpful
Posts: 7
Joined: Wed Nov 02, 2011 1:09 pm
by dwitt » Thu Sep 06, 2012 2:44 pm
Why in some examples is malloc used before mmap to allocate memory whose pointer is then used as a hint for mmap and in other example malloc is not used. I saw a statement that malloc was not required and from my reading it seems redundant but Gert uses this in his examples. Can someone explain why or point me to where I can find the solution.

Thanks
Posts: 2
Joined: Fri Jan 06, 2012 2:04 am
by pharos » Sat Sep 08, 2012 12:57 pm
Yep, I also removed the malloc code and it works just the same.

Code: Select all
   if ((gpio = (volatile unsigned int *) mmap((caddr_t) 0x13370000, 4096, PROT_READ|PROT_WRITE,
                  MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE)) == MAP_FAILED) {
      perror("mmap GPIO_BASE");
      return -1;
   }

   OUT_GPIO(N_WRITE_PROTECT);
   GPIO_SET_1(N_WRITE_PROTECT);
Posts: 5
Joined: Thu Jul 12, 2012 10:05 pm
by pharos » Sat Sep 08, 2012 6:23 pm
Actually, the snippet of code I posted, altough it works, could not work because there is no guarantee something is not already mapped ay 0x13370000 (like a library for instance). The original method of mallocing two pages of memory ensures that there is a portion of memory aligned to one page that will not conflict with anything else, so this method feels more correct.
Posts: 5
Joined: Thu Jul 12, 2012 10:05 pm
by dwitt » Mon Sep 10, 2012 11:38 am
As I read the man page for mmap it appears that passing a NULL for the addr allows the kernel to choose the location for the mapping and mmap should also make sure it is block aligned. I think malloc was only used help control fragmentation of memory and force the mapping to the heap but I'm not sure. Can anyone confirm.

Thanks,
Dave
Posts: 2
Joined: Fri Jan 06, 2012 2:04 am