Skip to content

Commit 2fd39b1

Browse files
authored
Handle APB frequency change (#2250)
* Add APB change callbacks and move cpu code to own file * Properly set esp_timer and FreeRTOS tick dividers * Improve updated devisors * No need to update REF_TICK yet * Add initial handling for UART baud change * fix uartWriteBuf and uartDetectBaudrate * trigger callbacks even when APB did not change * toggle UART ISR on CPU change * add XTAL freq getter and add cpu freq validation * Support CPU frequency changes in I2C (#2287) **esp32-hal-i2c.c** * add callback for cpu frequency changes * adjust fifo thresholds based on cpu frequency and i2c bus frequency * reduce i2c bus frequency if differential is too small **Wire.h** * version to 1.1.0 * Implement clock change for the other peripherals * remove bad CPU clock values from the menu * Add note to CPU freqs that support WiFi and BT
1 parent ff18a21 commit 2fd39b1

13 files changed

+484
-113
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(CORE_SRCS
33
cores/esp32/cbuf.cpp
44
cores/esp32/esp32-hal-adc.c
55
cores/esp32/esp32-hal-bt.c
6+
cores/esp32/esp32-hal-cpu.c
67
cores/esp32/esp32-hal-dac.c
78
cores/esp32/esp32-hal-gpio.c
89
cores/esp32/esp32-hal-i2c.c

boards.txt

+4-16
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,22 @@ esp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080
5050
esp32.menu.PartitionScheme.fatflash=16M Fat
5151
esp32.menu.PartitionScheme.fatflash.build.partitions=ffat
5252

53-
esp32.menu.CPUFreq.240=240MHz
53+
esp32.menu.CPUFreq.240=240MHz (WiFi/BT)
5454
esp32.menu.CPUFreq.240.build.f_cpu=240000000L
55-
esp32.menu.CPUFreq.160=160MHz
55+
esp32.menu.CPUFreq.160=160MHz (WiFi/BT)
5656
esp32.menu.CPUFreq.160.build.f_cpu=160000000L
57-
esp32.menu.CPUFreq.80=80MHz
57+
esp32.menu.CPUFreq.80=80MHz (WiFi/BT)
5858
esp32.menu.CPUFreq.80.build.f_cpu=80000000L
5959
esp32.menu.CPUFreq.40=40MHz (40MHz XTAL)
6060
esp32.menu.CPUFreq.40.build.f_cpu=40000000L
6161
esp32.menu.CPUFreq.26=26MHz (26MHz XTAL)
6262
esp32.menu.CPUFreq.26.build.f_cpu=26000000L
6363
esp32.menu.CPUFreq.20=20MHz (40MHz XTAL)
6464
esp32.menu.CPUFreq.20.build.f_cpu=20000000L
65-
esp32.menu.CPUFreq.13=13MHz
65+
esp32.menu.CPUFreq.13=13MHz (26MHz XTAL)
6666
esp32.menu.CPUFreq.13.build.f_cpu=13000000L
6767
esp32.menu.CPUFreq.10=10MHz (40MHz XTAL)
6868
esp32.menu.CPUFreq.10.build.f_cpu=10000000L
69-
esp32.menu.CPUFreq.8=8MHz (40MHz XTAL)
70-
esp32.menu.CPUFreq.8.build.f_cpu=8000000L
71-
esp32.menu.CPUFreq.5=5MHz
72-
esp32.menu.CPUFreq.5.build.f_cpu=5000000L
73-
esp32.menu.CPUFreq.4=4MHz
74-
esp32.menu.CPUFreq.4.build.f_cpu=4000000L
75-
esp32.menu.CPUFreq.3=3MHz
76-
esp32.menu.CPUFreq.3.build.f_cpu=3000000L
77-
esp32.menu.CPUFreq.2=2MHz
78-
esp32.menu.CPUFreq.2.build.f_cpu=2000000L
79-
esp32.menu.CPUFreq.1=1MHz
80-
esp32.menu.CPUFreq.1.build.f_cpu=1000000L
8169

8270
esp32.menu.FlashMode.qio=QIO
8371
esp32.menu.FlashMode.qio.build.flash_mode=dio

cores/esp32/esp32-hal-cpu.c

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "sdkconfig.h"
16+
#include "freertos/FreeRTOS.h"
17+
#include "freertos/semphr.h"
18+
#include "freertos/task.h"
19+
#include "freertos/xtensa_timer.h"
20+
#include "esp_attr.h"
21+
#include "esp_log.h"
22+
#include "soc/rtc.h"
23+
#include "soc/rtc_cntl_reg.h"
24+
#include "rom/rtc.h"
25+
#include "soc/apb_ctrl_reg.h"
26+
#include "esp32-hal.h"
27+
#include "esp32-hal-cpu.h"
28+
29+
typedef struct apb_change_cb_s {
30+
struct apb_change_cb_s * next;
31+
void * arg;
32+
apb_change_cb_t cb;
33+
} apb_change_t;
34+
35+
const uint32_t MHZ = 1000000;
36+
37+
static apb_change_t * apb_change_callbacks = NULL;
38+
static xSemaphoreHandle apb_change_lock = NULL;
39+
40+
static void initApbChangeCallback(){
41+
static volatile bool initialized = false;
42+
if(!initialized){
43+
initialized = true;
44+
apb_change_lock = xSemaphoreCreateMutex();
45+
if(!apb_change_lock){
46+
initialized = false;
47+
}
48+
}
49+
}
50+
51+
static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
52+
initApbChangeCallback();
53+
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
54+
apb_change_t * r = apb_change_callbacks;
55+
while(r != NULL){
56+
r->cb(r->arg, ev_type, old_apb, new_apb);
57+
r=r->next;
58+
}
59+
xSemaphoreGive(apb_change_lock);
60+
}
61+
62+
bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
63+
initApbChangeCallback();
64+
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t));
65+
if(!c){
66+
log_e("Callback Object Malloc Failed");
67+
return false;
68+
}
69+
c->next = NULL;
70+
c->arg = arg;
71+
c->cb = cb;
72+
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
73+
if(apb_change_callbacks == NULL){
74+
apb_change_callbacks = c;
75+
} else {
76+
apb_change_t * r = apb_change_callbacks;
77+
if(r->cb != cb || r->arg != arg){
78+
while(r->next){
79+
r = r->next;
80+
if(r->cb == cb && r->arg == arg){
81+
free(c);
82+
goto unlock_and_exit;
83+
}
84+
}
85+
r->next = c;
86+
}
87+
}
88+
unlock_and_exit:
89+
xSemaphoreGive(apb_change_lock);
90+
return true;
91+
}
92+
93+
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){
94+
initApbChangeCallback();
95+
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
96+
apb_change_t * r = apb_change_callbacks;
97+
if(r == NULL){
98+
xSemaphoreGive(apb_change_lock);
99+
return false;
100+
}
101+
if(r->cb == cb && r->arg == arg){
102+
apb_change_callbacks = r->next;
103+
free(r);
104+
} else {
105+
while(r->next && (r->next->cb != cb || r->next->arg != arg)){
106+
r = r->next;
107+
}
108+
if(r->next == NULL || r->next->cb != cb || r->next->arg != arg){
109+
xSemaphoreGive(apb_change_lock);
110+
return false;
111+
}
112+
apb_change_t * c = r->next;
113+
r->next = c->next;
114+
free(c);
115+
}
116+
xSemaphoreGive(apb_change_lock);
117+
return true;
118+
}
119+
120+
static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){
121+
if(conf->freq_mhz >= 80){
122+
return 80 * MHZ;
123+
}
124+
return (conf->source_freq_mhz * MHZ) / conf->div;
125+
}
126+
127+
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF
128+
129+
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz){
130+
rtc_cpu_freq_config_t conf, cconf;
131+
uint32_t capb, apb;
132+
//Get XTAL Frequency and calculate min CPU MHz
133+
rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get();
134+
uint32_t min_cpu_mhz = 10;
135+
if(xtal > RTC_XTAL_FREQ_AUTO){
136+
if(xtal < RTC_XTAL_FREQ_40M) {
137+
min_cpu_mhz = xtal / 2; //13Mhz for 26Mhz XTAL
138+
if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2)){
139+
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
140+
return false;
141+
}
142+
} else if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2) && cpu_freq_mhz != (xtal/4)){
143+
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
144+
return false;
145+
}
146+
}
147+
if(cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 80){
148+
if(xtal >= RTC_XTAL_FREQ_40M){
149+
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
150+
} else {
151+
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
152+
}
153+
return false;
154+
}
155+
//Get current CPU clock configuration
156+
rtc_clk_cpu_freq_get_config(&cconf);
157+
//return if frequency has not changed
158+
if(cconf.freq_mhz == cpu_freq_mhz){
159+
return true;
160+
}
161+
//Get configuration for the new CPU frequency
162+
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){
163+
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz);
164+
return false;
165+
}
166+
//Current APB
167+
capb = calculateApb(&cconf);
168+
//New APB
169+
apb = calculateApb(&conf);
170+
log_d("%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb);
171+
//Call peripheral functions before the APB change
172+
if(apb_change_callbacks){
173+
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
174+
}
175+
//Make the frequency change
176+
rtc_clk_cpu_freq_set_config_fast(&conf);
177+
if(capb != apb){
178+
//Update REF_TICK (uncomment if REF_TICK is different than 1MHz)
179+
//if(conf.freq_mhz < 80){
180+
// ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1;
181+
//}
182+
//Update APB Freq REG
183+
rtc_clk_apb_freq_update(apb);
184+
//Update esp_timer divisor
185+
esp_timer_impl_update_apb_freq(apb / MHZ);
186+
}
187+
//Update FreeRTOS Tick Divisor
188+
uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb);
189+
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC;
190+
//Call peripheral functions after the APB change
191+
if(apb_change_callbacks){
192+
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
193+
}
194+
return true;
195+
}
196+
197+
uint32_t getCpuFrequencyMhz(){
198+
rtc_cpu_freq_config_t conf;
199+
rtc_clk_cpu_freq_get_config(&conf);
200+
return conf.freq_mhz;
201+
}
202+
203+
uint32_t getXtalFrequencyMhz(){
204+
return rtc_clk_xtal_freq_get();
205+
}
206+
207+
uint32_t getApbFrequency(){
208+
rtc_cpu_freq_config_t conf;
209+
rtc_clk_cpu_freq_get_config(&conf);
210+
return calculateApb(&conf);
211+
}

cores/esp32/esp32-hal-cpu.h

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef _ESP32_HAL_CPU_H_
16+
#define _ESP32_HAL_CPU_H_
17+
18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif
21+
22+
#include <stdint.h>
23+
#include <stdbool.h>
24+
#include <stdlib.h>
25+
26+
typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t;
27+
28+
typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
29+
30+
bool addApbChangeCallback(void * arg, apb_change_cb_t cb);
31+
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb);
32+
33+
//function takes the following frequencies as valid values:
34+
// 240, 160, 80 <<< For all XTAL types
35+
// 40, 20, 10 <<< For 40MHz XTAL
36+
// 26, 13 <<< For 26MHz XTAL
37+
// 24, 12 <<< For 24MHz XTAL
38+
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz);
39+
40+
uint32_t getCpuFrequencyMhz(); // In MHz
41+
uint32_t getXtalFrequencyMhz(); // In MHz
42+
uint32_t getApbFrequency(); // In Hz
43+
44+
#ifdef __cplusplus
45+
}
46+
#endif
47+
48+
#endif /* _ESP32_HAL_CPU_H_ */

0 commit comments

Comments
 (0)