Serial Peripheral Interface SPI

The Serial Peripheral Interface is used for high-speed data exchange between the controller and some peripheral devices. There are several modes available.

It consists of three signal lines plus one per peripheral device (called slave). All peripheral devices share the signal lines and use the selecting line exclusivly. For any given data transfer only one of the selecting lines must be at LOW level, all others must be HIGH.

The basic data transfer operation is a data exchange of 8 bits. The sender transmits 8 bits and recieves 8 other bits in return from the communication partner.

The basic forth word is c!@spi which translates to character store/fetch via SPI. It uses the hardware SPI module of the controller and thus the pre-defined pins of it.

Basic Workflow

The built-in SPI module uses a few pins to establish the communication with any device. To distinguish between different SPI attached devices a separate signalling line is used: slave select. Every slave device is connected with one such line. The SPI communication takes place with the one which signalling line is LOW. All other lines have to be HIGH.

The setup of the slave select lines includes two steps: configuring as output and give it HIGH level when idle. Note that a pin that is configured as output will immediatly go to LOW level. This may disturb a SPI slave so after configuring the line direction the port has to go to HIGH exllicitly. When all slave select lines are configured, the remaining SPI setup can take place

\ requires bitnames, quotations and spi loaded
> PORTB 0 portpin: \ define hardware
> to        \ assign ss pin to lib
> is_output        \ short LOW pulse
> high             \ de-select slave
> +spi                    \ turn on SPI module

Data Exchange

Any SPI transfer starts with pulling the slave select line LOW. Now any number of read/write operations may take place. To stop an exchange, the slace select line goes back to HIGH. This signals the slave device that the communication has ended and it usually goes back to a state that awaits a new commication.

The basic c!@spi is the building block for the next words

\ single byte transfers
: c@spi ( -- c ) 0 c!@spi ;
: c!cpi ( c -- ) c!@spi drop ;

\ read len bytes from SPI and store
\ them starting at addr
: n@spi ( addr len -- )
   0 ?do
    c@spi over c! 1+
   loop drop ;
\ write len bytes from addr to SPI
: n!spi ( addr len --- )
   0 ?do
    dup c@ c!spi 1+
   drop ;

The file core/words/n-spi.asm contains speed optimized implementations of the n@spi and n!spi words.


MMC and SD-Cards have an SPI mode which is slower than the usual mode used on PC’s but is simpler to program.

\ standard stuff, only if not already uploaded
#require postpone.frt
#require marker.frt
#require bitnames.frt

\ board definitions
#include netio.frt

\ SPI library
#require quotations.frt
#require 2rvalue.frt
#include spi.frt

\ SD Card specific
#include mmc.frt

The include order of the file is important. The board specfic definitions need to define the words +spi, -spi for global SPI port setup. In addition the commands +mmc and -mmc are used to perform a single communication with the device. The portpin definitions are not used elsewhere but should match the hardware.

PORTB 0 portpin: sdcard
sdcard to

: +mmc
  sdcard low
: -mmc
  sdcard high

After successfully loading these files, the command mmc_init initializes the communication and enables the remaining access. It has to be issued every time the card has changed.

(ATmega640)> mmc_init
(ATmega640)> mmc_CID . cr 10 0 mmc.
 1 50 41 53 30 32 47 46 12 39 B6 28 D6 0 B4 99  ok