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 .
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
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:
- 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:
- Double click on Blinky.pro to open it for editing and add new libraries by inserting a new line as follows:
- 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.
- Press Ctrl+c to stop running the application.
source/PwmOut.cpp@2:131555dc6fb7, 21 months ago (annotated)
- Committer:
- hudakz
- Date:
- Tue Dec 20 12:16:18 2022 +0000
- Revision:
- 2:131555dc6fb7
- Parent:
- 1:1f2d9982fa8c
Mbed API for Raspberry Pi boards equipped with BCM2836 SoC.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
hudakz | 1:1f2d9982fa8c | 1 | #include "mbed.h" |
hudakz | 1:1f2d9982fa8c | 2 | #include "math.h" |
hudakz | 1:1f2d9982fa8c | 3 | |
hudakz | 1:1f2d9982fa8c | 4 | extern volatile uint32_t *bcm2835_pwm; |
hudakz | 1:1f2d9982fa8c | 5 | extern volatile uint32_t *bcm2835_clk; |
hudakz | 1:1f2d9982fa8c | 6 | |
hudakz | 1:1f2d9982fa8c | 7 | /******************************************************************** |
hudakz | 1:1f2d9982fa8c | 8 | * |
hudakz | 1:1f2d9982fa8c | 9 | * PwmOut |
hudakz | 1:1f2d9982fa8c | 10 | * |
hudakz | 1:1f2d9982fa8c | 11 | ********************************************************************/ |
hudakz | 1:1f2d9982fa8c | 12 | /** Create a PwmOut connected to the specified pin |
hudakz | 1:1f2d9982fa8c | 13 | * |
hudakz | 1:1f2d9982fa8c | 14 | * @param pin PwmOut pin to connect to |
hudakz | 1:1f2d9982fa8c | 15 | */ |
hudakz | 1:1f2d9982fa8c | 16 | PwmOut::PwmOut(PinName pin) : |
hudakz | 1:1f2d9982fa8c | 17 | _pwmPin(pin), |
hudakz | 1:1f2d9982fa8c | 18 | _duty_cycle(0) |
hudakz | 1:1f2d9982fa8c | 19 | { |
hudakz | 1:1f2d9982fa8c | 20 | // Set the output pin to Alt Fun 5, to allow PWM channel 0 to be output there |
hudakz | 1:1f2d9982fa8c | 21 | bcm2835_gpio_fsel(_pwmPin, BCM2835_GPIO_FSEL_ALT5); |
hudakz | 1:1f2d9982fa8c | 22 | // Set default PWM period to 20ms (usually used by servos) |
hudakz | 1:1f2d9982fa8c | 23 | period_ms(20); |
hudakz | 1:1f2d9982fa8c | 24 | } |
hudakz | 1:1f2d9982fa8c | 25 | |
hudakz | 1:1f2d9982fa8c | 26 | /** |
hudakz | 1:1f2d9982fa8c | 27 | * @brief |
hudakz | 1:1f2d9982fa8c | 28 | * @note |
hudakz | 1:1f2d9982fa8c | 29 | * @param |
hudakz | 1:1f2d9982fa8c | 30 | * @retval |
hudakz | 1:1f2d9982fa8c | 31 | */ |
hudakz | 1:1f2d9982fa8c | 32 | PwmOut::~PwmOut() |
hudakz | 1:1f2d9982fa8c | 33 | { |
hudakz | 1:1f2d9982fa8c | 34 | bcm2835_gpio_fsel(_pwmPin, BCM2835_GPIO_FSEL_INPT); |
hudakz | 1:1f2d9982fa8c | 35 | } |
hudakz | 1:1f2d9982fa8c | 36 | |
hudakz | 1:1f2d9982fa8c | 37 | /** Set the output duty-cycle, specified as a percentage (float) |
hudakz | 1:1f2d9982fa8c | 38 | * |
hudakz | 1:1f2d9982fa8c | 39 | * @param value A floating-point value representing the output duty-cycle, |
hudakz | 1:1f2d9982fa8c | 40 | * specified as a percentage. The value should lie between |
hudakz | 1:1f2d9982fa8c | 41 | * 0.0f (representing on 0%) and 1.0f (representing on 100%). |
hudakz | 1:1f2d9982fa8c | 42 | * Values outside this range will be saturated to 0.0f or 1.0f. |
hudakz | 1:1f2d9982fa8c | 43 | */ |
hudakz | 1:1f2d9982fa8c | 44 | void PwmOut::write(float value) |
hudakz | 1:1f2d9982fa8c | 45 | { |
hudakz | 1:1f2d9982fa8c | 46 | _duty_cycle = value; |
hudakz | 1:1f2d9982fa8c | 47 | |
hudakz | 1:1f2d9982fa8c | 48 | if (value < 0) { |
hudakz | 1:1f2d9982fa8c | 49 | _duty_cycle = 0; |
hudakz | 1:1f2d9982fa8c | 50 | } |
hudakz | 1:1f2d9982fa8c | 51 | |
hudakz | 1:1f2d9982fa8c | 52 | if (value > 1.0) { |
hudakz | 1:1f2d9982fa8c | 53 | _duty_cycle = 1.0; |
hudakz | 1:1f2d9982fa8c | 54 | } |
hudakz | 1:1f2d9982fa8c | 55 | |
hudakz | 1:1f2d9982fa8c | 56 | bcm2835_pwm_set_data(PWM_CHANNEL, _duty_cycle * _range); |
hudakz | 1:1f2d9982fa8c | 57 | bcm2835_pwm_set_mode(PWM_CHANNEL, 1, 1); // channel, MARKSPACE mode, active |
hudakz | 1:1f2d9982fa8c | 58 | } |
hudakz | 1:1f2d9982fa8c | 59 | |
hudakz | 1:1f2d9982fa8c | 60 | /** Return the current output duty-cycle setting, measured as a percentage (float) |
hudakz | 1:1f2d9982fa8c | 61 | * |
hudakz | 1:1f2d9982fa8c | 62 | * @returns |
hudakz | 1:1f2d9982fa8c | 63 | * A floating-point value representing the current duty-cycle being output on the pin, |
hudakz | 1:1f2d9982fa8c | 64 | * measured as a percentage. The returned value will lie between |
hudakz | 1:1f2d9982fa8c | 65 | * 0.0f (representing on 0%) and 1.0f (representing on 100%). |
hudakz | 1:1f2d9982fa8c | 66 | * |
hudakz | 1:1f2d9982fa8c | 67 | * @note |
hudakz | 1:1f2d9982fa8c | 68 | * This value may not match exactly the value set by a previous write(). |
hudakz | 1:1f2d9982fa8c | 69 | */ |
hudakz | 1:1f2d9982fa8c | 70 | float PwmOut::read() |
hudakz | 1:1f2d9982fa8c | 71 | { |
hudakz | 1:1f2d9982fa8c | 72 | return _duty_cycle; |
hudakz | 1:1f2d9982fa8c | 73 | } |
hudakz | 1:1f2d9982fa8c | 74 | |
hudakz | 1:1f2d9982fa8c | 75 | /** Set the PWM period, specified in bcm2835PWMPulseWidth (micro/nano seconds), keeping the duty cycle the same. |
hudakz | 1:1f2d9982fa8c | 76 | * @note Sets clock divider according to the required period. |
hudakz | 1:1f2d9982fa8c | 77 | * @param period Change the period of a PWM signal. The allowed values are: |
hudakz | 1:1f2d9982fa8c | 78 | * BCM2835_PWM_PERIOD_833_NS -> 833.33 ns = 1200.000 kHz |
hudakz | 1:1f2d9982fa8c | 79 | */ |
hudakz | 1:1f2d9982fa8c | 80 | void PwmOut::period_ms(int period_ms) |
hudakz | 1:1f2d9982fa8c | 81 | { |
hudakz | 1:1f2d9982fa8c | 82 | _range = period_ms * 1200; |
hudakz | 1:1f2d9982fa8c | 83 | bcm2835_pwm_set_clock(BCM2835_PWM_PERIOD_833_NS); // clock pulse = 833.33 ns |
hudakz | 1:1f2d9982fa8c | 84 | bcm2835_pwm_set_range(PWM_CHANNEL, _range); |
hudakz | 1:1f2d9982fa8c | 85 | } |
hudakz | 1:1f2d9982fa8c | 86 | |
hudakz | 1:1f2d9982fa8c | 87 | /** Set the PWM period, specified in bcm2835PWMPulseWidth (micro/nano seconds), keeping the duty cycle the same. |
hudakz | 1:1f2d9982fa8c | 88 | * @note Sets clock divider according to the required period. |
hudakz | 1:1f2d9982fa8c | 89 | * @param period Change the period of a PWM signal. The allowed values are: |
hudakz | 1:1f2d9982fa8c | 90 | * BCM2835_PWM_PERIOD_104_NS -> 104.16 ns = 9600.000 kHz |
hudakz | 1:1f2d9982fa8c | 91 | */ |
hudakz | 1:1f2d9982fa8c | 92 | void PwmOut::period_us(int period_us) |
hudakz | 1:1f2d9982fa8c | 93 | { |
hudakz | 1:1f2d9982fa8c | 94 | _range = rintf(period_us * 9.600f); |
hudakz | 1:1f2d9982fa8c | 95 | bcm2835_pwm_set_clock(BCM2835_PWM_PERIOD_104_NS); |
hudakz | 1:1f2d9982fa8c | 96 | bcm2835_pwm_set_range(PWM_CHANNEL, _range); |
hudakz | 1:1f2d9982fa8c | 97 | } |
hudakz | 1:1f2d9982fa8c | 98 | |
hudakz | 1:1f2d9982fa8c | 99 | /*********************************************************************** |
hudakz | 1:1f2d9982fa8c | 100 | * |
hudakz | 1:1f2d9982fa8c | 101 | * PWM |
hudakz | 1:1f2d9982fa8c | 102 | * |
hudakz | 1:1f2d9982fa8c | 103 | ***********************************************************************/ |
hudakz | 1:1f2d9982fa8c | 104 | |
hudakz | 1:1f2d9982fa8c | 105 | /*! Sets the PWM clock divisor, |
hudakz | 1:1f2d9982fa8c | 106 | to control the basic PWM pulse widths. |
hudakz | 1:1f2d9982fa8c | 107 | \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common |
hudakz | 1:1f2d9982fa8c | 108 | values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider |
hudakz | 1:1f2d9982fa8c | 109 | */ |
hudakz | 1:1f2d9982fa8c | 110 | void bcm2835_pwm_set_clock(uint32_t divisor) |
hudakz | 1:1f2d9982fa8c | 111 | { |
hudakz | 1:1f2d9982fa8c | 112 | if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) { |
hudakz | 1:1f2d9982fa8c | 113 | return; /* bcm2835_init() failed or not root */ |
hudakz | 1:1f2d9982fa8c | 114 | } |
hudakz | 1:1f2d9982fa8c | 115 | |
hudakz | 1:1f2d9982fa8c | 116 | /* From Gerts code */ |
hudakz | 1:1f2d9982fa8c | 117 | divisor &= 0xfff; |
hudakz | 1:1f2d9982fa8c | 118 | |
hudakz | 1:1f2d9982fa8c | 119 | /* Stop PWM clock */ |
hudakz | 1:1f2d9982fa8c | 120 | bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); |
hudakz | 1:1f2d9982fa8c | 121 | bcm2835_delay(110); /* Prevents clock going slow */ |
hudakz | 1:1f2d9982fa8c | 122 | |
hudakz | 1:1f2d9982fa8c | 123 | /* Wait for the clock to be not busy */ |
hudakz | 1:1f2d9982fa8c | 124 | while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) |
hudakz | 1:1f2d9982fa8c | 125 | bcm2835_delay(1); |
hudakz | 1:1f2d9982fa8c | 126 | |
hudakz | 1:1f2d9982fa8c | 127 | /* set the clock divider and enable PWM clock */ |
hudakz | 1:1f2d9982fa8c | 128 | bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); |
hudakz | 1:1f2d9982fa8c | 129 | bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); /* Source=osc and enable */ |
hudakz | 1:1f2d9982fa8c | 130 | } |
hudakz | 1:1f2d9982fa8c | 131 | |
hudakz | 1:1f2d9982fa8c | 132 | /*! Sets the mode of the given PWM channel, |
hudakz | 1:1f2d9982fa8c | 133 | allowing you to control the PWM mode and enable/disable that channel |
hudakz | 1:1f2d9982fa8c | 134 | \param[in] channel The PWM channel. 0 or 1. |
hudakz | 1:1f2d9982fa8c | 135 | \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode. |
hudakz | 1:1f2d9982fa8c | 136 | \param[in] enabled Set true to enable this channel and produce PWM pulses. |
hudakz | 1:1f2d9982fa8c | 137 | */ |
hudakz | 1:1f2d9982fa8c | 138 | void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) |
hudakz | 1:1f2d9982fa8c | 139 | { |
hudakz | 1:1f2d9982fa8c | 140 | if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) { |
hudakz | 1:1f2d9982fa8c | 141 | return; /* bcm2835_init() failed or not root */ |
hudakz | 1:1f2d9982fa8c | 142 | } |
hudakz | 1:1f2d9982fa8c | 143 | |
hudakz | 1:1f2d9982fa8c | 144 | /* If you use the barrier here, wierd things happen, and the commands dont work */ |
hudakz | 1:1f2d9982fa8c | 145 | |
hudakz | 1:1f2d9982fa8c | 146 | /* |
hudakz | 1:1f2d9982fa8c | 147 | uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); |
hudakz | 1:1f2d9982fa8c | 148 | |
hudakz | 1:1f2d9982fa8c | 149 | if (channel == 0) { |
hudakz | 1:1f2d9982fa8c | 150 | if (markspace) |
hudakz | 1:1f2d9982fa8c | 151 | control |= BCM2835_PWM0_MS_MODE; |
hudakz | 1:1f2d9982fa8c | 152 | else |
hudakz | 1:1f2d9982fa8c | 153 | control &= ~BCM2835_PWM0_MS_MODE; |
hudakz | 1:1f2d9982fa8c | 154 | if (enabled) |
hudakz | 1:1f2d9982fa8c | 155 | control |= BCM2835_PWM0_ENABLE; |
hudakz | 1:1f2d9982fa8c | 156 | else |
hudakz | 1:1f2d9982fa8c | 157 | control &= ~BCM2835_PWM0_ENABLE; |
hudakz | 1:1f2d9982fa8c | 158 | } |
hudakz | 1:1f2d9982fa8c | 159 | else |
hudakz | 1:1f2d9982fa8c | 160 | if (channel == 1) { |
hudakz | 1:1f2d9982fa8c | 161 | if (markspace) |
hudakz | 1:1f2d9982fa8c | 162 | control |= BCM2835_PWM1_MS_MODE; |
hudakz | 1:1f2d9982fa8c | 163 | else |
hudakz | 1:1f2d9982fa8c | 164 | control &= ~BCM2835_PWM1_MS_MODE; |
hudakz | 1:1f2d9982fa8c | 165 | if (enabled) |
hudakz | 1:1f2d9982fa8c | 166 | control |= BCM2835_PWM1_ENABLE; |
hudakz | 1:1f2d9982fa8c | 167 | else |
hudakz | 1:1f2d9982fa8c | 168 | control &= ~BCM2835_PWM1_ENABLE; |
hudakz | 1:1f2d9982fa8c | 169 | } |
hudakz | 1:1f2d9982fa8c | 170 | |
hudakz | 1:1f2d9982fa8c | 171 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); |
hudakz | 1:1f2d9982fa8c | 172 | */ |
hudakz | 1:1f2d9982fa8c | 173 | |
hudakz | 1:1f2d9982fa8c | 174 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); |
hudakz | 1:1f2d9982fa8c | 175 | } |
hudakz | 1:1f2d9982fa8c | 176 | |
hudakz | 1:1f2d9982fa8c | 177 | /*! Sets the maximum range of the PWM output. |
hudakz | 1:1f2d9982fa8c | 178 | The data value can vary between 0 and this range to control PWM output |
hudakz | 1:1f2d9982fa8c | 179 | \param[in] channel The PWM channel. 0 or 1. |
hudakz | 1:1f2d9982fa8c | 180 | \param[in] range The maximum value permitted for DATA. |
hudakz | 1:1f2d9982fa8c | 181 | */ |
hudakz | 1:1f2d9982fa8c | 182 | void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) |
hudakz | 1:1f2d9982fa8c | 183 | { |
hudakz | 1:1f2d9982fa8c | 184 | if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) |
hudakz | 1:1f2d9982fa8c | 185 | return; /* bcm2835_init() failed or not root */ |
hudakz | 1:1f2d9982fa8c | 186 | |
hudakz | 1:1f2d9982fa8c | 187 | if (channel == 0) |
hudakz | 1:1f2d9982fa8c | 188 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); |
hudakz | 1:1f2d9982fa8c | 189 | else |
hudakz | 1:1f2d9982fa8c | 190 | if (channel == 1) |
hudakz | 1:1f2d9982fa8c | 191 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); |
hudakz | 1:1f2d9982fa8c | 192 | } |
hudakz | 1:1f2d9982fa8c | 193 | |
hudakz | 1:1f2d9982fa8c | 194 | /*! Sets the PWM pulse ratio to emit to DATA/RANGE, |
hudakz | 1:1f2d9982fa8c | 195 | where RANGE is set by bcm2835_pwm_set_range(). |
hudakz | 1:1f2d9982fa8c | 196 | \param[in] channel The PWM channel. 0 or 1. |
hudakz | 1:1f2d9982fa8c | 197 | \param[in] data Controls the PWM output ratio as a fraction of the range. |
hudakz | 1:1f2d9982fa8c | 198 | Can vary from 0 to RANGE. |
hudakz | 1:1f2d9982fa8c | 199 | */ |
hudakz | 1:1f2d9982fa8c | 200 | void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) |
hudakz | 1:1f2d9982fa8c | 201 | { |
hudakz | 1:1f2d9982fa8c | 202 | if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) |
hudakz | 1:1f2d9982fa8c | 203 | return; /* bcm2835_init() failed or not root */ |
hudakz | 1:1f2d9982fa8c | 204 | |
hudakz | 1:1f2d9982fa8c | 205 | if (channel == 0) |
hudakz | 1:1f2d9982fa8c | 206 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); |
hudakz | 1:1f2d9982fa8c | 207 | else |
hudakz | 1:1f2d9982fa8c | 208 | if (channel == 1) |
hudakz | 1:1f2d9982fa8c | 209 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); |
hudakz | 1:1f2d9982fa8c | 210 | } |