or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apidiff.mdconstraints.mdebnf.mderrors.mdevent.mdgorelease.mdindex.mdio-i2c.mdio-spi.mdjsonrpc2.mdmaps.mdmmap.mdmodgraphviz.mdrand.mdshiny.mdslices.mdslog.mdstats.mdsumdb.mdtrace.mdtxtar.mdtypeparams.mdutf8string.md
tile.json

io-spi.mddocs/

golang.org/x/exp/io/spi

The golang.org/x/exp/io/spi package provides a comprehensive interface for reading from and writing to SPI (Serial Peripheral Interface) devices. It includes both the main spi package for device communication and the spi/driver package that defines interfaces for SPI driver implementations.

Package Information

  • Package Name: golang.org/x/exp/io/spi
  • Package Type: Go (golang)
  • Language: Go
  • Installation: go get golang.org/x/exp/io/spi
  • Status: Deprecated (actively maintained alternatives available at https://periph.io/)

Core Imports

import (
	"golang.org/x/exp/io/spi"
	"golang.org/x/exp/io/spi/driver"
	"time"
)

Basic Usage

package main

import (
	"fmt"
	"golang.org/x/exp/io/spi"
	"golang.org/x/exp/io/spi/driver"
	"time"
)

func main() {
	// Create a devfs opener for Linux /dev/spidev interface
	opener := &spi.Devfs{
		Dev:      "/dev/spidev0.0",
		Mode:     spi.Mode0,
		MaxSpeed: 1000000,
	}

	// Open the SPI device
	device, err := spi.Open(opener)
	if err != nil {
		fmt.Println("Error opening device:", err)
		return
	}
	defer device.Close()

	// Configure the device
	device.SetBitsPerWord(8)
	device.SetMaxSpeed(1000000)
	device.SetDelay(time.Microsecond)

	// Perform a duplex transaction (write and read simultaneously)
	write := []byte{0xAA, 0xBB, 0xCC}
	read := make([]byte, len(write))

	if err := device.Tx(write, read); err != nil {
		fmt.Println("Error in transaction:", err)
		return
	}

	fmt.Printf("Sent: %v\n", write)
	fmt.Printf("Received: %v\n", read)
}

Architecture

The spi package follows a driver-based architecture with two main layers:

  1. High-level Device API (spi package): Provides Device struct and configuration methods for end-user applications
  2. Driver Interface (spi/driver package): Defines interfaces (Conn, Opener) that SPI driver implementations must satisfy

The architecture supports multiple SPI implementations through the driver interface:

  • Devfs: Linux devfs-based driver for /dev/spidev* devices
  • Custom implementations can be created by implementing the driver.Opener and driver.Conn interfaces

Capabilities

Device Communication

Communication with SPI devices through full-duplex transactions (simultaneous read and write).

package spi

func Open(o driver.Opener) (*Device, error)

Opens an SPI device using the provided driver opener and returns a Device instance.

Parameters:

  • o (driver.Opener): The driver opener that implements the interface for opening SPI connections

Returns:

  • (*Device, error): A pointer to the Device or an error if opening fails

Device Configuration

Configuration methods to set SPI communication parameters on an open Device.

func (d *Device) SetMode(mode Mode) error

SetMode sets the SPI mode. SPI mode is a combination of polarity and phases. CPOL (clock polarity) is the high order bit, CPHA (clock phase) is the low order bit. Pre-computed mode values are Mode0, Mode1, Mode2 and Mode3. The value can be changed by the SPI device's driver.

func (d *Device) SetMaxSpeed(speed int) error

SetMaxSpeed sets the maximum clock speed in Hz. The value can be overridden by the SPI device's driver.

func (d *Device) SetBitsPerWord(bits int) error

SetBitsPerWord sets how many bits it takes to represent a word, for example, 8 represents 8-bit words. The default is 8 bits per word.

func (d *Device) SetBitOrder(o Order) error

SetBitOrder sets the bit justification used to transfer SPI words. Valid values are MSBFirst and LSBFirst.

func (d *Device) SetDelay(t time.Duration) error

SetDelay sets the amount of pause that will be added after each frame write. Some SPI devices require a minimum amount of wait time after each frame transmission.

func (d *Device) SetCSChange(leaveEnabled bool) error

SetCSChange sets whether to leave the chipselect enabled after a Tx operation. When true, the chipselect remains active after a transaction completes.

SPI Transactions

Perform duplex SPI transactions (simultaneous read and write operations).

func (d *Device) Tx(w, r []byte) error

Tx performs a duplex transmission to write w to the SPI device and read len(r) bytes into r. The user should not mutate w and r until this call returns. Both write and read buffers are processed simultaneously.

Parameters:

  • w ([]byte): Data to write to the SPI device. If nil, no data is written.
  • r ([]byte): Buffer to read data into. If nil, no data is read. Must be equal length to w.

Returns:

  • error: An error if the transaction fails

Resource Management

Properly close and release SPI device resources.

func (d *Device) Close() error

Close closes the SPI device and releases the related resources. Should be called when the device is no longer needed.

Types

SPI Modes and Constants

type Mode int

const (
	Mode0 = Mode(0)
	Mode1 = Mode(1)
	Mode2 = Mode(2)
	Mode3 = Mode(3)
)

Mode represents the SPI mode number where clock polarity (CPOL) is the high order bit and clock edge (CPHA) is the low order bit. The four modes are:

  • Mode0: CPOL=0, CPHA=0
  • Mode1: CPOL=0, CPHA=1
  • Mode2: CPOL=1, CPHA=0
  • Mode3: CPOL=1, CPHA=1
type Order int

const (
	MSBFirst = Order(0)
	LSBFirst = Order(1)
)

Order is the bit justification to be used while transferring words to the SPI device. MSB-first encoding is more popular than LSB-first.

Device Types

type Device struct {
	// Has unexported fields.
}

Device represents an open SPI device connection. It provides methods to configure the SPI device and perform transactions.

type Devfs struct {
	Dev      string
	Mode     Mode
	MaxSpeed int64
}

Devfs is an SPI driver that works against the Linux devfs. You need to have loaded the "spidev" Linux kernel module to use this driver.

Fields:

  • Dev (string): The device to be opened. Device name is usually in the /dev/spidev<bus>.<chip> format. Required.
  • Mode (Mode): The SPI mode. SPI mode is a combination of polarity and phases. CPOL is the high order bit, CPHA is the low order. Pre-computed mode values are Mode0, Mode1, Mode2 and Mode3. The value of the mode argument can be overridden by the device's driver. Required.
  • MaxSpeed (int64): The max clock speed in Hz and can be overridden by the device's driver. Required.
func (d *Devfs) Open() (driver.Conn, error)

Open opens the provided device with the specified options and returns a connection implementing the driver.Conn interface.

Driver Package API

The golang.org/x/exp/io/spi/driver package defines the interfaces that SPI driver implementations must satisfy.

Driver Constants

const (
	Mode     = iota
	Bits
	MaxSpeed
	Order
	Delay
	CSChange
)

These constants represent configuration keys used with the Configure method:

  • Mode: The SPI mode (valid values are 0, 1, 2 and 3)
  • Bits: Bits per word (default is 8-bit per word)
  • MaxSpeed: The max clock speed (in Hz)
  • Order: Bit order to be used in transfers. Zero value represents MSB-first, non-zero values represent LSB-first encoding
  • Delay: The pause time between frames (in microseconds)
  • CSChange: Whether to leave the device's chipselect active after a Tx

Driver Interfaces

type Conn interface {
	Configure(k, v int) error
	Tx(w, r []byte) error
	Close() error
}

Conn is a connection to an SPI device. It must be implemented by SPI driver implementations.

Methods:

  • Configure(k, v int) error: Configures the SPI device with configuration keys and values. Available configuration keys are Mode, Bits, MaxSpeed, Order, Delay, and CSChange. SPI devices can override these values. Returns an error if configuration fails.
  • Tx(w, r []byte) error: Performs a SPI transaction: w is written if not nil, the result is put into r if not nil. len(w) must be equal to len(r), otherwise the driver should return an error.
  • Close() error: Frees the underlying resources and closes the connection.
type Opener interface {
	Open() (Conn, error)
}

Opener is an interface to be implemented by the SPI driver to open a connection to an SPI device. Returns a Conn implementation and an error if opening fails.

Advanced Usage Examples

Configuring SPI for Specific Requirements

package main

import (
	"fmt"
	"golang.org/x/exp/io/spi"
	"time"
)

func configureSPIDevice() {
	opener := &spi.Devfs{
		Dev:      "/dev/spidev0.0",
		Mode:     spi.Mode3,
		MaxSpeed: 5000000,
	}

	device, err := spi.Open(opener)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer device.Close()

	// Set 16-bit word size (useful for some sensors/devices)
	device.SetBitsPerWord(16)

	// Set clock speed to 5 MHz
	device.SetMaxSpeed(5000000)

	// Add 10 microsecond delay between frames
	device.SetDelay(10 * time.Microsecond)

	// Keep chipselect active between transactions
	device.SetCSChange(true)

	// Use LSB-first bit order
	device.SetBitOrder(spi.LSBFirst)

	fmt.Println("Device configured successfully")
}

Multiple Register Writes

package main

import (
	"fmt"
	"golang.org/x/exp/io/spi"
)

func writeRegisters(device *spi.Device, registers []byte) error {
	// SPI registers are typically read/write in single transaction
	response := make([]byte, len(registers))

	err := device.Tx(registers, response)
	if err != nil {
		return fmt.Errorf("register write failed: %w", err)
	}

	fmt.Printf("Wrote registers: %v\n", registers)
	fmt.Printf("Response: %v\n", response)
	return nil
}

Reading Sensor Data

package main

import (
	"fmt"
	"golang.org/x/exp/io/spi"
)

func readSensorData(device *spi.Device) ([]byte, error) {
	// Send dummy bytes to read data (SPI requires simultaneous write/read)
	command := []byte{0x00, 0x00, 0x00, 0x00}
	response := make([]byte, len(command))

	err := device.Tx(command, response)
	if err != nil {
		return nil, fmt.Errorf("sensor read failed: %w", err)
	}

	return response, nil
}

Error Handling

The spi package returns errors in the following scenarios:

  1. Device Opening Errors: Returned by Open() if the device cannot be opened or the opener fails
  2. Configuration Errors: Returned by configuration methods if the device driver doesn't support the requested configuration
  3. Transaction Errors: Returned by Tx() if the SPI transaction fails
  4. Close Errors: Returned by Close() if resource cleanup fails

Example error handling:

if err := device.SetMaxSpeed(speed); err != nil {
	fmt.Printf("Failed to set SPI speed: %v\n", err)
}

if err := device.Tx(write, read); err != nil {
	fmt.Printf("SPI transaction failed: %v\n", err)
}

Implementing Custom SPI Drivers

To create a custom SPI driver, implement the driver.Opener and driver.Conn interfaces:

package customspi

import (
	"golang.org/x/exp/io/spi/driver"
)

type CustomOpener struct {
	// Custom configuration fields
}

func (o *CustomOpener) Open() (driver.Conn, error) {
	// Implementation to open connection
	return &CustomConn{}, nil
}

type CustomConn struct {
	// Connection state
}

func (c *CustomConn) Configure(k, v int) error {
	// Implement configuration handling
	return nil
}

func (c *CustomConn) Tx(w, r []byte) error {
	// Implement SPI transaction
	if len(w) != len(r) {
		return fmt.Errorf("write and read buffer lengths must match")
	}
	// Perform actual SPI transfer
	return nil
}

func (c *CustomConn) Close() error {
	// Implement resource cleanup
	return nil
}

Important Notes

  • Deprecated Package: This package is no longer actively maintained. Consider using https://periph.io/ for new projects and actively supported cross-platform SPI support.
  • Linux-Specific: The Devfs driver requires the Linux "spidev" kernel module to be loaded.
  • Buffer Length Requirements: In Tx operations, the write and read buffers must be of equal length.
  • Concurrent Access: The Device struct should not be accessed concurrently from multiple goroutines without synchronization.
  • Device Permissions: SPI devices typically require root or specific group permissions to access on Linux systems.