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.
Revision:
1:1f2d9982fa8c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/Peripheral.cpp	Tue Dec 20 12:08:07 2022 +0000
@@ -0,0 +1,322 @@
+//#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();
+
+