Raspberry Pi IoT In C Using Linux Drivers - The I2C Linux Driver
Written by Harry Fairhead   
Monday, 25 July 2022
Article Index
Raspberry Pi IoT In C Using Linux Drivers - The I2C Linux Driver
A Real Device
Reading Temperature Data
Checksum

Checksum Calculation

Although computing a checksum isn't specific to I2C, it is another common task. The datasheet explains that the polynomial used is:

X8 + X5 + X4 + 1

Once you have this information you can work out the divisor by writing a binary number with a one in each location corresponding to a power of X in the polynomial. In this case the 8th, 5th, 4th and 1st bit. Hence the divisor is:

0x0131

What you do next is roughly the same for all CRCs. First you put the data that was used to compute the checksum together with the checksum value as the low order bits:

uint32_t data32 = ((uint32_t) msb << 16) |
((uint32_t) lsb << 8) | (uint32_t) check;

Now you have three bytes, i.e 24 bits in a 32-bit value. Next you adjust the divisor so that its most significant non-zero bit aligns with the most significant bit of the three bytes. As this divisor has a 1 at bit eight it needs to be shifted 15 places to the right to move it to be the 24th bit:

uint32_t divisor = 0x988000;

Now that you have both the data and the divisor aligned, you step through the top-most 16 bits, i.e. you don't process the low order eight bits which is the received checksum. For each bit you check to see if it is a 1 - if it is you replace the data with the data XOR divisor. In either case you shift the divisor one place to the right:

    for (int i = 0; i < 16; i++) {
        if (data32 & (uint32_t) 1 << (23 - i))
data32 ^= divisor; divisor >>= 1; };

When the loop ends, if there was no error, the data32 should be zeroed and the received checksum is correct and as computed on the data received.

A complete function to compute the checksum is:

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

It is rare to get a CRC error on an I2C bus unless it is overloaded or subject to a lot of noise.

The Complete Program

#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
void checkI2CBus();
FILE * doCommand(char *cmd);
uint8_t crcCheck(uint8_t msb, uint8_t lsb,
uint8_t check); int main(int argc, char** argv) { checkI2CBus(); int i2cfd = open("/dev/i2c-1", O_RDWR); ioctl(i2cfd, I2C_SLAVE, 0x40); char buf[3] = {0xF3}; write(i2cfd, buf, 1); while (1) { int result = read(i2cfd, buf, 3); if (result > 0) break; usleep(10 * 1000); } uint8_t msb = buf[0]; uint8_t lsb = buf[1]; uint8_t check = buf[2]; printf("msb %d \n\rlsb %d \n\rchecksum %d \n\r",
msb, lsb, check); unsigned int data16 = ( (unsigned int) msb << 8) | (unsigned int) (lsb & 0xFC); float temp = (float) (-46.85 + (175.72 * data16 / (float) 65536)); printf("Temperature %f C \n\r", temp); printf("crc = %d\n\r", crcCheck(msb, lsb, check)); buf[0] = 0xF5; write(i2cfd, buf, 1); while (1) { int result = read(i2cfd, buf, 3); if (result > 0) break; usleep(10 * 1000); } msb = buf[0]; lsb = buf[1]; check = buf[2]; 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); close(i2cfd); return (EXIT_SUCCESS); } 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; }
void checkI2CBus() { FILE *fd = doCommand("sudo dtparam -l"); char output[1024]; int txfound = 0; while (fgets(output, sizeof (output), fd) != NULL) { printf("%s\n\r", output); fflush(stdout); if (strstr(output, "i2c_arm=on") != NULL) { txfound = 1; } if (strstr(output, "i2c_arm=off") != NULL) { txfound = 0; } } pclose(fd); if (txfound == 0) { fd = doCommand("sudo dtparam i2c_arm=on"); pclose(fd); } } FILE * doCommand(char *cmd) { FILE *fp = popen(cmd, "r"); if (fp == NULL) { printf("Failed to run command %s \n\r", cmd); exit(1); } return fp; }

 

Not included in this extract but in chapter

  • I2C Tools

Summary

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

  • The I2C driver creates a number of new folder and it also accepts ioctl commands.

  • 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.

 

 HTU21

Raspberry Pi IoT In C Using Linux Drivers

By Harry Fairhead

Cdrivers360

Buy from Amazon.

Contents

  1.  Choosing A Pi For IoT

  2. C and Visual Studio Code

  3.  Drivers: A First Program

  4.  The GPIO Character Driver
         Extract: GPIO Character Driver

  5. GPIO Using I/O Control

  6.  GPIO Events

  7.  The Device Tree
        Extract: The DHT22

  8.  Some Electronics

  9.  Pulse Width Modulation
    Extract:  The PWM Driver 

  10. SPI Devices
    Extract: The SPI Driver 

  11. I2C Basics

  12. The I2C Linux Driver ***NEW!

     

  13. Advanced I2C

  14. Sensor Drivers – Linux IIO & Hwmon
      Extract: Hwmon  

  15. 1-Wire Bus
      Extract: 1-Wire And The DS18B20 

  16. Going Further With Drivers

  17. Appendix I

 <ASIN:1871962641>

<ASIN:B08W9V7TP9>

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


Ursina - A Game Engine Powered by Python
08/11/2024

Ursina is a new open source game engine in which you can code any type of game in Python, be it 2-D, 3-D, an application, a visualization, you name it.



Remembering Thomas Kurtz, Co-creator of BASIC
15/11/2024

Thomas Eugene Kurtz, the co-founder of the BASIC programming language, has died at the age of 96. BASIC, which was developed for the purpose of education, popularized computer programming making it ac [ ... ]


More News

espbook

 

Comments




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

<ASIN:187196265X>

<ASIN:1871962692>

<ASIN:1871962609>

 <ASIN:1871962617>

<ASIN:1871962455>

<ASIN:1871962463>

 



Last Updated ( Monday, 25 July 2022 )