-
Notifications
You must be signed in to change notification settings - Fork 7.6k
[WIP] Handle APB frequency change #2250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
daea578
Add APB change callbacks and move cpu code to own file
me-no-dev f7c56e3
Properly set esp_timer and FreeRTOS tick dividers
me-no-dev 67d5ebc
Improve updated devisors
me-no-dev 55c725f
No need to update REF_TICK yet
me-no-dev 344c76d
Add initial handling for UART baud change
me-no-dev 0532c88
fix uartWriteBuf and uartDetectBaudrate
me-no-dev 7443fe2
trigger callbacks even when APB did not change
me-no-dev c31ea22
toggle UART ISR on CPU change
me-no-dev 8b6483d
add XTAL freq getter and add cpu freq validation
me-no-dev 28dd812
Support CPU frequency changes in I2C (#2287)
stickbreaker 7cda07e
Implement clock change for the other peripherals
me-no-dev 30f4fdc
remove bad CPU clock values from the menu
me-no-dev 25a2d94
Add note to CPU freqs that support WiFi and BT
me-no-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
|
||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include "sdkconfig.h" | ||
#include "freertos/FreeRTOS.h" | ||
#include "freertos/semphr.h" | ||
#include "freertos/task.h" | ||
#include "freertos/xtensa_timer.h" | ||
#include "esp_attr.h" | ||
#include "esp_log.h" | ||
#include "soc/rtc.h" | ||
#include "soc/rtc_cntl_reg.h" | ||
#include "rom/rtc.h" | ||
#include "soc/apb_ctrl_reg.h" | ||
#include "esp32-hal.h" | ||
#include "esp32-hal-cpu.h" | ||
|
||
typedef struct apb_change_cb_s { | ||
struct apb_change_cb_s * next; | ||
void * arg; | ||
apb_change_cb_t cb; | ||
} apb_change_t; | ||
|
||
const uint32_t MHZ = 1000000; | ||
|
||
static apb_change_t * apb_change_callbacks = NULL; | ||
static xSemaphoreHandle apb_change_lock = NULL; | ||
|
||
static void initApbChangeCallback(){ | ||
static volatile bool initialized = false; | ||
if(!initialized){ | ||
initialized = true; | ||
apb_change_lock = xSemaphoreCreateMutex(); | ||
if(!apb_change_lock){ | ||
initialized = false; | ||
} | ||
} | ||
} | ||
|
||
static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ | ||
initApbChangeCallback(); | ||
xSemaphoreTake(apb_change_lock, portMAX_DELAY); | ||
apb_change_t * r = apb_change_callbacks; | ||
while(r != NULL){ | ||
r->cb(r->arg, ev_type, old_apb, new_apb); | ||
r=r->next; | ||
} | ||
xSemaphoreGive(apb_change_lock); | ||
} | ||
|
||
bool addApbChangeCallback(void * arg, apb_change_cb_t cb){ | ||
initApbChangeCallback(); | ||
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t)); | ||
if(!c){ | ||
log_e("Callback Object Malloc Failed"); | ||
return false; | ||
} | ||
c->next = NULL; | ||
c->arg = arg; | ||
c->cb = cb; | ||
xSemaphoreTake(apb_change_lock, portMAX_DELAY); | ||
if(apb_change_callbacks == NULL){ | ||
apb_change_callbacks = c; | ||
} else { | ||
apb_change_t * r = apb_change_callbacks; | ||
if(r->cb != cb || r->arg != arg){ | ||
while(r->next){ | ||
r = r->next; | ||
if(r->cb == cb && r->arg == arg){ | ||
free(c); | ||
goto unlock_and_exit; | ||
me-no-dev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
r->next = c; | ||
} | ||
} | ||
unlock_and_exit: | ||
xSemaphoreGive(apb_change_lock); | ||
return true; | ||
} | ||
|
||
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){ | ||
initApbChangeCallback(); | ||
xSemaphoreTake(apb_change_lock, portMAX_DELAY); | ||
apb_change_t * r = apb_change_callbacks; | ||
if(r == NULL){ | ||
xSemaphoreGive(apb_change_lock); | ||
return false; | ||
} | ||
if(r->cb == cb && r->arg == arg){ | ||
apb_change_callbacks = r->next; | ||
free(r); | ||
} else { | ||
while(r->next && (r->next->cb != cb || r->next->arg != arg)){ | ||
r = r->next; | ||
} | ||
if(r->next == NULL || r->next->cb != cb || r->next->arg != arg){ | ||
xSemaphoreGive(apb_change_lock); | ||
return false; | ||
} | ||
apb_change_t * c = r->next; | ||
r->next = c->next; | ||
free(c); | ||
} | ||
xSemaphoreGive(apb_change_lock); | ||
return true; | ||
} | ||
|
||
static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){ | ||
if(conf->freq_mhz >= 80){ | ||
return 80 * MHZ; | ||
} | ||
return (conf->source_freq_mhz * MHZ) / conf->div; | ||
} | ||
|
||
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF | ||
|
||
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz){ | ||
rtc_cpu_freq_config_t conf, cconf; | ||
uint32_t capb, apb; | ||
//Get XTAL Frequency and calculate min CPU MHz | ||
rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get(); | ||
uint32_t min_cpu_mhz = 10; | ||
if(xtal > RTC_XTAL_FREQ_AUTO){ | ||
if(xtal < RTC_XTAL_FREQ_40M) { | ||
min_cpu_mhz = xtal / 2; //13Mhz for 26Mhz XTAL | ||
if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2)){ | ||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2); | ||
return false; | ||
} | ||
} else if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2) && cpu_freq_mhz != (xtal/4)){ | ||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4); | ||
return false; | ||
} | ||
} | ||
if(cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 80){ | ||
if(xtal >= RTC_XTAL_FREQ_40M){ | ||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4); | ||
} else { | ||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2); | ||
} | ||
return false; | ||
} | ||
//Get current CPU clock configuration | ||
rtc_clk_cpu_freq_get_config(&cconf); | ||
//return if frequency has not changed | ||
if(cconf.freq_mhz == cpu_freq_mhz){ | ||
return true; | ||
} | ||
//Get configuration for the new CPU frequency | ||
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){ | ||
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz); | ||
return false; | ||
} | ||
//Current APB | ||
capb = calculateApb(&cconf); | ||
//New APB | ||
apb = calculateApb(&conf); | ||
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); | ||
//Call peripheral functions before the APB change | ||
if(apb_change_callbacks){ | ||
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb); | ||
} | ||
//Make the frequency change | ||
rtc_clk_cpu_freq_set_config_fast(&conf); | ||
if(capb != apb){ | ||
//Update REF_TICK (uncomment if REF_TICK is different than 1MHz) | ||
//if(conf.freq_mhz < 80){ | ||
// ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1; | ||
//} | ||
//Update APB Freq REG | ||
rtc_clk_apb_freq_update(apb); | ||
//Update esp_timer divisor | ||
esp_timer_impl_update_apb_freq(apb / MHZ); | ||
} | ||
//Update FreeRTOS Tick Divisor | ||
uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb); | ||
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC; | ||
//Call peripheral functions after the APB change | ||
if(apb_change_callbacks){ | ||
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); | ||
} | ||
return true; | ||
} | ||
|
||
uint32_t getCpuFrequencyMhz(){ | ||
rtc_cpu_freq_config_t conf; | ||
rtc_clk_cpu_freq_get_config(&conf); | ||
return conf.freq_mhz; | ||
} | ||
|
||
uint32_t getXtalFrequencyMhz(){ | ||
return rtc_clk_xtal_freq_get(); | ||
} | ||
|
||
uint32_t getApbFrequency(){ | ||
rtc_cpu_freq_config_t conf; | ||
rtc_clk_cpu_freq_get_config(&conf); | ||
return calculateApb(&conf); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
|
||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#ifndef _ESP32_HAL_CPU_H_ | ||
#define _ESP32_HAL_CPU_H_ | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#include <stdint.h> | ||
#include <stdbool.h> | ||
#include <stdlib.h> | ||
|
||
typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t; | ||
|
||
typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb); | ||
|
||
bool addApbChangeCallback(void * arg, apb_change_cb_t cb); | ||
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb); | ||
|
||
//function takes the following frequencies as valid values: | ||
// 240, 160, 80 <<< For all XTAL types | ||
// 40, 20, 10 <<< For 40MHz XTAL | ||
// 26, 13 <<< For 26MHz XTAL | ||
// 24, 12 <<< For 24MHz XTAL | ||
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz); | ||
|
||
uint32_t getCpuFrequencyMhz(); // In MHz | ||
uint32_t getXtalFrequencyMhz(); // In MHz | ||
uint32_t getApbFrequency(); // In Hz | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif /* _ESP32_HAL_CPU_H_ */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a template class that can be used for linked lists that is generic enough to plug in here, maybe even something from ESP-IDF that can be leveraged instead of managing the list here (and later in remove)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know of one, but I have been thinking of such thing for some time. This is C though... so IDK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a reason to stick with C only here, there are already a few C++ pieces here and there...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can also use something like this to mix C++ into C...
it has C linkage but you can call it with different values to get different versions of it (this is part of how I do ISRs using one method but two diff data sources)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the reason why it is C is so the whole HAL can be used in C-only libs and extensions. Not really sure if it's worth it, but it's how it started :)