When configuring the SPI (Serial Peripheral Interface) on an ARM Cortex-M0 chip to communicate with an external device, a common issue that arises is the SPI outputting 16-bit data instead of 8-bit as expected. This can occur if the SPI is not properly initialized and configured in the software. In this article, we will explore the root causes of this issue and walk through the steps to resolve it.
Common Causes of 16-bit SPI Output
There are a few key reasons why the Cortex-M0 SPI may output 16 bits per transfer instead of 8 bits:
- The SPI data register width is incorrectly set to 16-bit in the initialization
- The external device expects 16-bit data frames
- The SPI frame format register is configured for 16-bit frames
- The data type for the transmit buffer is 16-bit (uint16_t)
Understanding the root cause is important to determine the proper solution. Let’s examine each of these in more detail.
1. SPI Data Register Width
One of the first things to check is the initialization of the SPI peripheral. Many Cortex-M0 MCUs have configurable data register widths – commonly 8-bit or 16-bit. For example, on an STM32L0 chip the SPI_CR1 register contains a DFF bit to select 8 or 16-bit data frames.
If this width setting is incorrectly configured for 16-bit, then any data loaded into the transmit buffer will be sent out as 16-bit frames. Double-check this data register width setting against your expected 8-bit peripheral.
2. External Device Expects 16-bit
In some cases, the external SPI device you are interfacing with actually expects 16-bit data instead of 8-bit. This could be a 16-bit ADC or DAC, for example. Make sure to verify the SPI frame size expected by the external component matches your software configuration.
Reading the device datasheet and any sample code can provide insight into the expected frame format. If the device uses 16-bit data, you will need to adjust the software accordingly.
3. SPI Frame Format Configuration
Related to the data register width setting, many Cortex-M0 chips also have SPI frame format registers to configure the length. For STM32L0, this is the SPI_CR2 register which contains a FRF[2:0] field to set the frame format.
If this is set to a 16-bit variant like Motorola mode, when 8-bit data is written to the buffer it will be packed into 16-bit frames for transmission. Verify this frame format matches your expected setting.
4. 16-bit Transmit Data Buffer
Lastly, the data type of the transmit buffer itself can lead to 16-bit output. For example:
uint16_t tx_buffer[10];
// Load 8-bit data
tx_buffer[0] = 0xAB;
// Transmit buffer
SPI_Transmit(tx_buffer);
Even though the data written to tx_buffer is 8-bit, because the buffer was defined as uint16_t the SPI will transmit 16-bit values. Make sure the transmit buffer matches your 8-bit data requirement.
Solutions
Now that we’ve explored some common root causes of 16-bit SPI output, let’s discuss solutions to configure the SPI for 8-bit operation.
Configure Data Register Width
First, set the SPI data register width appropriately for 8-bit communication. For STM32L0 this would be:
SPI1->CR1 &= ~SPI_CR1_DFF; // Clear DFF bit for 8-bit
Refer to your MCU reference manual for the corresponding control bit.
Set Frame Format to 8-bit
Next, configure the frame format register for 8-bit operation. Continuing the STM32L0 example:
SPI1->CR2 &= ~SPI_CR2_FRF; // Set FRF for 8-bit MOTOROLA mode
The frame format must match your peripheral requirement.
Use 8-bit Transmit Buffer
Finally, make sure your transmit buffer matches the 8-bit data you want to send:
uint8_t tx_buffer[10];
tx_buffer[0] = 0xAB;
SPI_Transmit(tx_buffer);
With these three steps – configuring the data register width, frame format, and buffer type for 8-bit – the SPI should now transmit 8-bit data as expected.
Example Code
Here is an example initialization sequence for 8-bit SPI on an STM32L0 MCU:
void SPI1_Init(void)
{
// Enable clock for SPI1
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
// Configure pins for SPI1 alternate function
GPIOA->MODER |= (2 << 2*5) | (2 << 2*6) | (2 << 2*7);
// Set up SPI1 registers
SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_SSI | SPI_CR1_SSM;
SPI1->CR1 &= ~SPI_CR1_DFF; // 8-bit data frame format
SPI1->CR2 = SPI_CR2_DS_2 | SPI_CR2_DS_1 | SPI_CR2_DS_0;
SPI1->CR2 &= ~SPI_CR2_FRF; // 8-bit MOTOROLA frame format
// Enable SPI1
SPI1->CR1 |= SPI_CR1_SPE;
}
// Transmit Data
void SPI1_Transmit(uint8_t *data, uint16_t size)
{
for(int i=0; i<size; i++) {
// Wait for TX buffer empty
while(!(SPI1->SR & SPI_SR_TXE));
// Write byte to TX buffer
SPI1->DR = data[i];
}
}
This example demonstrates the key steps of configuring the SPI1 peripheral on an STM32L0 Cortex-M0 chip for 8-bit communication. The data register width and frame format are explicitly set for 8-bit. And the transmit buffer is uint8_t to match the 8-bit data.
Following these good SPI initialization practices will ensure you avoid errant 16-bit output when trying to interface Cortex-M0 chips with 8-bit peripherals.
Other Tips for SPI on Cortex-M0
Beyond the issue of 16-bit vs 8-bit data, there are some other best practices worth covering for SPI on Cortex-M0 platforms:
- Use DMA if available – SPI transfers will be more efficient
- Implement SPI delay if needed between transfers
- Use SPI FIFOs on more advanced MCUs like STM32G0
- Keep SPI SCK below the maximum rated frequency of your peripheral
- Use multiple chip selects if communicating with multiple external devices
Additionally, be sure to utilize oscilloscope measurements during development. Probing the SPI MOSI, MISO, and SCK signals can give invaluable insight into the actual data transactions.
Conclusion
Configuring SPI on the ARM Cortex-M0 can certainly be tricky. Hopefully this article provided some helpful troubleshooting tips if you run into issues with unexpected 16-bit output. The key takeaways are:
- Verify correct configuration of the data register width for 8-bit frames
- Set the SPI frame format to match your peripheral requirement
- Use uint8_t transmit buffers for 8-bit data
- Follow best practices like utilizing DMA and SPI FIFOs when possible
With attention to these software settings and hardware interfaces, you should be able to resolve 16-bit SPI output problems and successfully utilize the Cortex-M0 SPI to add communication capabilities to your embedded systems.