summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/library/pyb.rst12
-rw-r--r--stmhal/modpyb.c173
2 files changed, 120 insertions, 65 deletions
diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst
index d221e9720c..8bb08215b5 100644
--- a/docs/library/pyb.rst
+++ b/docs/library/pyb.rst
@@ -103,6 +103,18 @@ Power related functions
supported and the largest supported frequency not greater than
the given sys_freq will be selected.
+ Supported frequencies are (in MHz): 8, 16, 24, 30, 32, 36, 40, 42, 48,
+ 54, 56, 60, 64, 72, 84, 96, 108, 120, 144, 168.
+
+ 8MHz uses the HSE (external crystal) directly and 16MHz uses the HSI
+ (internal oscillator) directly. The higher frequencies use the HSE to
+ drive the PLL (phase locked loop), and then use the output of the PLL.
+
+ Note that if you change the frequency while the USB is enabled then
+ the USB may become unreliable. It is best to change the frequency
+ in boot.py, before the USB peripheral is started. Also note that
+ frequencies below 36MHz do not allow the USB to function correctly.
+
.. function:: wfi()
Wait for an interrupt.
diff --git a/stmhal/modpyb.c b/stmhal/modpyb.c
index 062202aade..d3ea797976 100644
--- a/stmhal/modpyb.c
+++ b/stmhal/modpyb.c
@@ -195,80 +195,123 @@ STATIC mp_obj_t pyb_freq(mp_uint_t n_args, const mp_obj_t *args) {
} else {
// set
mp_int_t wanted_sysclk = mp_obj_get_int(args[0]) / 1000000;
- // search for a valid PLL configuration that keeps USB at 48MHz
- for (; wanted_sysclk > 0; wanted_sysclk--) {
- for (mp_uint_t p = 2; p <= 8; p += 2) {
- if (wanted_sysclk * p % 48 != 0) {
- continue;
- }
- mp_uint_t q = wanted_sysclk * p / 48;
- if (q < 2 || q > 15) {
- continue;
- }
- if (wanted_sysclk * p % (HSE_VALUE / 1000000) != 0) {
- continue;
- }
- mp_uint_t n_by_m = wanted_sysclk * p / (HSE_VALUE / 1000000);
- mp_uint_t m = 192 / n_by_m;
- while (m < (HSE_VALUE / 2000000) || n_by_m * m < 192) {
- m += 1;
- }
- if (m > (HSE_VALUE / 1000000)) {
- continue;
- }
- mp_uint_t n = n_by_m * m;
- if (n < 192 || n > 432) {
- continue;
- }
- // found values!
-
- // let the USB CDC have a chance to process before we change the clock
- HAL_Delay(USBD_CDC_POLLING_INTERVAL + 2);
-
- // set HSE as system clock source to allow modification of the PLL configuration
- RCC_ClkInitTypeDef RCC_ClkInitStruct;
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
- goto fail;
+ // default PLL parameters that give 48MHz on PLL48CK
+ uint32_t m = HSE_VALUE / 1000000, n = 336, p = 2, q = 7;
+ uint32_t sysclk_source;
+
+ // the following logic assumes HSE < HSI
+ if (HSE_VALUE / 1000000 <= wanted_sysclk && wanted_sysclk < HSI_VALUE / 1000000) {
+ // use HSE as SYSCLK
+ sysclk_source = RCC_SYSCLKSOURCE_HSE;
+ } else if (HSI_VALUE / 1000000 <= wanted_sysclk && wanted_sysclk < 24) {
+ // use HSI as SYSCLK
+ sysclk_source = RCC_SYSCLKSOURCE_HSI;
+ } else {
+ // search for a valid PLL configuration that keeps USB at 48MHz
+ for (; wanted_sysclk > 0; wanted_sysclk--) {
+ for (p = 2; p <= 8; p += 2) {
+ // compute VCO_OUT
+ mp_uint_t vco_out = wanted_sysclk * p;
+ // make sure VCO_OUT is between 192MHz and 432MHz
+ if (vco_out < 192 || vco_out > 432) {
+ continue;
+ }
+ // make sure Q is an integer
+ if (vco_out % 48 != 0) {
+ continue;
+ }
+ // solve for Q to get PLL48CK at 48MHz
+ q = vco_out / 48;
+ // make sure Q is in range
+ if (q < 2 || q > 15) {
+ continue;
+ }
+ // make sure N/M is an integer
+ if (vco_out % (HSE_VALUE / 1000000) != 0) {
+ continue;
+ }
+ // solve for N/M
+ mp_uint_t n_by_m = vco_out / (HSE_VALUE / 1000000);
+ // solve for M, making sure VCO_IN (=HSE/M) is between 1MHz and 2MHz
+ m = 192 / n_by_m;
+ while (m < (HSE_VALUE / 2000000) || n_by_m * m < 192) {
+ m += 1;
+ }
+ if (m > (HSE_VALUE / 1000000)) {
+ continue;
+ }
+ // solve for N
+ n = n_by_m * m;
+ // make sure N is in range
+ if (n < 192 || n > 432) {
+ continue;
+ }
+
+ // found values!
+ sysclk_source = RCC_SYSCLKSOURCE_PLLCLK;
+ goto set_clk;
}
+ }
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't make valid freq"));
+ }
- // re-configure PLL
- RCC_OscInitTypeDef RCC_OscInitStruct;
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- RCC_OscInitStruct.PLL.PLLM = m;
- RCC_OscInitStruct.PLL.PLLN = n;
- RCC_OscInitStruct.PLL.PLLP = p;
- RCC_OscInitStruct.PLL.PLLQ = q;
- if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
- goto fail;
- }
+ set_clk:
+ //printf("%lu %lu %lu %lu %lu\n", sysclk_source, m, n, p, q);
- // set PLL as system clock source
- RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
- goto fail;
- }
+ // let the USB CDC have a chance to process before we change the clock
+ HAL_Delay(USBD_CDC_POLLING_INTERVAL + 2);
- // re-init TIM3 for USB CDC rate
- timer_tim3_init();
+ // desired system clock source is in sysclk_source
+ RCC_ClkInitTypeDef RCC_ClkInitStruct;
+ RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
+ if (sysclk_source == RCC_SYSCLKSOURCE_PLLCLK) {
+ // set HSE as system clock source to allow modification of the PLL configuration
+ // we then change to PLL after re-configuring PLL
+ RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
+ } else {
+ // directly set the system clock source as desired
+ RCC_ClkInitStruct.SYSCLKSource = sysclk_source;
+ }
+ RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+ RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
+ RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
+ if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
+ goto fail;
+ }
- return mp_const_none;
+ // re-configure PLL
+ // even if we don't use the PLL for the system clock, we still need it for USB, RNG and SDIO
+ RCC_OscInitTypeDef RCC_OscInitStruct;
+ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+ RCC_OscInitStruct.HSEState = RCC_HSE_ON;
+ RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+ RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+ RCC_OscInitStruct.PLL.PLLM = m;
+ RCC_OscInitStruct.PLL.PLLN = n;
+ RCC_OscInitStruct.PLL.PLLP = p;
+ RCC_OscInitStruct.PLL.PLLQ = q;
+ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
+ goto fail;
+ }
- void __fatal_error(const char *msg);
- fail:
- __fatal_error("can't change freq");
+ // set PLL as system clock source if wanted
+ if (sysclk_source == RCC_SYSCLKSOURCE_PLLCLK) {
+ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
+ RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+ if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
+ goto fail;
}
}
- nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't make valid freq"));
+
+ // re-init TIM3 for USB CDC rate
+ timer_tim3_init();
+
+ return mp_const_none;
+
+ fail:;
+ void NORETURN __fatal_error(const char *msg);
+ __fatal_error("can't change freq");
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_freq_obj, 0, 1, pyb_freq);