Vimsent
Vimsent

Reputation: 11

How to directly access BCM2835 registers in C programming for GPIO control on a Raspberry Pi running FreeBSD 14.2

I am trying to control GPIO pins of a Raspberry Pi 3/4 running FreeBSD, using direct access to the BCM2835 registers. My code is based on Mike McCauley's C library.

It works fine on Linux (Raspberry Pi OS, hence Debian bookworm), but I fail to get the same code working on FreeBSD (latest FreeBSD 14.2 image for RPI (3/4) armv7). My C code builds fine on both OS'es, but on FreeBSD it the executable runs but does not actually control the GPIO pins.

Here is a simple piece of code to show what I'm doing. For simplicity with hardcoded addresses for the Raspberry Pi 3:

/*
Program to test direct access to bcm2835 registers to blink a LED
Build as follows: gcc -Wall -o gpio gpio.c
Run as root
*/

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

// GPIO pin to which a LED is connected:
#define GPIO_PIN (23)

int main() {   
    int  mem_fd;
    uint32_t *periph_base;;
    uint32_t *periph_gpio;
    uint32_t *gpio_function_register;
    uint32_t gpio_function_register_pin_mask;
    uint32_t function_register_value;
    volatile uint32_t *gpio_set_lower_register;
    volatile uint32_t *gpio_clear_lower_register;
    uint32_t gpio_pin_mask;


    mem_fd = open("/dev/mem", O_RDWR|O_SYNC);
    if (!mem_fd) return 1;

    periph_base = (uint32_t *)mmap(
        NULL,                   // Any adddress in our space will do
        0x01000000,             // Map length on the Raspberry Pi 3
        PROT_READ|PROT_WRITE,   // Enable reading & writting to mapped memory
        MAP_SHARED,             // Shared with other processes
        mem_fd,                 // File to map
        0x3F000000              // Offset to BCM peripherals on the Raspberry Pi 3
    );

    close(mem_fd);              // No need to keep mem_fd open after mmap

    if (periph_base == (void *)-1) return 2;

    /********* calculate addresses to all registers we need *********/

    // calculate address to gpio registers
    periph_gpio = periph_base + 0x200000/4;

    // address of pin function select for pin GPIO_PIN
    gpio_function_register = periph_gpio + GPIO_PIN/10;
    // bit mask for the 3 bits within the function select register for pin GPIO_PIN
    gpio_function_register_pin_mask = (7 << ((GPIO_PIN%10)*3));

    // address of gpio set register for the first 32 pins
    gpio_set_lower_register = periph_gpio + 7;
    // address of gpio clear register for the first 32 pins
    gpio_clear_lower_register = periph_gpio + 10;
    // bit position in register for pin GPIO_PIN
    gpio_pin_mask = (uint32_t)1 << GPIO_PIN;

    /********* set the function of pin GPIO_PIN to "output" *********/

    // get current value of function register
    function_register_value = *gpio_function_register;

    // reset function select bits for this pin (this selects the "input" function)
    function_register_value &= ~gpio_function_register_pin_mask;    
    // set function select bits for this pin to function "1" ("output")
    function_register_value |= (((uint32_t)1) << ((GPIO_PIN%10)*3));
    // update function register
    *gpio_function_register = function_register_value;

    /********* set and clear the gpio pin to make the LED blink *********/
    for (;;) {
        *gpio_set_lower_register = gpio_pin_mask;
        sleep(1);
        *gpio_clear_lower_register = gpio_pin_mask;
        sleep(1);
    }

    return 0;
}

How do I get this working on FreeBSD?

Thanks a lot for your help!

Upvotes: 1

Views: 63

Answers (0)

Related Questions