Guide How To Connect SPI Device To BeagleBone AI-64
As example we use the GC9A01 SPI display.
To add some funcionality we also connect the AHT15 – Temperature and humidity sensor
BBAI64 Configuration
Install the Device Tree configuration for BBAI64
In the BBAI64 terminal type:
debian@BeagleBone:~$ cd /opt/source/dtb-5.10-ti-arm64/
debian@BeagleBone:/opt/source/dtb-5.10-ti-arm64$ git pull
debian@BeagleBone:/opt/source/dtb-5.10-ti-arm64$ make
debian@BeagleBone:/opt/source/dtb-5.10-ti-arm64$ sudo make install
debian@BeagleBone:/opt/source/dtb-5.10-ti-arm64$ git pull
To the /boot/firmware/extlinux/extlinux.conf add line:
fdtoverlays /overlays/BONE-SPI0_0.dtbo
debian@BeagleBone:~$ cat /boot/firmware/extlinux/extlinux.conf
label Linux microSD
kernel /Image
append console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000 root=/dev/mmcblk1p2 ro rootfstype=ext4 rootwait net.ifnames=0
fdtdir /
fdtoverlays /overlays/BONE-SPI0_0.dtbo
initrd /initrd.img
Reboot the BBAI64
debian@BeagleBone:~$ sudo reboot
Connection to 192.168.7.2 closed by remote host.
Connection to 192.168.7.2 closed.
After reboot check if the SPI0_0 overlay is active.
debian@BeagleBone:~$ sudo beagle-version | grep UBOOT
UBOOT: Booted Device-Tree:[k3-j721e-beagleboneai64.dts]
UBOOT: Loaded Overlay:[BONE-SPI0_0.kernel]
To configure the BBAI64 to load SPI module at boot at time,
add spidev line to /etc/modules file.
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
spidev
Configure BBAI64 to access SPI without root permissions:
Create file /etc/udev/rules.d/80-spi-noroot.rules
# /etc/udev/rules.d/80-spi-noroot.rules
#
# udevadm test $(udevadm info --query=path --name=spidev0.0)
#
SUBSYSTEM=="spidev", GROUP="gpio", ACTION=="add", \
RUN+="/bin/chgrp -R gpio '/sys%p'", \
RUN+="/bin/chmod -R g=u '/sys%p'"
See the BeagleBone AI-64 – I2C to check how to enable I2C for AHT15
Example how to control SPI and GPIO from BBAI64 with C/C++ :
/* Outputs.h */
#ifndef OUTPUTS_H_
#define OUTPUTS_H_
#include <iostream>
#include <string>
#include <stdio.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
class Outputs{
private :
FILE *RSTHandle;
FILE *DCHandle;
DIR *pDir;
int spi_dev;
/*GPIO P9_15*/
const char *RSTDirection = "/sys/class/gpio/gpio347/direction";
const char *RSTValue = "/sys/class/gpio/gpio347/value";
const char *RSTExport = "/sys/class/gpio/export";
const char *RSTDir = "/sys/class/gpio/gpio347";
/*GPIO P9_23*/
const char *DCDirection = "/sys/class/gpio/gpio310/direction";
const char *DCValue = "/sys/class/gpio/gpio310/value";
const char *DCExport = "/sys/class/gpio/export";
const char *DCDir = "/sys/class/gpio/gpio310";
std::string spi_device;
uint32_t mode;
uint8_t bits;
uint32_t speed;
uint16_t delay;
public:
Outputs();
bool configureDC(void);
bool configureRST(void);
void writeRST(uint8_t value);
void writeDC(uint8_t value);
bool openSPI (void);
void transferSPI(const uint8_t *data, size_t len);
void closeSPI (void);
};
#endif /* OUTPUTS_H_ */
/* Outputs.cpp */
/* Configure the SPI as spidev0.0 */
Outputs::Outputs(){
this->spi_device = "/dev/spidev0.0";
this->bits = 8;
this->speed = 125000000L;
this->delay = 0;
}
/* Configure RST output as P9_15 (gpio347) */
bool Outputs::configureRST(void){
RSTHandle = nullptr;
pDir = nullptr;
bool RSTConfigured = true;
pDir = opendir (RSTDir);
if (pDir != nullptr) {
cout << "gpio347 configured" << endl;
closedir(pDir);
} else {
cout << "gpio347 not configured" << endl;
if ((RSTHandle = fopen(RSTExport,"a")) != nullptr) {
fwrite("347",sizeof(char),3,RSTHandle);
fclose(RSTHandle);
cout << "gpio347 added to /sys/class/gpio/" << endl;
usleep(10000L);
} else {
cout << "Failed to open /sys/class/gpio/export" << endl;
RSTConfigured = false;
}
}
if (RSTConfigured && ((RSTHandle = fopen(RSTDirection,"a")) != nullptr)) {
fwrite("out",sizeof(char),3,RSTHandle);
fclose(RSTHandle);
cout << "gpio347 configured as output" << endl;
usleep(10000L);
} else {
cout << "Failed to open /sys/class/gpio/gpio347/direction" << endl;
RSTConfigured = false;
}
return RSTConfigured;
}
/* Configure DC (Data Command) as P9_23 (gpio310) */
bool Outputs::configureDC(void){
RSTHandle = nullptr;
pDir = nullptr;
bool DCConfigured = true;
pDir = opendir (DCDir);
if (pDir != nullptr) {
cout << "gpio310 configured" << endl;
closedir(pDir);
} else {
cout << "gpio310 not configured" << endl;
if ((DCHandle = fopen(DCExport,"a")) != nullptr) {
fwrite("310",sizeof(char),3,DCHandle);
fclose(DCHandle);
cout << "gpio310 added to /sys/class/gpio/" << endl;
usleep(10000L);
} else {
cout << "Failed to open /sys/class/gpio/export" << endl;
DCConfigured = false;
}
}
if (DCConfigured && ((DCHandle = fopen(DCDirection,"a")) != nullptr)) {
fwrite("out",sizeof(char),3,DCHandle);
fclose(DCHandle);
cout << "gpio310 configured as output" << endl;
usleep(10000L);
} else {
cout << "Failed to open /sys/class/gpio/gpio310/direction" << endl;
DCConfigured = false;
}
return DCConfigured;
}
/* Control RST signal - output P9_15 (gpio347) */
void Outputs::writeRST(uint8_t value){
if ((RSTHandle = fopen(RSTValue,"r+")) != nullptr) {
fwrite((value == 0) ? "0" : "1",sizeof(char),1,RSTHandle);
fclose(RSTHandle);
} else {
cout << "Failed to open /sys/class/gpio/gpio347/value" << endl;
}
}
/* Control Data Command signal - output P9_23 (gpio310) */
void Outputs::writeDC(uint8_t value){
if ((DCHandle = fopen(DCValue,"r+")) != nullptr) {
fwrite((value == 0) ? "0" : "1",sizeof(char),1,DCHandle);
fclose(DCHandle);
} else {
cout << "Failed to open /sys/class/gpio/gpio310/value" << endl;
}
}
/* Open and configure SPI */
bool Outputs::openSPI (void){
bool spi_OK = true;
int ret = 0;
spi_dev = -1;
spi_dev = open(spi_device.c_str(), O_RDWR);
if (spi_dev < 0) {
cout << "Failed to open " << spi_device << endl;
spi_OK = false;
}
ret = ioctl(spi_dev, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
cout << "can't set spi mode" << endl;
ret = ioctl(spi_dev, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
cout << "can't get spi mode" << endl;
/*
* bits per word
*/
ret = ioctl(spi_dev, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
cout << "can't set bits per word" << endl;
ret = ioctl(spi_dev, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
cout << "can't get bits per word" << endl;
/*
* max speed hz
*/
ret = ioctl(spi_dev, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
cout << "can't set max speed hz" << endl;
ret = ioctl(spi_dev, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
cout << "can't get max speed hz" << endl;
cout << "spi mode: " << mode << endl;
cout << "bits per word: " << bits << endl;
cout << "max speed: " << speed/1000 << " [kHz] : " << speed/1000000 << " [MHz]" << endl;
return spi_OK;
}
/* Write data over SPI */
void Outputs::transferSPI(const uint8_t *data, size_t len) {
int ret;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)data,
.rx_buf = 0,
.len = (unsigned int)len,
.speed_hz = speed,
.delay_usecs = delay,
.bits_per_word = bits,
};
ret = ioctl(spi_dev, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
cout << "Failed write to " << spi_device << endl;
}
/* Close SPI */
void Outputs::closeSPI (void){
close(spi_dev);
}