From 2fd39b1aff1c1f2be60bbd9b5fbd9c88638b0fcc Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 9 Jan 2019 10:07:54 +0100 Subject: [PATCH] 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 --- CMakeLists.txt | 1 + boards.txt | 20 +-- cores/esp32/esp32-hal-cpu.c | 211 +++++++++++++++++++++++++++++ cores/esp32/esp32-hal-cpu.h | 48 +++++++ cores/esp32/esp32-hal-i2c.c | 83 +++++++++--- cores/esp32/esp32-hal-ledc.c | 29 ++++ cores/esp32/esp32-hal-misc.c | 49 +------ cores/esp32/esp32-hal-sigmadelta.c | 22 +++ cores/esp32/esp32-hal-spi.c | 44 ++++-- cores/esp32/esp32-hal-timer.c | 22 ++- cores/esp32/esp32-hal-uart.c | 55 ++++++-- cores/esp32/esp32-hal.h | 10 +- libraries/Wire/src/Wire.h | 3 +- 13 files changed, 484 insertions(+), 113 deletions(-) create mode 100644 cores/esp32/esp32-hal-cpu.c create mode 100644 cores/esp32/esp32-hal-cpu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5290b2cc..d59a8b8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ set(CORE_SRCS cores/esp32/cbuf.cpp cores/esp32/esp32-hal-adc.c cores/esp32/esp32-hal-bt.c + cores/esp32/esp32-hal-cpu.c cores/esp32/esp32-hal-dac.c cores/esp32/esp32-hal-gpio.c cores/esp32/esp32-hal-i2c.c diff --git a/boards.txt b/boards.txt index 2249a5a3..ed1e85f6 100644 --- a/boards.txt +++ b/boards.txt @@ -50,11 +50,11 @@ esp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32.menu.PartitionScheme.fatflash=16M Fat esp32.menu.PartitionScheme.fatflash.build.partitions=ffat -esp32.menu.CPUFreq.240=240MHz +esp32.menu.CPUFreq.240=240MHz (WiFi/BT) esp32.menu.CPUFreq.240.build.f_cpu=240000000L -esp32.menu.CPUFreq.160=160MHz +esp32.menu.CPUFreq.160=160MHz (WiFi/BT) esp32.menu.CPUFreq.160.build.f_cpu=160000000L -esp32.menu.CPUFreq.80=80MHz +esp32.menu.CPUFreq.80=80MHz (WiFi/BT) esp32.menu.CPUFreq.80.build.f_cpu=80000000L esp32.menu.CPUFreq.40=40MHz (40MHz XTAL) esp32.menu.CPUFreq.40.build.f_cpu=40000000L @@ -62,22 +62,10 @@ esp32.menu.CPUFreq.26=26MHz (26MHz XTAL) esp32.menu.CPUFreq.26.build.f_cpu=26000000L esp32.menu.CPUFreq.20=20MHz (40MHz XTAL) esp32.menu.CPUFreq.20.build.f_cpu=20000000L -esp32.menu.CPUFreq.13=13MHz +esp32.menu.CPUFreq.13=13MHz (26MHz XTAL) esp32.menu.CPUFreq.13.build.f_cpu=13000000L esp32.menu.CPUFreq.10=10MHz (40MHz XTAL) esp32.menu.CPUFreq.10.build.f_cpu=10000000L -esp32.menu.CPUFreq.8=8MHz (40MHz XTAL) -esp32.menu.CPUFreq.8.build.f_cpu=8000000L -esp32.menu.CPUFreq.5=5MHz -esp32.menu.CPUFreq.5.build.f_cpu=5000000L -esp32.menu.CPUFreq.4=4MHz -esp32.menu.CPUFreq.4.build.f_cpu=4000000L -esp32.menu.CPUFreq.3=3MHz -esp32.menu.CPUFreq.3.build.f_cpu=3000000L -esp32.menu.CPUFreq.2=2MHz -esp32.menu.CPUFreq.2.build.f_cpu=2000000L -esp32.menu.CPUFreq.1=1MHz -esp32.menu.CPUFreq.1.build.f_cpu=1000000L esp32.menu.FlashMode.qio=QIO esp32.menu.FlashMode.qio.build.flash_mode=dio diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c new file mode 100644 index 00000000..d9e95dc8 --- /dev/null +++ b/cores/esp32/esp32-hal-cpu.c @@ -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; + } + } + 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); +} diff --git a/cores/esp32/esp32-hal-cpu.h b/cores/esp32/esp32-hal-cpu.h new file mode 100644 index 00000000..646b5985 --- /dev/null +++ b/cores/esp32/esp32-hal-cpu.h @@ -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 +#include +#include + +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_ */ diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 174e4376..f8c384af 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -24,7 +24,7 @@ #include "soc/i2c_struct.h" #include "soc/dport_reg.h" #include "esp_attr.h" - +#include "esp32-hal-cpu.h" // cpu clock change support 31DEC2018 //#define I2C_DEV(i) (volatile i2c_dev_t *)((i)?DR_REG_I2C1_EXT_BASE:DR_REG_I2C_EXT_BASE) //#define I2C_DEV(i) ((i2c_dev_t *)(REG_I2C_BASE(i))) #define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0)) @@ -206,8 +206,8 @@ static i2c_t _i2c_bus_array[2] = { {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0} }; #else -#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS) -#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock) +#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTakeRecursive(i2c->lock, portMAX_DELAY) != pdPASS) +#define I2C_MUTEX_UNLOCK() xSemaphoreGiveRecursive(i2c->lock) static i2c_t _i2c_bus_array[2] = { {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0}, @@ -445,6 +445,39 @@ static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char l } // end of debug support routines +/* Start of CPU Clock change Support +*/ + +static void i2cApbChangeCallback(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + i2c_t* i2c = (i2c_t*) arg; // recover data + if(i2c == NULL) { // point to peripheral control block does not exits + return false; + } + uint32_t oldFreq=0; + switch(ev_type){ + case APB_BEFORE_CHANGE : + if(new_apb < 3000000) {// too slow + log_e("apb speed %d too slow",new_apb); + break; + } + I2C_MUTEX_LOCK(); // lock will spin until current transaction is completed + break; + case APB_AFTER_CHANGE : + oldFreq = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); //read old apbCycles + if(oldFreq>0) { // was configured with value + oldFreq = old_apb / oldFreq; + i2cSetFrequency(i2c,oldFreq); + } + I2C_MUTEX_UNLOCK(); + break; + default : + log_e("unk ev %u",ev_type); + I2C_MUTEX_UNLOCK(); + } + return; +} +/* End of CPU Clock change Support +*/ static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check) { I2C_COMMAND_t cmd; @@ -1221,6 +1254,12 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) I2C_MUTEX_UNLOCK(); return I2C_ERROR_MEMORY; } + if( !addApbChangeCallback( i2c, i2cApbChangeCallback)) { + log_e("install apb Callback failed"); + I2C_MUTEX_UNLOCK(); + return I2C_ERROR_DEV; + } + } //hang until it completes. @@ -1352,6 +1391,9 @@ static void i2cReleaseISR(i2c_t * i2c) if(i2c->intr_handle) { esp_intr_free(i2c->intr_handle); i2c->intr_handle=NULL; + if (!removeApbChangeCallback( i2c, i2cApbChangeCallback)) { + log_e("unable to release apbCallback"); + } } } @@ -1437,8 +1479,7 @@ i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) * PUBLIC API * */ // 24Nov17 only supports Master Mode -i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //before this is called, pins should be detached, else glitch -{ +i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) { log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency); if(i2c_num > 1) { return NULL; @@ -1446,6 +1487,7 @@ i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //b i2c_t * i2c = &_i2c_bus_array[i2c_num]; + // pins should be detached, else glitch if(i2c->sda >= 0){ i2cDetachSDA(i2c, i2c->sda); } @@ -1457,7 +1499,7 @@ i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //b #if !CONFIG_DISABLE_HAL_LOCKS if(i2c->lock == NULL) { - i2c->lock = xSemaphoreCreateMutex(); + i2c->lock = xSemaphoreCreateRecursiveMutex(); if(i2c->lock == NULL) { return NULL; } @@ -1604,7 +1646,8 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b return last_error; } -#define MIN_I2C_CLKS 100 +#define MIN_I2C_CLKS 100 // minimum ratio between cpu and i2c Bus clocks +#define INTERRUPT_CYCLE_OVERHEAD 16000 // number of cpu clocks necessary to respond to interrupt i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) { if(i2c == NULL) { @@ -1614,17 +1657,18 @@ i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) uint32_t period = (apb/clk_speed) / 2; if((apb/8192 > clk_speed)||(apb/MIN_I2C_CLKS < clk_speed)){ //out of bounds - log_w("i2c freq(%d) out of bounds.vs APB Clock(%d), min=%d, max=%d",clk_speed,apb,(apb/8192),(apb/MIN_I2C_CLKS)); + log_d("i2c freq(%d) out of bounds.vs APB Clock(%d), min=%d, max=%d",clk_speed,apb,(apb/8192),(apb/MIN_I2C_CLKS)); } if(period < (MIN_I2C_CLKS/2) ){ period = (MIN_I2C_CLKS/2); clk_speed = apb/(period*2); - log_w("APB Freq too slow, Reducing i2c Freq to %d Hz",clk_speed); + log_d("APB Freq too slow, Reducing i2c Freq to %d Hz",clk_speed); } else if ( period> 4095) { period = 4095; clk_speed = apb/(period*2); - log_w("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed); + log_d("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed); } + log_v("freq=%dHz",clk_speed); uint32_t halfPeriod = period/2; uint32_t quarterPeriod = period/4; @@ -1633,14 +1677,19 @@ i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) I2C_FIFO_CONF_t f; - // Adjust Fifo thresholds based on frequency f.val = i2c->dev->fifo_conf.val; - uint32_t a = (clk_speed / 50000L )+1; - if (a > 24) a=24; - f.rx_fifo_full_thrhd = 32 - a; - f.tx_fifo_empty_thrhd = a; +/* Adjust Fifo thresholds based on differential between cpu frequency and bus clock. + The fifo_delta is calculated such that at least INTERRUPT_CYCLE_OVERHEAD cpu clocks are + available when a Fifo interrupt is triggered. This allows enough room in the Fifo so that + interrupt latency does not cause a Fifo overflow/underflow event. +*/ + log_v("cpu Freq=%dMhz, i2c Freq=%dHz",getCpuFrequencyMhz(),clk_speed); + uint32_t fifo_delta = (INTERRUPT_CYCLE_OVERHEAD/((getCpuFrequencyMhz()*1000000 / clk_speed)*10))+1; + if (fifo_delta > 24) fifo_delta=24; + f.rx_fifo_full_thrhd = 32 - fifo_delta; + f.tx_fifo_empty_thrhd = fifo_delta; i2c->dev->fifo_conf.val = f.val; // set thresholds - log_v("Fifo threshold=%d",a); + log_v("Fifo delta=%d",fifo_delta); //the clock num during SCL is low level i2c->dev->scl_low_period.period = period; @@ -1696,6 +1745,8 @@ uint32_t i2cGetStatus(i2c_t * i2c){ } else return 0; } + + /* todo 22JUL18 need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index 4d10d4ee..b23a17db 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -53,6 +53,33 @@ xSemaphoreHandle _ledc_sys_lock; #define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)] #define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)] +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){ + uint32_t iarg = (uint32_t)arg; + uint8_t chan = iarg; + uint8_t group=(chan/8), timer=((chan/2)%4); + old_apb /= 1000000; + new_apb /= 1000000; + if(LEDC_TIMER(group, timer).conf.tick_sel){ + LEDC_MUTEX_LOCK(); + uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider; + uint32_t div_num = (new_apb * old_div) / old_apb; + if(div_num > LEDC_DIV_NUM_HSTIMER0_V){ + new_apb = REF_CLK_FREQ / 1000000; + div_num = (new_apb * old_div) / old_apb; + if(div_num > LEDC_DIV_NUM_HSTIMER0_V) { + div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible + } + LEDC_TIMER(group, timer).conf.tick_sel = 0; + } else if(div_num < 256) { + div_num = 256;//highest clock possible + } + LEDC_TIMER(group, timer).conf.clock_divider = div_num; + LEDC_MUTEX_UNLOCK(); + } + } +} + //uint32_t frequency = (80MHz or 1MHz)/((div_num / 256.0)*(1 << bit_num)); static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk) { @@ -78,6 +105,8 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset. LEDC_TIMER(group, timer).conf.rst = 0; LEDC_MUTEX_UNLOCK(); + uint32_t iarg = chan; + addApbChangeCallback((void*)iarg, _on_apb_change); } //max div_num 0x3FFFF (262143) diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 3a22346b..06ed677d 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -27,6 +27,7 @@ #include #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" #include "rom/rtc.h" #include "esp_task_wdt.h" #include "esp32-hal.h" @@ -100,57 +101,19 @@ void disableCore1WDT(){ } #endif -static uint32_t _cpu_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; -static uint32_t _sys_time_multiplier = 1; - -bool setCpuFrequency(uint32_t cpu_freq_mhz){ - rtc_cpu_freq_config_t conf, cconf; - 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; - } -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - 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); - delay(10); -#endif - rtc_clk_cpu_freq_set_config_fast(&conf); - _cpu_freq_mhz = conf.freq_mhz; - _sys_time_multiplier = 80000000 / getApbFrequency(); - 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); - if(conf.freq_mhz >= 80){ - return 80000000; - } - return (conf.source_freq_mhz * 1000000) / conf.div; -} - unsigned long IRAM_ATTR micros() { - return (unsigned long) (esp_timer_get_time()) * _sys_time_multiplier; + return (unsigned long) (esp_timer_get_time()); } unsigned long IRAM_ATTR millis() { - return (unsigned long) (micros() / 1000); + return (unsigned long) (esp_timer_get_time() / 1000); } void delay(uint32_t ms) { - vTaskDelay((ms * _cpu_freq_mhz) / (portTICK_PERIOD_MS * 240)); + vTaskDelay(ms / portTICK_PERIOD_MS); } void IRAM_ATTR delayMicroseconds(uint32_t us) @@ -183,8 +146,10 @@ bool btInUse(){ return false; } void initArduino() { + //init proper ref tick value for PLL (uncomment if REF_TICK is different than 1MHz) + //ESP_REG(APB_CTRL_PLL_TICK_CONF_REG) = APB_CLK_FREQ / REF_CLK_FREQ - 1; #ifdef F_CPU - setCpuFrequency(F_CPU/1000000L); + setCpuFrequencyMhz(F_CPU/1000000); #endif #if CONFIG_SPIRAM_SUPPORT psramInit(); diff --git a/cores/esp32/esp32-hal-sigmadelta.c b/cores/esp32/esp32-hal-sigmadelta.c index 64355318..098181c7 100644 --- a/cores/esp32/esp32-hal-sigmadelta.c +++ b/cores/esp32/esp32-hal-sigmadelta.c @@ -31,6 +31,26 @@ xSemaphoreHandle _sd_sys_lock; #endif +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + if(old_apb == new_apb){ + return; + } + uint32_t iarg = (uint32_t)arg; + uint8_t channel = iarg; + if(ev_type == APB_BEFORE_CHANGE){ + SIGMADELTA.cg.clk_en = 0; + } else { + old_apb /= 1000000; + new_apb /= 1000000; + SD_MUTEX_LOCK(); + uint32_t old_prescale = SIGMADELTA.channel[channel].prescale + 1; + SIGMADELTA.channel[channel].prescale = ((new_apb * old_prescale) / old_apb) - 1; + SIGMADELTA.cg.clk_en = 0; + SIGMADELTA.cg.clk_en = 1; + SD_MUTEX_UNLOCK(); + } +} + uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500 { if(channel > 7) { @@ -53,6 +73,8 @@ uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-31 SIGMADELTA.cg.clk_en = 0; SIGMADELTA.cg.clk_en = 1; SD_MUTEX_UNLOCK(); + uint32_t iarg = channel; + addApbChangeCallback((void*)iarg, _on_apb_change); return apb_freq/((prescale + 1) * 256); } diff --git a/cores/esp32/esp32-hal-spi.c b/cores/esp32/esp32-hal-spi.c index b262bdaa..d28abf4a 100644 --- a/cores/esp32/esp32-hal-spi.c +++ b/cores/esp32/esp32-hal-spi.c @@ -372,6 +372,18 @@ void spiSetBitOrder(spi_t * spi, uint8_t bitOrder) SPI_MUTEX_UNLOCK(); } +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb) +{ + spi_t * spi = (spi_t *)arg; + if(ev_type == APB_BEFORE_CHANGE){ + SPI_MUTEX_LOCK(); + while(spi->dev->cmd.usr); + } else { + spi->dev->clock.val = spiFrequencyToClockDiv(old_apb / ((spi->dev->clock.clkdiv_pre + 1) * (spi->dev->clock.clkcnt_n + 1))); + SPI_MUTEX_UNLOCK(); + } +} + void spiStopBus(spi_t * spi) { if(!spi) { @@ -388,6 +400,7 @@ void spiStopBus(spi_t * spi) spi->dev->ctrl2.val = 0; spi->dev->clock.val = 0; SPI_MUTEX_UNLOCK(); + removeApbChangeCallback(spi, _on_apb_change); } spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) @@ -434,6 +447,7 @@ spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_ } SPI_MUTEX_UNLOCK(); + addApbChangeCallback(spi, _on_apb_change); return spi; } @@ -1008,17 +1022,17 @@ void IRAM_ATTR spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){ * */ typedef union { - uint32_t regValue; + uint32_t value; struct { - unsigned regL :6; - unsigned regH :6; - unsigned regN :6; - unsigned regPre :13; - unsigned regEQU :1; + uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/ + uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/ + uint32_t clkcnt_n: 6; /*it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/ + uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/ + uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/ }; } spiClk_t; -#define ClkRegToFreq(reg) (apb_freq / (((reg)->regPre + 1) * ((reg)->regN + 1))) +#define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1))) uint32_t spiClockDivToFrequency(uint32_t clockDiv) { @@ -1038,7 +1052,7 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq) const spiClk_t minFreqReg = { 0x7FFFF000 }; uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); if(freq < minFreq) { - return minFreqReg.regValue; + return minFreqReg.value; } uint8_t calN = 1; @@ -1051,18 +1065,18 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq) int32_t calPre; int8_t calPreVari = -2; - reg.regN = calN; + reg.clkcnt_n = calN; while(calPreVari++ <= 1) { - calPre = (((apb_freq / (reg.regN + 1)) / freq) - 1) + calPreVari; + calPre = (((apb_freq / (reg.clkcnt_n + 1)) / freq) - 1) + calPreVari; if(calPre > 0x1FFF) { - reg.regPre = 0x1FFF; + reg.clkdiv_pre = 0x1FFF; } else if(calPre <= 0) { - reg.regPre = 0; + reg.clkdiv_pre = 0; } else { - reg.regPre = calPre; + reg.clkdiv_pre = calPre; } - reg.regL = ((reg.regN + 1) / 2); + reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2); calFreq = ClkRegToFreq(®); if(calFreq == (int32_t) freq) { memcpy(&bestReg, ®, sizeof(bestReg)); @@ -1079,6 +1093,6 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq) } calN++; } - return bestReg.regValue; + return bestReg.value; } diff --git a/cores/esp32/esp32-hal-timer.c b/cores/esp32/esp32-hal-timer.c index 8fcee55f..c6b7fbf5 100644 --- a/cores/esp32/esp32-hal-timer.c +++ b/cores/esp32/esp32-hal-timer.c @@ -184,6 +184,18 @@ bool timerAlarmEnabled(hw_timer_t *timer){ return timer->dev->config.alarm_en; } +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + hw_timer_t * timer = (hw_timer_t *)arg; + if(ev_type == APB_BEFORE_CHANGE){ + timer->dev->config.enable = 0; + } else { + old_apb /= 1000000; + new_apb /= 1000000; + timer->dev->config.divider = (new_apb * timer->dev->config.divider) / old_apb; + timer->dev->config.enable = 1; + } +} + hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ if(num > 3){ return NULL; @@ -205,12 +217,14 @@ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ timerAttachInterrupt(timer, NULL, false); timerWrite(timer, 0); timer->dev->config.enable = 1; + addApbChangeCallback(timer, _on_apb_change); return timer; } void timerEnd(hw_timer_t *timer){ timer->dev->config.enable = 0; timerAttachInterrupt(timer, NULL, false); + removeApbChangeCallback(timer, _on_apb_change); } void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){ @@ -271,23 +285,23 @@ void timerDetachInterrupt(hw_timer_t *timer){ uint64_t timerReadMicros(hw_timer_t *timer){ uint64_t timer_val = timerRead(timer); uint16_t div = timerGetDivider(timer); - return timer_val * div / 80; + return timer_val * div / (getApbFrequency() / 1000000); } double timerReadSeconds(hw_timer_t *timer){ uint64_t timer_val = timerRead(timer); uint16_t div = timerGetDivider(timer); - return (double)timer_val * div / 80000000; + return (double)timer_val * div / getApbFrequency(); } uint64_t timerAlarmReadMicros(hw_timer_t *timer){ uint64_t timer_val = timerAlarmRead(timer); uint16_t div = timerGetDivider(timer); - return timer_val * div / 80; + return timer_val * div / (getApbFrequency() / 1000000); } double timerAlarmReadSeconds(hw_timer_t *timer){ uint64_t timer_val = timerAlarmRead(timer); uint16_t div = timerGetDivider(timer); - return (double)timer_val * div / 80000000; + return (double)timer_val * div / getApbFrequency(); } diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index a76c2409..9e60d6be 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -67,6 +67,8 @@ static uart_t _uart_bus_array[3] = { }; #endif +static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb); + static void IRAM_ATTR _uart_isr(void *arg) { uint8_t i, c; @@ -216,6 +218,7 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx uartAttachTx(uart, txPin, inverted); } + addApbChangeCallback(uart, uart_on_apb_change); return uart; } @@ -224,11 +227,10 @@ void uartEnd(uart_t* uart) if(uart == NULL) { return; } + removeApbChangeCallback(uart, uart_on_apb_change); UART_MUTEX_LOCK(); if(uart->queue != NULL) { - uint8_t c; - while(xQueueReceive(uart->queue, &c, 0)); vQueueDelete(uart->queue); uart->queue = NULL; } @@ -248,8 +250,6 @@ size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) { UART_MUTEX_LOCK(); if(uart->queue != NULL) { - uint8_t c; - while(xQueueReceive(uart->queue, &c, 0)); vQueueDelete(uart->queue); uart->queue = xQueueCreate(new_size, sizeof(uint8_t)); if(uart->queue == NULL) { @@ -319,10 +319,9 @@ void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len) } UART_MUTEX_LOCK(); while(len) { - while(len && uart->dev->status.txfifo_cnt < 0x7F) { - uart->dev->fifo.rw_byte = *data++; - len--; - } + while(uart->dev->status.txfifo_cnt == 0x7F); + uart->dev->fifo.rw_byte = *data++; + len--; } UART_MUTEX_UNLOCK(); } @@ -359,13 +358,49 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate) UART_MUTEX_UNLOCK(); } +static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb) +{ + uart_t* uart = (uart_t*)arg; + if(ev_type == APB_BEFORE_CHANGE){ + UART_MUTEX_LOCK(); + //disabple interrupt + uart->dev->int_ena.val = 0; + uart->dev->int_clr.val = 0xffffffff; + // read RX fifo + uint8_t c; + BaseType_t xHigherPriorityTaskWoken; + while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) { + c = uart->dev->fifo.rw_byte; + if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) { + xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken); + } + } + // wait TX empty + while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out); + } else { + //todo: + // set baudrate + uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F); + uint32_t baud_rate = ((old_apb<<4)/clk_div); + clk_div = ((new_apb<<4)/baud_rate); + uart->dev->clk_div.div_int = clk_div>>4 ; + uart->dev->clk_div.div_frag = clk_div & 0xf; + //enable interrupts + uart->dev->int_ena.rxfifo_full = 1; + uart->dev->int_ena.frm_err = 1; + uart->dev->int_ena.rxfifo_tout = 1; + uart->dev->int_clr.val = 0xffffffff; + UART_MUTEX_UNLOCK(); + } +} + uint32_t uartGetBaudRate(uart_t* uart) { if(uart == NULL) { return 0; } uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F); - return ((UART_CLK_FREQ<<4)/clk_div); + return ((getApbFrequency()<<4)/clk_div); } static void IRAM_ATTR uart0_write_char(char c) @@ -505,7 +540,7 @@ uartDetectBaudrate(uart_t *uart) uart->dev->auto_baud.en = 0; uartStateDetectingBaudrate = false; // Initialize for the next round - unsigned long baudrate = UART_CLK_FREQ / divisor; + unsigned long baudrate = getApbFrequency() / divisor; static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400}; diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 33595efd..36ffccac 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -62,6 +62,7 @@ void yield(void); #include "esp32-hal-timer.h" #include "esp32-hal-bt.h" #include "esp32-hal-psram.h" +#include "esp32-hal-cpu.h" #ifndef BOARD_HAS_PSRAM #ifdef CONFIG_SPIRAM_SUPPORT @@ -87,15 +88,6 @@ void enableCore1WDT(); void disableCore1WDT(); #endif -//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(); -uint32_t getApbFrequency(); - unsigned long micros(); unsigned long millis(); void delay(uint32_t); diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 031ea3e5..37288beb 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -30,7 +30,7 @@ #include "freertos/queue.h" #include "Stream.h" -#define STICKBREAKER V1.0.1 +#define STICKBREAKER 'V1.1.0' #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); @@ -138,6 +138,7 @@ extern TwoWire Wire1; /* +V1.1.0 08JAN2019 Support CPU Clock frequency changes V1.0.2 30NOV2018 stop returning I2C_ERROR_CONTINUE on ReSTART operations, regain compatibility with Arduino libs V1.0.1 02AUG2018 First Fix after release, Correct ReSTART handling, change Debug control, change begin() to a function, this allow reporting if bus cannot be initialized, Wire.begin() can be used to recover