Page 4 of 4
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
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.
Raspberry Pi IoT In C U sing Linux Drivers
By Harry Fairhead
Buy from Amazon .
Contents
Choosing A Pi For IoT
C and Visual Studio Code
Drivers: A First Program
The GPIO Character Driver Extract: GPIO Character Driver
GPIO Using I/O Control
GPIO Events
The Device Tree Extract: The DHT22
Some Electronics
Pulse Width Modulation Extract: The PWM Driver
SPI Devices Extract: The SPI Driver
I2C Basics
The I2C Linux Driver ***NEW!
Advanced I2C
Sensor Drivers – Linux IIO & Hwmon Extract: Hwmon
1-Wire Bus Extract: 1-Wire And The DS18B20
Going Further With Drivers
Appendix I
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 .
Apollo Launches MCP Server 15/05/2025 Apollo GraphQL has announced the Apollo MCP Server,
designed to connect GraphQL APIs to AI models such as Claude and ChatGPT using the Model Context Protocol (MCP).
LiteCLI SQLite Client Is Now Powered By LLM 19/05/2025
LiteCLI, a very handy SQLite client for the CLI diehards, is upgraded by getting a LLM feature that helps you write SQL.
More News
Comments
Make a Comment or View Existing Comments Using Disqus
or email your comment to: comments@i-programmer.info