Skip to content

Commit dea81ae

Browse files
Michael StoopsWren6991
authored andcommitted
Added spi/spi_master_slave.
1 parent 4f64f3a commit dea81ae

File tree

10 files changed

+277
-4
lines changed

10 files changed

+277
-4
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on getting up and running.
99

1010
App|Description | Link to prebuilt UF2
1111
---|---|---
12-
[hello_serial](hello_world/serial) | The obligatory Hello World program for Pico (Output over serial version) |
12+
[hello_serial](hello_world/serial) | The obligatory Hello World program for Pico (Output over serial version) |
1313
[hello_usb](hello_world/usb) | The obligatory Hello World program for Pico (Output over USB version) | https://rptl.io/pico-hello-usb
1414
[blink](blink) | Blink an LED on and off. | https://rptl.io/pico-blink
1515

@@ -43,7 +43,6 @@ App|Description
4343
[control_blocks](dma/control_blocks)| Build a control block list, to program a longer sequence of DMA transfers to the UART.
4444
[channel_irq](dma/channel_irq)| Use an IRQ handler to reconfigure a DMA channel, in order to continuously drive data through a PIO state machine.
4545

46-
4746
### Flash
4847

4948
App|Description
@@ -70,7 +69,6 @@ App|Description
7069
---|---
7170
[hello_divider](divider) | Show how to directly access the hardware integer dividers, in case AEABI injection is disabled.
7271

73-
7472
### I2C
7573

7674
App|Description
@@ -90,7 +88,7 @@ App|Description
9088
App|Description
9189
---|---
9290
[hello_multicore](multicore/hello_multicore) | Launch a function on the second core, printf some messages on each core, and pass data back and forth through the mailbox FIFOs.
93-
[multicore_fifo_irqs](multicore/multicore_fifo_irqs) | On each core, register and interrupt handler for the mailbox FIFOs. Show how the interrupt fires when that core receives a message.
91+
[multicore_fifo_irqs](multicore/multicore_fifo_irqs) | On each core, register and interrupt handler for the mailbox FIFOs. Show how the interrupt fires when that core receives a message.
9492
[multicore_runner](multicore/multicore_runner) | Set up the second core to accept, and run, any function pointer pushed into its mailbox FIFO. Push in a few pieces of code and get answers back.
9593

9694
### Pico Board
@@ -151,6 +149,7 @@ App|Description
151149
[mpu9250_spi](spi/mpu9250_spi) | Attach a MPU9250 accelerometer/gyoscope via SPI.
152150
[spi_dma](spi/spi_dma) | Use DMA to transfer data both to and from the SPI simultaneously. The SPI is configured for loopback.
153151
[spi_flash](spi/spi_flash) | Erase, program and read a serial flash device attached to one of the SPI controllers.
152+
[spi_master_slave](spi/spi_master_slave) | Demonstrate SPI communication as master and slave.
154153

155154
### System
156155

@@ -159,6 +158,7 @@ App|Description
159158
[hello_double_tap](system/hello_double_tap) | An LED blink with the `pico_bootsel_via_double_reset` library linked. This enters the USB bootloader when it detects the system being reset twice in quick succession, which is useful for boards with a reset button but no BOOTSEL button.
160159
[narrow_io_write](system/narrow_io_write) | Demonstrate the effects of 8-bit and 16-bit writes on a 32-bit IO register.
161160
[unique_board_id](system/unique_board_id) | Read the 64 bit unique ID from external flash, which serves as a unique identifier for the board.
161+
162162
### Timer
163163

164164
App|Description

spi/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ if (NOT PICO_NO_HARDWARE)
33
add_subdirectory(mpu9250_spi)
44
add_subdirectory(bme280_spi)
55
add_subdirectory(spi_dma)
6+
add_subdirectory(spi_master_slave)
67
endif ()

spi/spi_master_slave/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
if (NOT PICO_NO_HARDWARE)
2+
add_subdirectory(spi_master)
3+
add_subdirectory(spi_slave)
4+
endif ()

spi/spi_master_slave/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# spi_master and spi_slave
2+
3+
These examples demonstrate using the PL022 hardware SPI module both as master and slave. Each program is meant to be written to its own RP2040 module.
4+
5+
The two modules are meant to be wired together as follows. The RP2040 pin numbers are those on the RP2040 chip itself. The Pico pin numbers are mapped to the pads on the Raspberry Pi Pico board:
6+
7+
| Function | Master pin (RP2040) | Slave pin (RP2040) | Master pin (Pico) | Slave pin (Pico) |
8+
|-|-|-|-|-|
9+
| MOSI | DO0 | DI0 | 25 | 21 |
10+
| SCLK | SCK0 | SCK0 | 24 | 24 |
11+
| GND | GND | GND | 23 | 23 |
12+
| CS | CS0 | CS0 | 22 | 22 |
13+
| MISO | DI0 | DO0 | 21 | 25 |
14+
15+
You will also want to power the boards and at least watch the master's output using a serial terminal.
16+
17+
It is possible to execute one of the programs without the other, but the master will receive no response, as there is no slave listening. The slave program will initialize, but will neither transmit nor receive anything because there will be no clock pulses.
18+
19+
## Outputs
20+
21+
The slave will be confused if it starts while the master is transmitting. The easiest solution is to stop both, start the slave first, the master second.
22+
23+
The master should output the following:
24+
25+
```
26+
SPI master example
27+
SPI master initialised.
28+
The following buffer will be written to MOSI endlessly.
29+
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
30+
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
31+
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
32+
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
33+
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
34+
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
35+
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
36+
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
37+
80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
38+
90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
39+
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af
40+
b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
41+
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf
42+
d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df
43+
e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef
44+
f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
45+
Read the following from MISO, page 0:
46+
ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0
47+
ef ee ed ec eb ea e9 e8 e7 e6 e5 e4 e3 e2 e1 e0
48+
df de dd dc db da d9 d8 d7 d6 d5 d4 d3 d2 d1 d0
49+
cf ce cd cc cb ca c9 c8 c7 c6 c5 c4 c3 c2 c1 c0
50+
bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
51+
af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0
52+
9f 9e 9d 9c 9b 9a 99 98 97 96 95 94 93 92 91 90
53+
8f 8e 8d 8c 8b 8a 89 88 87 86 85 84 83 82 81 80
54+
7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70
55+
6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60
56+
5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50
57+
4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40
58+
3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30
59+
2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20
60+
1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10
61+
0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
62+
```
63+
64+
Additional pages follow, all containing the same content.
65+
66+
The slave should output the following:
67+
68+
```
69+
SPI slave example
70+
SPI slave initialised.
71+
When reading from MOSI, the following buffer will be written to MISO:
72+
ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0
73+
ef ee ed ec eb ea e9 e8 e7 e6 e5 e4 e3 e2 e1 e0
74+
df de dd dc db da d9 d8 d7 d6 d5 d4 d3 d2 d1 d0
75+
cf ce cd cc cb ca c9 c8 c7 c6 c5 c4 c3 c2 c1 c0
76+
bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
77+
af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0
78+
9f 9e 9d 9c 9b 9a 99 98 97 96 95 94 93 92 91 90
79+
8f 8e 8d 8c 8b 8a 89 88 87 86 85 84 83 82 81 80
80+
7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70
81+
6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60
82+
5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50
83+
4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40
84+
3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30
85+
2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20
86+
1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10
87+
0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
88+
```
89+
90+
The slave doesn't write anything else to the console.
91+
92+
## Electrical signals
93+
94+
The communication looks like this when seen through a logic analyzer.
95+
96+
![spi_master_slave.png](spi_master_slave.png)
97+
98+
Original [data file](spi_master_slave.logicdata) is included.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
add_executable(spi_master
2+
spi_master.c
3+
)
4+
5+
# Pull in basic dependencies
6+
target_link_libraries(spi_master pico_stdlib hardware_spi)
7+
8+
# create map/bin/hex file etc.
9+
pico_add_extra_outputs(spi_master)
10+
11+
# add url via pico_set_program_url
12+
example_auto_set_url(spi_master)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Copyright (c) 2021 Michael Stoops. All rights reserved.
3+
* Portions copyright (c) 2021 Raspberry Pi (Trading) Ltd.
4+
*
5+
* SPDX-License-Identifier: BSD-3-Clause
6+
*/
7+
8+
// Example of an SPI bus master using the PL022 SPI interface
9+
10+
#include <stdio.h>
11+
#include "pico/stdlib.h"
12+
#include "pico/binary_info.h"
13+
#include "hardware/spi.h"
14+
15+
#define BUF_LEN 0x100
16+
17+
void printbuf(uint8_t buf[], size_t len) {
18+
int i;
19+
for (i = 0; i < len; ++i) {
20+
if (i % 16 == 15)
21+
printf("%02x\n", buf[i]);
22+
else
23+
printf("%02x ", buf[i]);
24+
}
25+
26+
// append trailing newline if there isn't one
27+
if (i % 16) {
28+
putchar('\n');
29+
}
30+
}
31+
32+
int main() {
33+
// Enable UART so we can print
34+
stdio_init_all();
35+
#if !defined(spi_default) || !defined(PICO_DEFAULT_SPI_SCK_PIN) || !defined(PICO_DEFAULT_SPI_TX_PIN) || !defined(PICO_DEFAULT_SPI_RX_PIN) || !defined(PICO_DEFAULT_SPI_CSN_PIN)
36+
#warning spi/spi_master example requires a board with SPI pins
37+
puts("Default SPI pins were not defined");
38+
#else
39+
40+
printf("SPI master example\n");
41+
42+
// Enable SPI 0 at 1 KHz and connect to GPIOs
43+
spi_init(spi_default, 1000);
44+
gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
45+
gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
46+
gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
47+
gpio_set_function(PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI);
48+
// Make the SPI pins available to picotool
49+
bi_decl(bi_4pins_with_func(PICO_DEFAULT_SPI_RX_PIN, PICO_DEFAULT_SPI_TX_PIN, PICO_DEFAULT_SPI_SCK_PIN, PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI));
50+
51+
printf("SPI master initialised.\n");
52+
53+
uint8_t out_buf[BUF_LEN], in_buf[BUF_LEN];
54+
55+
// Initialize output buffer
56+
for (size_t i = 0; i < BUF_LEN; ++i) {
57+
out_buf[i] = i;
58+
}
59+
60+
printf("The following buffer will be written to MOSI endlessly.\n");
61+
printbuf(out_buf, BUF_LEN);
62+
63+
for (size_t i = 0; true; ++i) {
64+
// Write the output buffer to MOSI, and at the same time read from MISO.
65+
spi_write_read_blocking(spi_default, out_buf, in_buf, BUF_LEN);
66+
67+
// Write to stdio whatever came in on the MISO line.
68+
printf("Read the following from MISO, page %d:\n", i);
69+
printbuf(in_buf, BUF_LEN);
70+
}
71+
#endif
72+
}
Binary file not shown.
115 KB
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
add_executable(spi_slave
2+
spi_slave.c
3+
)
4+
5+
# Pull in basic dependencies
6+
target_link_libraries(spi_slave pico_stdlib hardware_spi)
7+
8+
# create map/bin/hex file etc.
9+
pico_add_extra_outputs(spi_slave)
10+
11+
# add url via pico_set_program_url
12+
example_auto_set_url(spi_slave)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Copyright (c) 2021 Michael Stoops. All rights reserved.
3+
* Portions copyright (c) 2021 Raspberry Pi (Trading) Ltd.
4+
*
5+
* SPDX-License-Identifier: BSD-3-Clause
6+
*/
7+
8+
// Example of an SPI bus slave using the PL022 SPI interface
9+
10+
#include <stdio.h>
11+
#include <string.h>
12+
#include "pico/stdlib.h"
13+
#include "pico/binary_info.h"
14+
#include "hardware/spi.h"
15+
16+
#define BUF_LEN 0x100
17+
18+
void printbuf(uint8_t buf[], size_t len) {
19+
int i;
20+
for (i = 0; i < len; ++i) {
21+
if (i % 16 == 15)
22+
printf("%02x\n", buf[i]);
23+
else
24+
printf("%02x ", buf[i]);
25+
}
26+
27+
// append trailing newline if there isn't one
28+
if (i % 16) {
29+
putchar('\n');
30+
}
31+
}
32+
33+
34+
int main() {
35+
// Enable UART so we can print
36+
stdio_init_all();
37+
#if !defined(spi_default) || !defined(PICO_DEFAULT_SPI_SCK_PIN) || !defined(PICO_DEFAULT_SPI_TX_PIN) || !defined(PICO_DEFAULT_SPI_RX_PIN) || !defined(PICO_DEFAULT_SPI_CSN_PIN)
38+
#warning spi/spi_slave example requires a board with SPI pins
39+
puts("Default SPI pins were not defined");
40+
#else
41+
42+
printf("SPI slave example\n");
43+
44+
// Enable SPI 0 at 1 KHz and connect to GPIOs
45+
spi_init(spi_default, 1000);
46+
spi_set_slave(spi_default, true);
47+
gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
48+
gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
49+
gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
50+
gpio_set_function(PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI);
51+
// Make the SPI pins available to picotool
52+
bi_decl(bi_4pins_with_func(PICO_DEFAULT_SPI_RX_PIN, PICO_DEFAULT_SPI_TX_PIN, PICO_DEFAULT_SPI_SCK_PIN, PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI));
53+
54+
printf("SPI slave initialised.\n");
55+
56+
uint8_t out_buf[BUF_LEN], in_buf[BUF_LEN];
57+
58+
// Initialize output buffer
59+
for (size_t i = 0; i < BUF_LEN; ++i) {
60+
// bit-inverted from i. The values should be: {0xff, 0xfe, 0xfd...}
61+
out_buf[i] = ~i;
62+
}
63+
64+
printf("When reading from MOSI, the following buffer will be written to MISO:\n");
65+
printbuf(out_buf, BUF_LEN);
66+
67+
for (size_t i = 0; true; ++i) {
68+
// Write the output buffer to MISO, and at the same time read from MOSI.
69+
spi_write_read_blocking(spi_default, out_buf, in_buf, BUF_LEN);
70+
71+
// The slave doesn't print the buffer. It can't afford to ignore the SPI bus while writing to stdio.
72+
}
73+
#endif
74+
}

0 commit comments

Comments
 (0)