How it works...

In this recipe, our peripheral devicean LCD screenis connected to the board over the I2C bus. It is a form of a serial interface, so the connection only requires four physical wires. An LCD screen, however, can do much more than a simple LED. This means that the communication protocol used to control it is also more complex.

We will use only a fraction of the functionality provided by the 1602 LCD screen. The communication logic is loosely based on the LiquidCrystal_I2C library for Arduino, adapted for Raspberry Pi. 

We define an Lcd class that hides all the complexities of I2C communication and the specifics of the 1602 control protocol in its private methods. Besides a constructor and a destructor, it exposes only two public methods: Clear and Display.

In Linux, we communicate to I2C devices via device files. To start working with a device, we need to open a device file corresponding to an I2C controller using the regular open call:

fd = open(device, O_RDWR);

There may be multiple devices attached to the same bus. We need to select the device we what to communicate to. We do this with an ioctl call:

if (ioctl(fd, I2C_SLAVE, address) < 0) {

At this point, the I2C communication is configured and we can issue I2C commands by writing data to the open file descriptor. The commands, however, are specific for each peripheral device. So, after generic I2C initialization, we need to proceed with the LCD initialization.

We put all the LCD-specific initialization into the Init private function. It configures the operation modes, the number of rows, and the size of the displayed characters. To do this, we define the helper methods, data types, and constants.

The basic helper function is SendToI2C. It is a simple method that writes a byte of data into the file descriptor configured for I2C communication and throws an exception in the case of an error:

      if (write(fd, &byte, 1) != 1) {
throw std::system_error(errno,
std::system_category(),
"Write to i2c device failed");
}

On top of SendToI2C, we define another helper method, SendToLcd. It sends a sequence of bytes to I2C, forming a command that the LCD controller can interpret. This involves setting different flags and taking care of delays required between chunks of data:

      SendToI2C(value);
SendToI2C(value | En);
std::this_thread::sleep_for(1us);
SendToI2C(value & ~En);
std::this_thread::sleep_for(50us);

The LCD is working in 4-bit mode, which means that each byte sent to the display requires two commands. We define the Write method to do it for us:

      SendToLcd((value & 0xF0) | mode);
SendToLcd((value << 4) | mode);

Finally, we define all possible commands supported by the device and put them into the Function enum class. Call helper function can be used to invoke the functions in a type-safe way:

    void Call(Function function, uint8_t value=0) {
Write((uint8_t)function | value);
}

Finally, we use these helper functions to define public methods to clear the screen and display a string.

Since all the complexity of the communication protocol is encapsulated in the Lcd class, our main function is relatively simple.

It creates an instance of the class, passing in a device filename and a device address that we are going to use. By default, a 1620 LCD with an I2C backpack has a 0x27 address:

  Lcd lcd("/dev/i2c-1", 0x27);

The constructor of the Lcd class performs all initialization and as soon as the instance is created, we can invoke the Display function. Instead of hardcoding the string to display, we use data passed by a user through the command-line parameters. The first parameter is displayed in the first row. If the second parameter is provided, it is also displayed in the second row of the display:

    lcd.Display(argv[1]);
if (argc > 2) {
lcd.Display(argv[2], true);
}

Our program is ready and we can copy it over to the Raspberry Pi board and build it there. But before running it, we need to wire the display to the board and enable I2C support.

We use the raspi-config tool to enable I2C. We need to do it only once, but a reboot is required unless I2C has not been previously enabled:

Finally, we can run our application. It will display the following output on the LCD display:

Now, we know how to control devices connected via an I2C bus from Linux user-space programs. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.125.171