2021-02-04 03:12 AM
I am trying to emit data on SPI MOSI without enabling the corresponding SCK pin. This is useful for instance to control WS2812-type LEDs.
However when I use `HAL_SPI_Transmit_IT` to transfer 3 words, the IRQ Handler gets called 3 times (triggered by a TXP event) but then it doesn't get called the 4th time because the EOT flag in SPI_SR is never raised. In other words the transfer doesn't finish. Similarly, a transfer started with `HAL_SPI_Transmit_DMA` will also not complete.
When I put any of the corresponding SCK GPIOs (that exist on the package) into SPI alternate function mode, the transfer completes normally.
On the STM32F722 the SPI works fine without SCK pin.
Is this expected behavior on the STM32H7A3? The reference manual (Figure 579) suggests some entanglement between SPI core and SCK pin but I didn't find a clear statement that SCK must be enabled.
Below is the code that I was using for the experiments. "Works" means that the variable `works` is incremented. "Doesn't work" means that the variable `works` remains zero and the CPU spins on line 185.
#include "main.h"
//// works ////
//#define SPI SPI1
//#define SCK_PORT GPIOA
//#define SCK_PIN GPIO_PIN_5
//// works ////
//#define SPI SPI1
//#define SCK_PORT GPIOB
//#define SCK_PIN GPIO_PIN_3
//// doesn't work (pin not available on package) ////
//#define SPI SPI1
//#define SCK_PORT GPIOG
//#define SCK_PIN GPIO_PIN_11
//// works ////
//#define SPI SPI2
//#define SCK_PORT GPIOA
//#define SCK_PIN GPIO_PIN_9
//// works ////
//#define SPI SPI2
//#define SCK_PORT GPIOA
//#define SCK_PIN GPIO_PIN_12
//// can't verify ////
//#define SPI SPI2
//#define SCK_PORT GPIOB
//#define SCK_PIN GPIO_PIN_10
//// works ////
//#define SPI SPI2
//#define SCK_PORT GPIOB
//#define SCK_PIN GPIO_PIN_13
//// doesn't work (pin not available on package) ////
//#define SPI SPI2
//#define SCK_PORT GPIOD
//#define SCK_PIN GPIO_PIN_3
//// doesn't work (pin not available on package) ////
//#define SPI SPI2
//#define SCK_PORT GPIOI
//#define SCK_PIN GPIO_PIN_1
//// works ////
//#define SPI SPI3
//#define SCK_PORT GPIOB
//#define SCK_PIN GPIO_PIN_3
//// works ////
//#define SPI SPI3
//#define SCK_PORT GPIOC
//#define SCK_PIN GPIO_PIN_10
//// doesn't work ////
//#define SPI SPI1
//#define NO_SCK
//// doesn't work ////
//#define SPI SPI2
//#define NO_SCK
//// doesn't work ////
//#define SPI SPI3
//#define NO_SCK
SPI_HandleTypeDef hspi;
void SystemClock_Config(void);
int main(void) {
HAL_Init();
__HAL_RCC_SYSCFG_CLK_ENABLE();
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 35;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK) {
Error_Handler();
}
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI1 | RCC_PERIPHCLK_SPI2 | RCC_PERIPHCLK_SPI3;
PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
Error_Handler();
}
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_SPI3_CLK_ENABLE();
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI2_IRQn);
HAL_NVIC_SetPriority(SPI3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI3_IRQn);
hspi.Instance = SPI;
hspi.Init.Mode = SPI_MODE_MASTER;
hspi.Init.Direction = SPI_DIRECTION_2LINES;
hspi.Init.DataSize = SPI_DATASIZE_16BIT;
hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi.Init.NSS = SPI_NSS_SOFT;
hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi.Init.TIMode = SPI_TIMODE_DISABLE;
hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi.Init.CRCPolynomial = 0x0;
hspi.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
hspi.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
hspi.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
hspi.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
hspi.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
hspi.Init.IOSwap = SPI_IO_SWAP_DISABLE;
if (HAL_SPI_Init(&hspi) != HAL_OK) {
Error_Handler();
}
#ifndef NO_SCK
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = SCK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = (SPI == SPI1) ? GPIO_AF5_SPI1 :
(SPI == SPI2) ? GPIO_AF5_SPI2 :
GPIO_AF6_SPI3;
HAL_GPIO_Init(SCK_PORT, &GPIO_InitStruct);
#endif
volatile int works = 0;
while (1) {
uint16_t data[] = {0xffff, 0xffff, 0xffff};
HAL_SPI_Transmit_IT(&hspi, (uint8_t*)data, 3);
while (hspi.State != HAL_SPI_STATE_READY);
works++;
}
}
void Error_Handler(void) {
for(;;);
}
void SysTick_Handler(void) {
HAL_IncTick();
}
void SPI1_IRQHandler(void) {
HAL_SPI_IRQHandler(&hspi);
}
void SPI2_IRQHandler(void) {
HAL_SPI_IRQHandler(&hspi);
}
void SPI3_IRQHandler(void) {
HAL_SPI_IRQHandler(&hspi);
}
2021-02-04 01:55 PM
In the "normal" STM32 SPI the receiver is clocked by a feedback = input from the SCK pin, so while Tx works, Rx won't without enabling the SCK pin.
I suspect this may be the underlying mechanism here, too; however, the 'H7 SPI is a beast and I don't intend to dig deep into it.
JW
2021-02-25 03:05 AM
Yes in slave mode the SCK=>SPI depenency makes sense, however in master mode it seems strange.
For the LED control I was able to work around the issue by using I2S instead of SPI.
However meanwhile on a different SPI instance I observed a similar issue where the SPI stalled during what might have been a strong transient coming from our power electronics. In this state `SR->CTSIZE` was 1 and `SR->EOT` remained at 0. Given the peculiar dependency between the SCK GPIO and SPI's ability to make progress I suspect that a stray signal could have been forced into SCK and tripped up the SPI.
Of course we should fix our electronics to avoid such issues but it's still surprising and noteworthy. For now we recover from stall conditions by resetting the SPI entirely (`__HAL_RCC_SPI3_FORCE_RESET(); __HAL_RCC_SPI3_RELEASE_RESET();`).
Perhaps an ST engineer could comment on this?