lec_11_SPI
lec_11_SPI
By
Dr. Han-Way Huang
Minnesota State University, Mankato
05/13/2025 1
Overview of Serial Peripheral Interface (SPI)
A synchronous serial communication interface specification used for
short distance communication between a MCU and a peripheral chip.
Developed by Motorola.
Devices in an SPI system are divided into the master and slaves. Only
the master device can initiate transfer.
Four SPI signals:
SCLK: serial clock (output from Master)
MOSI: master out slave in (master output)
MISO: master in slave out (slave output)
SS: slave select (often active low)
05/13/2025 2
Overview of Serial Peripheral Interface (SPI)—(continued)
SPI utilize the upper 4 pins of PORTC, D, E, and F
Pin 7: SCLK, Pin 6: MISO, Pin 5: MOSI, Pin 4: SS pin
These SPI modules are named as SPIC, SPID, SPIE, & SPIF.
05/13/2025 3
Overview of SPI—(continued)
SCLK SCLK
SPI MOSI MOSI SPI
Master MISO MISO Slave
SS SS
05/13/2025 4
SPI Data Transmission
SPI master configures the clock—the frequency must be
supported by the slave device.
The master selects a slave device—by asserting the SS
signal (low?).
The SPI master starts a data transfer by writing into the
data register.
The master sends a bit on the MOSI pin and the slave reads it
in every SCLK cycle, while the slave sends a bit on the MISO
line and the master reads it.
Data is usually shifted out most significant bit first. The master
05/13/2025 5
SPI Data Transmission—(continued)
Master Slave
SCLK
MOSI
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
MISO
05/13/2025 6
Clock Polarity and Phase
The SPI master must specify the SCLK clock polarity when
idle and phase in data transfer.
The clock polarity and phase are named as CPOL and
CPHA.
The combinations of CPOL and CPHA are given in Figure S9.3.
The SCLK is idle low when CPOL = 0. Otherwise, SCLK is idle
high.
CPHA determines when data (on MOSI and MISO) is sampled.
When CPHA = 0, data (on MOSI and MISO) is sampled on the
05/13/2025
leading edge. Otherwise, data is sampled on the trailing edge 7
CPOL=0
SCLK
CPOL=1
Cycle # 1 2 3 4 5 6 7 8
CPHA=0 MISO 1 2 3 4 5 6 7 8 z
MOSI 1 2 3 4 5 6 7 8 z
Cycle # 1 2 3 4 5 6 7 8
CPHA=1 MISO 1 2 3 4 5 6 7 8 z
MOSI 1 2 3 4 5 6 7 8 z
05/13/2025 8
The combinations of CPOL and CPHA are often referred to as
SPI modes (0 to 3). The available modes are listed in Table S9.1.
Atmel AVR XMEGA SPI modes follows Table S9.1b.
Table S9.1a SPI Modes (PIC & ARM) Table S9.1b SPI Modes (others, AVR)
Mode CPOL CPHA Mode CPOL CPHA
0 0 1 0 0 0
1 0 0 1 0 1
2 1 1 2 1 0
3 1 0 3 1 1
05/13/2025 9
SPI Bus Connection Method
Two methods: parallel and serial method
Parallel method is used when the master has the need to
select each individual SPI slave to perform data transfer (shown
in Figure S9.4).
Serial method is used when multiple SPI slaves of the same
type are used at the same time and must receive/transfer data
from/to the master simultaneously (shown in Figure S9.5)—also
called daisy-chained method.
05/13/2025 10
SPI Parallel Connection Method
05/13/2025 11
SPI Serial Connection Method
05/13/2025 12
The XMEGA SPI Module
Four SPI modules associated with PORTC, PORTD, PORTE,
and PORTF.
These four SPI modules are named as SPIC, SPID, SPIE,
and SPIF, respectively.
The upper four pins (pin 7 to 4) of these four ports are used
as SCLK, MISO, MOSI, and SS signals.
Four registers are provided to support the functioning of
each SPI module. These registers are CTRL, INTCTRL,
STATUS, and DATA.
CTRL
05/13/2025Register 13
05/13/2025 14
Table 12.1 Relationship between SCK and the peripheral clock frequency
CLK2X PRESCALE[1:0] SCK frequency
0 00 CLKPER/4 8 MHz
0 01 CLKPER/16 2 MHz
0 10 CLKPER/64 0.5 MHz
0 11 CLKPER/128 0.25 MHz
1 00 CLKPER/2 16 MHz
1 01 CLKPER/8 4 MHz
1 10 CLKPER/32 1 MHz
1 11 CLKPER/64
0.5 MHz
05/13/2025 15
INTCTRL Register
This register enables/disables the SPI interrupt and sets its
priority.
7 6 5 4 3 2 1 0
Reset value
-- -- -- -- -- -- INTLVL[1:0] = 0x00
R R R R R R R/W R/W
INTLVL[1:0]: SPI interrupt level
00 = SPI interrupt disabled
01 = SPI interrupt at low level
10 = SPI interrupt at medium level
11 = SPI interrupt at high level
Figure 12.7 XMEGA SPI interrupt control register (INTCTRL)
05/13/2025 16
Status
Register
7 6 5 4 3 2 1 0 Reset value
IF WRCOL -- -- -- -- -- -- = 0x00
R/W R/W R R R R R R
IF: SPI interrupt flag
When a serial transfer is complete and one byte is completely shifted in/out of the
DATA register, the IF bit is set. If SS is an input and is driven low when the SPI is in
Master mode, this will also set the IF bit. This flag will be cleared when the IF
interrupt service routine is entered. It can also be cleared by first reading STATUS
followed by accessing the DATA register.
WRCOL: Write collision flag
The WRCOL bit is set if the DATA register is written during a data transfer. The
WRCOL bit is cleared by first reading the STATUS register with WRCOL set, and then
accessing the DATA register.
Figure 12.8 XMEGA SPI status register (STATUS)
05/13/2025 17
Port Pins assigned to SPI Signals
Px4: SS’ x = C, D, E, or F
Px5: MOSI
Px6: MISO
Px7: SCK
05/13/2025 18
Example 10.1. Write a subroutine to configure the SPI module
associated with PORTD to operate with the following parameters
assuming that peripheral clock is 32 MHz:
4-MHz shift rate
Interrupt disabled
Master mode
SPI enabled
SCK idle low and sampled data on the rising edge
Data shift most significant bit first
Solution:
05/13/2025 19
initSPID:ldi r16, 0xB0 ; configure PORTD pins 7, 5, & 4 for output
sts PORTD_DIRSET, r16; (SCK:7, MISO:6, MOSI:5, SS:4)
ldi r16, 0x40 ; configure PORTD pin 6 (MISO) for input
sts PORTD_DIRCLR, r16 ; “
ldi r16, 0xF1 ; set baud rate to 4 MHz, master mode, idle
sts SPID_CTRL, r16 ; low, sample data on rising edge
clr r16
sts SPID_INTCTRL, r16 ; disable SPI interrupt
ret
05/13/2025 20
Example 10.2 Write a subroutine to send out a byte passed in r16 via the
SPI module associated with PORTD.
Solution:
putcSPID_xmaster:
sts SPID_DATA, r16 ; write into data register
waitTxx:lds r20, SPID_STATUS ; wait for the byte to be shifted out
sbrs r20, 7 ; “ (check IF bit)
rjmp waitTxx
lds r22, SPID_DATA ; clear the IF flag
ret
05/13/2025 21
Example 10.3 Write a subroutine to read a byte from the SPI module associated
with PORTD and return the byte in r22.
Solution:
getcSPID_Xmaster:
sts SPID_DATA, r16 ; trigger SCLK pulses to shift in data byte
waitRxx: lds r20, SPID_STATUS ; wait for data to shift in
sbrs r20, SPI_IF_bp ; “
rjmp waitRxx
lds r22, SPID_DATA ; return data in r22
ret
05/13/2025 22
Interfacing with Shift Register 74HC595
16
VCC
14 15
QB 1 16 VCC DS QA
1 QB
QC 2 15 QA 2 QC
QD 3 14 DS 3 QD
Shift Latch 4
4 13 register QE
QE OE 5 QF
QF 5 12 LC 11 6 QG
SC
7 QH
QG 6 11 SC
QH 7 10 Reset
Reset 10 9
GND 8 9 SQH SQH
12
LC
13 GND
OE
05/13/2025 23
Pin Function
DS: serial data input
SC: Shift clock. Shift using the rising edge.
LC: Latch clock. Transfer data from shift register to output
buffer using the
rising edge of LC.
OE: Output enable. Asserted low to allow buffer to drive
external pins.
If not asserted (high), the output pins are in high
impedance state.
SQH: Serial data output. The 8th stage output of the shift
05/13/2025 24
Using Two 74HC595 to Drive Up to 8 Seven-Segment
Displays
3.3 V
#7 #6 #0
470 a a
reset QG . . . a
OE QF b b . . . b
. . .
. . .
74HC595 #1
. 470 .
g . g . . . g
QA
DS
SC LC SQH common common common
3.3 V cathode cathode cathode
IMAX
IMAX
MOSI reset
IMAX = 20.85 mA
DS QH
SCK SC
Q. G
.
PD0 LC .
.
. .
OE
QA
XMEGA128A1
IMAX =(3.3 – 0.1– 1.8 ) ¸0.47 ´7 =20.85 mA
74HC595 #2
05/13/2025 25
How to sent data to 74HC595?
The bit at the DS input is shifted into QA during the
first clock cycle.
The bit first shifted in will reach QH after 8 clock
cycles.
First, send out the digit select
Second, send the segment pattern
05/13/2025 26
Example 10.4 Write a program to display 12345678 on displays #7…#0 in the
circuit shown in Figure 12.10.
Solution:
Use time-multiplexing technique to display these eight digits:
.include <atxmega128a1def.inc>
.def lpCnt = r19
.cseg
.org 0x00
jmp start
.org 0xF6
start: ldi r16, low(RAMEND)
out CPU_SPL, r16
ldi r16, high(RAMEND)
out CPU_SPH, r16
call setCPUClkto32Mwith32MIntOsc
call initSPID
05/13/2025 27
forever: ldi ZL,low(seg7Tab << 1)
ldi ZH,high(seg7Tab << 1)
ldi lpCnt,8
loop: lpm r16,Z+
call putcSPID_Xmaster send out digit select value
lpm r16,Z+
call putcSPID_Xmaster send out digit pattern
ldi r16,0x01 ; transfer data to output latch
sts PORTD_OUTCLR, r16 ; by generating a rising edge on the
LC pin
sts PORTD_OUTSET, r16 ; "
ldi r16, 1
call delayby1ms ; time-multiplexing delay
dec lpCnt
brne loop
jmp forever
05/13/2025 28
;
-------------------------------------------------------------------------------------------------------------
-----------------------
; This function configure SPID to master mode, enable it, set baud rate to
; 4 MHz, SCK idle low, sample data on rising edge. Disable interrupt.
;
-------------------------------------------------------------------------------------------------------------
-----------------------
initSPID: ldi r16, 0xB1 ; configure PORTD pins 7,5,4
sts PORTD_DIR, r16 ; for output and pin PD6 for input
ldi r16, 0xF1 ; set baud rate to 4 MHz, master mode,
idle
sts SPID_CTRL, r16 ; low, sample data on rising edge
clr r16
sts SPID_INTCTRL, r16 ; disable SPI interrupt
ret
.include "sysClock_xmega.asm"
.include "delays_xmega.asm"
05/13/2025 .include "spiUtil_xmega.asm" 29
MCP4922 Digital-to-Analog Converter
12-bit resolution, SPI interface with 4.5 ms conversion
time
Maximal data shift rate = 20 MHz
Two-channel outputs
Power supply from 2.7 V to 5.5 V
05/13/2025 30
CS SDI SCK LDAC VDD
Interface Logic
Power-on
Reset
AVSS
Input Input
Register A Register B
VREFB
VREFA
DACA DACB
buffer buffer
SCK 4 11 VREFB Gain Gain
Logic Logic
SDI 5 10 VOUTB
Output
logic
NC 6 9 SHDN
NC 7 8 LDAC
VOUTA SHDN VOUTB
05/13/202 32
5
Example 10.5 Let VDD = VREFA (or VREFB) = 3.3 V & set gain to 1,
reference voltage is
unbuffered. Compute the value to generate 2.5 V output from V OUTA
XMEGA128A1 MCP4922
3.3V
05/13/2025 34
Example 10.5 Write a program to generate the waveform shown in Figure
12.14 from the VOUTA pin using the SPI module associated with PORTD.
3.3 V
time
Figure 12.14 Waveform to be generated
Solution:
Divide one cycle of waveform into 61 points (there are 60 segments in one
cycle).
Calculate the code value corresponding to the voltage of each point (or use
the equation (j × 4095)/60, j = 0~60). (two points separate 68.25)
Place the computed values in a table, stay in an infinite loop, and convert
these values into voltage by sending these values to the MCP4922.
05/13/2025 35
.include <atxmega128A1def.inc>
.def tmp = r18
.def lpCnt = r24
.cseg
.org 0x00
jmp start
.org 0xF6
start: ldi tmp, low(RAMEND)
out CPU_SPL, tmp
ldi tmp, high(RAMEND)
out CPU_SPH, tmp
call setCPUClkto32Mwith32MIntOsc
rcall initSPID
05/13/2025 36
forever: ldi lpCnt, 61
ldi ZL, low(sawtooth << 1)
ldi ZH, high(sawtooth << 1)
loop: lpm r17, z+
lpm r16, z+
call sendMCP4922
dec lpCnt
brne loop
jmp forever
;
-----------------------------------------------------------------------------------------------------
----------------------
; This subroutine enables XMEGA SPID module, shift data at 4 MHz,
select
; master mode, shift data most significant bit first & disable SPI
interrupt.
;
-----------------------------------------------------------------------------------------------------
----------------------
initSPID:ldi r19, 0xD1
05/13/2025 37
sts SPID_CTRL, r19
;
--------------------------------------------------------------------------------------------------------
--------------------------
; This subroutine sends 16-bit value passed in r16:r17 to the MCP4922.
;
--------------------------------------------------------------------------------------------------------
--------------------------
.equ SS = 0x10 ; select pin 4
sendMCP4922:
ldi r20, SS
sts PORTD_OUTCLR, r20 ; pull SS pin low to enable SPI
transfer
call putcSPID_Xmaster ; send the upper byte of the 16-bit
value
mov r16, r17
call putcSPID_Xmaster ; send the lower byte of the 16-bit
value
sts PORTD_OUTSET, r20 ; pull SS high to start DAC
ret
05/13/2025 .include “sysClock_xmega.asm” 38
sawtoothTable:
.dw 0x3000, 0x3044, 0x3089, 0x30CD, 0x3111
.dw 0x3155, 0x319A, 0x31DE, 0x3222, 0x3266
.dw 0x32AB, 0x32EF, 0x3333, 0x3377, 0x33BC
.dw 0x3400, 0x3444, 0x3489, 0x34CD, 0x3511
.dw 0x3555, 0x359A, 0x35DE, 0x3622, 0x3666
.dw 0x36AB, 0x36EF, 0x3733, 0x3777, 0x37BC
.dw 0x3800, 0x3844, 0x3889, 0x38CD, 0x3911
.dw 0x3955, 0x399A, 0x39DE, 0x3A22, 0x3A66
.dw 0x3AAB, 0x3AEF, 0x3B33, 0x3B77, 0x3BBC
.dw 0x3C00, 0x3C44, 0x3C89, 0x3CCD, 0x3D11
.dw 0x3D55, 0x3D9A, 0x3DDE, 0x3E22, 0x3E66
.dw 0x3EAB, 0x3EAB, 0x3F33, 0x3F77, 0x3FBC
.dw 0x3FFF
05/13/2025 39
TC72 Digital Temperature Sensor
10-bit resolution (8 bits for integer, 2 bits for
fraction)
With SPI interface
Made by Microchip
Has four registers—addressed by read and write
address
05/13/2025 40
TC72
VDD Internal
diode
temperature
sensor
Manufacturer
ID register
NC 1 8 VDD 10-bit
sigma Delta
CE 2 7 NC A/D converter
TC72 CE
SCK 3 6 SDI Serial SCK
port SDO
GND 4 5 SDO interface
Temperature SDI
register
GND Control
register
05/13/2025 41
TC72 Registers
Table 12.3 Register for TC72
Read Write Bit Bit Bit Bit Bit Bit Bit Bit Value on
Register
address address 7 6 5 4 3 2 1 0 POR/BOR
05/13/2025 42
General Operation of TC72
Temperature conversion modes: one-shot or continuous
mode.
In continuous mode, TC performs temperature conversion
every 150 ms.
Temperature conversion is started after writing into the control
register.
To access a TC72 register, send out its read or write address.
TC72 has a decrementing address pointer. By sending the
highest register address, the user can read all four registers out
of the TC72.
05/13/2025 43
Table 12.5 TC72 Temperature output data
Binary
Hex Temperature
High Byte/Low Byte
05/13/2025 44
Circuit Connection
TC72 XMEGA128A1U
VDD VDD
0.1F CE PD1
SCK SCK/PD7
SDO MISO/PD6
SDI MOSI/PD5
GND
Figure 12.16 Circuit connection between the TC72 and the XMEGA
05/13/2025 45
Example 10.6 Write an assembly program to read the temperature from the
TC72 every 200 ms.
Solution:
.include <atxmega128A1Udef.inc>
.dseg
.org 0x2000
buf: .byte 10 ; reserve 10 byes to hold temperature reading
.cseg
.org 0x00
jmp start
.org 0xF6
start: ldi r16, low(RAMEND)
out CPU_SPL, r16
ldi r16, high(RAMEND)
out CPU_SPH, r16
call setCPUClkto32Mwith32MIntOsc
call initSPID
05/13/2025 46
forever: ldi r16, 0x20 ; initialize buffer to
sts buf, r16 ; ^^0.00\0
sts buf+1, r16 ; "
ldi r16, 0x30 ; “ (ASCII code of 0)
sts buf+2, r16 ; "
sts buf+4, r16 ; "
sts buf+5, r16 ; "
ldi r16, 0x2E ; store period character
sts buf+3, r16 ; "
clr r16 ; terminate the string with a NULL character
sts buf+6, r16 ; "
call st_Temp_Conversion ; start one-shot mode temperature
conversion
ldi r16,2 ; wait for temperature conversion to
call delayby100ms ; complete
call readTemp ; read back the temperature (in r22:r23)
movw r16,r22 ; transfer temperature reading to r16:r17
ldi ZL,low(buf) ; Z points to buf
ldi ZH,high(buf) ; “
call bin2BCD ; convert to BCD string
05/13/2025 jmp forever 47
;
-----------------------------------------------------------------------------------------------------------------
---------
; This subroutine enables XMEGA SPID module, shift data at 4 MHz, select
; master mode, shift data most significant bit first.
;
-----------------------------------------------------------------------------------------------------------------
---------
initSPID: ldi r16, 0xB2
sts PORTD_DIRSET, r16 ; configure PD7, PD5, PD4, & PD1 for output
ldi r16, 0x40 ; configure PD6 for input
sts PORTD_DIRCLR, r16
ldi r19, 0xF1
sts SPID_CTRL, r19
clr r19
sts SPID_INTCTRL, r19 ; disable SPID interrupt
ret
05/13/2025 48
;
---------------------------------------------------------------------------------------------------------
--------------------------------------
; This subroutine starts a temperature conversion.
;
---------------------------------------------------------------------------------------------------------
--------------------------------------
st_Temp_Conversion:
ldi r16, 0x02 ; drive PD1 (CE signal) high to
sts PORTD_OUTSET, r16 ; enable SPI transfer
ldi r16, 0x80 ; send out control register write
address
call putcSPID_Xmaster ; "
ldi r16, 0x11 ; select one shot conversion
call putcSPID_Xmaster ; "
ldi r16, 0x02
sts PORTD_OUTCLR, r16; start temperature conversion
ret
05/13/2025 49
;
-------------------------------------------------------------------------------------------------------------
-----------
; This subroutine reads back the temperature value.
;
-------------------------------------------------------------------------------------------------------------
-----------
readTemp:
ldi r16,0x02 ; drive PD1 (CE signal) high
sts PORTD_OUTSET, r16 ; to enable SPI transfer
ldi r16, 0x02 ; send out temperature MSB read
address
call putcSPID_Xmaster ; "
call getcSPID_Xmaster ; read back the temperature high byte
mov r23, r22 ; transfer high byte to r23
call getcSPID_Xmaster ; read back the temperature low byte (in
r22)
ldi r16, 0x02
05/13/2025 sts PORTD_OUTCLR, r16 ; terminate the TC72 access. 50
;
---------------------------------------------------------------------------------------------------------------
-----------------------
; This subroutine converts the binary temperature reading into BCD ASCII
string.
;
---------------------------------------------------------------------------------------------------------------
-----------------------
bin2BCD: andi r16,0xC0 ; mask out bit 5 ~ bit 0 of
fraction
sbrs r17, 7 ; skip if sign bit is 1
rjmp normal
com r16 ; find one's complement
com r17 ; “
ldi r19,0x40 ; add 1 to lsb in temperature reading
add r16, r19 ; "
clr r19 ; add carry to upper byte
adc r17,r19 ; "
ldi r19,'-' ; save the minus sign to buffer
05/13/2025 st Z+, r19 ; " 51
ldi r18,0x32 ; fraction part is .25
sts buf+4,r18 ; "
ldi r18,0x35 ; "
sts buf+5,r18 ; "
jmp intpart
chk80: cpi r16,0x80
brne isC0
ldi r16,0x35 ; fraction is .5
sts buf+4,r16
clr r16 ; store a NULL character
sts buf+5,r16 ; “
jmp intpart
isC0: ldi r16,0x37 ; fraction is .75
sts buf+4,r16
ldi r16,0x35
sts buf+5,r16
05/13/2025 52
intpart: mov r16,r17 ; move integer part to r16
clr r17 ; "
ldi r18,10 ; set divisor to 10
clr r19 ; "
call div16U ; perform the division
ldi r19,0x30 ; find the ASCII code of the remainder
add r22,r19 ; "
sts buf+2,r22 ; save the one's digit in buffer
cpi r24,0 ; is the quotient 0?
breq done ; if quotient = 0, then done
mov r16,r24 ; transfer quotient to r17:r16
ldi r18,10 ; set divisor to 10
clr r19 ; "
call div16U ; separate ten's digit
ldi r19,0x30 ; find the ASCII code of ten's digit
add r22,r19 ; "
sts buf+1,r22 ; save the ten's digit in buffer
cpi r24,0 ; is hundred’s digit 0?
05/13/2025 53
breq done
ldi r19,0x31 ; hundred's digit is 1
sts buf, r19 ; save it in buffer
done: ret
.include "div16U.asm"
.include "delays_xmega.asm"
.include "spiUtil_xmega.asm"
.include "sysClock_xmega.asm"
05/13/2025 54