@@ -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