Sign in Join
Wiki copytemporary

AVR Embedded Tutorial - SPI Shiftregister: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: AVR Embedded Tutorial - SPI Shiftregister

OldNewLine
1+# Controlling a 74HC595 shift register via SPI
2+
3+While other shift registers can also be controlled, the SPI compatible [74HC595](https://www.ti.com/product/SN74HC595) has a big advantage over a normal shift register in that it is about 10 times faster (ATmega328). This is noticeable with a larger multiplex.
4+
5+The following functions are described in other tutorials:
6+
7+- [[AVR Embedded Tutorial - Simple GPIO on and off output|GPIO - Output / Input]] - How do I make a GPIO access on the AVR.
8+- [[AVR Embedded Tutorial - SPI|SPI]] - use of the hardware SPI interface.
9+
10+## Atmega328 / Arduino Uno / Nano example
11+
12+### Constants
13+
14+Pins which are required at PORTB for SPI.
15+
16+``` pascal
17+ const
18+ SPI_SlaveSelect = 2;
19+ SPI_MOSI = 3;
20+ SPI_Clock = 5;
21+```
22+
23+### Write to a pin
24+
25+Writing a PORTB pin is used for SlaveSelect and for the software version.
26+
27+``` pascal
28+ procedure WritePortB(Pin : byte; Value : boolean);
29+ begin
30+ if Value then
31+ begin
32+ PORTB := PORTB or (1 shl pin);
33+ end
34+ else
35+ begin
36+ PORTB := PORTB and not (1 shl pin);
37+ end;
38+ end;
39+```
40+
41+### Write character string to the SPI port
42+
43+Writing the string
44+
45+``` pascal
46+ procedure WriteDataHardSPI(p : PByte; len : byte);
47+ var
48+ i : byte;
49+ begin
50+ WritePortB(SPI_SlaveSelect, False);
51+
52+ for i := len - 1 downto 0 do
53+ begin
54+ SPDR := p[i];
55+
56+ while(SPSR and (1 shl SPIF)) = 0 do
57+ begin
58+ end;
59+ end;
60+
61+ WritePortB(SPI_SlaveSelect, True);
62+ end;
63+```
64+
65+### Main program
66+
67+The SPI register is set to the highest possible speed.
68+
69+``` pascal
70+ var
71+ z : uint16;
72+ begin
73+ // All pins set to output
74+ DDRB := (1 shl SPI_MOSI) or (1 shl SPI_Clock) or (1 shl SPI_SlaveSelect);
75+
76+ // Set the SPI register
77+ SPCR := (1 shl SPE) or (1 shl MSTR) or (%00 shl SPR);
78+ SPSR := (1 shl SPI2X);
79+
80+ repeat
81+ // Increment the counter, return to 0 if overflow occurs
82+ Inc(z);
83+
84+ // SPI port described
85+ WriteDataHardSPI(@z, 2); // write 2 bytes.
86+ until 1 = 2;
87+ end.
88+```
89+
90+## ATTiny2313 example
91+
92+The ATTiny2313 has no direct SPI interface, but there is the universal USI interface, which is used here for SPI . This is a little more complicated when writing than with the direct SPI interface of the Atmega.
93+
94+Note: The ATTiny2313 also has MOSI and MISO connections, but these are only used for the programmer. There are DO and DI for this, but they work exactly in the reverse order.
95+
96+### Declarations
97+
98+Pins which are required at PORTB for SPI.
99+
100+A bitpacked record is used for this, so you can access the pins directly.
101+
102+``` pascal
103+ type
104+ TSPIGPIO = bitpacked record
105+ p0, p1, p2, p3, SlaveSelect, DataInt, DataOut, Clock : boolean;
106+ end;
107+
108+ var
109+ SPI_PORT : TSPIGPIO absolute PORTB;
110+ SPI_DDR : TSPIGPIO absolute DDRB;
111+```
112+
113+### Write a character string to the SPI port
114+
115+Writing the string
116+
117+``` pascal
118+ procedure SPIWriteData(p : PByte; len : byte);
119+ var
120+ i : byte;
121+ begin
122+ SPI_PORT.SlaveSelect := False;
123+
124+ for i : = len - 1 downto 0 do
125+ begin
126+ USIDR := p[i];
127+ USISR := 1 shl USIOIF;
128+
129+ repeat
130+ USICR := (%01 shl USIWM) or (%10 shl USICS) or (1 shl USICLK) or (1 shl USITC);
131+ until (USISR and (1 shl USIOIF)) <> 0;
132+ end;
133+
134+ SPI_PORT.SlaveSelect := True;
135+ end;
136+```
137+
138+### Main program - Hardware version
139+
140+This example shows how to write 2 cascaded shift registers.
141+
142+An integer value is simply output.
143+
144+You can also see that no configuration of USI is necessary at the beginning, except that you have to set the ports to output.
145+
146+``` pascal
147+ var
148+ z : uint16 = 0;
149+ begin
150+ SPI_DDR.DataOut := True;
151+ SPI_DDR.Clock := True;
152+ SPI_DDR.SlaveSelect := True;
153+
154+ repeat
155+ Inc(z);
156+ SPIWriteData(@z, 2); // output integer
157+ until 1 = 2;
158+ end.
159+```
160+
161+### Main program - Software version
162+
163+I can show that you can also do software emulation very well here.
164+
165+However, this is about a factor of 10 slower than the hardware version of the Atmega328, you can use any ports for this.
166+
167+The software version has the advantage that it runs on every AVR and can also be used if you need multiple SPI interfaces.
168+
169+``` pascal
170+ procedure WriteDataSoftSPI(p : PByte; len : byte);
171+ var
172+ i , j : byte;
173+ begin
174+ WritePortB(SPI_SlaveSelect, False);
175+
176+ for j := 0 to len - 1 do
177+ begin
178+ for i := 7 downto 0 do
179+ begin
180+ if(p[j] and (1 shl i)) <> 0 then
181+ begin
182+ WritePortB(SPI_MOSI, True);
183+ end
184+ else
185+ begin
186+ WritePortB(SPI_MOSI, False);
187+ end;
188+
189+ WritePortB(SPI_Clock, True);
190+ WritePortB(SPI_Clock, False);
191+ end;
192+
193+ WritePortB(SPI_SlaveSelect, True);
194+ end;
195+ end;
196+```
197+
198+# See also
199+
200+- Overview page - [[AVR Embedded Tutorials]].
201+- [[AVR Embedded Tutorial - Shiftregister|Shift registers]] - How do I control shift registers?
202+- [[AVR Embedded Tutorial - SPI|SPI]] - Use of the hardware SPI interface with an ATmega328 / Arduino.
203+
204+---
205+*From the Free Pascal wiki, page "AVR Embedded Tutorial - SPI Shiftregister", as it stood on 2020-03-08 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history