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/PwmOut.cpp	Tue Dec 20 12:08:07 2022 +0000
@@ -0,0 +1,210 @@
+#include "mbed.h"
+#include "math.h"
+
+extern volatile uint32_t *bcm2835_pwm;
+extern volatile uint32_t *bcm2835_clk;
+
+/********************************************************************
+ *
+ *  PwmOut
+ *
+ ********************************************************************/
+/** Create a PwmOut connected to the specified pin
+ *
+ *  @param pin PwmOut pin to connect to
+ */
+PwmOut::PwmOut(PinName pin) :
+    _pwmPin(pin),
+    _duty_cycle(0)
+{
+    // Set the output pin to Alt Fun 5, to allow PWM channel 0 to be output there
+    bcm2835_gpio_fsel(_pwmPin, BCM2835_GPIO_FSEL_ALT5);
+    // Set default PWM period to 20ms (usually used by servos)
+    period_ms(20);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+PwmOut::~PwmOut()
+{
+    bcm2835_gpio_fsel(_pwmPin, BCM2835_GPIO_FSEL_INPT);
+}
+
+/** Set the output duty-cycle, specified as a percentage (float)
+ *
+ *  @param value A floating-point value representing the output duty-cycle,
+ *    specified as a percentage. The value should lie between
+ *    0.0f (representing on 0%) and 1.0f (representing on 100%).
+ *    Values outside this range will be saturated to 0.0f or 1.0f.
+ */
+void PwmOut::write(float value)
+{
+    _duty_cycle = value;
+
+    if (value < 0) {
+        _duty_cycle = 0;
+    }
+
+    if (value > 1.0) {
+        _duty_cycle = 1.0;
+    }
+
+    bcm2835_pwm_set_data(PWM_CHANNEL, _duty_cycle * _range);
+    bcm2835_pwm_set_mode(PWM_CHANNEL, 1, 1);    // channel, MARKSPACE mode, active
+}
+
+/** Return the current output duty-cycle setting, measured as a percentage (float)
+ *
+ *  @returns
+ *    A floating-point value representing the current duty-cycle being output on the pin,
+ *    measured as a percentage. The returned value will lie between
+ *    0.0f (representing on 0%) and 1.0f (representing on 100%).
+ *
+ *  @note
+ *  This value may not match exactly the value set by a previous write().
+ */
+float PwmOut::read()
+{
+    return _duty_cycle;
+}
+
+/** Set the PWM period, specified in bcm2835PWMPulseWidth (micro/nano seconds), keeping the duty cycle the same.
+ *  @note  Sets clock divider according to the required period.
+ *  @param period Change the period of a PWM signal. The allowed values are:
+ *         BCM2835_PWM_PERIOD_833_NS  ->  833.33 ns  = 1200.000  kHz
+ */
+void PwmOut::period_ms(int period_ms)
+{
+    _range = period_ms * 1200;
+    bcm2835_pwm_set_clock(BCM2835_PWM_PERIOD_833_NS);   // clock pulse = 833.33 ns
+    bcm2835_pwm_set_range(PWM_CHANNEL, _range);
+}
+
+/** Set the PWM period, specified in bcm2835PWMPulseWidth (micro/nano seconds), keeping the duty cycle the same.
+ *  @note  Sets clock divider according to the required period.
+ *  @param period Change the period of a PWM signal. The allowed values are:
+ *         BCM2835_PWM_PERIOD_104_NS  -> 104.16 ns  = 9600.000  kHz
+ */
+void PwmOut::period_us(int period_us)
+{
+    _range = rintf(period_us * 9.600f);
+    bcm2835_pwm_set_clock(BCM2835_PWM_PERIOD_104_NS);
+    bcm2835_pwm_set_range(PWM_CHANNEL, _range);
+}
+
+/***********************************************************************
+ *
+ *  PWM
+ *
+ ***********************************************************************/
+
+/*! Sets the PWM clock divisor,
+  to control the basic PWM pulse widths.
+  \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common
+  values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider
+*/
+void bcm2835_pwm_set_clock(uint32_t divisor)
+{
+    if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) {
+        return;         /* bcm2835_init() failed or not root */
+    }
+
+    /* From Gerts code */
+    divisor &= 0xfff;
+
+    /* Stop PWM clock */
+    bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01);
+    bcm2835_delay(110); /* Prevents clock going slow */
+
+    /* Wait for the clock to be not busy */
+    while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0)
+        bcm2835_delay(1);
+
+    /* set the clock divider and enable PWM clock */
+    bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12));
+    bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11);  /* Source=osc and enable */
+}
+
+/*! Sets the mode of the given PWM channel,
+  allowing you to control the PWM mode and enable/disable that channel
+  \param[in] channel The PWM channel. 0 or 1.
+  \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode.
+  \param[in] enabled Set true to enable this channel and produce PWM pulses.
+*/
+void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled)
+{
+    if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) {
+        return; /* bcm2835_init() failed or not root */
+    }
+
+    /* If you use the barrier here, wierd things happen, and the commands dont work */
+
+    /*
+    uint32_t    control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL);
+
+    if (channel == 0) {
+        if (markspace)
+            control |= BCM2835_PWM0_MS_MODE;
+        else
+            control &= ~BCM2835_PWM0_MS_MODE;
+        if (enabled)
+            control |= BCM2835_PWM0_ENABLE;
+        else
+            control &= ~BCM2835_PWM0_ENABLE;
+    }
+    else
+    if (channel == 1) {
+        if (markspace)
+            control |= BCM2835_PWM1_MS_MODE;
+        else
+            control &= ~BCM2835_PWM1_MS_MODE;
+        if (enabled)
+            control |= BCM2835_PWM1_ENABLE;
+        else
+            control &= ~BCM2835_PWM1_ENABLE;
+    }
+
+    bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control);
+    */
+
+    bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE);
+}
+
+/*! Sets the maximum range of the PWM output.
+  The data value can vary between 0 and this range to control PWM output
+  \param[in] channel The PWM channel. 0 or 1.
+  \param[in] range The maximum value permitted for DATA.
+*/
+void bcm2835_pwm_set_range(uint8_t channel, uint32_t range)
+{
+    if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED)
+        return; /* bcm2835_init() failed or not root */
+
+    if (channel == 0)
+        bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range);
+    else
+    if (channel == 1)
+        bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range);
+}
+
+/*! Sets the PWM pulse ratio to emit to DATA/RANGE,
+  where RANGE is set by bcm2835_pwm_set_range().
+  \param[in] channel The PWM channel. 0 or 1.
+  \param[in] data Controls the PWM output ratio as a fraction of the range.
+  Can vary from 0 to RANGE.
+*/
+void bcm2835_pwm_set_data(uint8_t channel, uint32_t data)
+{
+    if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED)
+        return; /* bcm2835_init() failed or not root */
+
+    if (channel == 0)
+        bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data);
+    else
+    if (channel == 1)
+        bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data);
+}