Raspberry Pi 5 IoT In C - I2C with Gpio5
Written by Harry Fairhead   
Wednesday, 16 July 2025
Article Index
Raspberry Pi 5 IoT In C - I2C with Gpio5
I2C Functions -Read/Write
End User Functions

With all of this defined we can now create some end user-callable functions that are the same as the Pico SDK:

A blocking 8-bit read

int i2c_read_blocking(I2C i2c, uint8_t addr,
                  uint8_t *dst, size_t len, bool nostop)
{
    return i2c_read_blocking_internal(i2c, addr, dst,
                             len, nostop, 0xFFFFFFFF);
}

A blocking 8-bit write

int i2c_write_blocking(I2C i2c, uint8_t addr, 
           const uint8_t *src, size_t len, bool nostop)
{
    return i2c_write_blocking_internal(i2c, addr, src,
len, nostop, 0xFFFFFFFF); }

An 8-bit write with per character timeout

int i2c_write_timeout_per_char_us(I2C i2c, uint8_t addr,
       const uint8_t *src, size_t len, bool nostop, 
                          uint32_t timeout_per_char_us)
{
    return i2c_write_blocking_internal(i2c, addr, src,
len, nostop, timeout_per_char_us); }

An 8-bit read with per character timeout

int i2c_read_timeout_per_char_us(I2C i2c, uint8_t addr, 
         uint8_t *dst, size_t len, bool nostop, 
                         uint32_t timeout_per_char_us)
{
    return i2c_read_blocking_internal(i2c, addr, dst,
len, nostop, timeout_per_char_us); }

These use the internal read/write functions in obvious ways.

The Gpio5 approach has the advantage that you can now add features that are supported by the hardware, such as 10-bit addresses and working as a slave, simply by writing to the appropriate registers.

CIoTPi5180

HTU1D Using Gpio5

With the I2C functions defined in Gpio5 we can now take a Pico program that reads the HTU1D and run it with only minor modifications:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "Gpio5.h"
uint8_t crcCheck(uint8_t msb, uint8_t lsb, uint8_t check)
{
    uint32_t data32 = ((uint32_t)msb << 16) | 
                  ((uint32_t)lsb << 8) | (uint32_t)check;
    uint32_t divisor = 0x988000;
    for (int i = 0; i < 16; i++)
    {
        if (data32 & (uint32_t)1 << (23 - i))
            data32 ^= divisor;
        divisor >>= 1;
    };
    return (uint8_t)data32;
}
int main(int argc, char **argv)
{
    rp1_Init();
    gpio_set_function(2, GPIO_FUNC_I2C);
    gpio_set_function(3, GPIO_FUNC_I2C);
    i2c_init(I2C1, 100 * 1000);
    uint8_t buf[4] = {0xE3};
    i2c_write_blocking(I2C1, 0x40, buf, 1, true);
    i2c_read_blocking(I2C1, 0x40, buf, 3, false);
    uint8_t msb = buf[0];
    uint8_t lsb = buf[1];
    uint8_t check = buf[2];
    printf("msb %d \n\r lsb %d \n\r checksum %d \n\r",
                                       msb, lsb, check);
    unsigned int data16 = ((unsigned int)msb << 8) | 
                             (unsigned int)(lsb & 0xFC);
    printf("crc = %d\n\r", crcCheck(msb, lsb, check));
    float temp = (float)(-46.85 + (175.72 * 
                                data16 / (float)65536));
    printf("Temperature %f C \n\r", temp);
    buf[0] = 0xF5;
    i2c_write_blocking(I2C1, 0x40, buf, 1, true);
    
    while (i2c_read_blocking(I2C1, 0x40, buf, 3, false) 
& 0x1 ) { sleep_ms(1); }; msb = buf[0]; lsb = buf[1]; check = buf[2]; printf("msb %d \n\r lsb %d \n\r checksum %d \n\r", msb, lsb, check); printf("crc = %d\n\r", crcCheck(msb, lsb, check)); data16 = ((unsigned int)msb << 8) |
(unsigned int)(lsb & 0xFC); float hum = -6 + (125.0 * (float)data16) / 65536; printf("Humidity %f %% \n\r", hum); return (EXIT_SUCCESS); }

Reading the temperature is performed using clock stretching and works perfectly. Reading the humidity is performed using polling, just as an illustration of how this works. After performing the write, the master simply loops until it reads data without getting a NAK from the slave. Notice that we could simply test for a negative return value or the more specific NAK error indicated by bit one.

Summary

  • Each I2C device has a built-in address or set of addresses that it can respond to. Address clashes are a particular problem if you want to mix devices on a single I2C bus.

  • There are a few subtle variations on the data protocol that modify how multiple blocks of data are sent. Getting this exactly right is another common I2C problem.

  • Slow devices are accommodated by either polling for the device to be ready or allowing the device to hold down the clock line, clock stretching, until it is ready.

  • The Pi 5 has four I2C bus controllers of which the first two are the same as found in other Pis.

  • The I2C driver can be loaded dynamically and it provides the basic facilities to interface with any I2C device.

  • As an example of using the driver, the HTU21D is easy to set up and read. It also has a dedicated Linux driver which is discussed in Chapter 14.

  • Without clock-stretching support, all we can do is to poll for data to be ready to read.

  • Computing a CRC is something every IoT programmer needs to know how to do in the general case.

  • There are a number of command line tools that let you work with I2C, but they need to be used with caution.

  • Using the additional I2C interfaces that the Pi 5 supplies is just a matter of configuring the Linux driver and using appropriate pins.

  • You can make use of clock stretching on all recent Pis running the latest OS.

  • At the time of writing only a few ioctl operations are supported, but they can be used to write a register address and read the result with the correct stop bits.

  • Adding I2C support to Gpio5 is fairly easy and, once you have the necessary functions, reading the HTU21D without the use of a driver

You can find the complete Gpio5 at its Github repo:

 https://github.com/IOPress/Gpio5,

at the book’s web page:

https://iopress.info/index.php/books/raspberry-pi-5-iot-in-c

Raspberry Pi 5 IoT In C
Drivers and Gpio5

By Harry Fairhead

CIoTPi5360
Buy from Amazon.

Contents

  1. The Pi 5 For The IoT
  2. C and Visual Studio Code
  3. Drivers: A First Program
  4. The GPIO Character Drive
  5. GPIO Using I/O Control
  6. GPIO Events
  7. GPIO Hardware With Gpio5
         Extract: GPIO Registers
         Extract: GPIO5
  8. Some Electronics
  9. The Device Tree
  10. Pulse Width Modulation
         Extract: PWM Using GPIO5
  11. SPI Devices
  12. I2C Driver and Gpio5
        
    Extract: I2C with Gpio5 ***NEW!
  13. Sensor Drivers – Linux IIO & hwmon
  14. 1-Wire Bus
  15. The PIO
  16. Going Further With Drivers
  17. Appendix I Gpio5

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Amazon Q Developer Now With Github
28/07/2025

Amazon Q Developer now integrates directly with Github, so that developers who use GitHub can leverage its Gen AI coding capabilities instantly.



Microsoft's Generative AI For Beginners With JavaScript
05/08/2025

In this Github-based course provided by Microsoft, you'll learn how to build GenAI application using JavaScript.


More News

pico book

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Saturday, 19 July 2025 )