Skip to main content
jean
Senior
April 28, 2021
Question

H7 migration, send SPI without using HAL

  • April 28, 2021
  • 9 replies
  • 4503 views

Hello dear community!

I've migrated from F7 to H7, everything fine so far.

To save CPU time I don't want to use HAL for sending two SPI bytes.

My F7 transmit SPI code was very simple and perfectly working :

SPI6->DR = byte_0;
SPI6->DR = byte_1;

But on the H7, the same code doesn't work :

SPI6->TXDR = byte_0;
SPI6->TXDR = byte_1;

In both F7 and H7 projects, this is always working (but time-consuming) :

HAL_SPI_Transmit(&hspi6, const_cast<uint8_t*>(buffer), 2, 1000);

Do you know why bypassing HAL is not possible on the H7 ?

Thanks!

Jean

9 replies

Pavel A.
Super User
April 28, 2021

Perhaps because in H7 SPI data register is sensitive to the width of access. To write a byte, use 8-bit access.

Without HAL, use LL.

--pa

waclawek.jan
Super User
April 28, 2021

The 'H7 SPI is an overly complex beast.

The master at full duplex (or in any transmit-only mode) starts to communicate when the SPI

is enabled, the CSTART bit is set and the TxFIFO is not empty, or with the next write to

TxFIFO.

JW

Tesla DeLorean
Guru
April 28, 2021

*((volatile uint8_t *)&SPI6->TXDR) = byte_0; // 8-bit transaction on the data bus

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
jean
jeanAuthor
Senior
April 29, 2021

Thanks a lot for your helpful answers!

I ended up by using the low level driver and it working!

LL_SPI_TransmitData8(SPI6, byte_0);
LL_SPI_TransmitData8(SPI6, byte_1);
LL_SPI_StartMasterTransfer(SPI6);

waclawek.jan
Super User
April 29, 2021

Okay so I looked it up... yep, no surprise, it's the CSTART bit:

__STATIC_INLINE void LL_SPI_StartMasterTransfer(SPI_TypeDef *SPIx)
{
 SET_BIT(SPIx->CR1, SPI_CR1_CSTART);
}

If you don't use transactions, i.e. you keep TSIZE=0, it's enough to set it once, at the beginning, and then the SPI should behave in the same way as in older models.

JW

PS. You may want to read AN5543. It's poorly written - CSTART is not mentioned there and TSIZE is mentioned only marginally - but at least there's a table with SPI versions in different STM32 models.

Pavel A.
Super User
April 29, 2021

I'm struggling with this 'H7 SPI too. Learned to use these "transfers" - but was happy too soon...

After few "transfers", they start failing. The end of transfer flag SPI_SR_EOT fails to go up after a reasonable time, and I don't understand why.

Using the simplest mode: Motorola, mode 0, unit=byte, no CRC or auto NSS manipulations.

Noticed some errata item about the EOT but don't quite understand what it means.

Tried to disable & enable the SPI after every transfer, as they recommend - this only made situation worse.

So I'm going to get rid of "transfers", set SPI_CR2_TSIZE to 0 (another recommended workaround)...

--pa

P.S. The "transfer-less" mode seems to work well in my case.

This mode is demonstrated in "BSP" example for Nucleo H743 in the Cube H7.

Pavel A.
Super User
July 24, 2024

 I have tried both "transfer" (by setting TSIZE to 1 to send a single 8-bit data frame) and "transferless" modes. Unfortunately, neither seem to work.

"Transferless" works for me on 'H753 and it is used in the "BSP" Cube example: 

Projects/NUCLEO-H743ZI/Examples/BSP/Src/stm32h7xx_nucleo_bus.c

Start from the example, use a scope to verify.

My SPI init code snippet: (fast & dirty, mostly borrowed from the example. not something to boast about) :

 

 

#include "stm32h7xx_ll_spi.h"
....
SPI_TypeDef *SPIx = SPI1;
.....

 /* Enable SPI1 Clock */
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

 /* Pulse reset of SPI1 */
 LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1);
 LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1);

............
 LL_SPI_InitTypeDef SPI_InitStruct = {};
 SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
 SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
 /* The connected device needs: MODE0, CPOL=0, CPHA=0 */
 SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
 SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
 SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
 SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
 SPI_InitStruct.NSS = LL_SPI_NSS_SOFT; // no NSS, using a GPIO as SS!
 SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
 SPI_InitStruct.BaudRate = SPIx_GetBitrateDivisor(SPIx, g_SPI_bitrate); // HCLK divisor
 if (0 != LL_SPI_Init(SPIx, &SPI_InitStruct)) {
 printf("SPI init failed!\n");
 return;
 }

 /* This locks SPI pins in AF mode when the SPI is disabled, to avoid glitches on CLK */
 /* Temporary disable may be required to recover from weird states; see errata - pa01 */
 LL_SPI_EnableGPIOControl(SPIx);
 LL_SPI_DisableMasterRxAutoSuspend(SPIx);

 /* Set transfer size = 0 (not using 'transfers') */
 LL_SPI_SetTransferSize(SPIx, 0);
 LL_SPI_SetFIFOThreshold(SPIx, LL_SPI_FIFO_TH_01DATA);

 /* Enable the SPI */
 LL_SPI_Enable(SPIx);
 /* Start master in automatic (no-transaction) mode */
 LL_SPI_StartMasterTransfer(SPIx);

 

 

Note at the end the LL_SPI_StartMasterTransfer call, mentioned by Jan W.

Good luck!

Explorer
April 23, 2026
Hi Pavel A can you insert read & write functions.
Pavel A.
Super User
April 24, 2026

Something like this...

void SPI_TX_BYTE(uint8_t d)
{
 unsigned cnt = 20000;
 while (!LL_SPI_IsActiveFlag_TXP(SPIx)) {
 if (--cnt == 0) {
 Error_handler();
 return;
 }
 }
 LL_SPI_TransmitData8(SPIx, d);
}


uint8_t SPI_RX_BYTE()
{
 unsigned cnt = 20000;
 while (!LL_SPI_IsActiveFlag_RXP(SPIx)) {
 if (--cnt == 0) {
 Error_handler();
 return 0;
 }
 }

 return LL_SPI_ReceiveData8(SPIx);
}
Pavel A.
Super User
July 24, 2024

I forgot to insert read & write functions. Will add later.  Please meanwhile try to make the example work, it uses the most simple, reliable mode.

Unfortunately bare register-based code is hard to read, most people use some library. But others use different libraries and don't grok yours one. It's complicated. 

July 25, 2024

Hi again,

I have successfully identified the issue: the default kernel clock source for SPI2 is pll_ck, but I don't have PLL set up, so the SPI module was not getting any clock signal. The issue was resolved by changing the SPI clock source to per_ck.

Thank you for your time, Pavel, I really appreciate it. I apologize for possible inconvenience. Good luck with all of your future endeavors!
-Ivan

Andrew Neil
Super User
July 25, 2024
A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
April 23, 2026

Migration + SPI (No HAL)

User migrated from F7 to H7 and is optimizing SPI by avoiding HAL to reduce CPU usage. SPI is now handled using low-level register access for better performance.

More details here: <a href="https://smartsimreg.ph">Smart SIM Registration</a>