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

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

File content as of revision 2:131555dc6fb7:

#include "mbed.h"

extern struct bcm2835_peripheral   bsc0;
extern timeval  start_program, end_point;
extern volatile uint32_t *bcm2835_bsc1;

I2C::I2C()
{
    // start timer
    gettimeofday(&start_program, NULL);

    //Initiate the Wire library and join the I2C bus.
    volatile uint32_t*  paddr = bcm2835_bsc1 + BCM2835_BSC_DIV / 4;

    // Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them

    bcm2835_gpio_fsel(SDA, BCM2835_GPIO_FSEL_ALT0);
    bcm2835_gpio_fsel(SCL, BCM2835_GPIO_FSEL_ALT0);

    // Read the clock divider register
    uint16_t    cdiv = bcm2835_peri_read(paddr);
    // Calculate time for transmitting one byte

    // 1000000 = micros seconds in a second
    // 9 = Clocks per byte : 8 bits + ACK
    _i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
I2C::~I2C()
{
    // Set all the I2C/BSC1 pins back to input

    bcm2835_gpio_fsel(SDA, BCM2835_GPIO_FSEL_INPT); // SDA
    bcm2835_gpio_fsel(SCL, BCM2835_GPIO_FSEL_INPT); // SCL
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint8_t I2C::read(uint8_t address, char* buf, int len, bool repeat)
{
    if (repeat) {
        _addr = address;
        return read_repeat(buf, len);
    }
    else {
        requestFrom(address, len);
        return read(buf);
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint8_t I2C::read(bool ack)
{
    char    buf[1] = { 0 };

    if (ack) {
        _i2c_bytes_to_read = 1;
        read(buf);
    }
    else {
        read_repeat(buf, 1);
    }

    return buf[0];
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int I2C::write(uint8_t address, const char* buf, int len, bool repeat)
{
    setAddress(address);
    return write(buf, len);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int I2C::write(uint8_t data)
{
    char    i2cdata[1] = { data };

    return write(i2cdata, 1);
}

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

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void I2C::setAddress(uint8_t address)
{
    _addr = address;

    volatile uint32_t*  paddr = bcm2835_bsc1 + BCM2835_BSC_A / 4;
    bcm2835_peri_write(paddr, _addr);
}

/**
 * @brief
 * @note    Used by the master to request bytes from a slave device
 * @param
 * @retval
 */
void I2C::requestFrom(unsigned char address, int len)
{
    setAddress(address);
    _i2c_bytes_to_read = len;
}

/**
 * @brief   Reads bytes from slave after a call to WirePi::requestFrom(address, len)
 * @note
 * @param
 * @retval
 */
uint8_t I2C::read(char* buf)
{
    volatile uint32_t*  dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN / 4;
    volatile uint32_t*  fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO / 4;
    volatile uint32_t*  status = bcm2835_bsc1 + BCM2835_BSC_S / 4;
    volatile uint32_t*  control = bcm2835_bsc1 + BCM2835_BSC_C / 4;

    uint32_t            remaining = _i2c_bytes_to_read;
    uint32_t            i = 0;
    uint8_t             reason = BCM2835_I2C_REASON_OK;

    //

    // Clear FIFO
    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1);

    // Clear Status
    bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);

    // Set Data Length
    bcm2835_peri_write_nb(dlen, _i2c_bytes_to_read);

    // Start read
    bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ);

    // wait for transfer to complete
    while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) {

        // we must empty the FIFO as it is populated and not use any delay
        while (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) {

            // Read from FIFO, no barrier
            buf[i] = bcm2835_peri_read_nb(fifo);
            i++;
            remaining--;
        }
    }

    // transfer has finished - grab any remaining stuff in FIFO
    while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) {

        // Read from FIFO, no barrier
        buf[i] = bcm2835_peri_read_nb(fifo);
        i++;
        remaining--;
    }

    // Received a NACK
    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) {
        reason = BCM2835_I2C_REASON_ERROR_NACK;
    }

    // Received Clock Stretch Timeout
    else
    if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) {
        reason = BCM2835_I2C_REASON_ERROR_CLKT;
    }

    // Not all data is received
    else
    if (remaining) {
        reason = BCM2835_I2C_REASON_ERROR_DATA;
    }

    bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE);

    return reason;
}

/**
 * @brief   Read len bytes from I2C sending a repeated start after writing the required register.
 * @note
 * @param
 * @retval
 */
uint8_t I2C::read_repeat(char* buf, int len)
{
    volatile uint32_t*  dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN / 4;
    volatile uint32_t*  fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO / 4;
    volatile uint32_t*  status = bcm2835_bsc1 + BCM2835_BSC_S / 4;
    volatile uint32_t*  control = bcm2835_bsc1 + BCM2835_BSC_C / 4;

    uint32_t            remaining = len;
    uint32_t            i = 0;
    uint8_t             reason = BCM2835_I2C_REASON_OK;

    // Clear FIFO

    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1);

    // Clear Status
    bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);

    // Set Data Length
    bcm2835_peri_write_nb(dlen, 1);

    // Enable device and start transfer
    bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN);
    bcm2835_peri_write_nb(fifo, (uint32_t) _addr);
    bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST);

    // poll for transfer has started
    while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA)) {

        // Linux may cause us to miss entire transfer stage
        if (bcm2835_peri_read(status) & BCM2835_BSC_S_DONE)
            break;
    }

    // Send a repeated start with read bit set in address
    bcm2835_peri_write_nb(dlen, len);
    bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ);

    // Wait for write to complete and first byte back.
    wait_us(_i2c_byte_wait_us * 3);

    // wait for transfer to complete
    while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) {

        // we must empty the FIFO as it is populated and not use any delay
        while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) {

            // Read from FIFO, no barrier
            buf[i] = bcm2835_peri_read_nb(fifo);
            i++;
            remaining--;
        }
    }

    // transfer has finished - grab any remaining stuff in FIFO
    while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) {

        // Read from FIFO, no barrier
        buf[i] = bcm2835_peri_read_nb(fifo);
        i++;
        remaining--;
    }

    // Received a NACK
    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) {
        reason = BCM2835_I2C_REASON_ERROR_NACK;
    }

    // Received Clock Stretch Timeout
    else
    if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) {
        reason = BCM2835_I2C_REASON_ERROR_CLKT;
    }

    // Not all data is sent
    else
    if (remaining) {
        reason = BCM2835_I2C_REASON_ERROR_DATA;
    }

    bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE);

    return reason;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int I2C::write(const char* buf, int len)
{
    volatile uint32_t*  dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN / 4;
    volatile uint32_t*  fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO / 4;
    volatile uint32_t*  status = bcm2835_bsc1 + BCM2835_BSC_S / 4;
    volatile uint32_t*  control = bcm2835_bsc1 + BCM2835_BSC_C / 4;

    uint32_t            remaining = len;
    uint32_t            i = 0;
    uint8_t             reason = BCM2835_I2C_REASON_OK;

    // Clear FIFO

    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1);

    // Clear Status
    bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);

    // Set Data Length
    bcm2835_peri_write_nb(dlen, len);

    // pre populate FIFO with max buffer
    while (remaining && (i < BCM2835_BSC_FIFO_SIZE)) {
        bcm2835_peri_write_nb(fifo, buf[i]);
        i++;
        remaining--;
    }

    // Enable device and start transfer
    bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST);

    // Transfer is over when BCM2835_BSC_S_DONE
    while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) {
        while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TXD)) {

            // Write to FIFO, no barrier
            bcm2835_peri_write_nb(fifo, buf[i]);
            i++;
            remaining--;
        }
    }

    // Received a NACK
    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) {
        reason = BCM2835_I2C_REASON_ERROR_NACK;
    }

    // Received Clock Stretch Timeout
    else
    if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) {
        reason = BCM2835_I2C_REASON_ERROR_CLKT;
    }

    // Not all data is sent
    else
    if (remaining) {
        reason = BCM2835_I2C_REASON_ERROR_DATA;
    }

    bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE);

    return reason;
}

// Exposes the physical address defined in the passed structure using mmap on /dev/mem
int I2C::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 I2C::unmap_peripheral(struct bcm2835_peripheral* p)
{
    munmap(p->map, BLOCK_SIZE);
    unistd::close(p->mem_fd);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void I2C::wait_i2c_done()
{
    //Wait till done, let's use a timeout just in case
    int timeout = 50;
    while ((!((BSC0_S) & BSC_S_DONE)) && --timeout) {
        unistd::usleep(1000);
    }

    if (timeout == 0)
        printf("wait_i2c_done() timeout. Something went wrong.\n");
}