Skip to content

I2C fix READ of zero bytes hardware hang #2294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
211 changes: 211 additions & 0 deletions cores/esp32/esp32-hal-cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/xtensa_timer.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "rom/rtc.h"
#include "soc/apb_ctrl_reg.h"
#include "esp32-hal.h"
#include "esp32-hal-cpu.h"

typedef struct apb_change_cb_s {
struct apb_change_cb_s * next;
void * arg;
apb_change_cb_t cb;
} apb_change_t;

const uint32_t MHZ = 1000000;

static apb_change_t * apb_change_callbacks = NULL;
static xSemaphoreHandle apb_change_lock = NULL;

static void initApbChangeCallback(){
static volatile bool initialized = false;
if(!initialized){
initialized = true;
apb_change_lock = xSemaphoreCreateMutex();
if(!apb_change_lock){
initialized = false;
}
}
}

static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t * r = apb_change_callbacks;
while(r != NULL){
r->cb(r->arg, ev_type, old_apb, new_apb);
r=r->next;
}
xSemaphoreGive(apb_change_lock);
}

bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
initApbChangeCallback();
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t));
if(!c){
log_e("Callback Object Malloc Failed");
return false;
}
c->next = NULL;
c->arg = arg;
c->cb = cb;
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
if(apb_change_callbacks == NULL){
apb_change_callbacks = c;
} else {
apb_change_t * r = apb_change_callbacks;
if(r->cb != cb || r->arg != arg){
while(r->next){
r = r->next;
if(r->cb == cb && r->arg == arg){
free(c);
goto unlock_and_exit;
}
}
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 changes: 48 additions & 0 deletions cores/esp32/esp32-hal-cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _ESP32_HAL_CPU_H_
#define _ESP32_HAL_CPU_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t;

typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);

bool addApbChangeCallback(void * arg, apb_change_cb_t cb);
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb);

//function takes the following frequencies as valid values:
// 240, 160, 80 <<< For all XTAL types
// 40, 20, 10 <<< For 40MHz XTAL
// 26, 13 <<< For 26MHz XTAL
// 24, 12 <<< For 24MHz XTAL
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz);

uint32_t getCpuFrequencyMhz(); // In MHz
uint32_t getXtalFrequencyMhz(); // In MHz
uint32_t getApbFrequency(); // In Hz

#ifdef __cplusplus
}
#endif

#endif /* _ESP32_HAL_CPU_H_ */
Loading