-
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
Changes from 1 commit
daea578
f7c56e3
67d5ebc
55c725f
344c76d
0532c88
7443fe2
c31ea22
8b6483d
28dd812
7cda07e
30f4fdc
25a2d94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// 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 "esp_attr.h" | ||
#include "esp_log.h" | ||
#include "soc/rtc.h" | ||
#include "soc/rtc_cntl_reg.h" | ||
#include "rom/rtc.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; | ||
|
||
static apb_change_t * apb_change_callbacks = NULL; | ||
static xSemaphoreHandle apb_change_lock = NULL; | ||
|
||
static uint32_t _cpu_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; | ||
static uint32_t _sys_time_multiplier = 1; | ||
|
||
uint64_t IRAM_ATTR micros64(){ | ||
return (uint64_t)(esp_timer_get_time()) * _sys_time_multiplier; | ||
} | ||
|
||
//ToDo: figure out how to set FreeRTOS tick properly | ||
void delay(uint32_t ms){ | ||
vTaskDelay((ms * _cpu_freq_mhz) / (portTICK_PERIOD_MS * CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)); | ||
} | ||
|
||
static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){ | ||
if(conf->freq_mhz >= 80){ | ||
return 80000000; | ||
} | ||
return (conf->source_freq_mhz * 1000000) / conf->div; | ||
} | ||
|
||
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); | ||
} | ||
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){ | ||
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; | ||
} | ||
|
||
bool setCpuFrequency(uint32_t cpu_freq_mhz){ | ||
rtc_cpu_freq_config_t conf, cconf; | ||
uint32_t capb, apb; | ||
rtc_clk_cpu_freq_get_config(&cconf); | ||
if(cconf.freq_mhz == cpu_freq_mhz && _cpu_freq_mhz == cpu_freq_mhz){ | ||
return true; | ||
} | ||
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; | ||
} | ||
capb = calculateApb(&cconf); | ||
apb = calculateApb(&conf); | ||
log_i("%s: %u / %u = %u Mhz", (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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this may be better at log_d instead of log_i but I'd defer to your judgement on specific level to log this at. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will remove it all together but it's a good test point to ensure that UART flushes before cpu can be changed. nd I often keep log at info so I do not get all unnecessary debug :P |
||
if(capb != apb && apb_change_callbacks){ | ||
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb); | ||
} | ||
rtc_clk_cpu_freq_set_config_fast(&conf); | ||
_cpu_freq_mhz = conf.freq_mhz; | ||
_sys_time_multiplier = 80000000 / apb; | ||
me-no-dev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if(capb != apb && apb_change_callbacks){ | ||
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); | ||
} | ||
return true; | ||
} | ||
|
||
uint32_t getCpuFrequency(){ | ||
rtc_cpu_freq_config_t conf; | ||
rtc_clk_cpu_freq_get_config(&conf); | ||
return conf.freq_mhz; | ||
} | ||
|
||
uint32_t getApbFrequency(){ | ||
rtc_cpu_freq_config_t conf; | ||
rtc_clk_cpu_freq_get_config(&conf); | ||
return calculateApb(&conf); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// 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, 13, 10, 8, 5, 4, 3, 2, 1 <<< For 40MHz XTAL | ||
// 26, 13, 5, 4, 3, 2, 1 <<< For 26MHz XTAL | ||
// 24, 12, 8, 6, 4, 3, 2, 1 <<< For 24MHz XTAL | ||
bool setCpuFrequency(uint32_t cpu_freq_mhz); | ||
|
||
uint32_t getCpuFrequency(); // In MHz | ||
uint32_t getApbFrequency(); // In Hz | ||
|
||
uint64_t micros64(); // 64bit version of micros() | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif /* _ESP32_HAL_CPU_H_ */ |
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 :)