Skip to content

Commit 2411917

Browse files
committed
stmhal: Allow pyb.freq() function to change SYSCLK frequency.
Eg pyb.freq(120000000) sets the CPU to 120MHz. The frequency can be set at any point in the code, and can be changed as many times as you like. Note that any active timers will need to be reconfigured after a freq change. Valid range is 24MHz to 168MHz (but not all freqs are supported). The code maintains a 48MHz clock for the USB at all times and it's possible to change the frequency at a USB REPL and keep the REPL alive (well, most of the time it stays, sometimes it resets the USB for some reason). Note that USB does not work with pyb.freq of 24MHz.
1 parent c568a2b commit 2411917

File tree

3 files changed

+136
-16
lines changed

3 files changed

+136
-16
lines changed

stmhal/modpyb.c

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_bootloader_obj, pyb_bootloader);
9090

9191
/// \function info([dump_alloc_table])
9292
/// Print out lots of information about the board.
93-
STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
93+
STATIC mp_obj_t pyb_info(mp_uint_t n_args, const mp_obj_t *args) {
9494
// get and print unique id; 96 bits
9595
{
9696
byte *id = (byte*)0x1fff7a10;
@@ -124,7 +124,7 @@ STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
124124

125125
// qstr info
126126
{
127-
uint n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
127+
mp_uint_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
128128
qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes);
129129
printf("qstr:\n n_pool=%u\n n_qstr=%u\n n_str_data_bytes=%u\n n_total_bytes=%u\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes);
130130
}
@@ -164,19 +164,105 @@ STATIC mp_obj_t pyb_unique_id(void) {
164164
}
165165
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_unique_id_obj, pyb_unique_id);
166166

167-
/// \function freq()
168-
/// Return a tuple of clock frequencies: (SYSCLK, HCLK, PCLK1, PCLK2).
169-
// TODO should also be able to set frequency via this function
170-
STATIC mp_obj_t pyb_freq(void) {
171-
mp_obj_t tuple[4] = {
172-
mp_obj_new_int(HAL_RCC_GetSysClockFreq()),
173-
mp_obj_new_int(HAL_RCC_GetHCLKFreq()),
174-
mp_obj_new_int(HAL_RCC_GetPCLK1Freq()),
175-
mp_obj_new_int(HAL_RCC_GetPCLK2Freq()),
176-
};
177-
return mp_obj_new_tuple(4, tuple);
167+
/// \function freq([sys_freq])
168+
///
169+
/// If given no arguments, returns a tuple of clock frequencies:
170+
/// (SYSCLK, HCLK, PCLK1, PCLK2).
171+
///
172+
/// If given an argument, sets the system frequency to that value in Hz.
173+
/// Eg freq(120000000) gives 120MHz. Note that not all values are
174+
/// supported and the largest supported frequency not greater than
175+
/// the given sys_freq will be selected.
176+
STATIC mp_obj_t pyb_freq(mp_uint_t n_args, const mp_obj_t *args) {
177+
if (n_args == 0) {
178+
// get
179+
mp_obj_t tuple[4] = {
180+
mp_obj_new_int(HAL_RCC_GetSysClockFreq()),
181+
mp_obj_new_int(HAL_RCC_GetHCLKFreq()),
182+
mp_obj_new_int(HAL_RCC_GetPCLK1Freq()),
183+
mp_obj_new_int(HAL_RCC_GetPCLK2Freq()),
184+
};
185+
return mp_obj_new_tuple(4, tuple);
186+
} else {
187+
// set
188+
mp_int_t wanted_sysclk = mp_obj_get_int(args[0]) / 1000000;
189+
// search for a valid PLL configuration that keeps USB at 48MHz
190+
for (; wanted_sysclk > 0; wanted_sysclk--) {
191+
for (mp_uint_t p = 2; p <= 8; p += 2) {
192+
if (wanted_sysclk * p % 48 != 0) {
193+
continue;
194+
}
195+
mp_uint_t q = wanted_sysclk * p / 48;
196+
if (q < 2 || q > 15) {
197+
continue;
198+
}
199+
if (wanted_sysclk * p % (HSE_VALUE / 1000000) != 0) {
200+
continue;
201+
}
202+
mp_uint_t n_by_m = wanted_sysclk * p / (HSE_VALUE / 1000000);
203+
mp_uint_t m = 192 / n_by_m;
204+
while (m < (HSE_VALUE / 2000000) || n_by_m * m < 192) {
205+
m += 1;
206+
}
207+
if (m > (HSE_VALUE / 1000000)) {
208+
continue;
209+
}
210+
mp_uint_t n = n_by_m * m;
211+
if (n < 192 || n > 432) {
212+
continue;
213+
}
214+
215+
// found values!
216+
217+
// let the USB CDC have a chance to process before we change the clock
218+
HAL_Delay(USBD_CDC_POLLING_INTERVAL + 2);
219+
220+
// set HSE as system clock source to allow modification of the PLL configuration
221+
RCC_ClkInitTypeDef RCC_ClkInitStruct;
222+
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
223+
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
224+
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
225+
goto fail;
226+
}
227+
228+
// re-configure PLL
229+
RCC_OscInitTypeDef RCC_OscInitStruct;
230+
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
231+
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
232+
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
233+
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
234+
RCC_OscInitStruct.PLL.PLLM = m;
235+
RCC_OscInitStruct.PLL.PLLN = n;
236+
RCC_OscInitStruct.PLL.PLLP = p;
237+
RCC_OscInitStruct.PLL.PLLQ = q;
238+
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
239+
goto fail;
240+
}
241+
242+
// set PLL as system clock source
243+
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
244+
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
245+
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
246+
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
247+
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
248+
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
249+
goto fail;
250+
}
251+
252+
// re-init TIM3 for USB CDC rate
253+
timer_tim3_init();
254+
255+
return mp_const_none;
256+
257+
void __fatal_error(const char *msg);
258+
fail:
259+
__fatal_error("can't change freq");
260+
}
261+
}
262+
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't make valid freq"));
263+
}
178264
}
179-
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_freq_obj, pyb_freq);
265+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_freq_obj, 0, 1, pyb_freq);
180266

181267
/// \function sync()
182268
/// Sync all file systems.
@@ -336,7 +422,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_have_cdc_obj, pyb_have_cdc);
336422

337423
/// \function repl_uart(uart)
338424
/// Get or set the UART object that the REPL is repeated on.
339-
STATIC mp_obj_t pyb_repl_uart(uint n_args, const mp_obj_t *args) {
425+
STATIC mp_obj_t pyb_repl_uart(mp_uint_t n_args, const mp_obj_t *args) {
340426
if (n_args == 0) {
341427
if (pyb_stdio_uart == NULL) {
342428
return mp_const_none;

stmhal/system_stm32f4xx.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,40 @@ void SystemCoreClockUpdate(void)
312312
* Flash Latency(WS) = 5
313313
* @param None
314314
* @retval None
315+
*
316+
* PLL is configured as follows:
317+
*
318+
* VCO_IN = HSE / M
319+
* VCO_OUT = HSE / M * N
320+
* PLLCLK = HSE / M * N / P
321+
* PLL48CK = HSE / M * N / Q
322+
*
323+
* SYSCLK = PLLCLK
324+
* HCLK = SYSCLK / AHB_PRESC
325+
* PCLKx = HCLK / APBx_PRESC
326+
*
327+
* Constraints on parameters:
328+
*
329+
* VCO_IN between 1MHz and 2MHz (2MHz recommended)
330+
* VCO_OUT between 192MHz and 432MHz
331+
* HSE = 8MHz
332+
* M = 2 .. 63 (inclusive)
333+
* N = 192 ... 432 (inclusive)
334+
* P = 2, 4, 6, 8
335+
* Q = 2 .. 15 (inclusive)
336+
*
337+
* AHB_PRESC=1,2,4,8,16,64,128,256,512
338+
* APBx_PRESC=1,2,4,8,16
339+
*
340+
* Output clocks:
341+
*
342+
* CPU SYSCLK max 168MHz
343+
* USB,RNG,SDIO PLL48CK must be 48MHz for USB
344+
* AHB HCLK max 168MHz
345+
* APB1 PCLK1 max 42MHz
346+
* APB2 PCLK2 max 84MHz
347+
*
348+
* Timers run from APBx if APBx_PRESC=1, else 2x APBx
315349
*/
316350
void SystemClock_Config(void)
317351
{

stmhal/timer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ void timer_tim3_init(void) {
181181

182182
TIM3_Handle.Instance = TIM3;
183183
TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms
184-
TIM3_Handle.Init.Prescaler = 84-1; // for System clock at 168MHz, TIM3 runs at 1MHz
184+
TIM3_Handle.Init.Prescaler = 2 * HAL_RCC_GetPCLK1Freq() / 1000000 - 1; // TIM3 runs at 1MHz
185185
TIM3_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
186186
TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
187187
HAL_TIM_Base_Init(&TIM3_Handle);

0 commit comments

Comments
 (0)