Skip to content

Serial Interfaces - SPI

Overview

  • Serial Peripheral Interface
  • synchronous serial data bus → additional clock line
  • significantly faster transmissions possible compared to UART
  • The clock signale is controlled by the master.
  • 1 clock edge → Master sends 1 bit to slave, slave sends 1 bit to master
  • after 8 clock edges 1 byte was exchanged between master ⇆ slave

alt: "SPI bus topology with one slave", src:"Wikipedia-Commons", w:50

  • SPI signal lines:
    • SCLK: serial clock
    • MOSI: master out slave in
    • MISO: master in slave out
    • SS or CS: slave or chip select, active low

alt: "SPI bus topology with multiple slaves - multiple SS signals", src: "Wikipedia-Commons", w:50

  • Multiple SS Pins
    • Each slave gets its own SS signal from the master.
    • SS pins on the master are freely usable output pins.
    • All SS pins are set to high, all slaves are deactivated.
    • Only one SS pin is set to low, only one slave is selected.
    • The communication over SPI works normally, deactivated slaves ignore the SPI signal.

SPI Slave Example: SPIRIT1

SPIRIT1

  • The SPIRIT1 is a sub-Gigahertz wireless transceiver.
  • Multiple register can be read and written to configure proper wireless data transmission and reception.
  • In WuRx-API multiple modules are written to simplify the SPIRIT1 setup procedure.
  • In this section only the reading of one register is shown as an example.
  • Register 0xF0 contains information on part number and firmware version of the SPIRIT1.

Connections

  • SPI is supported by the MSP430's USCIA and USCIB.
  • The SPIRIT1 is connected to SPIA (see previous section).
  • Summary of SPIRIT1 pinout
SPIRIT1 MSP430
MISO P1.1
MOSI P1.2
SCK P1.4
IRQ P2.0
SPIRIT_CS P2.1 (active low)
AS3933_CS P2.5 (active high!)
  • The MOSI, MISO and SCK pins are fixed to MSP430's USCIA pins.
  • For CS nearly every pin of the MSP430 can be used. This pin is used as a regular digital output to enable the CS on communication.
  • Only one SPI slave can be active at a time → AS3933 must be disabled by AS3933_CS=L.

MSP430 Registers

alt: "Register UCxxCTL0 for SPI", src: "Family Guide, page 445", w:75

alt: "Register UCxxCTL1 for SPI", src: "Family Guide, page 445", w:75

Step-by-Step Instruction

Initialization

  • USCI Initialization:

    UCA0CTL1 |= UCSWRST;
    UCA0CTL0 = UCCKPH + UCMSB + UCMST + UCSYNC; // SPI phase, MSB first, master mode, synchronous
    UCA0BR0 = 1;
    UCA0BR1 = 0;
    UCA0CTL1 = UCSSEL_2;
    
    • Hold the reset bit while initialization.
    • MSB first (UCMSB)
    • MSP430 is master (UCMST)
    • Setting Clock Phase and Clock Polarity
      alt: "Setting USCIs Clock Phase and Polarity", src: "Family Guide, page 442", w:75
      • Clock Phase and Polarity needs to be set according the device.
      • The industry quasi standard is:
        • Base value of SCLK is zero.
        • Data are read on the clock's rising edge and data are changed on the clock's falling edge.
        • Resulting in UCCKPL=0 and UCCKPH=1.
    • The SMCLK is used as clock reference (UCSSEL_2).
    • The clock divider is set to 1.

      \[ \text{Divider} = \text{UCB0BR0} + 256 \cdot \text{UCB0BR1} \]
    • Resulting a SPI clock frequency of 1 MHz.
    • The reset bit is released while writing to UCB0CTL1 = UCSSEL_2.

Reading PARTNUM and VERSION

As an example we want to read the values PARTNUM and VERSION from register 0xF0:

  • Enable the device by pulling the CS pin to L.
  • The first byte that needs to be transferred has the value 0x01 (reading from a register, see datasheet p. 76).
  • Use the following flag polling loop to ensure, no communication is running.

    while (!(IFG2 & UCA0TXIFG)) {
    }
    
  • When writing to the UCA0TXBUF register the transmission is started.

    UCA0TXBUF = 0x01;
    
  • Add another flag polling loop before sending the second byte. This byte is the register address 0xF0.
  • After this SPI interaction the SPIRIT1 will send the data bytes.
  • We need to send any byte over SPI, wait from transmission complete and store the received byte.
while (!(IFG2 & UCA0TXIFG)) {
}
UCA0TXBUF = 0x00;
while (!(IFG2 & UCA0TXIFG)) {
}
uint8_t partnum = UCA0RXBUF;
  • The same is repeated with the second byte, containing the version number instead.
UCA0TXBUF = 0x00;
while (!(IFG2 & UCA0TXIFG)) {
}
uint8_t version = UCA0RXBUF;
  • Disable the device by pulling the CS pin to H.

Displaying the result

  • No UART output can be used, so the LED will be lit up, when the received values are correct.
  • The partnum=0x01 and version=0x30 corresponds the current SPIRIT1 devices.
  • Light up the LED and wait before repeating the SPI interaction.
if (partnum == 0x01 && version == 0x30) {
    p_led_h();
}
p_delay_ms(500);
p_led_l();
p_delay_ms(500);

SPI Library Module and Debug Terminal

  • The debug terminal is a very useful tool to debug the WuRx-API.
  • On startup tests are run to ensure the corresponding hardware (wireless transceiver, WuRx, sensor, ...) correspond as expected.
  • During program execution additional debug messages are printed.

Warning

Only for older versions of the WuRx hardware

  • Problem: USCIA is used for SPI, wireless transceiver and WuRx are connected
  • Solution: Switch to UART mode for output of debug messages, switch back after that
  • Consequences:
    • term_log_begin() and term_end() must be called at the beginning and end of every debug message
    • Debug messages during an SPI interaction are not allowed
    • Some corrupted characters appear on UART terminal, due to SPI interactions
    • Note that debug messages delay the program flow.
    • e.g. transmission of 10 character: 10 · 8 bit / 9600 baud = 8.33 ms

Warning

For the Kokowei versions of the WuRx hardware

  • USCIA is only used for UART communication and terminal output
  • USCIB is used for both SPI and I²C communication
  • Because I²C is only used for sensors, reconfiguring USCIB back to SPI mode is necessary after I²C
  • The SPI library is optimized for speed.
  • With SPIA_USE_FLAG_POLLING disabled the MSP430 uses __delay_cycles() to wait a precise time until the next byte is transferred.
  • Call spia_init() to initialize the module. (Pins have to be connected separably!)
  • Use spia_send() to send data to the slave. The slave's answer is discarded.
  • Use spia_transmit() to send and receive data. This function is slower than spia_send()!
  • Call spia_wait_cs_disable() before releasing the CS pin.

Info

Using the SPI library and debug terminal output
This source code is only for your information. You are missing essential files to run the program on your device. Nevertheless, study the code carefully.

#include <msp430.h>
#include <stdint.h>

#include "pins.h"
#include "term.h"
#include "spia.h"

int main(void) {
    WDTCTL = WDTPW | WDTHOLD;    // stop watchdog timer

    p_setup();
    spia_init();
    term_wait_and_clear();

    p_as3933_cs_l(); // disable AS3933

    while (1) {
        p_spirit_cs_l();
        spia_send(0x01);
        spia_send(0xF0);
        uint8_t partnum = spia_transmit(0);
        uint8_t version = spia_transmit(0);
        p_spirit_cs_h();

        term_log_begin();
        term_print("PARTNUM = 0x");
        term_hex(partnum, 2);
        term_print(", VERSION = 0x");
        term_hex(version, 2);
        term_print(" ");
        term_test_result(partnum == 0x01 && version == 0x30);
        term_end();

        p_delay_ms(1000);
    }
}