
This guide demonstrates how to use SPI communication on an STM32F4 microcontroller with MicroPython. It shows how to transmit 5 bytes of data repeatedly using SPI1 with PA0 configured as the chip select (CS) pin.
In this guide, we shall cover the following:
- Introduction.
- Firmware Development.
- SPI pins.
- Results.
1. Introduction:
SPI, which stands for Serial Peripheral Interface, is a standard with a very specific hardware interface. A connection is between a master and a slave, with the master typical being a processor, and the slave being a peripheral such as a sensor, flash memory device, or a modem chip. It can also be used for processor to processor communications, but in this case, an additional handshake signal is often used. There are normally four signals between a master and a slave, the first is a clock signal, and this signal is always driven by the master, regard which device is transmitting. The second line is a data line for data going from the master to the slave, and this is designated as the master output, slave input line, are MOSI for short. It connects the SPI data out connection on the master and to the SPI data in connection on the slave.
The next conductor is for data in the opposite direction and is labelled as the master input slave output line, are MISO for short. The last conductor is slave select with the slave chip selecting input, and is active low.
If you have more than one slave, with the first being perhaps a sensor of some kind, the slave will be dedicated to slave 1. If you add a second sensor, the top 3 interface line will be shared, but it dedicates for slave’s line will be required for the second device, and the same is true of course for each of additional slave device.
Most processors have a maxim of 4 slave selected lines. The four lines could be used effectively as a multiplexed address lines to access more than 4 slaves. You cannot have more than one master on the bus, since the interface is not support coordination between two masters as to which one is controlling the bus.
Transmissions are typically sent as a sequence of bytes, but without a formal protocol, there is nothing restricting communication being byte based. Typical by frame sizes, are in 8 to 32 bits range. Also note the bytes and packets are not acknowledged as they are in i2c, and you could have a master synching communicating with the slave but, you don’t really know of your communications are being received OK. However, some slave devices will echo bytes sent to it, which provides an acknowledgement to the master. it is how the data lines are synchronized with a clock signal.
Clock Polarity and Phasing
There are four different modes available, one mode each combination clocking in a low state and high state, with a data being read in a rising edge or falling edge of the clock signal. For modes 0 and 1, the clock is low in idle, which is referred to as clock and 0, For modes 2 and 3 then the clock is in high state when idle, so it has polarity , one , For modes 0 and 2, the data will be sampled by the receiving device on the leading edge of a clock signal. Relative to the idle state, which is referred to a clock phase of zero. So for mode 0, this means the rising edge of the clock and for mode 2, means the following edge of the clock, the other two modes, use a clock phase 1 which means that trailing edge of clock as a returns to an idle state. And this translates to a following edge for mode 1 and the rising edge for mode 3. Mode 0 is the most commonly supported setting. If multiple slaves in the same bus, you may have to reconfigure the settings for the master to which modes when you want to communicate with a different slave.
2. Firmware Development:
Before we start the firmware development, we need to install and configure thonny. For more details, please refer to this guide here.
Once Thonny has been configured, we can start with firmware development.
We start by importing machine and time libraries as following:
import machine import time
Next, initialize the SPI as following:
# Initialize SPI1 (SCK=PA5, MISO=PA6, MOSI=PA7 on most STM32F4 boards) spi = machine.SPI(1, baudrate=1_000_000, # 1 MHz polarity=0, phase=0, bits=8, firstbit=machine.SPI.MSB)
The function shall initialize the SPI and take the following parameters:
1
: Specifies the use of SPI1 peripheral.baudrate=1_000_000
: Sets the communication speed to 1 MHz.polarity=0
: Sets the clock idle state to low (CPOL = 0).phase=0
: Data is captured on the first clock edge (CPHA = 0).bits=8
: Configures SPI to send 8 bits (1 byte) per transfer.firstbit=machine.SPI.MSB
: Sets data to be transmitted Most Significant Bit first.
It returns an SPI
object which is assigned to the variable spi
for use in SPI communication functions such as spi.write()
.
Next, we shall define the SPI CS pin as following:
# Set PA0 as CS (Chip Select) cs = machine.Pin('PA0', machine.Pin.OUT) cs.value(1) # Set CS high initially (inactive)
Next, define the array that holds the data to be transmitted as following:
# Data to be transmitted (5 bytes) data = bytearray([0x01, 0x23, 0x45, 0x67, 0x89])
Next, define the function to transmit the data as following:
# Function to transmit SPI data def transmit_data(): cs.value(0) # Select the slave spi.write(data) # Send 5 bytes cs.value(1) # Deselect the slave
The function shall doe the following:
- Set CS to low to enable the slave device.
- Transmit the data.
- Set CS to high to disable the slave device.
In while true loop, we shall keep transmitting the data each 100ms as following:
# Loop: transmit every 100 ms while True: transmit_data() time.sleep_ms(100)
Hence, the entire code as follows:
import machine import time # Initialize SPI1 (SCK=PA5, MISO=PA6, MOSI=PA7 on most STM32F4 boards) spi = machine.SPI(1, baudrate=1_000_000, # 1 MHz polarity=0, phase=0, bits=8, firstbit=machine.SPI.MSB) # Set PA0 as CS (Chip Select) cs = machine.Pin('PA0', machine.Pin.OUT) cs.value(1) # Set CS high initially (inactive) # Data to be transmitted (5d bytes) data = bytearray([0x01, 0x23, 0x45, 0x67, 0x89]) # Function to transmit SPI data def transmit_data(): cs.value(0) # Select the slave spi.write(data) # Send 5 bytes cs.value(1) # Deselect the slave # Loop: transmit every 100 ms while True: transmit_data() time.sleep_ms(100)
Click on run as following:

3. SPI Pins:
The SPI pins as follows:

4. Results:
Using either oscilloscope or logic analyzer, you should the following:

We have successfully transmitted 5 bytes of data over SPI bus.
Next, we shall communicate with a slave device for read operation.
Stay tuned.
Happy coding 😉
Add Comment