From daea578841059fef36f98d72cef15e22733bdc3e Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Sat, 29 Dec 2018 00:15:38 +0200 Subject: [PATCH 01/11] Add APB change callbacks and move cpu code to own file --- CMakeLists.txt | 1 + cores/esp32/esp32-hal-cpu.c | 169 +++++++++++++++++++++++++++++++++++ cores/esp32/esp32-hal-cpu.h | 49 ++++++++++ cores/esp32/esp32-hal-misc.c | 53 +---------- cores/esp32/esp32-hal.h | 10 +-- 5 files changed, 224 insertions(+), 58 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 5290b2cc9c8..d59a8b8e210 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/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c new file mode 100644 index 00000000000..591ca9d03ac --- /dev/null +++ b/cores/esp32/esp32-hal-cpu.c @@ -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; + } + } + 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); + 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; + 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); +} diff --git a/cores/esp32/esp32-hal-cpu.h b/cores/esp32/esp32-hal-cpu.h new file mode 100644 index 00000000000..53101232ee8 --- /dev/null +++ b/cores/esp32/esp32-hal-cpu.h @@ -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 +#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, 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_ */ diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 3a22346b995..5686887c65c 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -100,57 +100,12 @@ 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) (micros64()); } -unsigned long IRAM_ATTR micros() -{ - return (unsigned long) (esp_timer_get_time()) * _sys_time_multiplier; -} - -unsigned long IRAM_ATTR millis() -{ - return (unsigned long) (micros() / 1000); -} - -void delay(uint32_t ms) -{ - vTaskDelay((ms * _cpu_freq_mhz) / (portTICK_PERIOD_MS * 240)); +unsigned long IRAM_ATTR millis(){ + return (unsigned long) (micros64() / 1000); } void IRAM_ATTR delayMicroseconds(uint32_t us) diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 33595efdaa6..36ffccac231 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); From f7c56e3f14976f6cee08ee08b8a3e1990ad0665d Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Sun, 30 Dec 2018 15:59:52 +0200 Subject: [PATCH 02/11] Properly set esp_timer and FreeRTOS tick dividers --- cores/esp32/esp32-hal-cpu.c | 23 ++++++++--------------- cores/esp32/esp32-hal-cpu.h | 2 -- cores/esp32/esp32-hal-misc.c | 8 ++++++-- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index 591ca9d03ac..664d2109939 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -16,6 +16,7 @@ #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" @@ -33,18 +34,6 @@ typedef struct apb_change_cb_s { 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; @@ -130,11 +119,13 @@ bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){ return true; } +void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF + 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){ + if(cconf.freq_mhz == cpu_freq_mhz){ return true; } if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){ @@ -148,9 +139,11 @@ bool setCpuFrequency(uint32_t cpu_freq_mhz){ 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; + //Update FreeRTOS Tick Divisor + _xt_tick_divisor = cpu_freq_mhz * 1000000 / XT_TICK_PER_SEC; if(capb != apb && apb_change_callbacks){ + //Update esp_timer divisor + esp_timer_impl_update_apb_freq(apb / 1000000); triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); } return true; diff --git a/cores/esp32/esp32-hal-cpu.h b/cores/esp32/esp32-hal-cpu.h index 53101232ee8..e9eccdeeedf 100644 --- a/cores/esp32/esp32-hal-cpu.h +++ b/cores/esp32/esp32-hal-cpu.h @@ -40,8 +40,6 @@ 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 diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 5686887c65c..01b88432673 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -101,11 +101,15 @@ void disableCore1WDT(){ #endif unsigned long IRAM_ATTR micros(){ - return (unsigned long) (micros64()); + return (unsigned long) (esp_timer_get_time()); } unsigned long IRAM_ATTR millis(){ - return (unsigned long) (micros64() / 1000); + return (unsigned long) (esp_timer_get_time() / 1000); +} + +void delay(uint32_t ms){ + vTaskDelay(ms / portTICK_PERIOD_MS); } void IRAM_ATTR delayMicroseconds(uint32_t us) From 67d5ebc0a6352fe002c0ed026dc72aaa83bb853b Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 31 Dec 2018 09:41:31 +0200 Subject: [PATCH 03/11] Improve updated devisors --- cores/esp32/esp32-hal-cpu.c | 43 +++++++++++++++++++++++++++--------- cores/esp32/esp32-hal-misc.c | 9 +++++--- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index 664d2109939..4674ed14546 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -22,6 +22,7 @@ #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" @@ -31,16 +32,11 @@ typedef struct apb_change_cb_s { 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 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){ @@ -119,31 +115,56 @@ bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){ 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 setCpuFrequency(uint32_t cpu_freq_mhz){ rtc_cpu_freq_config_t conf, cconf; uint32_t capb, apb; + //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_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); + log_i("%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(capb != apb && 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 + uint32_t xtal_mhz = rtc_clk_xtal_freq_get(); + uint32_t tick_freq_mhz = (conf.freq_mhz >= xtal_mhz)?xtal_mhz:conf.freq_mhz; + uint32_t tick_conf = tick_freq_mhz / (REF_CLK_FREQ / MHZ) - 1; + ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = tick_conf; + //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 - _xt_tick_divisor = cpu_freq_mhz * 1000000 / XT_TICK_PER_SEC; + 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(capb != apb && apb_change_callbacks){ - //Update esp_timer divisor - esp_timer_impl_update_apb_freq(apb / 1000000); triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); } return true; diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 01b88432673..1338fa2e4c6 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -100,15 +100,18 @@ void disableCore1WDT(){ } #endif -unsigned long IRAM_ATTR micros(){ +unsigned long IRAM_ATTR micros() +{ return (unsigned long) (esp_timer_get_time()); } -unsigned long IRAM_ATTR millis(){ +unsigned long IRAM_ATTR millis() +{ return (unsigned long) (esp_timer_get_time() / 1000); } -void delay(uint32_t ms){ +void delay(uint32_t ms) +{ vTaskDelay(ms / portTICK_PERIOD_MS); } From 55c725f6696cb6fbddc0be2f1f8c7abd782f2003 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 31 Dec 2018 10:38:18 +0200 Subject: [PATCH 04/11] No need to update REF_TICK yet --- cores/esp32/esp32-hal-cpu.c | 9 ++++----- cores/esp32/esp32-hal-misc.c | 5 ++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index 4674ed14546..835c8270211 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -150,11 +150,10 @@ bool setCpuFrequency(uint32_t cpu_freq_mhz){ //Make the frequency change rtc_clk_cpu_freq_set_config_fast(&conf); if(capb != apb){ - //Update REF_TICK - uint32_t xtal_mhz = rtc_clk_xtal_freq_get(); - uint32_t tick_freq_mhz = (conf.freq_mhz >= xtal_mhz)?xtal_mhz:conf.freq_mhz; - uint32_t tick_conf = tick_freq_mhz / (REF_CLK_FREQ / MHZ) - 1; - ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = tick_conf; + //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 diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 1338fa2e4c6..0473c295c91 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" @@ -145,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); + setCpuFrequency(F_CPU/1000000); #endif #if CONFIG_SPIRAM_SUPPORT psramInit(); From 344c76d825858bb47187decffab67cd1bd1b5ef6 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 31 Dec 2018 11:33:35 +0200 Subject: [PATCH 05/11] Add initial handling for UART baud change --- cores/esp32/esp32-hal-cpu.c | 1 + cores/esp32/esp32-hal-uart.c | 37 +++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index 835c8270211..99728c78f4c 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -54,6 +54,7 @@ static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, 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); } diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index a76c2409f22..a050f422f6d 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,6 +227,7 @@ void uartEnd(uart_t* uart) if(uart == NULL) { return; } + removeApbChangeCallback(uart, uart_on_apb_change); UART_MUTEX_LOCK(); if(uart->queue != NULL) { @@ -359,13 +363,44 @@ 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){ + //todo: + UART_MUTEX_LOCK(); + // detach RX + // 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; + // attach rx + 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) From 0532c8839a062ab56db37aa3da22c40161cf6394 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 31 Dec 2018 17:36:33 +0200 Subject: [PATCH 06/11] fix uartWriteBuf and uartDetectBaudrate --- cores/esp32/esp32-hal-uart.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index a050f422f6d..0d3d5705a28 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -323,10 +323,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(); } @@ -540,7 +539,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}; From 7443fe2b18c97debfe497db383769ee83a45ec2b Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 31 Dec 2018 18:28:28 +0200 Subject: [PATCH 07/11] trigger callbacks even when APB did not change --- cores/esp32/esp32-hal-cpu.c | 4 ++-- cores/esp32/esp32-hal-uart.c | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index 99728c78f4c..c0c40af19f1 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -145,7 +145,7 @@ bool setCpuFrequency(uint32_t cpu_freq_mhz){ apb = calculateApb(&conf); log_i("%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(capb != apb && apb_change_callbacks){ + if(apb_change_callbacks){ triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb); } //Make the frequency change @@ -164,7 +164,7 @@ bool setCpuFrequency(uint32_t cpu_freq_mhz){ 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(capb != apb && apb_change_callbacks){ + if(apb_change_callbacks){ triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); } return true; diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 0d3d5705a28..eaccb70eb80 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -231,8 +231,6 @@ void uartEnd(uart_t* uart) UART_MUTEX_LOCK(); if(uart->queue != NULL) { - uint8_t c; - while(xQueueReceive(uart->queue, &c, 0)); vQueueDelete(uart->queue); uart->queue = NULL; } @@ -252,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) { From c31ea222d7413a058e6634da70a64cf856f220f7 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Fri, 4 Jan 2019 01:44:41 +0200 Subject: [PATCH 08/11] toggle UART ISR on CPU change --- cores/esp32/esp32-hal-cpu.c | 1 + cores/esp32/esp32-hal-uart.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index c0c40af19f1..0daf4d96a63 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -78,6 +78,7 @@ bool addApbChangeCallback(void * arg, apb_change_cb_t cb){ while(r->next){ r = r->next; if(r->cb == cb && r->arg == arg){ + free(c); goto unlock_and_exit; } } diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index eaccb70eb80..9e60d6bedf1 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -362,9 +362,10 @@ static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old { uart_t* uart = (uart_t*)arg; if(ev_type == APB_BEFORE_CHANGE){ - //todo: UART_MUTEX_LOCK(); - // detach RX + //disabple interrupt + uart->dev->int_ena.val = 0; + uart->dev->int_clr.val = 0xffffffff; // read RX fifo uint8_t c; BaseType_t xHigherPriorityTaskWoken; @@ -384,7 +385,11 @@ static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old 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; - // attach rx + //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(); } } From 8b6483d5982f9c1f035d4b04f6e77844d4d466b7 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 7 Jan 2019 23:21:08 +0100 Subject: [PATCH 09/11] add XTAL freq getter and add cpu freq validation --- cores/esp32/esp32-hal-cpu.c | 33 ++++++++++++++++++++++++++++++--- cores/esp32/esp32-hal-cpu.h | 17 +++++++++-------- cores/esp32/esp32-hal-misc.c | 2 +- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/cores/esp32/esp32-hal-cpu.c b/cores/esp32/esp32-hal-cpu.c index 0daf4d96a63..d9e95dc8067 100644 --- a/cores/esp32/esp32-hal-cpu.c +++ b/cores/esp32/esp32-hal-cpu.c @@ -126,9 +126,32 @@ static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){ void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF -bool setCpuFrequency(uint32_t cpu_freq_mhz){ +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 @@ -144,7 +167,7 @@ bool setCpuFrequency(uint32_t cpu_freq_mhz){ capb = calculateApb(&cconf); //New APB apb = calculateApb(&conf); - log_i("%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); + 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); @@ -171,12 +194,16 @@ bool setCpuFrequency(uint32_t cpu_freq_mhz){ return true; } -uint32_t getCpuFrequency(){ +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); diff --git a/cores/esp32/esp32-hal-cpu.h b/cores/esp32/esp32-hal-cpu.h index e9eccdeeedf..646b59858f5 100644 --- a/cores/esp32/esp32-hal-cpu.h +++ b/cores/esp32/esp32-hal-cpu.h @@ -31,14 +31,15 @@ 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 +// 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 } diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 0473c295c91..06ed677d5d2 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -149,7 +149,7 @@ 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/1000000); + setCpuFrequencyMhz(F_CPU/1000000); #endif #if CONFIG_SPIRAM_SUPPORT psramInit(); From 276faf5fab71de7784ea310471faed56069d1e85 Mon Sep 17 00:00:00 2001 From: stickbreaker Date: Mon, 7 Jan 2019 20:43:14 -0700 Subject: [PATCH 10/11] Support CPU frequency changes in I2C **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 --- cores/esp32/esp32-hal-i2c.c | 83 ++++++++++++++++++++++++++++++------- libraries/Wire/src/Wire.h | 3 +- 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 174e437682b..f8c384afb7f 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/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 031ea3e5740..37288beb686 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 From 946e4dc28ab84b48e99881150554dafa783098ed Mon Sep 17 00:00:00 2001 From: stickbreaker Date: Wed, 9 Jan 2019 20:07:25 -0700 Subject: [PATCH 11/11] I2C fix READ of zero bytes hardware hang The i2c peripheral will hang if a READ request is issued with a zero data length. The peripheral drops into a continuous timeout interrupt response. The STOP command can not be set out to the I2C bus. The SLAVE device correctly ACK'd the address byte, with READ bit set, it has control of the SDA pin. The ESP32 send out the next SCL HIGH pulse but, since the SLAVE is in READ Mode, and the First bit it is sending happened to be a ZERO, the ESP32 cannot send the STOP. When it releases SDA during the SCL HIGH, the pin does not change state. The pin stays low because the SLAVE is outputing a LOW! The ESP32 drops into a perminent WAIT state waiting for SDA to go HIGH (the STOP). **esp32-hal-i2c.c** * add databuff length checks to `i2cRead()` and `i2cWrite()` waiting for the ESP32 peripheral to send clock pulses on the SCL pin. --- cores/esp32/esp32-hal-i2c.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index f8c384afb7f..780020f0215 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1609,6 +1609,9 @@ i2c_err_t i2cFlush(i2c_t * i2c) } i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis){ + if((i2c==NULL)||((size>0)&&(buff==NULL))) { // need to have location to store requested data + return I2C_ERROR_DEV; + } i2c_err_t last_error = i2cAddQueueWrite(i2c, address, buff, size, sendStop, NULL); if(last_error == I2C_ERROR_OK) { //queued @@ -1628,6 +1631,9 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, } i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount){ + if((size == 0)||(i2c == NULL)||(buff==NULL)){ // hardware will hang if no data requested on READ + return I2C_ERROR_DEV; + } i2c_err_t last_error=i2cAddQueueRead(i2c, address, buff, size, sendStop, NULL); if(last_error == I2C_ERROR_OK) { //queued