-2

Background

As of this writing, it appears the main C library for working with GPIO input and outputs is with gpiod.h. Unless, another option, is to work with unofficial forks of previous library trying to make things work for the RPi5 running Bookworm (Raspbian).

Question

Having read previous stack exchange posts on working with I2C busses and seeing an example code. Aside from turning on the VDD, will gpiod.h be useless at helping me transfer data or synchronising devices between controllers and targets? I comprehend the difficulty of 8 or 9 bit communications between controllers and targets and so libraries are important.

But to work with I2C, is it now necessary to learn kernel or compile with make files?

I was hoping my code could look something like this:

#include <stdio.h>
#include <gpiod.h>
#include <unistd.h>

#define i2cpath "dev/i2c-1" #define lcdadd "0x27"

enum LCD { GND = 39, VDD = 26, SDA = 3, SCL = 5 };

struct gpiod_chip *chip = gpiod_chip_open("/dev/gpiochip0");

struct gpiod_line *line = gpiod_get_line(chip, VDD);

void freechip(struct gpiod_chip *chip){ gpiod_chip_close(chip); }

int main(){

    ...C code for I2C frames...

    freechip(chip);

return 0;

}

Is the above code not longer possible to enable I2C unless I deal with linux headers as it was cited previously?

Seamus
  • 23,558
  • 5
  • 42
  • 83
Human006
  • 1
  • 3

2 Answers2

0

"to work with I2C, is it now necessary to learn kernel or compile with make files?" No.
You can use the library from the command line
e.g. gcc -Wall -o prog prog.c -lpi-i2c

I wrote a wrapper around I²C kernel driver which simplifies I²C programming and can be downloaded from GitHub. pi-i2c is an I²C C library for Raspberry Pi using the I²C kernel driver

The library includes GPIO access routines for MCP23017 16-Bit I/O Expander and a few examples (including one to display status of DS3231 RTC).

This is based on the SMBus (System Management Bus) protocol, which is a subset of the I²C protocol. Fortunately, many devices use only the same subset, which makes it possible to put them on an SMBus.

Milliways
  • 62,573
  • 32
  • 113
  • 225
-1

@milliways answer certainly is for more experienced backgrounds.

But below has been my brute force learning the theory Introduction to I2C bus and the rest with reading the default I2C standard I2C standard - maybe not up to date to finally succeeding to display in my LCD to say "Hello World".

ESPECIALLY learning the importance of the data sheet of the target device LCD (TD) which I was trying to instruct. Which was in my case the LCD Module Data Sheet.

First On learning the concept of how I2C communication works - on sending data via bytes and to instantiate variables down the line using the built in <linux/12c-dev.h> and <sys/ioctl.h> - intuitively, the same concept of trying to open a file and writing to it...

#include <stdio.h>         // Standard I/O functions (e.g., printf, perror)
#include <fcntl.h>         // File control options used by open() for device file access
#include <unistd.h>        // Unix standard functions (e.g., close, sleep, usleep)
#include <linux/i2c-dev.h> // Definitions and macros for I²C communication (e.g., I2C_SLAVE)
#include <sys/ioctl.h>     // For controlling device parameters via ioctl() (e.g., setting device address)
#include <stdlib.h>        // Standard library functions (e.g., strtol for conversions)

#define I2C_PATH "/dev/i2c-1" // Path to the I²C device #define LCD_ADDR 0x27 // I²C address for our LCD

Second the very important role of data-sheet of the TD that I had.

For example, TD needed enough time between receiving data, therefore I had to use usleep() frequently. Furthermore, TD needed to know whether it would communicate in 8 bit or 4 bit (such as when you might have all 8/10 pins connected to RPI5 or only the "actual I2C 4 pins interface; 8bit writes or 2 sets of 4 bit rights respectively). I could tell from the I2C I/O repeater chip, I could send only 4 bits at a time.

But what was most important was the prerequisites before TD can display some characters

#define LCD_CLEAR_DISPLAY 0x01
#define LCD_RETURN_HOME 0x02
#define LCD_ENTRY_MODE_SET 0x06
#define LCD_DISPLAY_ON_CURSOR_OFF_BLINK_OFF 0x0C
#define LCD_FUNCTION_SET_4BIT 0x28
#define LCD_SET_DDRAM_ADDR_LINE_1 0x80
#define LCD_SET_DDRAM_ADDR_LINE_2 0xC0

This then naturally went into a simple void function to initialise all as a whole in main:

// Send a command (RS = 0) to the LCD
void lcd_command(int file, unsigned char cmd) {
    write_4bit(file, cmd, 0x00);
    usleep(2000);
}

// Send data (RS = 1) to the LCD void lcd_data(int file, unsigned char data) { write_4bit(file, data, 0x01); usleep(200); }

// Initialize the LCD in 4-bit mode following the datasheet sequence void lcd_init(int file) { usleep(50000); // Wait >40ms for power stabilization lcd_command(file, 0x33); // Initialization sequence part 1 (8-bit temporarily) lcd_command(file, 0x32); // Initialization sequence part 2: switch to 4-bit mode lcd_command(file, LCD_FUNCTION_SET_4BIT); // Configure for 4-bit, 2 line, 5x8 font lcd_command(file, LCD_DISPLAY_ON_CURSOR_OFF_BLINK_OFF); // Turn display on lcd_command(file, LCD_CLEAR_DISPLAY); // Clear the display usleep(2000); lcd_command(file, LCD_ENTRY_MODE_SET); // Set entry mode (increment cursor) lcd_command(file, LCD_RETURN_HOME); // Return cursor to home position }

I had to configure the RPI5 and the Device (connected together) to start "transmission" in a specific way (synchronise writing and reading data ensuring I had given the device enough time in milliseconds before sending more commands to format my display) as above refers.

The main() therefore went on naturally as follows:

int main() {
int file = open(I2C_PATH, O_RDWR); //open the I2C device file with some debugging below
if (file &lt; 0) {
    perror(&quot;Failed to open I2C bus&quot;);
    return -1;
}
// join the file device file with the address TD (CLI: i2cdetect -y 1). Second parameter &quot;I2C_SLAVE&quot; is a defauly macro from kernel to state we are about to set a &quot;target device&quot; less commonly referred to as a slave device.
if (ioctl(file, I2C_SLAVE, LCD_ADDR) &lt; 0) {
    perror(&quot;Failed to set I2C slave address&quot;);
    close(file);
    return -1;
}

lcd_init(file);

write_string_to_line(file, &quot;Hello&quot;, LCD_SET_DDRAM_ADDR_LINE_1);
write_string_to_line(file, &quot;World&quot;, LCD_SET_DDRAM_ADDR_LINE_2);

close(file);

return 0;

}

Finally thinking my code wasn't working when in fact, my LCD was connected to a 3.3v and the data sheet specified at least 4v, therefore it was difficult to see the shade of black text "Hello World" emitting from the LCD display.

Seamus
  • 23,558
  • 5
  • 42
  • 83
Human006
  • 1
  • 3