|
| 1 | +/* |
| 2 | + * SPDX-License-Identifier: SPDX-License-Identifier: Apache-2.0 |
| 3 | + * Author: Imre Deak <[email protected]> |
| 4 | + * |
| 5 | + * This simple app demonstrates how to use Zephyr for SPI transfers. |
| 6 | + * |
| 7 | + * It uses the SPI1 bus which on the STM32F411RE has the following pinout |
| 8 | + * configuration (by default configured that way by the Zephyr core): |
| 9 | + * |
| 10 | + * SPI1_SCK: PA5 (alternative pin: PB3) |
| 11 | + * SPI1_MISO: PA6 (alternative pin: PB4) |
| 12 | + * SPI1_MOSI: PA7 (alternative pin: PB5) |
| 13 | + * SPI1_NSS: PA4 (alternative pin: PA15) |
| 14 | + * |
| 15 | + * See |
| 16 | + * |
| 17 | + * https://www.st.com/resource/en/datasheet/stm32f411ce.pdf |
| 18 | + * |
| 19 | + * Chapter 4 "Pinouts and pin description" for more details on configuring the |
| 20 | + * pins for alternate functions and |
| 21 | + * |
| 22 | + * https://os.mbed.com/platforms/ST-Nucleo-F411RE |
| 23 | + * |
| 24 | + * "Morpho headers" for locating the pins on the board. |
| 25 | + * |
| 26 | + * The SPI bus will be used in master (non-TI aka Motorola) mode, see the |
| 27 | + * STM32F411xC/E reference manual (RM0383) at |
| 28 | + * |
| 29 | + * https://www.st.com/resource/en/reference_manual/dm00119316.pdf |
| 30 | + * |
| 31 | + * Chapter 20.3.3 "Configuring the SPI in master mode" and |
| 32 | + * 20.3.5 "Data transmission and reception procedures"/ |
| 33 | + * "Start sequence in master mode" |
| 34 | + * for the corresponding low-level programming (done by Zephyr internally). |
| 35 | + * |
| 36 | + * For simplicity the NSS line (chip select) will be operated in |
| 37 | + * "hardware mode" see the reference manual Chapter 20.3.1 in general and |
| 38 | + * within that specifically the "Slave select (NSS) pin management" part. In |
| 39 | + * practice this means that the HW itself will assert the NSS line whenever it |
| 40 | + * transmits data and de-asserts it at the end of the transfer. |
| 41 | + * |
| 42 | + * For the register flags related to the above master mode and NSS setup see |
| 43 | + * the reference manual |
| 44 | + * Chapter 20.5.1 "SPI control register 1 (SPI_CR1)" SSM/SSI/MSTR and |
| 45 | + * Chapter 20.5.2 "SPI control register 2 (SPI_CR2)" SSOE flags. |
| 46 | + * |
| 47 | + * The NSS line needs to be pulled up but Zephyr doesn't configure an internal |
| 48 | + * pull-up for the corresponding PA4 pin by default. Since this app is geared |
| 49 | + * for an easy setup-and-measure with a scope scenario it will configure an |
| 50 | + * internal pull-up on the pin during init in stm32_spi_setup_nss_pin(). |
| 51 | + * |
| 52 | + * If your circuit has an external pull-up resistor then you don't need (or |
| 53 | + * even should not) configure the internal one (achieved by not calling |
| 54 | + * stm32_spi_setup_nss_pin()). |
| 55 | + * |
| 56 | + * As an alternative to the above NSS "hardware mode" it's also possible to |
| 57 | + * use any available pin in GPIO mode to provide for the SPI chip select |
| 58 | + * functionality. In this case struct spi_config::cs needs to be initialized |
| 59 | + * appropriately in main(), which will make Zephyr configure the SPI module in |
| 60 | + * the NSS "software mode". See the reference manual for details for the |
| 61 | + * corresponding low-level programming. This app itself would then need to |
| 62 | + * assert/de-assert the given GPIO around each SPI transfer. |
| 63 | + * |
| 64 | + * Probably for a real use case you want to use the NSS "software mode" |
| 65 | + * because that's the only way you can: |
| 66 | + * - control the timing of the chip select signal |
| 67 | + * - configure the CS signal as positive-asserted (rare case) |
| 68 | + * - use more than one CS signal to control multiple slaves |
| 69 | + * The "hardware mode" uses some kind of a fixed timing, supports only the |
| 70 | + * standard negative-asserted CS logic and a single CS signal. |
| 71 | + */ |
| 72 | +#include <string.h> |
| 73 | +#include <spi.h> |
| 74 | + |
| 75 | +#include <pinmux/stm32/pinmux_stm32.h> |
| 76 | + |
| 77 | +static int stm32_spi_send(struct device *spi, |
| 78 | + const struct spi_config *spi_cfg, |
| 79 | + const uint8_t *data, size_t len) |
| 80 | +{ |
| 81 | + const struct spi_buf_set tx = { |
| 82 | + .buffers = &(const struct spi_buf){ |
| 83 | + .buf = (uint8_t *)data, |
| 84 | + .len = len, |
| 85 | + }, |
| 86 | + .count = 1, |
| 87 | + }; |
| 88 | + |
| 89 | + return spi_write(spi, spi_cfg, &tx); |
| 90 | +} |
| 91 | + |
| 92 | +static int stm32_spi_send_str(struct device *spi, |
| 93 | + const struct spi_config *spi_cfg, |
| 94 | + const unsigned char *str) |
| 95 | +{ |
| 96 | + return stm32_spi_send(spi, spi_cfg, str, strlen(str)); |
| 97 | +} |
| 98 | + |
| 99 | +static void stm32_spi_setup_nss_pin(void) |
| 100 | +{ |
| 101 | + static const struct pin_config pin_config = { |
| 102 | + .pin_num = STM32_PIN_PA4, |
| 103 | + .mode = STM32_PINMUX_ALT_FUNC_5 | STM32_PUSHPULL_PULLUP, |
| 104 | + }; |
| 105 | + |
| 106 | + stm32_setup_pins(&pin_config, 1); |
| 107 | +} |
| 108 | + |
| 109 | +void main(void) |
| 110 | +{ |
| 111 | + struct spi_config spi_cfg = {}; |
| 112 | + struct device *spi; |
| 113 | + int err; |
| 114 | + |
| 115 | + printk("Starting stm32-spi on %s\n", CONFIG_BOARD); |
| 116 | + |
| 117 | + stm32_spi_setup_nss_pin(); |
| 118 | + |
| 119 | + spi = device_get_binding("SPI_1"); |
| 120 | + if (!spi) { |
| 121 | + printk("Could not find SPI driver\n"); |
| 122 | + return; |
| 123 | + } |
| 124 | + |
| 125 | + spi_cfg.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB | |
| 126 | + SPI_OP_MODE_MASTER; |
| 127 | + |
| 128 | + spi_cfg.frequency = 1000000U; |
| 129 | + |
| 130 | + for (;;) { |
| 131 | + err = stm32_spi_send_str(spi, &spi_cfg, "HELLO!"); |
| 132 | + if (err) |
| 133 | + break; |
| 134 | + k_sleep(1); |
| 135 | + } |
| 136 | + |
| 137 | + if (err) |
| 138 | + printk("SPI send error %d\n", err); |
| 139 | +} |
0 commit comments