mbed API for Raspberry Pi boards.

mbedPi

This is an attempt to implement a limited number of mbed APIs for Raspberry Pi single-board computers. The project was inspired by and based on the arduPi library developed for the Arduino by Cooking Hacks .

/media/uploads/hudakz/board01.jpg

Specifications

  • Chip: Broadcom BCM2836 SoC
  • Core architecture: Quad-core ARM Cortex-A7
  • CPU frequency: 900 MHz
  • GPU: Dual Core VideoCore IV® Multimedia Co-Processor
  • Memory: 1GB LPDDR2
  • Operating System: Boots from Micro SD card, running a version of the Linux operating system
  • Power: Micro USB socket 5V, 2A

Connectors

  • Ethernet: 10/100 BaseT Ethernet socket
  • Video Output: HDMI (rev 1.3 & 1.4)
  • Audio Output: 3.5mm jack, HDMI
  • USB: 4 x USB 2.0 Connector
  • GPIO Connector: 40-pin 2.54 mm (100 mil) expansion header: 2x20 strip providing 27 GPIO pins as well as +3.3 V, +5 V and GND supply lines
  • Camera Connector: 15-pin MIPI Camera Serial Interface (CSI-2)
  • JTAG: Not populated
  • Display Connector: Display Serial Interface (DSI) 15 way flat flex cable connector with two data lanes and a clock lane
  • Memory Card Slot: Micro SDIO

GPIO connector pinout

Zoom in /media/uploads/hudakz/mbedpi_pinout02.png

Information

Only the labels printed in blue/white or green/white (i.e. p3, gpio2 ...) must be used in your code. The other labels are given as information (alternate-functions, power pins, ...).


Building programs for the Raspberry Pi with mbedPi

I use Qt Creator for development, however you can use any other IDE available on the Raspberry Pi (e.g. Geany) if you like. For a quick try:

  • Install Qt and the Qt Creator onto your Raspberry Pi. Then create a new "Blinky" Plain non-Qt C++ Project as follows: /media/uploads/hudakz/newproject.png

  • Change the main code as below:

main.cpp

#include "mbedPi.h"

int main()
{
    DigitalOut  myled(p7);

    while(1) {
        myled = 1; // LED is ON
        wait(0.2); // 200 ms
        myled = 0; // LED is OFF
        wait(1.0); // 1 sec
        printf("Blink\r\n");
    }
}


  • Copy the mbedPi.zip file into your project's folder and unzip.
  • Add the mbedPi.h and mbedPi.cpp files to your project by right clicking on the "Blinky" project and then clicking on the "Add Existing Files..." option in the local menu:

    /media/uploads/hudakz/addfiles.png

    /media/uploads/hudakz/addfiles02.png

  • Double click on Blinky.pro to open it for editing and add new libraries by inserting a new line as follows:

    /media/uploads/hudakz/libs.png

  • Compile the project.

  • Connect an LED through a 1k resistor to pin 7 and the ground on the Raspberry Pi GPIO connector.

  • Run the binary as sudo (sudo ./Blinky) and you should see the LED blinking. /media/uploads/hudakz/mbedpi_run.png

  • Press Ctrl+c to stop running the application.

source/Peripheral.cpp

Committer:
hudakz
Date:
18 months ago
Revision:
2:131555dc6fb7
Parent:
1:1f2d9982fa8c

File content as of revision 2:131555dc6fb7:

//#include "mbed.h"
#include "BCM2835.h"
#include "Thread.h"
#include "ThisThread.h"
#include "Peripheral.h"

struct bcm2835_peripheral   bsc_rev1 = { BCM2835_PERI_BASE + 0X205000, 0, 0, 0 };
struct bcm2835_peripheral   bsc_rev2 = { BCM2835_PERI_BASE + 0X804000, 0, 0, 0 };
struct bcm2835_peripheral   bsc0;
struct bcm2835_peripheral   gpio = { BCM2835_PERI_BASE + 0x200000, 0, 0, 0 };

timeval  start_program, end_point;

off_t                       bcm2835_peripherals_base = BCM2835_PERI_BASE;
size_t                      bcm2835_peripherals_size = BCM2835_PERI_SIZE;
volatile uint32_t*          bcm2835_peripherals;
volatile uint32_t*          bcm2835_pwm  = (uint32_t *)MAP_FAILED;
volatile uint32_t*          bcm2835_clk  = (uint32_t *)MAP_FAILED;
volatile uint32_t*          bcm2835_bsc1 = (uint32_t *)MAP_FAILED;
volatile uint32_t*          bcm2835_spi0 = (uint32_t *)MAP_FAILED;
volatile uint32_t*          bcm2835_st	= (uint32_t *)MAP_FAILED;


//Constructor
Peripheral::Peripheral()
{
    REV = getBoardRev();
    if (map_peripheral(&gpio) == -1) {
        printf("Failed to map the physical GPIO registers into the virtual memory space.\n");
    }

    memfd = -1;

    // Open the master /dev/memory device
    if ((memfd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", strerror(errno));
        exit(1);
    }

    bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, bcm2835_peripherals_base);
    if (bcm2835_peripherals == MAP_FAILED)
        exit(1);

    /* Now compute the base addresses of various peripherals,
    // which are at fixed offsets within the mapped peripherals block
    // Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4
    */
    bcm2835_pwm  = bcm2835_peripherals + BCM2835_GPIO_PWM/4;
    bcm2835_clk  = bcm2835_peripherals + BCM2835_CLOCK_BASE/4;
    bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4;
    bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */
    bcm2835_st   = bcm2835_peripherals + BCM2835_ST_BASE/4;

    // start timer
    gettimeofday(&start_program, NULL);
}

//Destructor
Peripheral::~Peripheral()
{
    unmap_peripheral(&gpio);

    bcm2835_pwm  = (uint32_t *)MAP_FAILED;
    bcm2835_clk  = (uint32_t *)MAP_FAILED;
    bcm2835_spi0 = (uint32_t *)MAP_FAILED;
    bcm2835_bsc1 = (uint32_t *)MAP_FAILED;
}

/*******************
 * Private methods *
 *******************/

// Exposes the physical address defined in the passed structure using mmap on /dev/mem
int Peripheral::map_peripheral(struct bcm2835_peripheral* p)
{
    // Open /dev/mem

    if ((p->mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        printf("Failed to open /dev/mem, try checking permissions.\n");
        return -1;
    }

    p->map = mmap
        (
            NULL,
            BLOCK_SIZE,
            PROT_READ | PROT_WRITE,
            MAP_SHARED,
            p->mem_fd,  // File descriptor to physical memory virtual file '/dev/mem'
            p->addr_p   // Address in physical map that we want this memory block to expose
        );

    if (p->map == MAP_FAILED) {
        perror("mmap");
        return -1;
    }

    p->addr = (volatile unsigned int*)p->map;

    return 0;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void Peripheral::unmap_peripheral(struct bcm2835_peripheral* p)
{
    munmap(p->map, BLOCK_SIZE);
    unistd::close(p->mem_fd);
}

//
// Low level convenience functions
//

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint32_t* mapmem(const char* msg, size_t size, int fd, off_t off)
{
    uint32_t*   map = (uint32_t*)mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off);
    if (MAP_FAILED == map)
        fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno));
    return map;
}

// safe read from peripheral
uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
{
    uint32_t    ret = *paddr;
    ret = *paddr;
    return ret;
}

// read from peripheral without the read barrier
uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr)
{
    return *paddr;
}

// safe write to peripheral
void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
{
    *paddr = value;
    *paddr = value;
}

// write to peripheral without the write barrier
void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value)
{
    *paddr = value;
}

// Set/clear only the bits in value covered by the mask
void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask)
{
    uint32_t    v = bcm2835_peri_read(paddr);
    v = (v &~mask) | (value & mask);
    bcm2835_peri_write(paddr, v);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int getBoardRev()
{
    FILE*       cpu_info;
    char        line[120];
    char*       c, finalChar;
    //static int  rev = 0;

    if (REV != 0)
        return REV;

    if ((cpu_info = fopen("/proc/cpuinfo", "r")) == NULL) {
        fprintf(stderr, "Unable to open /proc/cpuinfo. Cannot determine board reivision.\n");
        exit(1);
    }

    while (fgets(line, 120, cpu_info) != NULL) {
        if (strncmp(line, "Revision", 8) == 0)
            break;
    }

    fclose(cpu_info);

    if (line == NULL) {
        fprintf(stderr, "Unable to determine board revision from /proc/cpuinfo.\n");
        exit(1);
    }

    for (c = line; *c; ++c)
        if (isdigit(*c))
            break;

    if (!isdigit(*c)) {
        fprintf(stderr, "Unable to determine board revision from /proc/cpuinfo\n");
        fprintf(stderr, "  (Info not found in: %s\n", line);
        exit(1);
    }

    finalChar = c[strlen(c) - 2];

    if ((finalChar == '2') || (finalChar == '3')) {
        bsc0 = bsc_rev1;
        return 1;
    }
    else {
        bsc0 = bsc_rev2;
        return 2;
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void attachInterrupt(PinName p, void (*f) (), Digivalue m)
{
    int                 GPIOPin = p;
    pthread_t*          threadId = getThreadIdFromPin(p);
    struct ThreadArg*   threadArgs = (ThreadArg*)malloc(sizeof(ThreadArg));
    threadArgs->func = f;
    threadArgs->pin = GPIOPin;

    //Export pin for interrupt
    FILE*   fp = fopen("/sys/class/gpio/export", "w");
    if (fp == NULL) {
        fprintf(stderr, "Unable to export pin %d for interrupt\n", p);
        exit(1);
    }
    else {
        fprintf(fp, "%d", GPIOPin);
    }

    fclose(fp);

    //The system to create the file /sys/class/gpio/gpio<GPIO number>
    //So we wait a bit
    ThisThread::sleep_for_ms(1);

    char*   interruptFile = NULL;
    asprintf(&interruptFile, "/sys/class/gpio/gpio%d/edge", GPIOPin);

    //Set detection condition
    fp = fopen(interruptFile, "w");
    if (fp == NULL) {
        fprintf(stderr, "Unable to set detection type on pin %d\n", p);
        exit(1);
    }
    else {
        switch (m) {
            case RISING:
                fprintf(fp, "rising");
                break;

            case FALLING:
                fprintf(fp, "falling");
                break;

            default:
                fprintf(fp, "both");
                break;
        }
    }

    fclose(fp);

    if (*threadId == 0) {

        //Create a thread passing the pin and function
        pthread_create(threadId, NULL, threadFunction, (void*)threadArgs);
    }
    else {

        //First cancel the existing thread for that pin
        pthread_cancel(*threadId);

        //Create a thread passing the pin, function and mode
        pthread_create(threadId, NULL, threadFunction, (void*)threadArgs);
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void detachInterrupt(PinName p)
{
    int     GPIOPin = p;

    FILE*   fp = fopen("/sys/class/gpio/unexport", "w");
    if (fp == NULL) {
        fprintf(stderr, "Unable to unexport pin %d for interrupt\n", p);
        exit(1);
    }
    else {
        fprintf(fp, "%d", GPIOPin);
    }

    fclose(fp);

    pthread_t*  threadId = getThreadIdFromPin(p);
    pthread_cancel(*threadId);
}

Peripheral  peripheral = Peripheral();