Using SPI Peripheral on a PIC64GX Curiosity Board from Linux® Userspace
Introduction
The Serial Peripheral Interface (SPI) protocol is a widely used solution that facilitates high-speed, synchronous data transfer. This article focuses on the SPI capabilities of the PIC64GX MPU series.
This guide will walk you through the basics of using the SPI Peripheral on a PIC64GX Curiosity Board from the Linux® userspace using the spidev_test utility and writing C application code.
Prerequisites
This application is developed for the CURIOSITY-PIC64GX1000-KIT-ES development platform. We can platform related documentation using the following links:
Get Familiar with Inter-Integrated Circuit (I2C):
Hardware Setup
For this application, we will need:
- CURIOSITY-PIC64GX1000-KIT-ES
- Linux Host PC
- Digital Logic Analyzer (not required, but useful for observing and analyzing signals)
Yocto Project® Configurations
Objectives:
- Setting up the Yocto Project Building Environment
- Configuring the Device Tree to enable the SPI Peripheral
- Including the spidev_test utility tool software package
- Build the image and deploy
Setting up the Yocto Project Building Environment
Follow the steps in the GitHub PIC64GX Yocto Project BSP Repo Readme to set up the Bitbake environment and build a core-image-minimal-dev Linux image with the default configuration.
Build a core-image-minimal-dev Linux image with default configuration:
Make sure that the initial Linux Image has been built without error, so we can start doing the modifications.
Device Tree
To modify the Device Tree, we will use devtool to extract, edit, and rebuild the necessary files before integrating the changes into the build system.
Set up the Bitbake environment:
Extract the source code of the pic64gx-linux recipe into a workspace for modification:
Locate the PIC64GX Curiosity Kit Device Tree Source for Modification:
Enable the SPI1 and add spidev as sub-node in the Device Tree Source:
2
3
4
5
6
7
8
9
10
status = "okay";
spidev@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "microchip,pic64gx-spidev";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
spidev Driver
We need to bind the Device Tree Node to the spidev driver code to use this node.
Locate the spidev.c Source Code for Modification:
Add the compatible string to the spi_device_id and of_device_id tables:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{ .name = "bh2228fv" },
..
{ .name = "si3210" },
{ .name = "pic64gx-spidev" },
{},
};
..
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "cisco,spi-petra", .data = &spidev_of_check },
..
{ .compatible = "silabs,si3210", .data = &spidev_of_check },
{ .compatible = "microchip,pic64gx-spidev", .data = &spidev_of_check },
{},
};
Line 5 and 13 are added.
Adding spidev_test Software Package
To run SPI test commands from the Linux userspace, we need to include the spidev_test utility software package in our Linux build. This utility allows us to interact with SPI peripherals from the userspace.
Locate the local.conf file, which defines various configuration variables that govern the OpenEmbedded build process:
Define the additional software packages to be installed by setting the CORE_IMAGE_EXTRA_INSTALL variable at the end of the config file.
This variable specifies the packages to be added on top of the default software package list defined for your base image. In our case, the base image is core-image-minimal-dev:
Linux Kernel Configuration
The SPI and SPI device driver (spidev) must be enabled in your Linux kernel configuration.
Open the terminal and navigate to the build directory and run the following command to run the Linux Kernel configuration:
Navigate to Device Drivers and enable the SPI Support:
Save the config file and exit.
Now that we've done all the necessary changes in the workspace, we can rebuild the linux-kernel recipe with devtool:
Build and Deploy the Linux Image on PIC64GX Curiosity Board
After completing all the necessary configurations, we can build the image:
After the build is done, you can locate your built image at:
Follow the GitHub instructions to deploy the built image to the SD card.
After booting the Linux on Curiosity board, check if the spidev has appeared under the devices:
spidev0.0
If nothing returns, that means the spidev is not enabled and you have to double-check the configuration and DTS modification needed to be done.
Hardware
For this application, we will be controlling SPI_1, which is connected to the mikroBUS™ socket of the Curiosity board.
We can check the SPI pins that are connected to the mikroBUS socket from the PIC64GX1000 Curiosity Kit Schematics.
PIC64 Pin | mikroBUS Pin |
---|---|
SPI_1_CLK (B)/GPIO_1_16 | SCK/RC3 |
SPI_1_SS0 (B)/GPIO_1_17 | CS/RE0 |
SPI_1_DO (B)/GPIO_1_18 | MOSI/RC5 |
SPI_1_DI (B)/GPIO_1_19 | MISO/RC4 |
mikroBUS Socket of the Curiosity Board
Software
spidev_test Utility
spidev_test is a utility program used to test and verify the functionality of SPI devices on Linux systems. It is typically used to send and receive data over SPI to ensure that the SPI bus and connected devices are working correctly.
The spidev_test program interacts with the SPI device driver through the /dev/spidevX.Y device files, where X and Y represent the bus and chip select numbers, respectively. It allows users to perform basic read and write operations, configure SPI settings such as mode, speed, and word length, and observe the data exchanged between the master (usually the microcontroller or processor) and the SPI slave device.
spidev_test Commands
Here is a list of arguments that can be used with spidev_test command:
spidev_test: unrecognized option '--help'
Usage: spidev_test [-2348CDFHILMNORSZbdilopsv]
general device settings:
-D --device device to use (default /dev/spidev1.1)
-s --speed max speed (Hz)
-d --delay delay (usec)
-l --loop loopback
spi mode:
-H --cpha clock phase
-O --cpol clock polarity
-F --rx-cpha-flip flip CPHA on Rx only xfer
number of wires for transmission:
-2 --dual dual transfer
-4 --quad quad transfer
-8 --octal octal transfer
-3 --3wire SI/SO signals shared
-Z --3wire-hiz high impedance turnaround
data:
-i --input input data from a file (e.g. "test.bin")
-o --output output data to a file (e.g. "results.bin")
-p Send data (e.g. "1234\xde\xad")
-S --size transfer size
-I --iter iterations
additional parameters:
-b --bpw bits per word
-L --lsb least significant bit first
-C --cs-high chip select active high
-N --no-cs no chip select
-R --ready slave pulls low to pause
-M --mosi-idle-low leave mosi line low when idle
misc:
-v --verbose Verbose (show tx buffer)
Using spidev_test on PIC64GX Curiosity Board
If you're interested in observing the generated signals, you can connect the Logic Analyzer to the mikroBUS socket and perform some tests.
Let us send a test SPI pattern of "123456789" with a 1 MHz frequency and observe the generated signal:
We can also perform a loopback test by shorting the MOSI and MISO pins with a jumper cable. We should get a response like this:
spi mode: 0x0
bits per word: 8
max speed: 1000000 Hz (1000 kHz)
TX | 31 32 33 34 35 36 37 38 39 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ |123456789|
RX | 31 32 33 34 35 36 37 38 39 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ |123456789|
The RX and TX values must be identical in case of loopback.
Application Programming in C Language Using spidev with ioctl
We can write a C program that runs from userspace and interacts with SPI devices using the ioctl functions. The example below is a C code that performs the same loopback test we did earlier.
Boot Linux on your Curiosity board, and navigate to /media and create spi_loopback.c using vim editor:
Copy and paste C code below to the spi_loopback.c:
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#define SPI_DEVICE "/dev/spidev0.0"
#define SPI_MODE SPI_MODE_0
#define SPI_BITS 8
#define SPI_SPEED 500000
int main() {
int fd;
int ret;
uint8_t tx[] = {0xDE, 0xAD, 0xBE, 0xEF};
uint8_t rx[sizeof(tx)] = {0};
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = sizeof(tx),
.speed_hz = SPI_SPEED,
.bits_per_word = SPI_BITS,
};
// Open the SPI device
fd = open(SPI_DEVICE, O_RDWR);
if (fd < 0) {
perror("Failed to open SPI device");
exit(EXIT_FAILURE);
}
// Set SPI mode
uint8_t mode = SPI_MODE;
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
perror("Failed to set SPI mode");
close(fd);
exit(EXIT_FAILURE);
}
// Set bits per word
uint8_t bits = SPI_BITS;
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1) {
perror("Failed to set bits per word");
close(fd);
exit(EXIT_FAILURE);
}
// Set max speed
uint32_t speed = SPI_SPEED;
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
perror("Failed to set max speed");
close(fd);
exit(EXIT_FAILURE);
}
// Perform SPI transaction
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
perror("Failed to perform SPI transaction");
close(fd);
exit(EXIT_FAILURE);
}
// Print received data
printf("Received: ");
for (int i = 0; i < sizeof(rx); i++) {
printf("%02X ", rx[i]);
}
printf("\n");
// Close the SPI device
close(fd);
return 0;
}
After saving the modification, compile the C code:
Run the gpio executable:
Summary
In this guide, we enabled and tested SPI communication on a PIC64GX Curiosity Board using Yocto Project, including device tree modifications, kernel configuration, and userspace testing with spidev_test and a custom C program. This setup provides a foundation for further development with SPI peripherals on the board.