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/PwmOut.cpp

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

File content as of revision 2:131555dc6fb7:

#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);
}