1. Description
The Raspberry Pi is a single board computer that now comes in 4 revisions and a minimalistic Zero variant. It is a popular choice for different projects because of its small size, high power consumption, fast processing speed, and being a complete Linux-based computer.
One way to connect multiple single-board computers and/or microcontrollers is by direct wiring. For this, the most commonly used protocols are I2C, SPI and UART. Previous posts in the blog series explained the principles of these protocols and introduced Arduino-specific C libraries. In this article, I'll explain the C++ libraries that enable the use of these protocols on the Raspberry Pi. For each protocol, I research the available libraries and give a short explanation and code example. Please don't say that these examples were not developed by me, but are from the library documentation and should serve as the basis for concrete working examples.
This article originally appeared on my blog admantium.com .
Two, I2C
I2C is supported via the SMBus protocol, which is described as a specific variant of the I2C bus. This protocol is available as a Linux kernel module . To use it, you need to configure your Raspberry Pi. In Terminal, run, select and .raspi-config
3 Interfacing Options
P5 I2C
Following the code example at kernel.org , you need to open the device file representing the attached I2C device, then send the SMBus command by writing to the device registers.
// SOURCE: https://www.kernel.org/doc/Documentation/i2c/dev-interface */
#include <linux/i2c-dev.h>
#include <i2c/smbus.h>int file;
int adapter_nr = 2;
char filename[20];snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
file = open(filename, O_RDWR);
if (file < 0) {
exit(1);
} int addr = 0x40;if (ioctl(file, I2C_SLAVE, addr) < 0) {
exit(1);
}__u8 reg = 0x10;
__s32 res;
char buf[10];res = i2c_smbus_read_word_data(file, reg);
if (res < 0) {
/* ERROR HANDLING: i2c transaction failed */
} else {
/* res contains the read word */
}buf[0] = reg;
buf[1] = 0x43;
buf[2] = 0x65;
if (write(file, buf, 3) != 3) {
/* ERROR HANDLING: i2c transaction failed */
}
3. SPI
To use SPI, you also need to add a specific kernel module: Spidev . Raspberry Pi supports this module, you need to configure it by calling, and then select and.raspi-config
3 Interfacing Options
P4 SPI
To access SPI functions using C/C++, the spidev wrapper library can be used . Following the example code , you need to configure the SPI connection, then open the device to connect to, and use the library methods to read and write data.
//SOURCE: https://raw.githubusercontent.com/milekium/spidev-lib/master/sample/spidev-testcpp.cc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <spidev_lib++.h>spi_config_t spi_config;
uint8_t tx_buffer[32];
uint8_t rx_buffer[32];int main( void)
{ SPI *mySPI = NULL; spi_config.mode=0;
spi_config.speed=1000000;
spi_config.delay=0;
spi_config.bits_per_word=8; mySPI=new SPI("/dev/spidev1.0",&spi_config); if (mySPI->begin())
{
memset(tx_buffer,0,32);
memset(rx_buffer,0,32);
sprintf((char*)tx_buffer,"hello world");
printf("sending %s, to spidev2.0 in full duplex \n ",(char*)tx_buffer);
mySPI->xfer(tx_buffer,strlen((char*)tx_buffer),rx_buffer,strlen((char*)tx_buffer));
printf("rx_buffer=%s\n",(char *)rx_buffer);
//mySPI->end();
delete mySPI;
}
return 1;
}
4. UART
A UART connection can be made with the common C library. Following the very detailed instructions in this article , you will need to open the device, then use the various properties that define the TTY device, then write to and read from the serial port.termios struct
// SOURCE: https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>int main() {
int serial_port = open("/dev/ttyUSB0", O_RDWR); struct termios tty; if(tcgetattr(serial_port, &tty) != 0) {
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
return 1;
} tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS;
tty.c_cflag |= CREAD | CLOCAL; tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO;
tty.c_lflag &= ~ECHOE;
tty.c_lflag &= ~ECHONL;
tty.c_lflag &= ~ISIG;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); tty.c_oflag &= ~OPOST;
tty.c_oflag &= ~ONLCR; tty.c_cc[VTIME] = 10;
tty.c_cc[VMIN] = 0; cfsetispeed(&tty, B9600);
cfsetospeed(&tty, B9600); if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
return 1;
} unsigned char msg[] = { 'H', 'e', 'l', 'l', 'o', '\r' };
write(serial_port, "Hello, world!", sizeof(msg)); char read_buf [256]; memset(&read_buf, '\0', sizeof(read_buf); int num_bytes = read(serial_port, &read_buf, sizeof(read_buf)); if (num_bytes < 0) {
printf("Error reading: %s", strerror(errno));
return 1;
} printf("Read %i bytes. Received message: %s", num_bytes, read_buf); close(serial_port)
return 0;
5. General GPIO access
The library libgpiod provides generic access to the GPIOs of any device running Linux. It detects available GPIOs, can read and write data, and waits for trigger events. With this, you can write ow code to communicate the UART with any connected device.
To install it, run the following command:
apt-get install autoconf autoconf-archive libtool libkmod-dev pkg-config
git clone https://github.com/brgl/libgpiod.gitcd libgpiod
./autogen.sh --enable-tools=yes --prefix=/usr/local/bin
make
sudo make install
If the compilation and installation were successful, you will find binaries in a subfolder that can be used to browse the GPIO. See the examples below../tools
gpiodetect
gpioinfo
./tools/gpiodetect
gpiochip0 [pinctrl-bcm2711] (58 lines)
gpiochip1 [raspberrypi-exp-gpio] (8 lines)./tools/gpioinfo
gpiochip0 - 58 lines:
line 0: "ID_SDA" unused input active-high
line 1: "ID_SCL" unused input active-high
line 2: "SDA1" unused input active-high
line 3: "SCL1" unused input active-high
line 4: "GPIO_GCLK" unused input active-high
line 5: "GPIO5" unused input active-high
...
If you want to use the library, read this article for detailed instructions.
6. Conclusion
In order to use I2C, SPI and UART on Raspberry Pi, not only Python but also C++ libraries can be used. Specifically, you need to activate the I2C and SPI functions, which will load the corresponding kernel modules locally. Then select the client library and other necessary C++ headers. Using the library follows the same principles: determine the device file for the connection, configure the connection object, open the device file, and read/write to it. Finally, the handy library libgpiod gives you direct access to all GPIO pins, which is helpful for debugging.raspi-config