Skip to content

Commit 016540d

Browse files
committed
fix: Cygnet initialization
Fixes clock initialization clash by mirroring Nucleo L432KC init. Reported to Blues here: https://discuss.blues.com/t/wire-begin-causes-mcu-reset-hang-on-cygnet/3486 Identifies odd dependency between Wire and USB. stm32duino issue: #2930 Identifies clock config as possible root cause.
1 parent 734f525 commit 016540d

File tree

1 file changed

+148
-30
lines changed

1 file changed

+148
-30
lines changed

variants/STM32L4xx/L433C(B-C)(T-U)_L443CC(T-U)/variant_CYGNET.cpp

Lines changed: 148 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,49 @@ WEAK void initVariant(void)
7575
{
7676
/* All pins set to high-Z (floating) initially */
7777
/* DS11449 Rev 8, Section 3.9.5 - Reset Mode: */
78-
/* In order to improve the consumption under reset, the I/Os state under and after reset is
79-
* “analog state” (the I/O schmitt trigger is disable). In addition, the internal reset pull-up is
80-
* deactivated when the reset source is internal.
78+
/* In order to improve the consumption under reset, the I/Os state under
79+
* and after reset is “analog state” (the I/O schmitt trigger is disable).
80+
* In addition, the internal reset pull-up is deactivated when the reset
81+
* source is internal.
8182
*/
8283

83-
/* Turn on the 3V3 regulator */
84-
__HAL_RCC_GPIOH_CLK_ENABLE();
85-
GPIO_InitTypeDef GPIO_InitStruct;
86-
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
87-
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
88-
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
89-
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); /* PH0 is ENABLE_3V3, PH1 is DISCHARGE_3V3 */
90-
HAL_GPIO_WritePin(GPIOH, GPIO_InitStruct.Pin, GPIO_PIN_SET); /* Enable 3V3 regulator and disable discharging */
84+
/* Configure the USB charge detection; leaks ~80uA if not configured. */
85+
{
86+
__HAL_RCC_GPIOA_CLK_ENABLE();
87+
GPIO_InitTypeDef GPIO_InitStruct = {};
88+
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
89+
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
90+
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
91+
/* PA15 is CHARGE_DETECT */
92+
GPIO_InitStruct.Pin = GPIO_PIN_15;
93+
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
94+
}
95+
96+
/* Configure D13 manually, to avoid stray current on D13 */
97+
{
98+
__HAL_RCC_GPIOB_CLK_ENABLE();
99+
GPIO_InitTypeDef GPIO_InitStruct = {};
100+
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
101+
GPIO_InitStruct.Pull = GPIO_NOPULL;
102+
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
103+
/* PB4 is D13 */
104+
GPIO_InitStruct.Pin = GPIO_PIN_4;
105+
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
106+
}
107+
108+
/* Configure the 3V3 regulator */
109+
{
110+
__HAL_RCC_GPIOH_CLK_ENABLE();
111+
GPIO_InitTypeDef GPIO_InitStruct = {};
112+
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
113+
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
114+
/* PH0 is ENABLE_3V3, PH1 is DISCHARGE_3V3 */
115+
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
116+
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
117+
118+
/* Enable 3V3 regulator and disable discharging */
119+
HAL_GPIO_WritePin(GPIOH, GPIO_InitStruct.Pin, GPIO_PIN_SET);
120+
}
91121
}
92122

93123
/**
@@ -101,66 +131,154 @@ WEAK void SystemClock_Config(void)
101131
RCC_ClkInitTypeDef RCC_ClkInitStruct = {};
102132
RCC_PeriphCLKInitTypeDef PeriphClkInit = {};
103133

134+
/** Enable PWR peripheral clock
135+
*
136+
* RM0394 §5.1.2: PWR registers are on APB1. PWREN (RCC_APB1ENR1 bit 28)
137+
* resets to 1, so this is defensive rather than strictly necessary, but
138+
* required for correctness if PWREN has been cleared by prior code.
139+
* CubeMX generates this unconditionally for all STM32L4 projects.
140+
*/
141+
__HAL_RCC_PWR_CLK_ENABLE();
142+
104143
/** Configure the main internal regulator output voltage
105144
*/
106145
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) {
107146
Error_Handler();
108147
}
109148

110149
/** Configure LSE Drive Capability
150+
*
151+
* Use MEDIUMLOW (not LOW): RCC_LSEDRIVE_LOW risks marginal LSE startup
152+
* on units near the crystal ESR tolerance limit and degrades MSI PLL mode
153+
* (MSIPLLEN) lock quality. ST recommends MEDIUMLOW as the minimum when
154+
* MSIPLLEN is in use.
155+
*
156+
* Backup domain access must be enabled before configuring LSE or selecting
157+
* the RTC clock source, as those registers (RCC->BDCR) are write-protected
158+
* after reset and silently ignore writes until the lock is cleared.
111159
*/
112160
HAL_PWR_EnableBkUpAccess();
113-
__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
161+
__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW);
114162

115163
/** Initializes the RCC Oscillators according to the specified parameters
116164
* in the RCC_OscInitTypeDef structure.
165+
*
166+
* Cygnet config:
167+
* - MSI: MSIRANGE_6 (4 MHz) — used as PLL input
168+
* - HSI: OFF — nothing in this config uses it
169+
* - PLL: NONE → ON (MSI 4 MHz × PLLN=40 / PLLR=2 → SYSCLK = 80 MHz)
170+
* - SYSCLK: MSI (48 MHz) → PLLCLK (80 MHz)
171+
* - USB clock: removed from PeriphCLKConfig (MSI) → PLLSAI1 (48 MHz)
172+
* - MSIRDY transient can not stall SysTick because SYSCLK = PLL, not MSI.
173+
* - FLASH_LATENCY: 2 → 4 (required for 80 MHz / VOS1 per RM0394 §3.3)
117174
*/
118175
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE
119-
| RCC_OSCILLATORTYPE_MSI
120-
| RCC_OSCILLATORTYPE_HSI;
176+
| RCC_OSCILLATORTYPE_MSI;
121177
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
178+
/* HSI is not used as SYSCLK source, PLL/PLLSAI1 input, or any peripheral
179+
* clock reference. Disabling it saves ~200-300 µA. */
180+
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
122181
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
123182
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
124-
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_11;
125-
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
126-
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
127-
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
183+
/* MSIRANGE_6 = 4 MHz — same as Nucleo. Low-frequency reference fed into
184+
* the main PLL (×40/÷2 = 80 MHz) and PLLSAI1 (×24/÷2 = 48 MHz). */
185+
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
186+
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
187+
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
188+
RCC_OscInitStruct.PLL.PLLM = 1;
189+
RCC_OscInitStruct.PLL.PLLN = 40;
190+
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
191+
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
192+
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; /* 4 × 40 / 2 = 80 MHz */
128193
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
129194
Error_Handler();
130195
}
131196

132197
/** Initializes the CPU, AHB and APB buses clocks
198+
*
199+
* SYSCLK = PLLCLK (80 MHz). SysTick and HAL_GetTick() are now driven by
200+
* the PLL output, completely decoupled from MSI. Any subsequent MSIRDY
201+
* transient (from HAL_RCCEx_EnableMSIPLLMode below) cannot stall SysTick.
133202
*/
134203
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
135204
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
136-
// RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
137-
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
205+
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
138206
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
139207
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
140208
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
141-
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
209+
/* FLASH_LATENCY_4: required for HCLK > 64 MHz at VOS1 (RM0394 §3.3.3) */
210+
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
142211
Error_Handler();
143212
}
144213

145214
/** Initializes the Peripheral clocks
215+
*
216+
* USB clock: PLLSAI1 (MSI 4 MHz × PLLSAI1N=24 / PLLSAI1Q=2 = 48 MHz).
217+
* This mirrors the Nucleo L432KC exactly. RCCEx_PLLSAI1_Config() waits
218+
* for PLLSAI1RDY using HAL_GetTick(). Because SYSCLK is now PLL-based,
219+
* HAL_GetTick() is immune to MSI transients — the wait is reliable.
220+
* PLLSAI1 and the main PLL share the same source (MSI) and M divider (1),
221+
* which the HAL enforces; both are configured consistently here.
222+
*
223+
* HAL_RCCEx_PeriphCLKConfig writes CLK48SEL first (pointing at PLLSAI1
224+
* before it is running), then enables PLLSAI1 and waits for its RDY flag.
225+
* During that brief window the USB peripheral has no 48 MHz clock and
226+
* stays quiescent — avoiding the race where a live MSI clock is handed
227+
* to USB before the peripheral is ready to handle the absence of VBUS.
146228
*/
147-
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB | RCC_PERIPHCLK_SDMMC1
148-
| RCC_PERIPHCLK_ADC /* | RCC_PERIPHCLK_OSPI */;
229+
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC | RCC_PERIPHCLK_USB;
149230
PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_SYSCLK;
150-
// PeriphClkInit.OspiClockSelection = RCC_OSPICLKSOURCE_SYSCLK;
151-
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_MSI;
152-
PeriphClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_MSI;
231+
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
232+
PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_MSI;
233+
PeriphClkInit.PLLSAI1.PLLSAI1M = 1;
234+
PeriphClkInit.PLLSAI1.PLLSAI1N = 24;
235+
PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
236+
PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; /* 4 × 24 / 2 = 48 MHz */
237+
PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
238+
PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK;
153239
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
154240
Error_Handler();
155241
}
156242

157-
/** Enable MSI Auto calibration
158-
*/
159-
HAL_RCCEx_EnableMSIPLLMode();
243+
/** Enable MSI Auto calibration (MSIPLLEN, RCC_CR[2])
244+
*
245+
* RM0394 §6.2 (MSI clock): setting MSIPLLEN causes the MSI hardware
246+
* to automatically trim itself against LSE as a phase reference,
247+
* reducing MSI frequency error to < ±0.25%. LSE must already be
248+
* stable (LSERDY=1) before the bit is set — guaranteed here because
249+
* HAL_RCC_OscConfig() waited for LSERDY before returning.
250+
*
251+
* Setting MSIPLLEN causes MSIRDY to deassert transiently while MSI
252+
* re-synchronises to LSE. HAL_RCCEx_EnableMSIPLLMode() returns
253+
* immediately (it is a single SET_BIT); no MSIRDY wait is performed
254+
* inside it. Two conclusions follow:
255+
*
256+
* (1) This call must come AFTER any HAL routine that polls MSIRDY
257+
* under a HAL_GetTick() timeout — if MSIRDY drops inside such
258+
* a routine, the routine returns HAL_TIMEOUT and leaves the
259+
* clock tree in an undefined state.
260+
*
261+
* (2) If SYSCLK were MSI, a deadlock would be possible: MSIRDY
262+
* drops → SysTick stalls → HAL_GetTick() freezes → any
263+
* subsequent timeout loop never exits. RM0394 §6.2.9 confirms
264+
* SysTick is driven by HCLK (= SYSCLK / AHBdiv). Because
265+
* SYSCLK is now PLLCLK (80 MHz), SysTick is completely
266+
* decoupled from MSI and the transient is harmless.
267+
*
268+
* Placement here — after PeriphCLKConfig — satisfies both constraints
269+
* and mirrors the ordering generated by CubeMX for the Nucleo L432KC.
270+
*/
271+
HAL_RCCEx_EnableMSIPLLMode();
160272

161273
/** Ensure that MSI is wake-up system clock
274+
*
275+
* After STOP mode, the PLL is not automatically re-enabled. MSI is used
276+
* as the initial wake-up clock; firmware must re-lock the PLL manually
277+
* if 80 MHz is required after wake. This is the same behaviour as any
278+
* PLL-based design on STM32L4.
162279
*/
163-
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
280+
HAL_RCCEx_WakeUpStopCLKConfig(RCC_STOP_WAKEUPCLOCK_MSI);
281+
#pragma message("Compiled using local Arduino_Core_STM32: 2.13.0-dev.")
164282
}
165283

166284
#ifdef __cplusplus

0 commit comments

Comments
 (0)