Using SPI Peripheral on a PIC64GX Curiosity Board from Linux® Userspace

Last modified by Microchip on 2025/05/13 12:23

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
Information

This application article is verified on PIC64GX Yocto Project BSP release 2024.10.

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.

Warning

Ensure that you set your GIT username and user email as global variables on your Linux Host PC:

git config --global user.name "your name"
git config --global user.email "your email"

Back to Top

Build a core-image-minimal-dev Linux image with default configuration:

MACHINE=pic64gx-curiosity-kit bitbake core-image-minimal-dev

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:

. ./meta-pic64gx-yocto-bsp/pic64gx_yocto_setup.sh

Back to Top

Extract the source code of the pic64gx-linux recipe into a workspace for modification:

devtool modify pic64gx-linux

Back to Top

Locate the PIC64GX Curiosity Kit Device Tree Source for Modification:

yocto-dev/build/workspace/sources/pic64gx-linux/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts

Back to Top

Enable the SPI1 and add spidev as sub-node in the Device Tree Source:

1
2
3
4
5
6
7
8
9
10
&spi1 {
 status = "okay";
   spidev@0 {
      #address-cells = <1>;
      #size-cells = <1>;
      compatible = "microchip,pic64gx-spidev";
      spi-max-frequency = <20000000>;
      reg = <0>;   
   };
};
Information

Line 1 enables the SPI1 controller.

Line 2 specifies the SPI1 controller is enabled.

Line 3 defines a child node for an SPI device at chip select 0.

Line 4 sets the address cells for child nodes to 1.

Line 5 sets the size cells for child nodes to 1.

Line 6 specifies the compatible driver as "microchip,pic64gx-spidev".

Line 7 sets the maximum SPI clock frequency to 20 MHz.

Line 8 sets the register address to 0 (chip select line 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:

yocto-dev/build/workspace/sources/pic64gx-linux/drivers/spi/spidev.c

Back to Top

Add the compatible string to the spi_device_id and of_device_id tables:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static const struct spi_device_id spidev_spi_ids[] = {
 { .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. 

Back to Top

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:

yocto-dev/build/conf/local.conf

Back to Top

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:

CORE_IMAGE_EXTRA_INSTALL += "spidev-test"
Information

To check the list of software packages included in your base image by default, refer to the following BitBake file:
yocto-dev/meta-pic64gx-yocto-bsp/meta-pic64gx-bsp/recipes-core/images/core-image-minimal-dev.bbappend

Back to Top

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:

MACHINE=pic64gx-curiosity-kit bitbake pic64gx-linux -c menuconfig

Linux Kernel configuration

Back to Top

Navigate to Device Drivers and enable the SPI Support:

Enable the SPI Support

Back to Top

Save the config file and exit.

Back to Top

Now that we've done all the necessary changes in the workspace, we can rebuild the linux-kernel recipe with devtool:

devtool build pic64gx-linux

Back to Top

Build and Deploy the Linux Image on PIC64GX Curiosity Board

After completing all the necessary configurations, we can build the image:

MACHINE=pic64gx-curiosity-kit bitbake core-image-minimal-dev

Back to Top

After the build is done, you can locate your built image at:

yocto-dev/build/tmp-glibc/deploy/images/pic64gx-curiosity-kit/core-image-minimal-dev-pic64gx-curiosity-kit***.wic

Back to Top

Follow the GitHub instructions to deploy the built image to the SD card.

Back to Top

After booting the Linux on Curiosity board, check if the spidev has appeared under the devices:

root@pic64gx-curiosity-kit:~# ls /dev | grep spidev
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. 

Back to Top

Hardware

For this application, we will be controlling SPI_1, which is connected to the mikroBUS™ socket of the Curiosity board.

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 PinmikroBUS Pin
SPI_1_CLK (B)/GPIO_1_16SCK/RC3
SPI_1_SS0 (B)/GPIO_1_17CS/RE0
SPI_1_DO (B)/GPIO_1_18MOSI/RC5
SPI_1_DI (B)/GPIO_1_19MISO/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:

root@pic64gx-curiosity-kit:~# spidev_test --help
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:

spidev_test -s 1000000 -D /dev/spidev0.0 -p "123456789" -v

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:

root@pic64gx-curiosity-kit:~# spidev_test -s 1000000 -D /dev/spidev0.0 -p "123456789" -v
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.

Back to Top

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:

cd /media && vim spi_loopback.c

Back to Top

Copy and paste C code below to the spi_loopback.c:

#include <stdio.h>
#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;
}

Back to Top

After saving the modification, compile the C code:

gcc spi_loopback.c -o spi_loopback

Back to Top

Run the gpio executable:

./spi_loopback
Success

With the loopback jumper connected, you will get the following response:

root@pic64gx-curiosity-kit:/media# ./spi_loopback
Received: DE AD BE EF

 Loopback jumper disconnected will give you the following response:

root@pic64gx-curiosity-kit:/media# ./spi_loopback
Received: FF FF FF FF

Back to Top

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.

Back to Top