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
This commit is contained in:
parent
ff18a211e4
commit
2fd39b1aff
@ -3,6 +3,7 @@ set(CORE_SRCS
|
|||||||
cores/esp32/cbuf.cpp
|
cores/esp32/cbuf.cpp
|
||||||
cores/esp32/esp32-hal-adc.c
|
cores/esp32/esp32-hal-adc.c
|
||||||
cores/esp32/esp32-hal-bt.c
|
cores/esp32/esp32-hal-bt.c
|
||||||
|
cores/esp32/esp32-hal-cpu.c
|
||||||
cores/esp32/esp32-hal-dac.c
|
cores/esp32/esp32-hal-dac.c
|
||||||
cores/esp32/esp32-hal-gpio.c
|
cores/esp32/esp32-hal-gpio.c
|
||||||
cores/esp32/esp32-hal-i2c.c
|
cores/esp32/esp32-hal-i2c.c
|
||||||
|
20
boards.txt
20
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=16M Fat
|
||||||
esp32.menu.PartitionScheme.fatflash.build.partitions=ffat
|
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.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.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.80.build.f_cpu=80000000L
|
||||||
esp32.menu.CPUFreq.40=40MHz (40MHz XTAL)
|
esp32.menu.CPUFreq.40=40MHz (40MHz XTAL)
|
||||||
esp32.menu.CPUFreq.40.build.f_cpu=40000000L
|
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.26.build.f_cpu=26000000L
|
||||||
esp32.menu.CPUFreq.20=20MHz (40MHz XTAL)
|
esp32.menu.CPUFreq.20=20MHz (40MHz XTAL)
|
||||||
esp32.menu.CPUFreq.20.build.f_cpu=20000000L
|
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.13.build.f_cpu=13000000L
|
||||||
esp32.menu.CPUFreq.10=10MHz (40MHz XTAL)
|
esp32.menu.CPUFreq.10=10MHz (40MHz XTAL)
|
||||||
esp32.menu.CPUFreq.10.build.f_cpu=10000000L
|
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=QIO
|
||||||
esp32.menu.FlashMode.qio.build.flash_mode=dio
|
esp32.menu.FlashMode.qio.build.flash_mode=dio
|
||||||
|
211
cores/esp32/esp32-hal-cpu.c
Normal file
211
cores/esp32/esp32-hal-cpu.c
Normal file
@ -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);
|
||||||
|
}
|
48
cores/esp32/esp32-hal-cpu.h
Normal file
48
cores/esp32/esp32-hal-cpu.h
Normal file
@ -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_ */
|
@ -24,7 +24,7 @@
|
|||||||
#include "soc/i2c_struct.h"
|
#include "soc/i2c_struct.h"
|
||||||
#include "soc/dport_reg.h"
|
#include "soc/dport_reg.h"
|
||||||
#include "esp_attr.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) (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_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))
|
#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}
|
{(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
|
#else
|
||||||
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS)
|
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTakeRecursive(i2c->lock, portMAX_DELAY) != pdPASS)
|
||||||
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock)
|
#define I2C_MUTEX_UNLOCK() xSemaphoreGiveRecursive(i2c->lock)
|
||||||
|
|
||||||
static i2c_t _i2c_bus_array[2] = {
|
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},
|
{(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
|
// 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)
|
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;
|
I2C_COMMAND_t cmd;
|
||||||
@ -1221,6 +1254,12 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
|||||||
I2C_MUTEX_UNLOCK();
|
I2C_MUTEX_UNLOCK();
|
||||||
return I2C_ERROR_MEMORY;
|
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.
|
//hang until it completes.
|
||||||
|
|
||||||
@ -1352,6 +1391,9 @@ static void i2cReleaseISR(i2c_t * i2c)
|
|||||||
if(i2c->intr_handle) {
|
if(i2c->intr_handle) {
|
||||||
esp_intr_free(i2c->intr_handle);
|
esp_intr_free(i2c->intr_handle);
|
||||||
i2c->intr_handle=NULL;
|
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
|
* PUBLIC API
|
||||||
* */
|
* */
|
||||||
// 24Nov17 only supports Master Mode
|
// 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);
|
log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency);
|
||||||
if(i2c_num > 1) {
|
if(i2c_num > 1) {
|
||||||
return NULL;
|
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];
|
i2c_t * i2c = &_i2c_bus_array[i2c_num];
|
||||||
|
|
||||||
|
// pins should be detached, else glitch
|
||||||
if(i2c->sda >= 0){
|
if(i2c->sda >= 0){
|
||||||
i2cDetachSDA(i2c, i2c->sda);
|
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 !CONFIG_DISABLE_HAL_LOCKS
|
||||||
if(i2c->lock == NULL) {
|
if(i2c->lock == NULL) {
|
||||||
i2c->lock = xSemaphoreCreateMutex();
|
i2c->lock = xSemaphoreCreateRecursiveMutex();
|
||||||
if(i2c->lock == NULL) {
|
if(i2c->lock == NULL) {
|
||||||
return 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;
|
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)
|
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed)
|
||||||
{
|
{
|
||||||
if(i2c == NULL) {
|
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;
|
uint32_t period = (apb/clk_speed) / 2;
|
||||||
|
|
||||||
if((apb/8192 > clk_speed)||(apb/MIN_I2C_CLKS < clk_speed)){ //out of bounds
|
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) ){
|
if(period < (MIN_I2C_CLKS/2) ){
|
||||||
period = (MIN_I2C_CLKS/2);
|
period = (MIN_I2C_CLKS/2);
|
||||||
clk_speed = apb/(period*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) {
|
} else if ( period> 4095) {
|
||||||
period = 4095;
|
period = 4095;
|
||||||
clk_speed = apb/(period*2);
|
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 halfPeriod = period/2;
|
||||||
uint32_t quarterPeriod = period/4;
|
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;
|
I2C_FIFO_CONF_t f;
|
||||||
|
|
||||||
// Adjust Fifo thresholds based on frequency
|
|
||||||
f.val = i2c->dev->fifo_conf.val;
|
f.val = i2c->dev->fifo_conf.val;
|
||||||
uint32_t a = (clk_speed / 50000L )+1;
|
/* Adjust Fifo thresholds based on differential between cpu frequency and bus clock.
|
||||||
if (a > 24) a=24;
|
The fifo_delta is calculated such that at least INTERRUPT_CYCLE_OVERHEAD cpu clocks are
|
||||||
f.rx_fifo_full_thrhd = 32 - a;
|
available when a Fifo interrupt is triggered. This allows enough room in the Fifo so that
|
||||||
f.tx_fifo_empty_thrhd = a;
|
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
|
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
|
//the clock num during SCL is low level
|
||||||
i2c->dev->scl_low_period.period = period;
|
i2c->dev->scl_low_period.period = period;
|
||||||
@ -1696,6 +1745,8 @@ uint32_t i2cGetStatus(i2c_t * i2c){
|
|||||||
}
|
}
|
||||||
else return 0;
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* todo
|
/* todo
|
||||||
22JUL18
|
22JUL18
|
||||||
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads
|
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads
|
||||||
|
@ -53,6 +53,33 @@ xSemaphoreHandle _ledc_sys_lock;
|
|||||||
#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)]
|
#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)]
|
||||||
#define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)]
|
#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));
|
//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)
|
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 = 1;//This bit is used to reset timer the counter will be 0 after reset.
|
||||||
LEDC_TIMER(group, timer).conf.rst = 0;
|
LEDC_TIMER(group, timer).conf.rst = 0;
|
||||||
LEDC_MUTEX_UNLOCK();
|
LEDC_MUTEX_UNLOCK();
|
||||||
|
uint32_t iarg = chan;
|
||||||
|
addApbChangeCallback((void*)iarg, _on_apb_change);
|
||||||
}
|
}
|
||||||
|
|
||||||
//max div_num 0x3FFFF (262143)
|
//max div_num 0x3FFFF (262143)
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include "soc/rtc.h"
|
#include "soc/rtc.h"
|
||||||
#include "soc/rtc_cntl_reg.h"
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/apb_ctrl_reg.h"
|
||||||
#include "rom/rtc.h"
|
#include "rom/rtc.h"
|
||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
#include "esp32-hal.h"
|
#include "esp32-hal.h"
|
||||||
@ -100,57 +101,19 @@ void disableCore1WDT(){
|
|||||||
}
|
}
|
||||||
#endif
|
#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()
|
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()
|
unsigned long IRAM_ATTR millis()
|
||||||
{
|
{
|
||||||
return (unsigned long) (micros() / 1000);
|
return (unsigned long) (esp_timer_get_time() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void delay(uint32_t ms)
|
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)
|
void IRAM_ATTR delayMicroseconds(uint32_t us)
|
||||||
@ -183,8 +146,10 @@ bool btInUse(){ return false; }
|
|||||||
|
|
||||||
void initArduino()
|
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
|
#ifdef F_CPU
|
||||||
setCpuFrequency(F_CPU/1000000L);
|
setCpuFrequencyMhz(F_CPU/1000000);
|
||||||
#endif
|
#endif
|
||||||
#if CONFIG_SPIRAM_SUPPORT
|
#if CONFIG_SPIRAM_SUPPORT
|
||||||
psramInit();
|
psramInit();
|
||||||
|
@ -31,6 +31,26 @@
|
|||||||
xSemaphoreHandle _sd_sys_lock;
|
xSemaphoreHandle _sd_sys_lock;
|
||||||
#endif
|
#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
|
uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500
|
||||||
{
|
{
|
||||||
if(channel > 7) {
|
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 = 0;
|
||||||
SIGMADELTA.cg.clk_en = 1;
|
SIGMADELTA.cg.clk_en = 1;
|
||||||
SD_MUTEX_UNLOCK();
|
SD_MUTEX_UNLOCK();
|
||||||
|
uint32_t iarg = channel;
|
||||||
|
addApbChangeCallback((void*)iarg, _on_apb_change);
|
||||||
return apb_freq/((prescale + 1) * 256);
|
return apb_freq/((prescale + 1) * 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +372,18 @@ void spiSetBitOrder(spi_t * spi, uint8_t bitOrder)
|
|||||||
SPI_MUTEX_UNLOCK();
|
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)
|
void spiStopBus(spi_t * spi)
|
||||||
{
|
{
|
||||||
if(!spi) {
|
if(!spi) {
|
||||||
@ -388,6 +400,7 @@ void spiStopBus(spi_t * spi)
|
|||||||
spi->dev->ctrl2.val = 0;
|
spi->dev->ctrl2.val = 0;
|
||||||
spi->dev->clock.val = 0;
|
spi->dev->clock.val = 0;
|
||||||
SPI_MUTEX_UNLOCK();
|
SPI_MUTEX_UNLOCK();
|
||||||
|
removeApbChangeCallback(spi, _on_apb_change);
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder)
|
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();
|
SPI_MUTEX_UNLOCK();
|
||||||
|
|
||||||
|
addApbChangeCallback(spi, _on_apb_change);
|
||||||
return spi;
|
return spi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1008,17 +1022,17 @@ void IRAM_ATTR spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){
|
|||||||
* */
|
* */
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
uint32_t regValue;
|
uint32_t value;
|
||||||
struct {
|
struct {
|
||||||
unsigned regL :6;
|
uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/
|
||||||
unsigned regH :6;
|
uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/
|
||||||
unsigned regN :6;
|
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)*/
|
||||||
unsigned regPre :13;
|
uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/
|
||||||
unsigned regEQU :1;
|
uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/
|
||||||
};
|
};
|
||||||
} spiClk_t;
|
} 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)
|
uint32_t spiClockDivToFrequency(uint32_t clockDiv)
|
||||||
{
|
{
|
||||||
@ -1038,7 +1052,7 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
|
|||||||
const spiClk_t minFreqReg = { 0x7FFFF000 };
|
const spiClk_t minFreqReg = { 0x7FFFF000 };
|
||||||
uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg);
|
uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg);
|
||||||
if(freq < minFreq) {
|
if(freq < minFreq) {
|
||||||
return minFreqReg.regValue;
|
return minFreqReg.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t calN = 1;
|
uint8_t calN = 1;
|
||||||
@ -1051,18 +1065,18 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
|
|||||||
int32_t calPre;
|
int32_t calPre;
|
||||||
int8_t calPreVari = -2;
|
int8_t calPreVari = -2;
|
||||||
|
|
||||||
reg.regN = calN;
|
reg.clkcnt_n = calN;
|
||||||
|
|
||||||
while(calPreVari++ <= 1) {
|
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) {
|
if(calPre > 0x1FFF) {
|
||||||
reg.regPre = 0x1FFF;
|
reg.clkdiv_pre = 0x1FFF;
|
||||||
} else if(calPre <= 0) {
|
} else if(calPre <= 0) {
|
||||||
reg.regPre = 0;
|
reg.clkdiv_pre = 0;
|
||||||
} else {
|
} else {
|
||||||
reg.regPre = calPre;
|
reg.clkdiv_pre = calPre;
|
||||||
}
|
}
|
||||||
reg.regL = ((reg.regN + 1) / 2);
|
reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2);
|
||||||
calFreq = ClkRegToFreq(®);
|
calFreq = ClkRegToFreq(®);
|
||||||
if(calFreq == (int32_t) freq) {
|
if(calFreq == (int32_t) freq) {
|
||||||
memcpy(&bestReg, ®, sizeof(bestReg));
|
memcpy(&bestReg, ®, sizeof(bestReg));
|
||||||
@ -1079,6 +1093,6 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
|
|||||||
}
|
}
|
||||||
calN++;
|
calN++;
|
||||||
}
|
}
|
||||||
return bestReg.regValue;
|
return bestReg.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +184,18 @@ bool timerAlarmEnabled(hw_timer_t *timer){
|
|||||||
return timer->dev->config.alarm_en;
|
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){
|
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
||||||
if(num > 3){
|
if(num > 3){
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -205,12 +217,14 @@ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
|||||||
timerAttachInterrupt(timer, NULL, false);
|
timerAttachInterrupt(timer, NULL, false);
|
||||||
timerWrite(timer, 0);
|
timerWrite(timer, 0);
|
||||||
timer->dev->config.enable = 1;
|
timer->dev->config.enable = 1;
|
||||||
|
addApbChangeCallback(timer, _on_apb_change);
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void timerEnd(hw_timer_t *timer){
|
void timerEnd(hw_timer_t *timer){
|
||||||
timer->dev->config.enable = 0;
|
timer->dev->config.enable = 0;
|
||||||
timerAttachInterrupt(timer, NULL, false);
|
timerAttachInterrupt(timer, NULL, false);
|
||||||
|
removeApbChangeCallback(timer, _on_apb_change);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
|
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 timerReadMicros(hw_timer_t *timer){
|
||||||
uint64_t timer_val = timerRead(timer);
|
uint64_t timer_val = timerRead(timer);
|
||||||
uint16_t div = timerGetDivider(timer);
|
uint16_t div = timerGetDivider(timer);
|
||||||
return timer_val * div / 80;
|
return timer_val * div / (getApbFrequency() / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
double timerReadSeconds(hw_timer_t *timer){
|
double timerReadSeconds(hw_timer_t *timer){
|
||||||
uint64_t timer_val = timerRead(timer);
|
uint64_t timer_val = timerRead(timer);
|
||||||
uint16_t div = timerGetDivider(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 timerAlarmReadMicros(hw_timer_t *timer){
|
||||||
uint64_t timer_val = timerAlarmRead(timer);
|
uint64_t timer_val = timerAlarmRead(timer);
|
||||||
uint16_t div = timerGetDivider(timer);
|
uint16_t div = timerGetDivider(timer);
|
||||||
return timer_val * div / 80;
|
return timer_val * div / (getApbFrequency() / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
double timerAlarmReadSeconds(hw_timer_t *timer){
|
double timerAlarmReadSeconds(hw_timer_t *timer){
|
||||||
uint64_t timer_val = timerAlarmRead(timer);
|
uint64_t timer_val = timerAlarmRead(timer);
|
||||||
uint16_t div = timerGetDivider(timer);
|
uint16_t div = timerGetDivider(timer);
|
||||||
return (double)timer_val * div / 80000000;
|
return (double)timer_val * div / getApbFrequency();
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,8 @@ static uart_t _uart_bus_array[3] = {
|
|||||||
};
|
};
|
||||||
#endif
|
#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)
|
static void IRAM_ATTR _uart_isr(void *arg)
|
||||||
{
|
{
|
||||||
uint8_t i, c;
|
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);
|
uartAttachTx(uart, txPin, inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addApbChangeCallback(uart, uart_on_apb_change);
|
||||||
return uart;
|
return uart;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,11 +227,10 @@ void uartEnd(uart_t* uart)
|
|||||||
if(uart == NULL) {
|
if(uart == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
removeApbChangeCallback(uart, uart_on_apb_change);
|
||||||
|
|
||||||
UART_MUTEX_LOCK();
|
UART_MUTEX_LOCK();
|
||||||
if(uart->queue != NULL) {
|
if(uart->queue != NULL) {
|
||||||
uint8_t c;
|
|
||||||
while(xQueueReceive(uart->queue, &c, 0));
|
|
||||||
vQueueDelete(uart->queue);
|
vQueueDelete(uart->queue);
|
||||||
uart->queue = NULL;
|
uart->queue = NULL;
|
||||||
}
|
}
|
||||||
@ -248,8 +250,6 @@ size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
|
|||||||
|
|
||||||
UART_MUTEX_LOCK();
|
UART_MUTEX_LOCK();
|
||||||
if(uart->queue != NULL) {
|
if(uart->queue != NULL) {
|
||||||
uint8_t c;
|
|
||||||
while(xQueueReceive(uart->queue, &c, 0));
|
|
||||||
vQueueDelete(uart->queue);
|
vQueueDelete(uart->queue);
|
||||||
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
|
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
|
||||||
if(uart->queue == NULL) {
|
if(uart->queue == NULL) {
|
||||||
@ -319,10 +319,9 @@ void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len)
|
|||||||
}
|
}
|
||||||
UART_MUTEX_LOCK();
|
UART_MUTEX_LOCK();
|
||||||
while(len) {
|
while(len) {
|
||||||
while(len && uart->dev->status.txfifo_cnt < 0x7F) {
|
while(uart->dev->status.txfifo_cnt == 0x7F);
|
||||||
uart->dev->fifo.rw_byte = *data++;
|
uart->dev->fifo.rw_byte = *data++;
|
||||||
len--;
|
len--;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
UART_MUTEX_UNLOCK();
|
UART_MUTEX_UNLOCK();
|
||||||
}
|
}
|
||||||
@ -359,13 +358,49 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
|
|||||||
UART_MUTEX_UNLOCK();
|
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)
|
uint32_t uartGetBaudRate(uart_t* uart)
|
||||||
{
|
{
|
||||||
if(uart == NULL) {
|
if(uart == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
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)
|
static void IRAM_ATTR uart0_write_char(char c)
|
||||||
@ -505,7 +540,7 @@ uartDetectBaudrate(uart_t *uart)
|
|||||||
uart->dev->auto_baud.en = 0;
|
uart->dev->auto_baud.en = 0;
|
||||||
uartStateDetectingBaudrate = false; // Initialize for the next round
|
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};
|
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ void yield(void);
|
|||||||
#include "esp32-hal-timer.h"
|
#include "esp32-hal-timer.h"
|
||||||
#include "esp32-hal-bt.h"
|
#include "esp32-hal-bt.h"
|
||||||
#include "esp32-hal-psram.h"
|
#include "esp32-hal-psram.h"
|
||||||
|
#include "esp32-hal-cpu.h"
|
||||||
|
|
||||||
#ifndef BOARD_HAS_PSRAM
|
#ifndef BOARD_HAS_PSRAM
|
||||||
#ifdef CONFIG_SPIRAM_SUPPORT
|
#ifdef CONFIG_SPIRAM_SUPPORT
|
||||||
@ -87,15 +88,6 @@ void enableCore1WDT();
|
|||||||
void disableCore1WDT();
|
void disableCore1WDT();
|
||||||
#endif
|
#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 micros();
|
||||||
unsigned long millis();
|
unsigned long millis();
|
||||||
void delay(uint32_t);
|
void delay(uint32_t);
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
|
||||||
#define STICKBREAKER V1.0.1
|
#define STICKBREAKER 'V1.1.0'
|
||||||
#define I2C_BUFFER_LENGTH 128
|
#define I2C_BUFFER_LENGTH 128
|
||||||
typedef void(*user_onRequest)(void);
|
typedef void(*user_onRequest)(void);
|
||||||
typedef void(*user_onReceive)(uint8_t*, int);
|
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.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()
|
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
|
to a function, this allow reporting if bus cannot be initialized, Wire.begin() can be used to recover
|
||||||
|
Loading…
Reference in New Issue
Block a user