diff --git a/components/FastLED-idf/hal/esp32-hal-adc.c b/components/FastLED-idf/hal/esp32-hal-adc.c new file mode 100644 index 0000000..ab9e762 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-adc.c @@ -0,0 +1,239 @@ +// 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 "esp32-hal-adc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "soc/rtc_io_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +#include "driver/adc.h" +#include "esp_adc_cal.h" + +#define DEFAULT_VREF 1100 +static esp_adc_cal_characteristics_t *__analogCharacteristics[2] = {NULL, NULL}; +static uint8_t __analogAttenuation = 3;//11db +static uint8_t __analogWidth = 3;//12 bits +static uint8_t __analogClockDiv = 1; +static uint16_t __analogVRef = 0; +static uint8_t __analogVRefPin = 0; + +void __analogSetWidth(uint8_t bits){ + if(bits < 9){ + bits = 9; + } else if(bits > 12){ + bits = 12; + } + __analogWidth = bits - 9; + adc1_config_width(__analogWidth); +} + +void __analogSetClockDiv(uint8_t clockDiv){ + if(!clockDiv){ + clockDiv = 1; + } + __analogClockDiv = clockDiv; + adc_set_clk_div(__analogClockDiv); +} + +void __analogSetAttenuation(adc_attenuation_t attenuation) +{ + __analogAttenuation = attenuation & 3; +} + +void __analogInit(){ + static bool initialized = false; + if(initialized){ + return; + } + initialized = true; + __analogSetClockDiv(__analogClockDiv); + __analogSetWidth(__analogWidth + 9);//in bits +} + +void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) +{ + int8_t channel = digitalPinToAnalogChannel(pin); + if(channel < 0 || attenuation > 3){ + return ; + } + if(channel > 9){ + adc2_config_channel_atten(channel - 10, attenuation); + } else { + adc1_config_channel_atten(channel, attenuation); + } + __analogInit(); +} + +bool __adcAttachPin(uint8_t pin){ + int8_t channel = digitalPinToAnalogChannel(pin); + if(channel < 0){ + log_e("Pin %u is not ADC pin!", pin); + return false; + } + int8_t pad = digitalPinToTouchChannel(pin); + if(pad >= 0){ + uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG); + if(touch & (1 << pad)){ + touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) + | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S)) + | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S))); + WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch); + } + } else if(pin == 25){ + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1 + } else if(pin == 26){ + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2 + } + + pinMode(pin, ANALOG); + __analogSetPinAttenuation(pin, __analogAttenuation); + return true; +} + +void __analogReadResolution(uint8_t bits) +{ + if(!bits || bits > 16){ + return; + } + __analogSetWidth(bits); // hadware from 9 to 12 +} + +uint16_t __analogRead(uint8_t pin) +{ + int8_t channel = digitalPinToAnalogChannel(pin); + int value = 0; + esp_err_t r = ESP_OK; + if(channel < 0){ + log_e("Pin %u is not ADC pin!", pin); + return value; + } + __adcAttachPin(pin); + if(channel > 9){ + channel -= 10; + r = adc2_get_raw( channel, __analogWidth, &value); + if ( r == ESP_OK ) { + return value; + } else if ( r == ESP_ERR_INVALID_STATE ) { + log_e("GPIO%u: %s: ADC2 not initialized yet.", pin, esp_err_to_name(r)); + } else if ( r == ESP_ERR_TIMEOUT ) { + log_e("GPIO%u: %s: ADC2 is in use by Wi-Fi.", pin, esp_err_to_name(r)); + } else { + log_e("GPIO%u: %s", pin, esp_err_to_name(r)); + } + } else { + return adc1_get_raw(channel); + } + return value; +} + +void __analogSetVRefPin(uint8_t pin){ + if(pin <25 || pin > 27){ + pin = 0; + } + __analogVRefPin = pin; +} + +uint32_t __analogReadMilliVolts(uint8_t pin){ + int8_t channel = digitalPinToAnalogChannel(pin); + if(channel < 0){ + log_e("Pin %u is not ADC pin!", pin); + return 0; + } + if(!__analogVRef){ + if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) { + log_d("eFuse Two Point: Supported"); + __analogVRef = DEFAULT_VREF; + } + if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) { + log_d("eFuse Vref: Supported"); + __analogVRef = DEFAULT_VREF; + } + if(!__analogVRef){ + __analogVRef = DEFAULT_VREF; + if(__analogVRefPin){ + esp_adc_cal_characteristics_t chars; + if(adc2_vref_to_gpio(__analogVRefPin) == ESP_OK){ + __analogVRef = __analogRead(__analogVRefPin); + esp_adc_cal_characterize(1, __analogAttenuation, __analogWidth, DEFAULT_VREF, &chars); + __analogVRef = esp_adc_cal_raw_to_voltage(__analogVRef, &chars); + log_d("Vref to GPIO%u: %u", __analogVRefPin, __analogVRef); + } + } + } + } + uint8_t unit = 1; + if(channel > 9){ + unit = 2; + } + uint16_t adc_reading = __analogRead(pin); + if(__analogCharacteristics[unit - 1] == NULL){ + __analogCharacteristics[unit - 1] = calloc(1, sizeof(esp_adc_cal_characteristics_t)); + if(__analogCharacteristics[unit - 1] == NULL){ + return 0; + } + esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, __analogAttenuation, __analogWidth, __analogVRef, __analogCharacteristics[unit - 1]); + if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + log_i("ADC%u: Characterized using Two Point Value: %u\n", unit, __analogCharacteristics[unit - 1]->vref); + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + log_i("ADC%u: Characterized using eFuse Vref: %u\n", unit, __analogCharacteristics[unit - 1]->vref); + } else if(__analogVRef != DEFAULT_VREF){ + log_i("ADC%u: Characterized using Vref to GPIO%u: %u\n", unit, __analogVRefPin, __analogCharacteristics[unit - 1]->vref); + } else { + log_i("ADC%u: Characterized using Default Vref: %u\n", unit, __analogCharacteristics[unit - 1]->vref); + } + } + return esp_adc_cal_raw_to_voltage(adc_reading, __analogCharacteristics[unit - 1]); +} + +int __hallRead() //hall sensor without LNA +{ + int Sens_Vp0; + int Sens_Vn0; + int Sens_Vp1; + int Sens_Vn1; + + pinMode(36, ANALOG); + pinMode(39, ANALOG); + SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_XPD_HALL_FORCE_M); // hall sens force enable + SET_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_XPD_HALL); // xpd hall + SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_HALL_PHASE_FORCE_M); // phase force + CLEAR_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE); // hall phase + Sens_Vp0 = __analogRead(36); + Sens_Vn0 = __analogRead(39); + SET_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE); + Sens_Vp1 = __analogRead(36); + Sens_Vn1 = __analogRead(39); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_XPD_HALL_FORCE); + CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_HALL_PHASE_FORCE); + return (Sens_Vp1 - Sens_Vp0) - (Sens_Vn1 - Sens_Vn0); +} + +extern uint16_t analogRead(uint8_t pin) __attribute__ ((weak, alias("__analogRead"))); +extern void analogReadResolution(uint8_t bits) __attribute__ ((weak, alias("__analogReadResolution"))); +extern void analogSetWidth(uint8_t bits) __attribute__ ((weak, alias("__analogSetWidth"))); +extern void analogSetClockDiv(uint8_t clockDiv) __attribute__ ((weak, alias("__analogSetClockDiv"))); +extern void analogSetAttenuation(adc_attenuation_t attenuation) __attribute__ ((weak, alias("__analogSetAttenuation"))); +extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) __attribute__ ((weak, alias("__analogSetPinAttenuation"))); +extern int hallRead() __attribute__ ((weak, alias("__hallRead"))); + +extern bool adcAttachPin(uint8_t pin) __attribute__ ((weak, alias("__adcAttachPin"))); + +extern void analogSetVRefPin(uint8_t pin) __attribute__ ((weak, alias("__analogSetVRefPin"))); +extern uint32_t analogReadMilliVolts(uint8_t pin) __attribute__ ((weak, alias("__analogReadMilliVolts"))); diff --git a/components/FastLED-idf/hal/esp32-hal-adc.h b/components/FastLED-idf/hal/esp32-hal-adc.h new file mode 100644 index 0000000..bba56fd --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-adc.h @@ -0,0 +1,101 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_ADC_H_ +#define MAIN_ESP32_HAL_ADC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" + +typedef enum { + ADC_0db, + ADC_2_5db, + ADC_6db, + ADC_11db +} adc_attenuation_t; + +/* + * Get ADC value for pin + * */ +uint16_t analogRead(uint8_t pin); + +/* + * Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096). + * If between 9 and 12, it will equal the set hardware resolution, else value will be shifted. + * Range is 1 - 16 + * + * Note: compatibility with Arduino SAM + */ +void analogReadResolution(uint8_t bits); + +/* + * Sets the sample bits and read resolution + * Default is 12bit (0 - 4095) + * Range is 9 - 12 + * */ +void analogSetWidth(uint8_t bits); + +/* + * Set the divider for the ADC clock. + * Default is 1 + * Range is 1 - 255 + * */ +void analogSetClockDiv(uint8_t clockDiv); + +/* + * Set the attenuation for all channels + * Default is 11db + * */ +void analogSetAttenuation(adc_attenuation_t attenuation); + +/* + * Set the attenuation for particular pin + * Default is 11db + * */ +void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation); + +/* + * Get value for HALL sensor (without LNA) + * connected to pins 36(SVP) and 39(SVN) + * */ +int hallRead(); + +/* + * Attach pin to ADC (will also clear any other analog mode that could be on) + * */ +bool adcAttachPin(uint8_t pin); + +/* + * Set pin to use for ADC calibration if the esp is not already calibrated (25, 26 or 27) + * */ +void analogSetVRefPin(uint8_t pin); + +/* + * Get MilliVolts value for pin + * */ +uint32_t analogReadMilliVolts(uint8_t pin); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_ADC_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-bt.c b/components/FastLED-idf/hal/esp32-hal-bt.c new file mode 100644 index 0000000..9976224 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-bt.c @@ -0,0 +1,99 @@ +// 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 "esp32-hal-bt.h" + +#ifdef CONFIG_BT_ENABLED + +bool btInUse(){ return true; } + +#ifdef CONFIG_BLUEDROID_ENABLED +#include "esp_bt.h" + +#ifdef CONFIG_CLASSIC_BT_ENABLED +#define BT_MODE ESP_BT_MODE_BTDM +#else +#define BT_MODE ESP_BT_MODE_BLE +#endif + +bool btStarted(){ + return (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED); +} + +bool btStart(){ + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ + return true; + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){ + esp_bt_controller_init(&cfg); + while(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){} + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED){ + if (esp_bt_controller_enable(BT_MODE)) { + log_e("BT Enable failed"); + return false; + } + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ + return true; + } + log_e("BT Start failed"); + return false; +} + +bool btStop(){ + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){ + return true; + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ + if (esp_bt_controller_disable()) { + log_e("BT Disable failed"); + return false; + } + while(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED); + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED){ + if (esp_bt_controller_deinit()) { + log_e("BT deint failed"); + return false; + } + vTaskDelay(1); + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) { + return false; + } + return true; + } + log_e("BT Stop failed"); + return false; +} + +#else +bool btStarted() +{ + return false; +} + +bool btStart() +{ + return false; +} + +bool btStop() +{ + return false; +} +#endif +#endif + diff --git a/components/FastLED-idf/hal/esp32-hal-bt.h b/components/FastLED-idf/hal/esp32-hal-bt.h new file mode 100644 index 0000000..56222da --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-bt.h @@ -0,0 +1,32 @@ +// 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_ESP32_HAL_BT_H_ +#define _ESP32_ESP32_HAL_BT_H_ + +#include "esp32-hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool btStarted(); +bool btStart(); +bool btStop(); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_ESP32_HAL_BT_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-cpu.c b/components/FastLED-idf/hal/esp32-hal-cpu.c new file mode 100644 index 0000000..db00285 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-cpu.c @@ -0,0 +1,230 @@ +// 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 "soc/efuse_reg.h" +#include "esp32-hal.h" +#include "esp32-hal-cpu.h" + +typedef struct apb_change_cb_s { + struct apb_change_cb_s * prev; + 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; + if( r != NULL ){ + if(ev_type == APB_BEFORE_CHANGE ) + while(r != NULL){ + r->cb(r->arg, ev_type, old_apb, new_apb); + r=r->next; + } + else { // run backwards through chain + while(r->next != NULL) r = r->next; // find first added + while( r != NULL){ + r->cb(r->arg, ev_type, old_apb, new_apb); + r=r->prev; + } + } + } + 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->prev = 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; + // look for duplicate callbacks + while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next; + if (r) { + log_e("duplicate func=%08X arg=%08X",c->cb,c->arg); + free(c); + xSemaphoreGive(apb_change_lock); + return false; + } + else { + c->next = apb_change_callbacks; + apb_change_callbacks-> prev = c; + apb_change_callbacks = c; + } + } + 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; + // look for matching callback + while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next; + if ( r == NULL ) { + log_e("not found func=%08X arg=%08X",cb,arg); + xSemaphoreGive(apb_change_lock); + return false; + } + else { + // patch links + if(r->prev) r->prev->next = r->next; + else { // this is first link + apb_change_callbacks = r->next; + } + if(r->next) r->next->prev = r->prev; + free(r); + } + 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(); + if(xtal > RTC_XTAL_FREQ_AUTO){ + if(xtal < RTC_XTAL_FREQ_40M) { + 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; + } + //check if cpu supports the frequency + if(cpu_freq_mhz == 240){ + //Check if ESP32 is rated for a CPU frequency of 160MHz only + if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) && + REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) { + log_e("Can not switch to 240 MHz! Chip CPU frequency rated for 160MHz."); + cpu_freq_mhz = 160; + } + } + //Get current CPU clock configuration + rtc_clk_cpu_freq_get_config(&cconf); + //return if frequency has not changed + if(cconf.freq_mhz == cpu_freq_mhz){ + return true; + } + //Get configuration for the new CPU frequency + if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){ + log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz); + return false; + } + //Current APB + capb = calculateApb(&cconf); + //New APB + apb = calculateApb(&conf); + log_d("%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb); + //Call peripheral functions before the APB change + if(apb_change_callbacks){ + triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb); + } + //Make the frequency change + rtc_clk_cpu_freq_set_config_fast(&conf); + if(capb != apb){ + //Update REF_TICK (uncomment if REF_TICK is different than 1MHz) + //if(conf.freq_mhz < 80){ + // ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1; + // } + //Update APB Freq REG + rtc_clk_apb_freq_update(apb); + //Update esp_timer divisor + esp_timer_impl_update_apb_freq(apb / MHZ); + } + //Update FreeRTOS Tick Divisor + uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb); + _xt_tick_divisor = fcpu / XT_TICK_PER_SEC; + //Call peripheral functions after the APB change + if(apb_change_callbacks){ + triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); + } + return true; +} + +uint32_t getCpuFrequencyMhz(){ + rtc_cpu_freq_config_t conf; + rtc_clk_cpu_freq_get_config(&conf); + return conf.freq_mhz; +} + +uint32_t getXtalFrequencyMhz(){ + return rtc_clk_xtal_freq_get(); +} + +uint32_t getApbFrequency(){ + rtc_cpu_freq_config_t conf; + rtc_clk_cpu_freq_get_config(&conf); + return calculateApb(&conf); +} diff --git a/components/FastLED-idf/hal/esp32-hal-cpu.h b/components/FastLED-idf/hal/esp32-hal-cpu.h new file mode 100644 index 0000000..646b598 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-cpu.h @@ -0,0 +1,48 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_HAL_CPU_H_ +#define _ESP32_HAL_CPU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t; + +typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb); + +bool addApbChangeCallback(void * arg, apb_change_cb_t cb); +bool removeApbChangeCallback(void * arg, apb_change_cb_t cb); + +//function takes the following frequencies as valid values: +// 240, 160, 80 <<< For all XTAL types +// 40, 20, 10 <<< For 40MHz XTAL +// 26, 13 <<< For 26MHz XTAL +// 24, 12 <<< For 24MHz XTAL +bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz); + +uint32_t getCpuFrequencyMhz(); // In MHz +uint32_t getXtalFrequencyMhz(); // In MHz +uint32_t getApbFrequency(); // In Hz + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_CPU_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-dac.c b/components/FastLED-idf/hal/esp32-hal-dac.c new file mode 100644 index 0000000..e407c22 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-dac.c @@ -0,0 +1,54 @@ +// 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 "esp32-hal-dac.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "soc/rtc_io_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +void IRAM_ATTR __dacWrite(uint8_t pin, uint8_t value) +{ + if(pin < 25 || pin > 26){ + return;//not dac pin + } + pinMode(pin, ANALOG); + uint8_t channel = pin - 25; + + + //Disable Tone + CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN); + + if (channel) { + //Disable Channel Tone + CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M); + //Set the Dac value + SET_PERI_REG_BITS(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC, value, RTC_IO_PDAC2_DAC_S); //dac_output + //Channel output enable + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE); + } else { + //Disable Channel Tone + CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M); + //Set the Dac value + SET_PERI_REG_BITS(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC, value, RTC_IO_PDAC1_DAC_S); //dac_output + //Channel output enable + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE); + } +} + +extern void dacWrite(uint8_t pin, uint8_t value) __attribute__ ((weak, alias("__dacWrite"))); diff --git a/components/FastLED-idf/hal/esp32-hal-dac.h b/components/FastLED-idf/hal/esp32-hal-dac.h new file mode 100644 index 0000000..47b2265 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-dac.h @@ -0,0 +1,36 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_DAC_H_ +#define MAIN_ESP32_HAL_DAC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "driver/gpio.h" + +void dacWrite(uint8_t pin, uint8_t value); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_DAC_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-gpio.c b/components/FastLED-idf/hal/esp32-hal-gpio.c new file mode 100644 index 0000000..beb358c --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-gpio.c @@ -0,0 +1,304 @@ +// 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 "esp32-hal-gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" // BB +#include "rom/gpio.h" +#include "soc/gpio_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_struct.h" +#include "soc/rtc_io_reg.h" + +const int8_t esp32_adc2gpio[20] = {36, 37, 38, 39, 32, 33, 34, 35, -1, -1, 4, 0, 2, 15, 13, 12, 14, 27, 25, 26}; + +const DRAM_ATTR esp32_gpioMux_t esp32_gpioMux[GPIO_PIN_COUNT]={ + {0x44, 11, 11, 1}, + {0x88, -1, -1, -1}, + {0x40, 12, 12, 2}, + {0x84, -1, -1, -1}, + {0x48, 10, 10, 0}, + {0x6c, -1, -1, -1}, + {0x60, -1, -1, -1}, + {0x64, -1, -1, -1}, + {0x68, -1, -1, -1}, + {0x54, -1, -1, -1}, + {0x58, -1, -1, -1}, + {0x5c, -1, -1, -1}, + {0x34, 15, 15, 5}, + {0x38, 14, 14, 4}, + {0x30, 16, 16, 6}, + {0x3c, 13, 13, 3}, + {0x4c, -1, -1, -1}, + {0x50, -1, -1, -1}, + {0x70, -1, -1, -1}, + {0x74, -1, -1, -1}, + {0x78, -1, -1, -1}, + {0x7c, -1, -1, -1}, + {0x80, -1, -1, -1}, + {0x8c, -1, -1, -1}, + {0, -1, -1, -1}, + {0x24, 6, 18, -1}, //DAC1 + {0x28, 7, 19, -1}, //DAC2 + {0x2c, 17, 17, 7}, + {0, -1, -1, -1}, + {0, -1, -1, -1}, + {0, -1, -1, -1}, + {0, -1, -1, -1}, + {0x1c, 9, 4, 8}, + {0x20, 8, 5, 9}, + {0x14, 4, 6, -1}, + {0x18, 5, 7, -1}, + {0x04, 0, 0, -1}, + {0x08, 1, 1, -1}, + {0x0c, 2, 2, -1}, + {0x10, 3, 3, -1} +}; + +typedef void (*voidFuncPtr)(void); +typedef void (*voidFuncPtrArg)(void*); +typedef struct { + voidFuncPtr fn; + void* arg; + bool functional; +} InterruptHandle_t; +static InterruptHandle_t __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,}; + +#include "driver/rtc_io.h" + +extern void IRAM_ATTR __pinMode(uint8_t pin, uint8_t mode) +{ + + if(!digitalPinIsValid(pin)) { + return; + } + + uint32_t rtc_reg = rtc_gpio_desc[pin].reg; + if(mode == ANALOG) { + if(!rtc_reg) { + return;//not rtc pin + } + //lock rtc + uint32_t reg_val = ESP_REG(rtc_reg); + if(reg_val & rtc_gpio_desc[pin].mux){ + return;//already in adc mode + } + reg_val &= ~( + (RTC_IO_TOUCH_PAD1_FUN_SEL_V << rtc_gpio_desc[pin].func) + |rtc_gpio_desc[pin].ie + |rtc_gpio_desc[pin].pullup + |rtc_gpio_desc[pin].pulldown); + ESP_REG(RTC_GPIO_ENABLE_W1TC_REG) = (1 << (rtc_gpio_desc[pin].rtc_num + RTC_GPIO_ENABLE_W1TC_S)); + ESP_REG(rtc_reg) = reg_val | rtc_gpio_desc[pin].mux; + //unlock rtc + ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = ((uint32_t)2 << MCU_SEL_S) | ((uint32_t)2 << FUN_DRV_S) | FUN_IE; + return; + } + + //RTC pins PULL settings + if(rtc_reg) { + //lock rtc + ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux); + if(mode & PULLUP) { + ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pullup) & ~(rtc_gpio_desc[pin].pulldown); + } else if(mode & PULLDOWN) { + ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pulldown) & ~(rtc_gpio_desc[pin].pullup); + } else { + ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown); + } + //unlock rtc + } + + uint32_t pinFunction = 0, pinControl = 0; + + //lock gpio + if(mode & INPUT) { + if(pin < 32) { + GPIO.enable_w1tc = ((uint32_t)1 << pin); + } else { + GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32)); + } + } else if(mode & OUTPUT) { + if(pin > 33){ + //unlock gpio + return;//pins above 33 can be only inputs + } else if(pin < 32) { + GPIO.enable_w1ts = ((uint32_t)1 << pin); + } else { + GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32)); + } + } + + if(mode & PULLUP) { + pinFunction |= FUN_PU; + } else if(mode & PULLDOWN) { + pinFunction |= FUN_PD; + } + + pinFunction |= ((uint32_t)2 << FUN_DRV_S);//what are the drivers? + pinFunction |= FUN_IE;//input enable but required for output as well? + + if(mode & (INPUT | OUTPUT)) { + pinFunction |= ((uint32_t)2 << MCU_SEL_S); + } else if(mode == SPECIAL) { + pinFunction |= ((uint32_t)(((pin)==1||(pin)==3)?0:1) << MCU_SEL_S); + } else { + pinFunction |= ((uint32_t)(mode >> 5) << MCU_SEL_S); + } + + ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction; + + if(mode & OPEN_DRAIN) { + pinControl = (1 << GPIO_PIN0_PAD_DRIVER_S); + } + + GPIO.pin[pin].val = pinControl; + //unlock gpio +} + +extern void IRAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) +{ + if(val) { + if(pin < 32) { + GPIO.out_w1ts = ((uint32_t)1 << pin); + } else if(pin < 34) { + GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32)); + } + } else { + if(pin < 32) { + GPIO.out_w1tc = ((uint32_t)1 << pin); + } else if(pin < 34) { + GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32)); + } + } +} + +extern int IRAM_ATTR __digitalRead(uint8_t pin) +{ + if(pin < 32) { + return (GPIO.in >> pin) & 0x1; + } else if(pin < 40) { + return (GPIO.in1.val >> (pin - 32)) & 0x1; + } + return 0; +} + +static intr_handle_t gpio_intr_handle = NULL; + +static void IRAM_ATTR __onPinInterrupt() +{ + uint32_t gpio_intr_status_l=0; + uint32_t gpio_intr_status_h=0; + + gpio_intr_status_l = GPIO.status; + gpio_intr_status_h = GPIO.status1.val; + GPIO.status_w1tc = gpio_intr_status_l;//Clear intr for gpio0-gpio31 + GPIO.status1_w1tc.val = gpio_intr_status_h;//Clear intr for gpio32-39 + + uint8_t pin=0; + if(gpio_intr_status_l) { + do { + if(gpio_intr_status_l & ((uint32_t)1 << pin)) { + if(__pinInterruptHandlers[pin].fn) { + if(__pinInterruptHandlers[pin].arg){ + ((voidFuncPtrArg)__pinInterruptHandlers[pin].fn)(__pinInterruptHandlers[pin].arg); + } else { + __pinInterruptHandlers[pin].fn(); + } + } + } + } while(++pin<32); + } + if(gpio_intr_status_h) { + pin=32; + do { + if(gpio_intr_status_h & ((uint32_t)1 << (pin - 32))) { + if(__pinInterruptHandlers[pin].fn) { + if(__pinInterruptHandlers[pin].arg){ + ((voidFuncPtrArg)__pinInterruptHandlers[pin].fn)(__pinInterruptHandlers[pin].arg); + } else { + __pinInterruptHandlers[pin].fn(); + } + } + } + } while(++pin= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) +#define INTBUFFMAX 64 +#define FIFOMAX 512 +static uint32_t intBuff[INTBUFFMAX][3][2]; +static uint32_t intPos[2]= {0,0}; +static uint16_t fifoBuffer[FIFOMAX]; +static uint16_t fifoPos = 0; +#endif + +// start from tools/sdk/include/soc/soc/i2c_struct.h + +typedef union { + struct { + uint32_t byte_num: 8; /*Byte_num represent the number of data need to be send or data need to be received.*/ + uint32_t ack_en: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t ack_exp: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t ack_val: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t op_code: 3; /*op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END.*/ + uint32_t reserved14: 17; + uint32_t done: 1; /*When command0 is done in I2C Master mode this bit changes to high level.*/ + }; + uint32_t val; +} I2C_COMMAND_t; + +typedef union { + struct { + uint32_t rx_fifo_full_thrhd: 5; + uint32_t tx_fifo_empty_thrhd:5; //Config tx_fifo empty threhd value when using apb fifo access * / + uint32_t nonfifo_en: 1; //Set this bit to enble apb nonfifo access. * / + uint32_t fifo_addr_cfg_en: 1; //When this bit is set to 1 then the byte after address represent the offset address of I2C Slave's ram. * / + uint32_t rx_fifo_rst: 1; //Set this bit to reset rx fifo when using apb fifo access. * / + // chuck while this bit is 1, the RX fifo is held in REST, Toggle it * / + uint32_t tx_fifo_rst: 1; //Set this bit to reset tx fifo when using apb fifo access. * / + // chuck while this bit is 1, the TX fifo is held in REST, Toggle it * / + uint32_t nonfifo_rx_thres: 6; //when I2C receives more than nonfifo_rx_thres data it will produce rx_send_full_int_raw interrupt and update the current offset address of the receiving data.* / + uint32_t nonfifo_tx_thres: 6; //when I2C sends more than nonfifo_tx_thres data it will produce tx_send_empty_int_raw interrupt and update the current offset address of the sending data. * / + uint32_t reserved26: 6; + }; + uint32_t val; +} I2C_FIFO_CONF_t; + +typedef union { + struct { + uint32_t rx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/ + uint32_t rx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/ + uint32_t tx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/ + uint32_t tx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/ + uint32_t reserved20: 12; + }; + uint32_t val; + } I2C_FIFO_ST_t; + +// end from tools/sdk/include/soc/soc/i2c_struct.h + +// sync between dispatch(i2cProcQueue) and worker(i2c_isr_handler_default) +typedef enum { + //I2C_NONE=0, + I2C_STARTUP=1, + I2C_RUNNING, + I2C_DONE +} I2C_STAGE_t; + +typedef enum { + I2C_NONE=0, + I2C_MASTER, + I2C_SLAVE, + I2C_MASTERSLAVE +} I2C_MODE_t; + +// internal Error condition +typedef enum { + // I2C_NONE=0, + I2C_OK=1, + I2C_ERROR, + I2C_ADDR_NAK, + I2C_DATA_NAK, + I2C_ARBITRATION, + I2C_TIMEOUT +} I2C_ERROR_t; + +// i2c_event bits for EVENTGROUP bits +// needed to minimize change events, FreeRTOS Daemon overload, so ISR will only set values +// on Exit. Dispatcher will set bits for each dq before/after ISR completion +#define EVENT_ERROR_NAK (BIT(0)) +#define EVENT_ERROR (BIT(1)) +#define EVENT_ERROR_BUS_BUSY (BIT(2)) +#define EVENT_RUNNING (BIT(3)) +#define EVENT_DONE (BIT(4)) +#define EVENT_IN_END (BIT(5)) +#define EVENT_ERROR_PREV (BIT(6)) +#define EVENT_ERROR_TIMEOUT (BIT(7)) +#define EVENT_ERROR_ARBITRATION (BIT(8)) +#define EVENT_ERROR_DATA_NAK (BIT(9)) +#define EVENT_MASK 0x3F + +// control record for each dq entry +typedef union { + struct { + uint32_t addr: 16; // I2C address, if 10bit must have 0x7800 mask applied, else 8bit + uint32_t mode: 1; // transaction direction 0 write, 1 read + uint32_t stop: 1; // sendStop 0 no, 1 yes + uint32_t startCmdSent: 1; // START cmd has been added to command[] + uint32_t addrCmdSent: 1; // addr WRITE cmd has been added to command[] + uint32_t dataCmdSent: 1; // all necessary DATA(READ/WRITE) cmds added to command[] + uint32_t stopCmdSent: 1; // completed all necessary commands + uint32_t addrReq: 2; // number of addr bytes need to send address + uint32_t addrSent: 2; // number of addr bytes added to FIFO + uint32_t reserved_31: 6; + }; + uint32_t val; +} I2C_DATA_CTRL_t; + +// individual dq element +typedef struct { + uint8_t *data; // data pointer for read/write buffer + uint16_t length; // size of data buffer + uint16_t position; // current position for next char in buffer (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}, + {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0} +}; +#endif + +/* + * index - command index (0 to 15) + * op_code - is the command + * byte_num - This register is to store the amounts of data that is read and written. byte_num in RSTART, STOP, END is null. + * ack_val - Each data byte is terminated by an ACK bit used to set the bit level. + * ack_exp - This bit is to set an expected ACK value for the transmitter. + * ack_check - This bit is to decide whether the transmitter checks ACK bit. 1 means yes and 0 means no. + * */ + + +/* Stickbreaker ISR mode debug support + */ +static void IRAM_ATTR i2cDumpCmdQueue(i2c_t *i2c) +{ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR)&&(defined ENABLE_I2C_DEBUG_BUFFER) + static const char * const cmdName[] ={"RSTART","WRITE","READ","STOP","END"}; + uint8_t i=0; + while(i<16) { + I2C_COMMAND_t c; + c.val=i2c->dev->command[i].val; + log_e("[%2d]\t%c\t%s\tval[%d]\texp[%d]\ten[%d]\tbytes[%d]",i,(c.done?'Y':'N'), + cmdName[c.op_code], + c.ack_val, + c.ack_exp, + c.ack_en, + c.byte_num); + i++; + } +#endif +} + +/* Stickbreaker ISR mode debug support + */ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) +static void i2cDumpDqData(i2c_t * i2c) +{ +#if defined (ENABLE_I2C_DEBUG_BUFFER) + uint16_t a=0; + char buff[140]; + I2C_DATA_QUEUE_t *tdq; + int digits=0,lenDigits=0; + a = i2c->queueCount; + while(a>0) { + digits++; + a /= 10; + } + while(aqueueCount) { // find maximum number of len decimal digits for formatting + if (i2c->dq[a].length > lenDigits ) lenDigits = i2c->dq[a].length; + a++; + } + a=0; + while(lenDigits>0){ + a++; + lenDigits /= 10; + } + lenDigits = a; + a = 0; + while(aqueueCount) { + tdq=&i2c->dq[a]; + char buf1[10],buf2[10]; + sprintf(buf1,"%0*d",lenDigits,tdq->length); + sprintf(buf2,"%0*d",lenDigits,tdq->position); + log_i("[%0*d] %sbit %x %c %s buf@=%p, len=%s, pos=%s, ctrl=%d%d%d%d%d",digits,a, + (tdq->ctrl.addr>0x100)?"10":"7", + (tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1), + (tdq->ctrl.mode)?'R':'W', + (tdq->ctrl.stop)?"STOP":"", + tdq->data, + buf1,buf2, + tdq->ctrl.startCmdSent,tdq->ctrl.addrCmdSent,tdq->ctrl.dataCmdSent,(tdq->ctrl.stop)?tdq->ctrl.stopCmdSent:0,tdq->ctrl.addrSent + ); + uint16_t offset = 0; + while(offsetlength) { + memset(buff,' ',140); + buff[139]='\0'; + uint16_t i = 0,j; + j=sprintf(buff,"0x%04x: ",offset); + while((i<32)&&(offset < tdq->length)) { + char ch = tdq->data[offset]; + sprintf((char*)&buff[(i*3)+41],"%02x ",ch); + if((ch<32)||(ch>126)) { + ch='.'; + } + j+=sprintf((char*)&buff[j],"%c",ch); + buff[j]=' '; + i++; + offset++; + } + log_i("%s",buff); + } + a++; + } +#else + log_i("Debug Buffer not Enabled"); +#endif +} +#endif +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +static void i2cDumpI2c(i2c_t * i2c) +{ + log_e("i2c=%p",i2c); + log_i("dev=%p date=%p",i2c->dev,i2c->dev->date); +#if !CONFIG_DISABLE_HAL_LOCKS + log_i("lock=%p",i2c->lock); +#endif + log_i("num=%d",i2c->num); + log_i("mode=%d",i2c->mode); + log_i("stage=%d",i2c->stage); + log_i("error=%d",i2c->error); + log_i("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0); + log_i("intr_handle=%p",i2c->intr_handle); + log_i("dq=%p",i2c->dq); + log_i("queueCount=%d",i2c->queueCount); + log_i("queuePos=%d",i2c->queuePos); + log_i("errorByteCnt=%d",i2c->errorByteCnt); + log_i("errorQueue=%d",i2c->errorQueue); + log_i("debugFlags=0x%08X",i2c->debugFlags); + if(i2c->dq) { + i2cDumpDqData(i2c); + } +} +#endif + +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) +static void i2cDumpInts(uint8_t num) +{ +#if defined (ENABLE_I2C_DEBUG_BUFFER) + uint32_t b; + log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num); + for(uint32_t a=1; a<=INTBUFFMAX; a++) { + b=(a+intPos[num])%INTBUFFMAX; + if(intBuff[b][0][num]!=0) { + log_i("[%02d]\t0x%04x\t0x%04x\t0x%04x\t0x%04x\t0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]); + } + } +#else + log_i("Debug Buffer not Enabled"); +#endif +} +#endif + +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) +static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){ + typedef union { + struct { + uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/ + uint32_t slave_rw: 1; /*when in slave mode 1:master read slave 0: master write slave.*/ + uint32_t time_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/ + uint32_t arb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/ + uint32_t bus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/ + uint32_t slave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/ + uint32_t byte_trans: 1; /*This register changes to high level when one byte is transferred.*/ + uint32_t reserved7: 1; + uint32_t rx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/ + uint32_t reserved14: 4; + uint32_t tx_fifo_cnt: 6; /*This register stores the amount of received data in ram.*/ + uint32_t scl_main_state_last: 3; /*This register stores the value of state machine for i2c module. 3'h0: SCL_MAIN_IDLE 3'h1: SCL_ADDRESS_SHIFT 3'h2: SCL_ACK_ADDRESS 3'h3: SCL_RX_DATA 3'h4 SCL_TX_DATA 3'h5:SCL_SEND_ACK 3'h6:SCL_WAIT_ACK*/ + uint32_t reserved27: 1; + uint32_t scl_state_last: 3; /*This register stores the value of state machine to produce SCL. 3'h0: SCL_IDLE 3'h1:SCL_START 3'h2:SCL_LOW_EDGE 3'h3: SCL_LOW 3'h4:SCL_HIGH_EDGE 3'h5:SCL_HIGH 3'h6:SCL_STOP*/ + uint32_t reserved31: 1; + }; + uint32_t val; + } status_reg; + + status_reg sr; + sr.val= i2c->dev->status_reg.val; + + log_i("ack(%d) sl_rw(%d) to(%d) arb(%d) busy(%d) sl(%d) trans(%d) rx(%d) tx(%d) sclMain(%d) scl(%d)",sr.ack_rec,sr.slave_rw,sr.time_out,sr.arb_lost,sr.bus_busy,sr.slave_addressed,sr.byte_trans, sr.rx_fifo_cnt, sr.tx_fifo_cnt,sr.scl_main_state_last, sr.scl_state_last); +} +#endif + +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) +static void i2cDumpFifo(i2c_t * i2c){ +char buf[64]; +uint16_t k = 0; +uint16_t i = fifoPos+1; + i %=FIFOMAX; +while((fifoBuffer[i]==0)&&(i!=fifoPos)){ + i++; + i %=FIFOMAX; +} +if(i != fifoPos){// actual data + do{ + if(fifoBuffer[i] & 0x8000){ // address byte + if(fifoBuffer[i] & 0x100) { // read + if(fifoBuffer[i] & 0x1) { // valid read dev id + k+= sprintf(&buf[k],"READ 0x%02X",(fifoBuffer[i]&0xff)>>1); + } else { // invalid read dev id + k+= sprintf(&buf[k],"Bad READ 0x%02X",(fifoBuffer[i]&0xff)); + } + } else { // write + if(fifoBuffer[i] & 0x1) { // bad write dev id + k+= sprintf(&buf[k],"bad WRITE 0x%02X",(fifoBuffer[i]&0xff)); + } else { // good Write + k+= sprintf(&buf[k],"WRITE 0x%02X",(fifoBuffer[i]&0xff)>>1); + } + } + } else k += sprintf(&buf[k],"% 4X ",fifoBuffer[i]); + + i++; + i %= FIFOMAX; + bool outBuffer=false; + if( fifoBuffer[i] & 0x8000){ + outBuffer=true; + k=0; + } + if((outBuffer)||(k>50)||(i==fifoPos)) log_i("%s",buf); + outBuffer = false; + if(k>50) { + k=sprintf(buf,"-> "); + } + }while( i!= fifoPos); +} +} +#endif + +static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char locus[]){ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) + if( trigger ){ + log_i("%s",locus); + if(trigger & 1) i2cDumpI2c(i2c); + if(trigger & 2) i2cDumpInts(i2c->num); + if(trigger & 4) i2cDumpCmdQueue(i2c); + if(trigger & 8) i2cDumpStatus(i2c); + if(trigger & 16) i2cDumpFifo(i2c); + } +#endif +} + // 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; + } + 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; + cmd.val=0; + cmd.ack_en = ack_check; + cmd.ack_exp = ack_exp; + cmd.ack_val = ack_val; + cmd.byte_num = byte_num; + cmd.op_code = op_code; + i2c->dev->command[index].val = cmd.val; +} + + +static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) +{ + /* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs + */ + uint16_t cmdIdx = 0; + uint16_t qp = i2c->queuePos; // all queues before queuePos have been completely processed, + // so look start by checking the 'current queue' so see if it needs commands added to + // hardware peripheral command list. walk through each queue entry until all queues have been + // checked + bool done; + bool needMoreCmds = false; + bool ena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ + bool ena_tx=false; // if we add a Write op, better enable TX_Fifo IRQ + + while(!needMoreCmds&&(qp < i2c->queueCount)) { // check if more possible cmds + if(i2c->dq[qp].ctrl.stopCmdSent) {// marks that all required cmds[] have been added to peripheral + qp++; + } else { + needMoreCmds=true; + } + } + //log_e("needMoreCmds=%d",needMoreCmds); + done=(!needMoreCmds)||(cmdIdx>14); + + while(!done) { // fill the command[] until either 0..14 filled or out of cmds and last cmd is STOP + //CMD START + I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler coding + + if((!tdq->ctrl.startCmdSent) && (cmdIdx < 14)) { // has this dq element's START command been added? + // (cmdIdx<14) because a START op cannot directly precede an END op, else a time out cascade occurs + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); + tdq->ctrl.startCmdSent=1; + } + + //CMD WRITE ADDRESS + if((!done)&&(tdq->ctrl.startCmdSent)) { // have to leave room for continue(END), and START must have been sent! + if(!tdq->ctrl.addrCmdSent) { + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, tdq->ctrl.addrReq, false, false, true); //load address in cmdlist, validate (low) ack + tdq->ctrl.addrCmdSent=1; + done =(cmdIdx>14); + ena_tx=true; // tx Data necessary + } + } + + /* Can I have another Sir? + ALL CMD queues must be terminated with either END or STOP. + + If END is used, when refilling the cmd[] next time, no entries from END to [15] can be used. + AND the cmd[] must be filled starting at [0] with commands. Either fill all 15 [0]..[14] and leave the + END in [15] or include a STOP in one of the positions [0]..[14]. Any entries after a STOP are IGNORED by the StateMachine. + The END operation does not complete until ctr->trans_start=1 has been issued. + + So, only refill from [0]..[14], leave [15] for a continuation(END) if necessary. + As a corollary, once END exists in [15], you do not need to overwrite it for the + next continuation. It is never modified. But, I update it every time because it might + actually be the first time! + + 23NOV17 START cannot proceed END. if START is in[14], END cannot be in [15]. + So, if END is moved to [14], [14] and [15] can no longer be used for anything other than END. + If a START is found in [14] then a prior READ or WRITE must be expanded so that there is no START element in [14]. + */ + + if((!done)&&(tdq->ctrl.addrCmdSent)) { //room in command[] for at least One data (read/Write) cmd + uint8_t blkSize=0; // max is 255 + while(( tdq->cmdBytesNeeded > tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END + blkSize = (tdq->cmdBytesNeeded > 255)?255:(tdq->cmdBytesNeeded - tdq->ctrl.mode); // Last read cmd needs different ACK setting, so leave 1 byte remainder on reads + tdq->cmdBytesNeeded -= blkSize; + if(tdq->ctrl.mode==1) { //read mode + i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read. + ena_rx=true; // need to enable rxFifo IRQ + } else { // write + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, blkSize, false, false, true); // check for Nak + ena_tx=true; // need to enable txFifo IRQ + } + done = cmdIdx>14; //have to leave room for END + } + + if(!done) { // buffer is not filled completely + if((tdq->ctrl.mode==1)&&(tdq->cmdBytesNeeded==1)) { //special last read byte NAK + i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false); + // send NAK to mark end of read + tdq->cmdBytesNeeded=0; + done = cmdIdx > 14; + ena_rx=true; + } + } + + tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data + + if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop + if(!tdq->ctrl.stopCmdSent){ + if(tdq->ctrl.stop) { //send a stop + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); + done = cmdIdx > 14; + } + tdq->ctrl.stopCmdSent = 1; + } + } + } + + if((cmdIdx==14)&&(!tdq->ctrl.startCmdSent)) { + // START would have preceded END, causes SM TIMEOUT + // need to stretch out a prior WRITE or READ to two Command[] elements + done = false; // reuse it + uint16_t i = 13; // start working back until a READ/WRITE has >1 numBytes + cmdIdx =15; + while(!done) { + i2c->dev->command[i+1].val = i2c->dev->command[i].val; // push it down + if (((i2c->dev->command[i].op_code == 1)||(i2c->dev->command[i].op_code==2))) { + /* add a dummy read/write cmd[] with num_bytes set to zero,just a place holder in the cmd[]list + */ + i2c->dev->command[i].byte_num = 0; + done = true; + + } else { + if(i > 0) { + i--; + } else { // unable to stretch, fatal + log_e("invalid CMD[] layout Stretch Failed"); + i2cDumpCmdQueue(i2c); + done = true; + } + } + } + + } + + + if(cmdIdx==15) { //need continuation, even if STOP is in 14, it will not matter + // cmd buffer is almost full, Add END as a continuation feature + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); + i2c->dev->int_ena.end_detect=1; + i2c->dev->int_clr.end_detect=1; + done = true; + } + + if(!done) { + if(tdq->ctrl.stopCmdSent) { // this queue element has been completely added to command[] buffer + qp++; + if(qp < i2c->queueCount) { + tdq = &i2c->dq[qp]; + } else { + done = true; + } + } + } + + }// while(!done) + if(INTS) { // don't want to prematurely enable fifo ints until ISR is ready to handle them. + if(ena_rx) { + i2c->dev->int_ena.rx_fifo_full = 1; + } + if(ena_tx) { + i2c->dev->int_ena.tx_fifo_empty = 1; + } + } +} + +static void IRAM_ATTR fillTxFifo(i2c_t * i2c) +{ + /* + 12/01/2017 The Fifo's are independent, 32 bytes of tx and 32 bytes of Rx. + overlap is not an issue, just keep them full/empty the status_reg.xx_fifo_cnt + tells the truth. And the INT's fire correctly + */ + uint16_t a=i2c->queuePos; // currently executing dq, + bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31); + uint8_t cnt; + bool rxQueueEncountered = false; + while((a < i2c->queueCount) && !full) { + I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; + cnt=0; + // add to address to fifo ctrl.addr already has R/W bit positioned correctly + if(tdq->ctrl.addrSent < tdq->ctrl.addrReq) { // need to send address bytes + if(tdq->ctrl.addrReq==2) { //10bit + if(tdq->ctrl.addrSent==0) { //10bit highbyte needs sent + if(!full) { // room in fifo + i2c->dev->fifo_data.val = ((tdq->ctrl.addr>>8)&0xFF); + cnt++; + tdq->ctrl.addrSent=1; //10bit highbyte sent + } + } + full=!(i2c->dev->status_reg.tx_fifo_cnt<31); + + if(tdq->ctrl.addrSent==1) { //10bit Lowbyte needs sent + if(!full) { // room in fifo + i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; + cnt++; + tdq->ctrl.addrSent=2; //10bit lowbyte sent + } + } + } else { // 7bit} + if(tdq->ctrl.addrSent==0) { // 7bit Lowbyte needs sent + if(!full) { // room in fifo + i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; + cnt++; + tdq->ctrl.addrSent=1; // 7bit lowbyte sent +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + uint16_t a = 0x8000 | tdq->ctrl.addr | (tdq->ctrl.mode<<8); + fifoBuffer[fifoPos++] = a; + fifoPos %= FIFOMAX; +#endif + } + } + } + } + // add write data to fifo + if(tdq->ctrl.mode==0) { // write + if(tdq->ctrl.addrSent == tdq->ctrl.addrReq) { //address has been sent, is Write Mode! + uint32_t moveCnt = 32 - i2c->dev->status_reg.tx_fifo_cnt; // how much room in txFifo? + while(( moveCnt>0)&&(tdq->position < tdq->length)) { +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + fifoBuffer[fifoPos++] = tdq->data[tdq->position]; + fifoPos %= FIFOMAX; +#endif + i2c->dev->fifo_data.val = tdq->data[tdq->position++]; + cnt++; + moveCnt--; + + } + } + } else { // read Queue Encountered, can't update QueuePos past this point, emptyRxfifo will do it + if( ! rxQueueEncountered ) { + rxQueueEncountered = true; + if(a > i2c->queuePos){ + i2c->queuePos = a; + } + } + } +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + + // update debug buffer tx counts + cnt += intBuff[intPos[i2c->num]][1][i2c->num]>>16; + intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16); + +#endif + full=!(i2c->dev->status_reg.tx_fifo_cnt<31); + if(!full) { + a++; // check next buffer for address tx, or write data + } + } + + if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued + i2c->dev->int_ena.tx_fifo_empty= 0; + } + + i2c->dev->int_clr.tx_fifo_empty=1; +} + + +static void IRAM_ATTR emptyRxFifo(i2c_t * i2c) +{ + uint32_t d, cnt=0, moveCnt; + + moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read + + while((moveCnt > 0)&&(i2c->queuePos < i2c->queueCount)){ // data to move + + I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut + + while((tdq->position >= tdq->length)&&( i2c->queuePos < i2c->queueCount)){ // find were to store + i2c->queuePos++; + tdq = &i2c->dq[i2c->queuePos]; + } + + if(i2c->queuePos >= i2c->queueCount){ // bad stuff, rx data but no place to put it! + log_e("no Storage location for %d",moveCnt); + // discard + while(moveCnt>0){ + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + } + break; + } + + if(tdq->ctrl.mode == 1){ // read command + if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq + // part of these reads go into the next dq + moveCnt = (tdq->length - tdq->position); + } + } else {// error + log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); + // discard + while(moveCnt>0){ + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + } + break; + } + + while(moveCnt > 0) { // store data + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + tdq->data[tdq->position++] = (d&0xFF); + } + + moveCnt = i2c->dev->status_reg.rx_fifo_cnt; //any more out there? + } + +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER) + // update Debug rxCount + cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&0xffFF; + intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt; +#endif +} + +static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal) +{ + + switch(eventCode) { + case EVENT_DONE: + i2c->error = I2C_OK; + break; + case EVENT_ERROR_NAK : + i2c->error =I2C_ADDR_NAK; + break; + case EVENT_ERROR_DATA_NAK : + i2c->error =I2C_DATA_NAK; + break; + case EVENT_ERROR_TIMEOUT : + i2c->error = I2C_TIMEOUT; + break; + case EVENT_ERROR_ARBITRATION: + i2c->error = I2C_ARBITRATION; + break; + default : + i2c->error = I2C_ERROR; + } + uint32_t exitCode = EVENT_DONE | eventCode |(Fatal?EVENT_ERROR:0); + if((i2c->queuePos < i2c->queueCount) && (i2c->dq[i2c->queuePos].ctrl.mode == 1)) { + emptyRxFifo(i2c); // grab last few characters + } + i2c->dev->int_ena.val = 0; // shut down interrupts + i2c->dev->int_clr.val = 0x1FFF; + i2c->stage = I2C_DONE; + i2c->exitCode = exitCode; //true eventcode + + portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; + // try to notify Dispatch we are done, + // else the 50ms time out will recover the APP, just a little slower + HPTaskAwoken = pdFALSE; + xResult = xEventGroupSetBitsFromISR(i2c->i2c_event, exitCode, &HPTaskAwoken); + if(xResult == pdPASS) { + if(HPTaskAwoken==pdTRUE) { + portYIELD_FROM_ISR(); + // log_e("Yield to Higher"); + } + } + +} + +static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c) +{ +/* i2c_update_error_byte_cnt 07/18/2018 + Only called after an error has occurred, so, most of the time this function is never used. + This function obliterates the need to interrupt monitor each byte transferred, at high bitrates + the byte interrupts were overwhelming the OS. Spurious Interrupts were being generated. + it updates errorByteCnt, errorQueue. + */ + uint16_t a=0; // start at top of DQ, count how many bytes added to tx fifo, and received from rx_fifo. + int16_t bc = 0; + I2C_DATA_QUEUE_t *tdq; + i2c->errorByteCnt = 0; + while( a < i2c->queueCount){ // add up all bytes loaded into fifo's + tdq = &i2c->dq[a]; + i2c->errorByteCnt += tdq->ctrl.addrSent; + i2c->errorByteCnt += tdq->position; + a++; + } + // now errorByteCnt contains total bytes moved into and out of FIFO's + // but, there may still be bytes waiting in Fifo's + i2c->errorByteCnt -= i2c->dev->status_reg.tx_fifo_cnt; // waiting to go out; + i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received +// now walk thru DQ again, find which byte is 'current' + bc = i2c->errorByteCnt; + i2c->errorQueue = 0; + while( i2c->errorQueue < i2c->queueCount ){ + tdq = &i2c->dq[i2c->errorQueue]; + if(bc>0){ // not found yet + if( tdq->ctrl.addrSent >= bc){ // in address + bc = -1; // in address + break; + } else { + bc -= tdq->ctrl.addrSent; + if( tdq->length > bc) { // data nak + break; + } else { // count down + bc -= tdq->length; + } + } + } else break; + + i2c->errorQueue++; + } + + i2c->errorByteCnt = bc; + } + +static void IRAM_ATTR i2c_isr_handler_default(void* arg) +{ + i2c_t* p_i2c = (i2c_t*) arg; // recover data + uint32_t activeInt = p_i2c->dev->int_status.val&0x7FF; + + if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured + p_i2c->dev->int_ena.val = 0; + p_i2c->dev->int_clr.val = 0x1FFF; +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE + uint32_t raw = p_i2c->dev->int_raw.val; + log_w("eject raw=%p, int=%p",raw,activeInt); +#endif + return; + } + while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change + + #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + if(activeInt==(intBuff[intPos[p_i2c->num]][0][p_i2c->num]&0x1fff)) { + intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (((intBuff[intPos[p_i2c->num]][0][p_i2c->num]>>16)+1)<<16)|activeInt; + } else { + intPos[p_i2c->num]++; + intPos[p_i2c->num] %= INTBUFFMAX; + intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (1<<16) | activeInt; + intBuff[intPos[p_i2c->num]][1][p_i2c->num] = 0; + } + + intBuff[intPos[p_i2c->num]][2][p_i2c->num] = xTaskGetTickCountFromISR(); // when IRQ fired + +#endif + + if (activeInt & I2C_TRANS_START_INT_ST_M) { + if(p_i2c->stage==I2C_STARTUP) { + p_i2c->stage=I2C_RUNNING; + } + + activeInt &=~I2C_TRANS_START_INT_ST_M; + p_i2c->dev->int_ena.trans_start = 1; // already enabled? why Again? + p_i2c->dev->int_clr.trans_start = 1; // so that will trigger after next 'END' + } + + if (activeInt & I2C_TXFIFO_EMPTY_INT_ST) {//should this be before Trans_start? + fillTxFifo(p_i2c); //fillTxFifo will enable/disable/clear interrupt + activeInt&=~I2C_TXFIFO_EMPTY_INT_ST; + } + + if(activeInt & I2C_RXFIFO_FULL_INT_ST) { + emptyRxFifo(p_i2c); + p_i2c->dev->int_clr.rx_fifo_full=1; + p_i2c->dev->int_ena.rx_fifo_full=1; //why? + + activeInt &=~I2C_RXFIFO_FULL_INT_ST; + } + + if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service + if (p_i2c->mode == I2C_MASTER) { + i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data + if(p_i2c->errorByteCnt < 0 ) { // address + i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); + } else { + i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data + } + } + return; + } + + if (activeInt & I2C_TIME_OUT_INT_ST_M) { + // let Gross timeout occur, Slave may release SCL before the configured timeout expires + // the Statemachine only has a 13.1ms max timout, some Devices >500ms + p_i2c->dev->int_clr.time_out =1; + activeInt &=~I2C_TIME_OUT_INT_ST; + // since a timeout occurred, capture the rxFifo data + emptyRxFifo(p_i2c); + p_i2c->dev->int_clr.rx_fifo_full=1; + p_i2c->dev->int_ena.rx_fifo_full=1; //why? + + } + + if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { + p_i2c->dev->int_clr.trans_complete = 1; + i2cIsrExit(p_i2c,EVENT_DONE,false); + return; // no more work to do + /* + // how does slave mode act? + if (p_i2c->mode == I2C_SLAVE) { // STOP detected + // empty fifo + // dispatch callback + */ + } + + if (activeInt & I2C_ARBITRATION_LOST_INT_ST_M) { //fatal + i2cIsrExit(p_i2c,EVENT_ERROR_ARBITRATION,true); + return; // no more work to do + } + + if (activeInt & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + p_i2c->dev->int_clr.slave_tran_comp = 1; + // need to complete this ! + } + + if (activeInt & I2C_END_DETECT_INT_ST_M) { + p_i2c->dev->int_ena.end_detect = 0; + p_i2c->dev->int_clr.end_detect = 1; + p_i2c->dev->ctr.trans_start=0; + fillCmdQueue(p_i2c,true); // enable interrupts + p_i2c->dev->ctr.trans_start=1; // go for it + activeInt&=~I2C_END_DETECT_INT_ST_M; + } + + if(activeInt) { // clear unhandled if possible? What about Disabling interrupt? + p_i2c->dev->int_clr.val = activeInt; + log_e("unknown int=%x",activeInt); + // disable unhandled IRQ, + p_i2c->dev->int_ena.val = p_i2c->dev->int_ena.val & (~activeInt); + } + +// activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened +// 01AUG2018 if another interrupt happened, the OS will schedule another interrupt +// this is the source of spurious interrupts + } +} + +/* Stickbreaker added for ISR 11/2017 +functional with Silicon date=0x16042000 + */ +static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, bool dataOnly, EventGroupHandle_t event) +{ + // need to grab a MUTEX for exclusive Queue, + // what about if ISR is running? + + if(i2c==NULL) { + return I2C_ERROR_DEV; + } + + I2C_DATA_QUEUE_t dqx; + dqx.data = dataPtr; + dqx.length = dataLen; + dqx.position = 0; + dqx.cmdBytesNeeded = dataLen; + dqx.ctrl.val = 0; + if( dataOnly) { + /* special case to add a queue data only element. + START and devAddr will not be sent, this dq element can have a STOP. + allows multiple buffers to be used for one transaction. + sequence: normal transaction(sendStop==false), [dataonly(sendStop==false)],dataOnly(sendStop==true) + *** Currently only works with WRITE, final byte NAK an READ will cause a fail between dq buffer elements. (in progress 30JUL2018) + */ + dqx.ctrl.startCmdSent = 1; // mark as already sent + dqx.ctrl.addrCmdSent = 1; + } else { + dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address + } + dqx.ctrl.addr = i2cDeviceAddr; + dqx.ctrl.mode = mode; + dqx.ctrl.stop= sendStop; + dqx.queueEvent = event; + + if(event) { // an eventGroup exist, so, initialize it + xEventGroupClearBits(event, EVENT_MASK); // all of them + } + + if(i2c->dq!=NULL) { // expand + //log_i("expand"); + I2C_DATA_QUEUE_t* tq =(I2C_DATA_QUEUE_t*)realloc(i2c->dq,sizeof(I2C_DATA_QUEUE_t)*(i2c->queueCount +1)); + if(tq!=NULL) { // ok + i2c->dq = tq; + memmove(&i2c->dq[i2c->queueCount++],&dqx,sizeof(I2C_DATA_QUEUE_t)); + } else { // bad stuff, unable to allocate more memory! + log_e("realloc Failure"); + return I2C_ERROR_MEMORY; + } + } else { // first Time + //log_i("new"); + i2c->queueCount=0; + i2c->dq =(I2C_DATA_QUEUE_t*)malloc(sizeof(I2C_DATA_QUEUE_t)); + if(i2c->dq!=NULL) { + memmove(&i2c->dq[i2c->queueCount++],&dqx,sizeof(I2C_DATA_QUEUE_t)); + } else { + log_e("malloc failure"); + return I2C_ERROR_MEMORY; + } + } + return I2C_ERROR_OK; +} + +i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event) +{ + return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event); +} + +i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event) +{ + //10bit read is kind of weird, first you do a 0byte Write with 10bit + // address, then a ReSTART then a 7bit Read using the the upper 7bit + + // readBit. + + // this might cause an internal register pointer problem with 10bit + // devices, But, Don't have any to test against. + // this is the Industry Standard specification. + + if((i2cDeviceAddr &0xFC00)==0x7800) { // ten bit read + i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,false,event); + if(err==I2C_ERROR_OK) { + return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,false,event); + } else { + return err; + } + } + return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event); +} + +i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) +{ + /* do the hard stuff here + install ISR if necessary + setup EventGroup + handle bus busy? + */ + //log_e("procQueue i2c=%p",&i2c); + if(readCount){ //total reads accomplished in all queue elements + *readCount = 0; + } + if(i2c == NULL) { + return I2C_ERROR_DEV; + } + if(i2c->debugFlags & 0xff000000) i2cTriggerDumps(i2c,(i2c->debugFlags>>24),"before ProcQueue"); + if (i2c->dev->status_reg.bus_busy) { // return error, let TwoWire() handle resetting the hardware. + /* if multi master then this if should be changed to this 03/12/2018 + if(multiMaster){// try to let the bus clear by its self + uint32_t timeOutTick = millis(); + while((i2c->dev->status_reg.bus_busy)&&(millis()-timeOutTickdev->status_reg.bus_busy){ // still busy, so die + */ + log_i("Bus busy, reinit"); + return I2C_ERROR_BUSY; + } + + I2C_MUTEX_LOCK(); + /* what about co-existence with SLAVE mode? + Should I check if a slaveMode xfer is in progress and hang + until it completes? + if i2c->stage == I2C_RUNNING or I2C_SLAVE_ACTIVE + */ + i2c->stage = I2C_DONE; // until ready + +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + for(uint16_t i=0; inum] = 0; + intBuff[i][1][i2c->num] = 0; + intBuff[i][2][i2c->num] = 0; + } + intPos[i2c->num] = 0; + fifoPos = 0; + memset(fifoBuffer,0,FIFOMAX); +#endif + // EventGroup is used to signal transmission completion from ISR + // not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request + // if that happens, this call hangs until the timeout period expires, then it continues. + if(!i2c->i2c_event) { + i2c->i2c_event = xEventGroupCreate(); + } + if(i2c->i2c_event) { + xEventGroupClearBits(i2c->i2c_event, 0xFF); + } else { // failed to create EventGroup + log_e("eventCreate failed=%p",i2c->i2c_event); + I2C_MUTEX_UNLOCK(); + return I2C_ERROR_MEMORY; + } + + i2c_err_t reason = I2C_ERROR_OK; + i2c->mode = I2C_MASTER; + i2c->dev->ctr.trans_start=0; // Pause Machine + i2c->dev->timeout.tout = 0xFFFFF; // max 13ms + i2c->dev->int_clr.val = 0x1FFF; // kill them All! + + i2c->dev->ctr.ms_mode = 1; // master! + i2c->queuePos=0; + i2c->errorByteCnt=0; + i2c->errorQueue = 0; + uint32_t totalBytes=0; // total number of bytes to be Moved! + // convert address field to required I2C format + while(i2c->queuePos < i2c->queueCount) { // need to push these address modes upstream, to AddQueue + I2C_DATA_QUEUE_t *tdq = &i2c->dq[i2c->queuePos++]; + uint16_t taddr=0; + if(tdq->ctrl.addrReq ==2) { // 10bit address + taddr =((tdq->ctrl.addr >> 7) & 0xFE) + |tdq->ctrl.mode; + taddr = (taddr <<8) | (tdq->ctrl.addr&0xFF); + } else { // 7bit address + taddr = ((tdq->ctrl.addr<<1)&0xFE) + |tdq->ctrl.mode; + } + tdq->ctrl.addr = taddr; // all fixed with R/W bit + totalBytes += tdq->length + tdq->ctrl.addrReq; // total number of byte to be moved! + } + i2c->queuePos=0; + + fillCmdQueue(i2c,false); // don't enable Tx/RX irq's + // start adding command[], END irq will keep it full + //Data Fifo will be filled after trans_start is issued + i2c->exitCode=0; + + I2C_FIFO_CONF_t f; + f.val = i2c->dev->fifo_conf.val; + f.rx_fifo_rst = 1; // fifo in reset + f.tx_fifo_rst = 1; // fifo in reset + f.nonfifo_en = 0; // use fifo mode + f.nonfifo_tx_thres = 31; + // need to adjust threshold based on I2C clock rate, at 100k, 30 usually works, + // sometimes the emptyRx() actually moves 31 bytes + // it hasn't overflowed yet, I cannot tell if the new byte is added while + // emptyRX() is executing or before? + // let i2cSetFrequency() set thrhds + // f.rx_fifo_full_thrhd = 30; // 30 bytes before INT is issued + // f.tx_fifo_empty_thrhd = 0; + f.fifo_addr_cfg_en = 0; // no directed access + i2c->dev->fifo_conf.val = f.val; // post them all + + f.rx_fifo_rst = 0; // release fifo + f.tx_fifo_rst = 0; + i2c->dev->fifo_conf.val = f.val; // post them all + + i2c->stage = I2C_STARTUP; // everything configured, now start the I2C StateMachine, and + // As soon as interrupts are enabled, the ISR will start handling them. + // it should receive a TXFIFO_EMPTY immediately, even before it + // receives the TRANS_START + + + uint32_t interruptsEnabled = + I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit + I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END + I2C_TIME_OUT_INT_ENA | //(BIT(8)) Trigger by SLAVE SCL stretching, NOT an ERROR + I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit + I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit + I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled + I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list + I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) unhandled + I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo() + I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo() + + i2c->dev->int_ena.val = interruptsEnabled; + + if(!i2c->intr_handle) { // create ISR for either peripheral + // log_i("create ISR %d",i2c->num); + uint32_t ret = 0; + uint32_t flags = ESP_INTR_FLAG_IRAM | //< ISR can be called if cache is disabled + ESP_INTR_FLAG_LOWMED | //< Low and medium prio interrupts. These can be handled in C. + ESP_INTR_FLAG_SHARED; //< Reduce resource requirements, Share interrupts + + if(i2c->num) { + ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle); + } else { + ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle); + } + + if(ret!=ESP_OK) { + log_e("install interrupt handler Failed=%d",ret); + 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. + + // how many ticks should it take to transfer totalBytes through the I2C hardware, + // add user supplied timeOutMillis to Calculated Value + + portTickType ticksTimeOut = ((totalBytes*10*1000)/(i2cGetFrequency(i2c))+timeOutMillis)/portTICK_PERIOD_MS; + + i2c->dev->ctr.trans_start=1; // go for it + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG + portTickType tBefore=xTaskGetTickCount(); +#endif + + // wait for ISR to complete the transfer, or until timeOut in case of bus fault, hardware problem + + uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ticksTimeOut); + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG + portTickType tAfter=xTaskGetTickCount(); +#endif + + + // if xEventGroupSetBitsFromISR() failed, the ISR could have succeeded but never been + // able to mark the success + + if(i2c->exitCode!=eBits) { // try to recover from O/S failure + // log_e("EventGroup Failed:%p!=%p",eBits,i2c->exitCode); + eBits=i2c->exitCode; + } + if((eBits&EVENT_ERROR)||(!(eBits & EVENT_DONE))){ // need accurate errorByteCnt for debug + i2c_update_error_byte_cnt(i2c); + } + + if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR_DATA_NAK|EVENT_ERROR|EVENT_DONE))) { // not only Done, therefore error, exclude ADDR NAK, DATA_NAK +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO + i2cDumpI2c(i2c); + i2cDumpInts(i2c->num); +#endif + } + + if(eBits&EVENT_DONE) { // no gross timeout + switch(i2c->error) { + case I2C_OK : + reason = I2C_ERROR_OK; + break; + case I2C_ERROR : + reason = I2C_ERROR_DEV; + break; + case I2C_ADDR_NAK: + reason = I2C_ERROR_ACK; + break; + case I2C_DATA_NAK: + reason = I2C_ERROR_ACK; + break; + case I2C_ARBITRATION: + reason = I2C_ERROR_BUS; + break; + case I2C_TIMEOUT: + reason = I2C_ERROR_TIMEOUT; + break; + default : + reason = I2C_ERROR_DEV; + } + } else { // GROSS timeout, shutdown ISR , report Timeout + i2c->stage = I2C_DONE; + i2c->dev->int_ena.val =0; + i2c->dev->int_clr.val = 0x1FFF; + i2c_update_error_byte_cnt(i2c); + if((i2c->errorByteCnt == 0)&&(i2c->errorQueue==0)) { // Bus Busy no bytes Moved + reason = I2C_ERROR_BUSY; + eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE; +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG + log_d(" Busy Timeout start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); + i2cDumpI2c(i2c); + i2cDumpInts(i2c->num); +#endif + } else { // just a timeout, some data made it out or in. + reason = I2C_ERROR_TIMEOUT; + eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG + log_d(" Gross Timeout Dead start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); + i2cDumpI2c(i2c); + i2cDumpInts(i2c->num); +#endif + } + } + + /* offloading all EventGroups to dispatch, EventGroups in ISR is not always successful + 11/20/2017 + if error, need to trigger all succeeding dataQueue events with the EVENT_ERROR_PREV + 07/22/2018 + Need to use the queueEvent value to identify transaction blocks, if an error occurs, + all subsequent queue items with the same queueEvent value will receive the EVENT_ERROR_PREV. + But, ProcQue should re-queue queue items that have a different queueEvent value(different transaction) + This change will support multi-thread i2c usage. Use the queueEvent as the transaction event + identifier. + */ + uint32_t b = 0; + + while(b < i2c->queueCount) { + if(i2c->dq[b].ctrl.mode==1 && readCount) { + *readCount += i2c->dq[b].position; // number of data bytes received + } + if(b < i2c->queuePos) { // before any error + if(i2c->dq[b].queueEvent) { // this data queue element has an EventGroup + xEventGroupSetBits(i2c->dq[b].queueEvent,EVENT_DONE); + } + } else if(b == i2c->queuePos) { // last processed queue + if(i2c->dq[b].queueEvent) { // this data queue element has an EventGroup + xEventGroupSetBits(i2c->dq[b].queueEvent,eBits); + } + } else { // never processed queues + if(i2c->dq[b].queueEvent) { // this data queue element has an EventGroup + xEventGroupSetBits(i2c->dq[b].queueEvent,eBits|EVENT_ERROR_PREV); + } + } + b++; + } + if(i2c->debugFlags & 0x00ff0000) i2cTriggerDumps(i2c,(i2c->debugFlags>>16),"after ProcQueue"); + + I2C_MUTEX_UNLOCK(); + return reason; +} + +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"); + } + } +} + +static bool i2cCheckLineState(int8_t sda, int8_t scl){ + if(sda < 0 || scl < 0){ + return false;//return false since there is nothing to do + } + // if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles + digitalWrite(sda, HIGH); + digitalWrite(scl, HIGH); + pinMode(sda, PULLUP|OPEN_DRAIN|INPUT); + pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT); + + if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state + log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, digitalRead(sda), scl, digitalRead(scl)); + digitalWrite(scl, HIGH); + for(uint8_t a=0; a<9; a++) { + delayMicroseconds(5); + digitalWrite(scl, LOW); + delayMicroseconds(5); + digitalWrite(scl, HIGH); + if(digitalRead(sda)){ // bus recovered + log_d("Recovered after %d Cycles",a+1); + break; + } + } + } + + if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state + log_e("Bus Invalid State, TwoWire() Can't init sda=%d, scl=%d",digitalRead(sda),digitalRead(scl)); + return false; // bus is busy + } + return true; +} + +i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) +{ + if(i2c == NULL) { + return I2C_ERROR_DEV; + } + digitalWrite(scl, HIGH); + pinMode(scl, OPEN_DRAIN | PULLUP | INPUT | OUTPUT); + pinMatrixOutAttach(scl, I2C_SCL_IDX(i2c->num), false, false); + pinMatrixInAttach(scl, I2C_SCL_IDX(i2c->num), false); + return I2C_ERROR_OK; +} + +i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl) +{ + if(i2c == NULL) { + return I2C_ERROR_DEV; + } + pinMatrixOutDetach(scl, false, false); + pinMatrixInDetach(I2C_SCL_IDX(i2c->num), false, false); + pinMode(scl, INPUT | PULLUP); + return I2C_ERROR_OK; +} + +i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda) +{ + if(i2c == NULL) { + return I2C_ERROR_DEV; + } + digitalWrite(sda, HIGH); + pinMode(sda, OPEN_DRAIN | PULLUP | INPUT | OUTPUT ); + pinMatrixOutAttach(sda, I2C_SDA_IDX(i2c->num), false, false); + pinMatrixInAttach(sda, I2C_SDA_IDX(i2c->num), false); + return I2C_ERROR_OK; +} + +i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) +{ + if(i2c == NULL) { + return I2C_ERROR_DEV; + } + pinMatrixOutDetach(sda, false, false); + pinMatrixInDetach(I2C_SDA_IDX(i2c->num), false, false); + pinMode(sda, INPUT | PULLUP); + return I2C_ERROR_OK; +} + +/* + * PUBLIC API + * */ +// 24Nov17 only supports Master Mode +i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) { +#ifdef ENABLE_I2C_DEBUG_BUFFER + log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency); +#endif + if(i2c_num > 1) { + return NULL; + } + + i2c_t * i2c = &_i2c_bus_array[i2c_num]; + + // pins should be detached, else glitch + if(i2c->sda >= 0){ + i2cDetachSDA(i2c, i2c->sda); + } + if(i2c->scl >= 0){ + i2cDetachSCL(i2c, i2c->scl); + } + i2c->sda = sda; + i2c->scl = scl; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(i2c->lock == NULL) { + i2c->lock = xSemaphoreCreateRecursiveMutex(); + if(i2c->lock == NULL) { + return NULL; + } + } +#endif + I2C_MUTEX_LOCK(); + + i2cReleaseISR(i2c); // ISR exists, release it before disabling hardware + + if(frequency == 0) {// don't change existing frequency + frequency = i2cGetFrequency(i2c); + if(frequency == 0) { + frequency = 100000L; // default to 100khz + } + } + + if(i2c_num == 0) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); //reset hardware + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST);// release reset + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); //reset Hardware + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); + } + i2c->dev->ctr.val = 0; + i2c->dev->ctr.ms_mode = 1; + i2c->dev->ctr.sda_force_out = 1 ; + i2c->dev->ctr.scl_force_out = 1 ; + i2c->dev->ctr.clk_en = 1; + + //the max clock number of receiving a data + i2c->dev->timeout.tout = 400000;//clocks max=1048575 + //disable apb nonfifo access + i2c->dev->fifo_conf.nonfifo_en = 0; + + i2c->dev->slave_addr.val = 0; + I2C_MUTEX_UNLOCK(); + + i2cSetFrequency(i2c, frequency); // reconfigure + + if(!i2cCheckLineState(i2c->sda, i2c->scl)){ + return NULL; + } + + if(i2c->sda >= 0){ + i2cAttachSDA(i2c, i2c->sda); + } + if(i2c->scl >= 0){ + i2cAttachSCL(i2c, i2c->scl); + } + return i2c; +} + +void i2cRelease(i2c_t *i2c) // release all resources, power down peripheral +{ + I2C_MUTEX_LOCK(); + + if(i2c->sda >= 0){ + i2cDetachSDA(i2c, i2c->sda); + } + if(i2c->scl >= 0){ + i2cDetachSCL(i2c, i2c->scl); + } + + i2cReleaseISR(i2c); + + if(i2c->i2c_event) { + vEventGroupDelete(i2c->i2c_event); + i2c->i2c_event = NULL; + } + + i2cFlush(i2c); + + // reset the I2C hardware and shut off the clock, power it down. + if(i2c->num == 0) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); //reset hardware + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); // shutdown hardware + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); //reset Hardware + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); // shutdown Hardware + } + + I2C_MUTEX_UNLOCK(); +} + +i2c_err_t i2cFlush(i2c_t * i2c) +{ + if(i2c==NULL) { + return I2C_ERROR_DEV; + } + i2cTriggerDumps(i2c,i2c->debugFlags & 0xff, "FLUSH"); + + // need to grab a MUTEX for exclusive Queue, + // what out if ISR is running? + i2c_err_t rc=I2C_ERROR_OK; + if(i2c->dq!=NULL) { + // log_i("free"); + // what about EventHandle? + free(i2c->dq); + i2c->dq = NULL; + } + i2c->queueCount=0; + i2c->queuePos=0; + // release Mutex + return rc; +} + +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 + if(sendStop) { //now actually process the queued commands, including READs + last_error = i2cProcQueue(i2c, NULL, timeOutMillis); + if(last_error == I2C_ERROR_BUSY) { // try to clear the bus + if(i2cInit(i2c->num, i2c->sda, i2c->scl, 0)) { + last_error = i2cProcQueue(i2c, NULL, timeOutMillis); + } + } + i2cFlush(i2c); + } else { // stop not received, so wait for I2C stop, + last_error = I2C_ERROR_CONTINUE; + } + } + return last_error; +} + +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 + if(sendStop) { //now actually process the queued commands, including READs + last_error = i2cProcQueue(i2c, readCount, timeOutMillis); + if(last_error == I2C_ERROR_BUSY) { // try to clear the bus + if(i2cInit(i2c->num, i2c->sda, i2c->scl, 0)) { + last_error = i2cProcQueue(i2c, readCount, timeOutMillis); + } + } + i2cFlush(i2c); + } else { // stop not received, so wait for I2C stop, + last_error = I2C_ERROR_CONTINUE; + } + } + return last_error; +} + +#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) { + return I2C_ERROR_DEV; + } + uint32_t apb = getApbFrequency(); + uint32_t period = (apb/clk_speed) / 2; + + if((apb/8192 > clk_speed)||(apb/MIN_I2C_CLKS < clk_speed)){ //out of bounds + 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_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_d("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed); + } +#ifdef ENABLE_I2C_DEBUG_BUFFER + log_v("freq=%dHz",clk_speed); +#endif + uint32_t halfPeriod = period/2; + uint32_t quarterPeriod = period/4; + + I2C_MUTEX_LOCK(); + + I2C_FIFO_CONF_t f; + + f.val = i2c->dev->fifo_conf.val; +/* 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. +*/ +#ifdef ENABLE_I2C_DEBUG_BUFFER + log_v("cpu Freq=%dMhz, i2c Freq=%dHz",getCpuFrequencyMhz(),clk_speed); +#endif + 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 +#ifdef ENABLE_I2C_DEBUG_BUFFER + log_v("Fifo delta=%d",fifo_delta); +#endif + //the clock num during SCL is low level + i2c->dev->scl_low_period.period = period; + //the clock num during SCL is high level + i2c->dev->scl_high_period.period = period; + + //the clock num between the negedge of SDA and negedge of SCL for start mark + i2c->dev->scl_start_hold.time = halfPeriod; + //the clock num between the posedge of SCL and the negedge of SDA for restart mark + i2c->dev->scl_rstart_setup.time = halfPeriod; + + //the clock num after the STOP bit's posedge + i2c->dev->scl_stop_hold.time = halfPeriod; + //the clock num between the posedge of SCL and the posedge of SDA + i2c->dev->scl_stop_setup.time = halfPeriod; + + //the clock num I2C used to hold the data after the negedge of SCL. + i2c->dev->sda_hold.time = quarterPeriod; + //the clock num I2C used to sample data on SDA after the posedge of SCL + i2c->dev->sda_sample.time = quarterPeriod; + I2C_MUTEX_UNLOCK(); + return I2C_ERROR_OK; +} + +uint32_t i2cGetFrequency(i2c_t * i2c) +{ + if(i2c == NULL) { + return 0; + } + uint32_t result = 0; + uint32_t old_count = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); + if(old_count>0) { + result = getApbFrequency() / old_count; + } else { + result = 0; + } + return result; +} + + +uint32_t i2cDebug(i2c_t * i2c, uint32_t setBits, uint32_t resetBits){ + if(i2c != NULL) { + i2c->debugFlags = ((i2c->debugFlags | setBits) & ~resetBits); + return i2c->debugFlags; + } + return 0; + + } + +uint32_t i2cGetStatus(i2c_t * i2c){ + if(i2c != NULL){ + return i2c->dev->status_reg.val; + } + else return 0; +} + + +/* todo + 22JUL18 + need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads + transactions are present in the same queue, and an error occurs, abort all succeeding unserviced transactions + with the same dq.queueEvent value. Succeeding unserviced transactions with different dq.queueEvent values + can be re-queued and processed independently. + 30JUL18 complete data only queue elements, this will allow transfers to use multiple data blocks, + */ + diff --git a/components/FastLED-idf/hal/esp32-hal-i2c.h b/components/FastLED-idf/hal/esp32-hal-i2c.h new file mode 100644 index 0000000..1a696ac --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-i2c.h @@ -0,0 +1,82 @@ +// 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. +// modified Nov 2017 by Chuck Todd to support Interrupt Driven I/O + +#ifndef _ESP32_HAL_I2C_H_ +#define _ESP32_HAL_I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +// External Wire.h equivalent error Codes +typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; + +struct i2c_struct_t; +typedef struct i2c_struct_t i2c_t; + +i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed); +void i2cRelease(i2c_t *i2c); // free ISR, Free DQ, Power off peripheral clock. Must call i2cInit() to recover +i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis); +i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount); +i2c_err_t i2cFlush(i2c_t *i2c); +i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed); +uint32_t i2cGetFrequency(i2c_t * i2c); +uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral + +//Functions below should be used only if well understood +//Might be deprecated and removed in future +i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl); +i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl); +i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda); +i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda); + +//Stickbreakers ISR Support +i2c_err_t i2cProcQueue(i2c_t *i2c, uint32_t *readCount, uint16_t timeOutMillis); +i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); +i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); + +//stickbreaker debug support +uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits); +// Debug actions have 3 currently defined locus +// 0xXX------ : at entry of ProcQueue +// 0x--XX---- : at exit of ProcQueue +// 0x------XX : at entry of Flush +// +// bit 0 causes DumpI2c to execute +// bit 1 causes DumpInts to execute +// bit 2 causes DumpCmdqueue to execute +// bit 3 causes DumpStatus to execute +// bit 4 causes DumpFifo to execute + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_I2C_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-ledc.c b/components/FastLED-idf/hal/esp32-hal-ledc.c new file mode 100644 index 0000000..24bbf32 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-ledc.c @@ -0,0 +1,280 @@ +// 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 "esp32-hal.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "rom/ets_sys.h" +#include "esp32-hal-matrix.h" +#include "soc/dport_reg.h" +#include "soc/ledc_reg.h" +#include "soc/ledc_struct.h" + +#if CONFIG_DISABLE_HAL_LOCKS +#define LEDC_MUTEX_LOCK() +#define LEDC_MUTEX_UNLOCK() +#else +#define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS) +#define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock) +xSemaphoreHandle _ledc_sys_lock = NULL; +#endif + +/* + * LEDC Chan to Group/Channel/Timer Mapping +** ledc: 0 => Group: 0, Channel: 0, Timer: 0 +** ledc: 1 => Group: 0, Channel: 1, Timer: 0 +** ledc: 2 => Group: 0, Channel: 2, Timer: 1 +** ledc: 3 => Group: 0, Channel: 3, Timer: 1 +** ledc: 4 => Group: 0, Channel: 4, Timer: 2 +** ledc: 5 => Group: 0, Channel: 5, Timer: 2 +** ledc: 6 => Group: 0, Channel: 6, Timer: 3 +** ledc: 7 => Group: 0, Channel: 7, Timer: 3 +** ledc: 8 => Group: 1, Channel: 0, Timer: 0 +** ledc: 9 => Group: 1, Channel: 1, Timer: 0 +** ledc: 10 => Group: 1, Channel: 2, Timer: 1 +** ledc: 11 => Group: 1, Channel: 3, Timer: 1 +** ledc: 12 => Group: 1, Channel: 4, Timer: 2 +** ledc: 13 => Group: 1, Channel: 5, Timer: 2 +** ledc: 14 => Group: 1, Channel: 6, Timer: 3 +** ledc: 15 => Group: 1, Channel: 7, Timer: 3 +*/ +#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)] +#define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)] + +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){ + uint16_t iarg = *(uint16_t*)arg; + uint8_t chan = 0; + old_apb /= 1000000; + new_apb /= 1000000; + while(iarg){ // run though all active channels, adjusting timing configurations + if(iarg & 1) {// this channel is active + uint8_t group=(chan/8), timer=((chan/2)%4); + 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){ + div_num = ((REF_CLK_FREQ /1000000) * 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(); + } + else { + log_d("using REF_CLK chan=%d",chan); + } + } + iarg = iarg >> 1; + chan++; + } + } +} + +//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) +{ + uint8_t group=(chan/8), timer=((chan/2)%4); + static bool tHasStarted = false; + static uint16_t _activeChannels = 0; + if(!tHasStarted) { + tHasStarted = true; + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_LEDC_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_LEDC_RST); + LEDC.conf.apb_clk_sel = 1;//LS use apb clock + addApbChangeCallback((void*)&_activeChannels, _on_apb_change); + +#if !CONFIG_DISABLE_HAL_LOCKS + _ledc_sys_lock = xSemaphoreCreateMutex(); +#endif + } + LEDC_MUTEX_LOCK(); + LEDC_TIMER(group, timer).conf.clock_divider = div_num;//18 bit (10.8) This register is used to configure parameter for divider in timer the least significant eight bits represent the decimal part. + LEDC_TIMER(group, timer).conf.duty_resolution = bit_num;//5 bit This register controls the range of the counter in timer. the counter range is [0 2**bit_num] the max bit width for counter is 20. + LEDC_TIMER(group, timer).conf.tick_sel = apb_clk;//apb clock + if(group) { + LEDC_TIMER(group, timer).conf.low_speed_update = 1;//This bit is only useful for low speed timer channels, reserved for high speed timers + } + LEDC_TIMER(group, timer).conf.pause = 0; + LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset. + LEDC_TIMER(group, timer).conf.rst = 0; + LEDC_MUTEX_UNLOCK(); + _activeChannels |= (1 << chan); // mark as active for APB callback +} + +//max div_num 0x3FFFF (262143) +//max bit_num 0x1F (31) +static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num) +{ + uint64_t clk_freq = getApbFrequency(); + clk_freq <<= 8;//div_num is 8 bit decimal + uint32_t div_num = (clk_freq >> bit_num) / freq; + bool apb_clk = true; + if(div_num > LEDC_DIV_NUM_HSTIMER0_V) { + clk_freq /= 80; + div_num = (clk_freq >> bit_num) / freq; + if(div_num > LEDC_DIV_NUM_HSTIMER0_V) { + div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible + } + apb_clk = false; + } else if(div_num < 256) { + div_num = 256;//highest clock possible + } + _ledcSetupTimer(chan, div_num, bit_num, apb_clk); + //log_i("Fin: %f, Fclk: %uMhz, bits: %u, DIV: %u, Fout: %f", + // freq, apb_clk?80:1, bit_num, div_num, (clk_freq >> bit_num) / (double)div_num); + return (clk_freq >> bit_num) / (double)div_num; +} + +static double _ledcTimerRead(uint8_t chan) +{ + uint32_t div_num; + uint8_t bit_num; + bool apb_clk; + uint8_t group=(chan/8), timer=((chan/2)%4); + LEDC_MUTEX_LOCK(); + div_num = LEDC_TIMER(group, timer).conf.clock_divider;//18 bit (10.8) This register is used to configure parameter for divider in timer the least significant eight bits represent the decimal part. + bit_num = LEDC_TIMER(group, timer).conf.duty_resolution;//5 bit This register controls the range of the counter in timer. the counter range is [0 2**bit_num] the max bit width for counter is 20. + apb_clk = LEDC_TIMER(group, timer).conf.tick_sel;//apb clock + LEDC_MUTEX_UNLOCK(); + uint64_t clk_freq = 1000000; + if(apb_clk) { + clk_freq = getApbFrequency(); + } + clk_freq <<= 8;//div_num is 8 bit decimal + return (clk_freq >> bit_num) / (double)div_num; +} + +static void _ledcSetupChannel(uint8_t chan, uint8_t idle_level) +{ + uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4); + LEDC_MUTEX_LOCK(); + LEDC_CHAN(group, channel).conf0.timer_sel = timer;//2 bit Selects the timer to attach 0-3 + LEDC_CHAN(group, channel).conf0.idle_lv = idle_level;//1 bit This bit is used to control the output value when channel is off. + LEDC_CHAN(group, channel).hpoint.hpoint = 0;//20 bit The output value changes to high when timer selected by channel has reached hpoint + LEDC_CHAN(group, channel).conf1.duty_inc = 1;//1 bit This register is used to increase the duty of output signal or decrease the duty of output signal for high speed channel + LEDC_CHAN(group, channel).conf1.duty_num = 1;//10 bit This register is used to control the number of increased or decreased times for channel + LEDC_CHAN(group, channel).conf1.duty_cycle = 1;//10 bit This register is used to increase or decrease the duty every duty_cycle cycles for channel + LEDC_CHAN(group, channel).conf1.duty_scale = 0;//10 bit This register controls the increase or decrease step scale for channel. + LEDC_CHAN(group, channel).duty.duty = 0; + LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel + LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware. + if(group) { + LEDC_CHAN(group, channel).conf0.low_speed_update = 1; + } else { + LEDC_CHAN(group, channel).conf0.clk_en = 0; + } + LEDC_MUTEX_UNLOCK(); +} + +double ledcSetup(uint8_t chan, double freq, uint8_t bit_num) +{ + if(chan > 15) { + return 0; + } + double res_freq = _ledcSetupTimerFreq(chan, freq, bit_num); + _ledcSetupChannel(chan, LOW); + return res_freq; +} + +void ledcWrite(uint8_t chan, uint32_t duty) +{ + if(chan > 15) { + return; + } + uint8_t group=(chan/8), channel=(chan%8); + LEDC_MUTEX_LOCK(); + LEDC_CHAN(group, channel).duty.duty = duty << 4;//25 bit (21.4) + if(duty) { + LEDC_CHAN(group, channel).conf0.sig_out_en = 1;//This is the output enable control bit for channel + LEDC_CHAN(group, channel).conf1.duty_start = 1;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware. + if(group) { + LEDC_CHAN(group, channel).conf0.low_speed_update = 1; + } else { + LEDC_CHAN(group, channel).conf0.clk_en = 1; + } + } else { + LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel + LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware. + if(group) { + LEDC_CHAN(group, channel).conf0.low_speed_update = 1; + } else { + LEDC_CHAN(group, channel).conf0.clk_en = 0; + } + } + LEDC_MUTEX_UNLOCK(); +} + +uint32_t ledcRead(uint8_t chan) +{ + if(chan > 15) { + return 0; + } + return LEDC.channel_group[chan/8].channel[chan%8].duty.duty >> 4; +} + +double ledcReadFreq(uint8_t chan) +{ + if(!ledcRead(chan)){ + return 0; + } + return _ledcTimerRead(chan); +} + +double ledcWriteTone(uint8_t chan, double freq) +{ + if(chan > 15) { + return 0; + } + if(!freq) { + ledcWrite(chan, 0); + return 0; + } + double res_freq = _ledcSetupTimerFreq(chan, freq, 10); + ledcWrite(chan, 0x1FF); + return res_freq; +} + +double ledcWriteNote(uint8_t chan, note_t note, uint8_t octave){ + const uint16_t noteFrequencyBase[12] = { + // C C# D Eb E F F# G G# A Bb B + 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902 + }; + + if(octave > 8 || note >= NOTE_MAX){ + return 0; + } + double noteFreq = (double)noteFrequencyBase[note] / (double)(1 << (8-octave)); + return ledcWriteTone(chan, noteFreq); +} + +void ledcAttachPin(uint8_t pin, uint8_t chan) +{ + if(chan > 15) { + return; + } + pinMode(pin, OUTPUT); + pinMatrixOutAttach(pin, ((chan/8)?LEDC_LS_SIG_OUT0_IDX:LEDC_HS_SIG_OUT0_IDX) + (chan%8), false, false); +} + +void ledcDetachPin(uint8_t pin) +{ + pinMatrixOutDetach(pin, false, false); +} diff --git a/components/FastLED-idf/hal/esp32-hal-ledc.h b/components/FastLED-idf/hal/esp32-hal-ledc.h new file mode 100644 index 0000000..159f98d --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-ledc.h @@ -0,0 +1,44 @@ +// 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_LEDC_H_ +#define _ESP32_HAL_LEDC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef enum { + NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B, NOTE_MAX +} note_t; + +//channel 0-15 resolution 1-16bits freq limits depend on resolution +double ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits); +void ledcWrite(uint8_t channel, uint32_t duty); +double ledcWriteTone(uint8_t channel, double freq); +double ledcWriteNote(uint8_t channel, note_t note, uint8_t octave); +uint32_t ledcRead(uint8_t channel); +double ledcReadFreq(uint8_t channel); +void ledcAttachPin(uint8_t pin, uint8_t channel); +void ledcDetachPin(uint8_t pin); + + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_LEDC_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-log.h b/components/FastLED-idf/hal/esp32-hal-log.h new file mode 100644 index 0000000..e1d4e56 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-log.h @@ -0,0 +1,158 @@ +// 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 __ARDUHAL_LOG_H__ +#define __ARDUHAL_LOG_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "sdkconfig.h" + +#define ARDUHAL_LOG_LEVEL_NONE (0) +#define ARDUHAL_LOG_LEVEL_ERROR (1) +#define ARDUHAL_LOG_LEVEL_WARN (2) +#define ARDUHAL_LOG_LEVEL_INFO (3) +#define ARDUHAL_LOG_LEVEL_DEBUG (4) +#define ARDUHAL_LOG_LEVEL_VERBOSE (5) + +#ifndef CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL +#define CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL ARDUHAL_LOG_LEVEL_NONE +#endif + +#ifndef CORE_DEBUG_LEVEL +#define ARDUHAL_LOG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL +#else +#define ARDUHAL_LOG_LEVEL CORE_DEBUG_LEVEL +#endif + +#ifndef CONFIG_ARDUHAL_LOG_COLORS +#define CONFIG_ARDUHAL_LOG_COLORS 0 +#endif + +#if CONFIG_ARDUHAL_LOG_COLORS +#define ARDUHAL_LOG_COLOR_BLACK "30" +#define ARDUHAL_LOG_COLOR_RED "31" //ERROR +#define ARDUHAL_LOG_COLOR_GREEN "32" //INFO +#define ARDUHAL_LOG_COLOR_YELLOW "33" //WARNING +#define ARDUHAL_LOG_COLOR_BLUE "34" +#define ARDUHAL_LOG_COLOR_MAGENTA "35" +#define ARDUHAL_LOG_COLOR_CYAN "36" //DEBUG +#define ARDUHAL_LOG_COLOR_GRAY "37" //VERBOSE +#define ARDUHAL_LOG_COLOR_WHITE "38" + +#define ARDUHAL_LOG_COLOR(COLOR) "\033[0;" COLOR "m" +#define ARDUHAL_LOG_BOLD(COLOR) "\033[1;" COLOR "m" +#define ARDUHAL_LOG_RESET_COLOR "\033[0m" + +#define ARDUHAL_LOG_COLOR_E ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_RED) +#define ARDUHAL_LOG_COLOR_W ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_YELLOW) +#define ARDUHAL_LOG_COLOR_I ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GREEN) +#define ARDUHAL_LOG_COLOR_D ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_CYAN) +#define ARDUHAL_LOG_COLOR_V ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GRAY) +#else +#define ARDUHAL_LOG_COLOR_E +#define ARDUHAL_LOG_COLOR_W +#define ARDUHAL_LOG_COLOR_I +#define ARDUHAL_LOG_COLOR_D +#define ARDUHAL_LOG_COLOR_V +#define ARDUHAL_LOG_RESET_COLOR +#endif + +const char * pathToFileName(const char * path); +int log_printf(const char *fmt, ...); + +#define ARDUHAL_SHORT_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter format ARDUHAL_LOG_RESET_COLOR "\r\n" +#define ARDUHAL_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter "[" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", pathToFileName(__FILE__), __LINE__, __FUNCTION__ + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE +#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#else +#define log_v(format, ...) +#define isr_log_v(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG +#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#else +#define log_d(format, ...) +#define isr_log_d(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#else +#define log_i(format, ...) +#define isr_log_i(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN +#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#else +#define log_w(format, ...) +#define isr_log_w(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#else +#define log_e(format, ...) +#define isr_log_e(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE +#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#else +#define log_n(format, ...) +#define isr_log_n(format, ...) +#endif + +#include "esp_log.h" + +#ifdef CONFIG_ARDUHAL_ESP_LOG +#undef ESP_LOGE +#undef ESP_LOGW +#undef ESP_LOGI +#undef ESP_LOGD +#undef ESP_LOGV +#undef ESP_EARLY_LOGE +#undef ESP_EARLY_LOGW +#undef ESP_EARLY_LOGI +#undef ESP_EARLY_LOGD +#undef ESP_EARLY_LOGV + +#define ESP_LOGE(tag, ...) log_e(__VA_ARGS__) +#define ESP_LOGW(tag, ...) log_w(__VA_ARGS__) +#define ESP_LOGI(tag, ...) log_i(__VA_ARGS__) +#define ESP_LOGD(tag, ...) log_d(__VA_ARGS__) +#define ESP_LOGV(tag, ...) log_v(__VA_ARGS__) +#define ESP_EARLY_LOGE(tag, ...) isr_log_e(__VA_ARGS__) +#define ESP_EARLY_LOGW(tag, ...) isr_log_w(__VA_ARGS__) +#define ESP_EARLY_LOGI(tag, ...) isr_log_i(__VA_ARGS__) +#define ESP_EARLY_LOGD(tag, ...) isr_log_d(__VA_ARGS__) +#define ESP_EARLY_LOGV(tag, ...) isr_log_v(__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_LOGGING_H__ */ diff --git a/components/FastLED-idf/hal/esp32-hal-matrix.c b/components/FastLED-idf/hal/esp32-hal-matrix.c new file mode 100644 index 0000000..fb1b498 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-matrix.c @@ -0,0 +1,47 @@ +// 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 "esp32-hal-matrix.h" +#include "esp_attr.h" +#include "rom/gpio.h" + +#define MATRIX_DETACH_OUT_SIG 0x100 +#define MATRIX_DETACH_IN_LOW_PIN 0x30 +#define MATRIX_DETACH_IN_LOW_HIGH 0x38 + +void IRAM_ATTR pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable) +{ + gpio_matrix_out(pin, function, invertOut, invertEnable); +} + +void IRAM_ATTR pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable) +{ + gpio_matrix_out(pin, MATRIX_DETACH_OUT_SIG, invertOut, invertEnable); +} + +void IRAM_ATTR pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted) +{ + gpio_matrix_in(pin, signal, inverted); +} + +void IRAM_ATTR pinMatrixInDetach(uint8_t signal, bool high, bool inverted) +{ + gpio_matrix_in(high?MATRIX_DETACH_IN_LOW_HIGH:MATRIX_DETACH_IN_LOW_PIN, signal, inverted); +} +/* +void IRAM_ATTR intrMatrixAttach(uint32_t source, uint32_t inum){ + intr_matrix_set(PRO_CPU_NUM, source, inum); +} +*/ + diff --git a/components/FastLED-idf/hal/esp32-hal-matrix.h b/components/FastLED-idf/hal/esp32-hal-matrix.h new file mode 100644 index 0000000..3bc9049 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-matrix.h @@ -0,0 +1,35 @@ +// 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_MATRIX_H_ +#define _ESP32_HAL_MATRIX_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "soc/gpio_sig_map.h" + +void pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable); +void pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable); +void pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted); +void pinMatrixInDetach(uint8_t signal, bool high, bool inverted); + +#ifdef __cplusplus +} +#endif + +#endif /* COMPONENTS_ARDUHAL_INCLUDE_ESP32_HAL_MATRIX_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-misc.c b/components/FastLED-idf/hal/esp32-hal-misc.c new file mode 100644 index 0000000..db7bf98 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-misc.c @@ -0,0 +1,251 @@ +// 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/task.h" +#include "esp_attr.h" +//#include "nvs_flash.h" +//#include "nvs.h" +//#include "esp_partition.h" + +#include "esp_log.h" +#include "esp_timer.h" + +//#ifdef CONFIG_APP_ROLLBACK_ENABLE +//#include "esp_ota_ops.h" +//#endif //CONFIG_APP_ROLLBACK_ENABLE + +#ifdef CONFIG_BT_ENABLED +#include "esp_bt.h" +#endif //CONFIG_BT_ENABLED +#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" + +//Undocumented!!! Get chip temperature in Farenheit +//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino +uint8_t temprature_sens_read(); + +float temperatureRead() +{ + return (temprature_sens_read() - 32) / 1.8; +} + +void __yield() +{ + vPortYield(); +} + +void yield() __attribute__ ((weak, alias("__yield"))); + +#if CONFIG_AUTOSTART_ARDUINO + +extern TaskHandle_t loopTaskHandle; +extern bool loopTaskWDTEnabled; + +void enableLoopWDT(){ + if(loopTaskHandle != NULL){ + if(esp_task_wdt_add(loopTaskHandle) != ESP_OK){ + log_e("Failed to add loop task to WDT"); + } else { + loopTaskWDTEnabled = true; + } + } +} + +void disableLoopWDT(){ + if(loopTaskHandle != NULL && loopTaskWDTEnabled){ + loopTaskWDTEnabled = false; + if(esp_task_wdt_delete(loopTaskHandle) != ESP_OK){ + log_e("Failed to remove loop task from WDT"); + } + } +} + +void feedLoopWDT(){ + esp_err_t err = esp_task_wdt_reset(); + if(err != ESP_OK){ + log_e("Failed to feed WDT! Error: %d", err); + } +} +#endif + +void enableCore0WDT(){ + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK){ + log_e("Failed to add Core 0 IDLE task to WDT"); + } +} + +void disableCore0WDT(){ + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK){ + log_e("Failed to remove Core 0 IDLE task from WDT"); + } +} + +#ifndef CONFIG_FREERTOS_UNICORE +void enableCore1WDT(){ + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK){ + log_e("Failed to add Core 1 IDLE task to WDT"); + } +} + +void disableCore1WDT(){ + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 == NULL || esp_task_wdt_delete(idle_1) != ESP_OK){ + log_e("Failed to remove Core 1 IDLE task from WDT"); + } +} +#endif + +BaseType_t xTaskCreateUniversal( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID ){ +#ifndef CONFIG_FREERTOS_UNICORE + if(xCoreID >= 0 && xCoreID < 2) { + return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID); + } else { +#endif + return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask); +#ifndef CONFIG_FREERTOS_UNICORE + } +#endif +} + +unsigned long IRAM_ATTR micros() +{ + return (unsigned long) (esp_timer_get_time()); +} + +unsigned long IRAM_ATTR millis() +{ + return (unsigned long) (esp_timer_get_time() / 1000ULL); +} + +void delay(uint32_t ms) +{ + vTaskDelay(ms / portTICK_PERIOD_MS); +} + +void IRAM_ATTR delayMicroseconds(uint32_t us) +{ + uint32_t m = micros(); + if(us){ + uint32_t e = (m + us); + if(m > e){ //overflow + while(micros() > e){ + NOP(); + } + } + while(micros() < e){ + NOP(); + } + } +} + +// BB I don't think any of this will be needed +#if 0 + +void initVariant() __attribute__((weak)); +void initVariant() {} + +void init() __attribute__((weak)); +void init() {} + +bool verifyOta() __attribute__((weak)); +bool verifyOta() { return true; } + +#ifdef CONFIG_BT_ENABLED +//overwritten in esp32-hal-bt.c +bool btInUse() __attribute__((weak)); +bool btInUse(){ return false; } +#endif + +void initArduino() +{ +#ifdef CONFIG_APP_ROLLBACK_ENABLE + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state; + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { + if (verifyOta()) { + esp_ota_mark_app_valid_cancel_rollback(); + } else { + log_e("OTA verification failed! Start rollback to the previous version ..."); + esp_ota_mark_app_invalid_rollback_and_reboot(); + } + } + } +#endif + //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 + setCpuFrequencyMhz(F_CPU/1000000); +#endif +#if CONFIG_SPIRAM_SUPPORT + psramInit(); +#endif + esp_log_level_set("*", CONFIG_LOG_DEFAULT_LEVEL); + esp_err_t err = nvs_flash_init(); + if(err == ESP_ERR_NVS_NO_FREE_PAGES){ + const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + if (partition != NULL) { + err = esp_partition_erase_range(partition, 0, partition->size); + if(!err){ + err = nvs_flash_init(); + } else { + log_e("Failed to format the broken NVS partition!"); + } + } + } + if(err) { + log_e("Failed to initialize NVS! Error: %u", err); + } +#ifdef CONFIG_BT_ENABLED + if(!btInUse()){ + esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); + } +#endif + init(); + initVariant(); +} + +//used by hal log +const char * IRAM_ATTR pathToFileName(const char * path) +{ + size_t i = 0; + size_t pos = 0; + char * p = (char *)path; + while(*p){ + i++; + if(*p == '/' || *p == '\\'){ + pos = i; + } + p++; + } + return path+pos; +} + +#endif \ No newline at end of file diff --git a/components/FastLED-idf/hal/esp32-hal-psram.c b/components/FastLED-idf/hal/esp32-hal-psram.c new file mode 100644 index 0000000..905cb96 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-psram.c @@ -0,0 +1,98 @@ + +#include "esp32-hal.h" + +#if CONFIG_SPIRAM_SUPPORT +#include "esp_spiram.h" +#include "soc/efuse_reg.h" +#include "esp_heap_caps.h" + +static volatile bool spiramDetected = false; +static volatile bool spiramFailed = false; + +bool psramInit(){ + if (spiramDetected) { + return true; + } +#ifndef CONFIG_SPIRAM_BOOT_INIT + if (spiramFailed) { + return false; + } + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { + spiramFailed = true; + log_w("PSRAM not supported!"); + return false; + } + esp_spiram_init_cache(); + if (esp_spiram_init() != ESP_OK) { + spiramFailed = true; + log_w("PSRAM init failed!"); + pinMatrixOutDetach(16, false, false); + pinMatrixOutDetach(17, false, false); + return false; + } + if (!esp_spiram_test()) { + spiramFailed = true; + log_e("PSRAM test failed!"); + return false; + } + if (esp_spiram_add_to_heapalloc() != ESP_OK) { + spiramFailed = true; + log_e("PSRAM could not be added to the heap!"); + return false; + } +#endif + spiramDetected = true; + log_d("PSRAM enabled"); + return true; +} + +bool IRAM_ATTR psramFound(){ + return spiramDetected; +} + +void IRAM_ATTR *ps_malloc(size_t size){ + if(!spiramDetected){ + return NULL; + } + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +void IRAM_ATTR *ps_calloc(size_t n, size_t size){ + if(!spiramDetected){ + return NULL; + } + return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +void IRAM_ATTR *ps_realloc(void *ptr, size_t size){ + if(!spiramDetected){ + return NULL; + } + return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +#else + +bool psramInit(){ + return false; +} + +bool IRAM_ATTR psramFound(){ + return false; +} + +void IRAM_ATTR *ps_malloc(size_t size){ + return NULL; +} + +void IRAM_ATTR *ps_calloc(size_t n, size_t size){ + return NULL; +} + +void IRAM_ATTR *ps_realloc(void *ptr, size_t size){ + return NULL; +} + +#endif diff --git a/components/FastLED-idf/hal/esp32-hal-psram.h b/components/FastLED-idf/hal/esp32-hal-psram.h new file mode 100644 index 0000000..36acc0a --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-psram.h @@ -0,0 +1,33 @@ +// 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_PSRAM_H_ +#define _ESP32_HAL_PSRAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +bool psramInit(); +bool psramFound(); + +void *ps_malloc(size_t size); +void *ps_calloc(size_t n, size_t size); +void *ps_realloc(void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_PSRAM_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-rmt.c b/components/FastLED-idf/hal/esp32-hal-rmt.c new file mode 100644 index 0000000..2a36a5e --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-rmt.c @@ -0,0 +1,836 @@ +// Copyright 2018 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 "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/semphr.h" + +#include "esp32-hal.h" +#include "esp8266-compat.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_reg.h" + +#include "esp32-hal-rmt.h" +#include "driver/periph_ctrl.h" + +#include "soc/rmt_struct.h" +#include "esp_intr_alloc.h" + +/** + * Internal macros + */ +#define MAX_CHANNELS 8 +#define MAX_DATA_PER_CHANNEL 64 +#define MAX_DATA_PER_ITTERATION 62 +#define _ABS(a) (a>0?a:-a) +#define _LIMIT(a,b) (a>b?b:a) +#define __INT_TX_END (1) +#define __INT_RX_END (2) +#define __INT_ERROR (4) +#define __INT_THR_EVNT (1<<24) + +#define _INT_TX_END(channel) (__INT_TX_END<<(channel*3)) +#define _INT_RX_END(channel) (__INT_RX_END<<(channel*3)) +#define _INT_ERROR(channel) (__INT_ERROR<<(channel*3)) +#define _INT_THR_EVNT(channel) ((__INT_THR_EVNT)<<(channel)) + +#if CONFIG_DISABLE_HAL_LOCKS +# define RMT_MUTEX_LOCK(channel) +# define RMT_MUTEX_UNLOCK(channel) +#else +# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS) +# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel]) +#endif /* CONFIG_DISABLE_HAL_LOCKS */ + +#define _RMT_INTERNAL_DEBUG +#ifdef _RMT_INTERNAL_DEBUG +# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1); +# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0); +#else +# define DEBUG_INTERRUPT_START(pin) +# define DEBUG_INTERRUPT_END(pin) +#endif /* _RMT_INTERNAL_DEBUG */ + +/** + * Typedefs for internal stuctures, enums + */ +typedef enum { + E_NO_INTR = 0, + E_TX_INTR = 1, + E_TXTHR_INTR = 2, + E_RX_INTR = 4, +} intr_mode_t; + +typedef enum { + E_INACTIVE = 0, + E_FIRST_HALF = 1, + E_LAST_DATA = 2, + E_END_TRANS = 4, + E_SET_CONTI = 8, +} transaction_state_t; + +struct rmt_obj_s +{ + bool allocated; + EventGroupHandle_t events; + int pin; + int channel; + bool tx_not_rx; + int buffers; + int data_size; + uint32_t* data_ptr; + intr_mode_t intr_mode; + transaction_state_t tx_state; + rmt_rx_data_cb_t cb; + bool data_alloc; +}; + +/** + * Internal variables for channel descriptors + */ +static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = { + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, +}; + +/** + * Internal variables for driver data + */ +static intr_handle_t intr_handle; + +static bool periph_enabled = false; + +static xSemaphoreHandle g_rmt_block_lock = NULL; + +/** + * Internal method (private) declarations + */ +static void _initPin(int pin, int channel, bool tx_not_rx); + +static bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size, bool continuous); + +static void IRAM_ATTR _rmt_isr(void* arg); + +static rmt_obj_t* _rmtAllocate(int pin, int from, int size); + +static void _initPin(int pin, int channel, bool tx_not_rx); + +static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel); + +static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch); + +static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch); + + +/** + * Public method definitions + */ +bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high) +{ + if (!rmt || low > 0xFFFF || high > 0xFFFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + + RMT.carrier_duty_ch[channel].low = low; + RMT.carrier_duty_ch[channel].high = high; + RMT.conf_ch[channel].conf0.carrier_en = carrier_en; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + + RMT_MUTEX_UNLOCK(channel); + + return true; + +} + +bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level) +{ + if (!rmt || filter_level > 0xFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + + RMT.conf_ch[channel].conf1.rx_filter_thres = filter_level; + RMT.conf_ch[channel].conf1.rx_filter_en = filter_en; + + RMT_MUTEX_UNLOCK(channel); + + return true; + +} + +bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value) +{ + if (!rmt || value > 0xFFFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + RMT.conf_ch[channel].conf0.idle_thres = value; + RMT_MUTEX_UNLOCK(channel); + + return true; +} + + +bool rmtDeinit(rmt_obj_t *rmt) +{ + if (!rmt) { + return false; + } + + // sanity check + if (rmt != &(g_rmt_objects[rmt->channel])) { + return false; + } + + size_t from = rmt->channel; + size_t to = rmt->buffers + rmt->channel; + size_t i; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(g_rmt_objlocks[from] != NULL) { + vSemaphoreDelete(g_rmt_objlocks[from]); + } +#endif + + if (g_rmt_objects[from].data_alloc) { + free(g_rmt_objects[from].data_ptr); + } + + for (i = from; i < to; i++) { + g_rmt_objects[i].allocated = false; + } + + g_rmt_objects[from].channel = 0; + g_rmt_objects[from].buffers = 0; + + return true; +} + +bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!rmt) { + return false; + } + + int channel = rmt->channel; + int allocated_size = MAX_DATA_PER_CHANNEL * rmt->buffers; + + if (size > allocated_size) { + return false; + } + return _rmtSendOnce(rmt, data, size, true); +} + +bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!rmt) { + return false; + } + + int channel = rmt->channel; + int allocated_size = MAX_DATA_PER_CHANNEL * rmt->buffers; + + if (size > allocated_size) { + + int half_tx_nr = MAX_DATA_PER_ITTERATION/2; + RMT_MUTEX_LOCK(channel); + // setup interrupt handler if not yet installed for half and full tx + if (!intr_handle) { + esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle); + } + + rmt->data_size = size - MAX_DATA_PER_ITTERATION; + rmt->data_ptr = ((uint32_t*)data) + MAX_DATA_PER_ITTERATION; + rmt->intr_mode = E_TX_INTR | E_TXTHR_INTR; + rmt->tx_state = E_SET_CONTI | E_FIRST_HALF; + + // init the tx limit for interruption + RMT.tx_lim_ch[channel].limit = half_tx_nr+2; + // reset memory pointer + RMT.conf_ch[channel].conf1.apb_mem_rst = 1; + RMT.conf_ch[channel].conf1.apb_mem_rst = 0; + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_rd_rst = 0; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 0; + + // set the tx end mark + RMTMEM.chan[channel].data32[MAX_DATA_PER_ITTERATION].val = 0; + + // clear and enable both Tx completed and half tx event + RMT.int_clr.val = _INT_TX_END(channel); + RMT.int_clr.val = _INT_THR_EVNT(channel); + RMT.int_clr.val = _INT_ERROR(channel); + + RMT.int_ena.val |= _INT_TX_END(channel); + RMT.int_ena.val |= _INT_THR_EVNT(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT_MUTEX_UNLOCK(channel); + + // start the transation + return _rmtSendOnce(rmt, data, MAX_DATA_PER_ITTERATION, false); + } else { + // use one-go mode if data fits one buffer + return _rmtSendOnce(rmt, data, size, false); + } +} + +bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) { + return false; + } + + size_t i; + volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val); + for (i=0; ichannel; + + RMT.int_clr.val = _INT_ERROR(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT.conf_ch[channel].conf1.mem_owner = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + RMT.conf_ch[channel].conf1.rx_en = 1; + + return true; +} + +bool rmtReceiveCompleted(rmt_obj_t* rmt) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + if (RMT.int_raw.val&_INT_RX_END(channel)) { + // RX end flag + RMT.int_clr.val = _INT_RX_END(channel); + return true; + } else { + return false; + } +} + +bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb) +{ + if (!rmt && !cb) { + return false; + } + int channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + rmt->intr_mode = E_RX_INTR; + rmt->tx_state = E_FIRST_HALF; + rmt->cb = cb; + // allocate internally two buffers which would alternate + if (!rmt->data_alloc) { + rmt->data_ptr = (uint32_t*)malloc(2*MAX_DATA_PER_CHANNEL*(rmt->buffers)*sizeof(uint32_t)); + rmt->data_size = MAX_DATA_PER_CHANNEL*rmt->buffers; + rmt->data_alloc = true; + } + + RMT.conf_ch[channel].conf1.mem_owner = 1; + + RMT.int_clr.val = _INT_RX_END(channel); + RMT.int_clr.val = _INT_ERROR(channel); + + RMT.int_ena.val |= _INT_RX_END(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + + RMT.conf_ch[channel].conf1.rx_en = 1; + RMT_MUTEX_UNLOCK(channel); + + return true; +} + +bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) { + return false; + } + + if (eventFlag) { + xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL); + rmt->events = eventFlag; + } + + if (data && size>0) { + rmt->data_ptr = (uint32_t*)data; + rmt->data_size = size; + } + + RMT_MUTEX_LOCK(channel); + rmt->intr_mode = E_RX_INTR; + + RMT.conf_ch[channel].conf1.mem_owner = 1; + + RMT.int_clr.val = _INT_RX_END(channel); + RMT.int_clr.val = _INT_ERROR(channel); + + RMT.int_ena.val |= _INT_RX_END(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + + RMT.conf_ch[channel].conf1.rx_en = 1; + RMT_MUTEX_UNLOCK(channel); + + // wait for data if requested so + if (waitForData && eventFlag) { + uint32_t flags = xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL, + pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout); + if (flags & RMT_FLAG_ERROR) { + return false; + } + } + + return true; +} + +float rmtSetTick(rmt_obj_t* rmt, float tick) +{ + if (!rmt) { + return false; + } + /* + divider field span from 1 (smallest), 2, 3, ... , 0xFF, 0x00 (highest) + * rmt tick from 1/80M -> 12.5ns (1x) div_cnt = 0x01 + 3.2 us (256x) div_cnt = 0x00 + * rmt tick for 1 MHz -> 1us (1x) div_cnt = 0x01 + 256us (256x) div_cnt = 0x00 + */ + int apb_div = _LIMIT(tick/12.5, 256); + int ref_div = _LIMIT(tick/1000, 256); + + float apb_tick = 12.5 * apb_div; + float ref_tick = 1000.0 * ref_div; + + size_t channel = rmt->channel; + + if (_ABS(apb_tick - tick) < _ABS(ref_tick - tick)) { + RMT.conf_ch[channel].conf0.div_cnt = apb_div & 0xFF; + RMT.conf_ch[channel].conf1.ref_always_on = 1; + return apb_tick; + } else { + RMT.conf_ch[channel].conf0.div_cnt = ref_div & 0xFF; + RMT.conf_ch[channel].conf1.ref_always_on = 0; + return ref_tick; + } +} + +rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize) +{ + int buffers = memsize; + rmt_obj_t* rmt; + size_t i; + size_t j; + + // create common block mutex for protecting allocs from multiple threads + if (!g_rmt_block_lock) { + g_rmt_block_lock = xSemaphoreCreateMutex(); + } + // lock + while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} + + for (i=0; i MAX_CHANNELS || j != buffers) { + xSemaphoreGive(g_rmt_block_lock); + return NULL; + } + rmt = _rmtAllocate(pin, i, buffers); + + xSemaphoreGive(g_rmt_block_lock); + + size_t channel = i; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(g_rmt_objlocks[channel] == NULL) { + g_rmt_objlocks[channel] = xSemaphoreCreateMutex(); + if(g_rmt_objlocks[channel] == NULL) { + return NULL; + } + } +#endif + + RMT_MUTEX_LOCK(channel); + + rmt->pin = pin; + rmt->tx_not_rx = tx_not_rx; + rmt->buffers =buffers; + rmt->channel = channel; + _initPin(pin, channel, tx_not_rx); + + // Initialize the registers in default mode: + // - no carrier, filter + // - timebase tick of 1us + // - idle threshold set to 0x8000 (max pulse width + 1) + RMT.conf_ch[channel].conf0.div_cnt = 1; + RMT.conf_ch[channel].conf0.mem_size = buffers; + RMT.conf_ch[channel].conf0.carrier_en = 0; + RMT.conf_ch[channel].conf0.carrier_out_lv = 0; + RMT.conf_ch[channel].conf0.mem_pd = 0; + + RMT.conf_ch[channel].conf0.idle_thres = 0x80; + RMT.conf_ch[channel].conf1.rx_en = 0; + RMT.conf_ch[channel].conf1.tx_conti_mode = 0; + RMT.conf_ch[channel].conf1.ref_cnt_rst = 0; + RMT.conf_ch[channel].conf1.rx_filter_en = 0; + RMT.conf_ch[channel].conf1.rx_filter_thres = 0; + RMT.conf_ch[channel].conf1.idle_out_lv = 0; // signal level for idle + RMT.conf_ch[channel].conf1.idle_out_en = 1; // enable idle + RMT.conf_ch[channel].conf1.ref_always_on = 0; // base clock + RMT.apb_conf.fifo_mask = 1; + + if (tx_not_rx) { + // RMT.conf_ch[channel].conf1.rx_en = 0; + RMT.conf_ch[channel].conf1.mem_owner = 0; + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + } else { + // RMT.conf_ch[channel].conf1.rx_en = 1; + RMT.conf_ch[channel].conf1.mem_owner = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + } + + // install interrupt if at least one channel is active + if (!intr_handle) { + esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle); + } + RMT_MUTEX_UNLOCK(channel); + + return rmt; +} + +/** + * Private methods definitions + */ +bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size, bool continuous) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + RMT.apb_conf.fifo_mask = 1; + if (data && size>0) { + size_t i; + volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val); + for (i = 0; i < size; i++) { + *rmt_mem_ptr++ = data[i].val; + } + // tx end mark + RMTMEM.chan[channel].data32[size].val = 0; + } + + RMT_MUTEX_LOCK(channel); + RMT.conf_ch[channel].conf1.tx_conti_mode = continuous; + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.tx_start = 1; + RMT_MUTEX_UNLOCK(channel); + + return true; +} + + +static rmt_obj_t* _rmtAllocate(int pin, int from, int size) +{ + size_t i; + // setup how many buffers shall we use + g_rmt_objects[from].buffers = size; + + for (i=0; i 0) { + size_t i; + uint32_t * data = g_rmt_objects[ch].data_ptr; + // in case of callback, provide switching between memories + if (g_rmt_objects[ch].cb) { + if (g_rmt_objects[ch].tx_state & E_FIRST_HALF) { + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + } else { + g_rmt_objects[ch].tx_state |= E_FIRST_HALF; + data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers); + } + } + uint32_t *data_received = data; + for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) { + *data++ = RMTMEM.chan[ch].data32[i].val; + } + if (g_rmt_objects[ch].cb) { + // actually received data ptr + (g_rmt_objects[ch].cb)(data_received, _rmt_get_mem_len(ch)); + + // restart the reception + RMT.conf_ch[ch].conf1.mem_owner = 1; + RMT.conf_ch[ch].conf1.mem_wr_rst = 1; + RMT.conf_ch[ch].conf1.rx_en = 1; + RMT.int_ena.val |= _INT_RX_END(ch); + } else { + // if not callback provide, expect only one Rx + g_rmt_objects[ch].intr_mode &= ~E_RX_INTR; + } + } + } else { + // Report error and disable Rx interrupt + log_e("Unexpected Rx interrupt!\n"); // TODO: eplace messages with log_X + RMT.int_ena.val &= ~_INT_RX_END(ch); + } + + + } + + if (intr_val & _INT_ERROR(ch)) { + // clear the flag + RMT.int_clr.val = _INT_ERROR(ch); + RMT.int_ena.val &= ~_INT_ERROR(ch); + // report error + log_e("RMT Error %d!\n", ch); + if (g_rmt_objects[ch].events) { + xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_ERROR); + } + // reset memory + RMT.conf_ch[ch].conf1.mem_rd_rst = 1; + RMT.conf_ch[ch].conf1.mem_rd_rst = 0; + RMT.conf_ch[ch].conf1.mem_wr_rst = 1; + RMT.conf_ch[ch].conf1.mem_wr_rst = 0; + } + + if (intr_val & _INT_TX_END(ch)) { + + RMT.int_clr.val = _INT_TX_END(ch); + _rmt_tx_mem_second(ch); + } + + if (intr_val & _INT_THR_EVNT(ch)) { + // clear the flag + RMT.int_clr.val = _INT_THR_EVNT(ch); + + // initial setup of continuous mode + if (g_rmt_objects[ch].tx_state & E_SET_CONTI) { + RMT.conf_ch[ch].conf1.tx_conti_mode = 1; + g_rmt_objects[ch].intr_mode &= ~E_SET_CONTI; + } + _rmt_tx_mem_first(ch); + } + } +} + +static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch) +{ + DEBUG_INTERRUPT_START(4) + uint32_t* data = g_rmt_objects[ch].data_ptr; + int half_tx_nr = MAX_DATA_PER_ITTERATION/2; + int i; + + RMT.tx_lim_ch[ch].limit = half_tx_nr+2; + RMT.int_clr.val = _INT_THR_EVNT(ch); + RMT.int_ena.val |= _INT_THR_EVNT(ch); + + g_rmt_objects[ch].tx_state |= E_FIRST_HALF; + + if (data) { + int remaining_size = g_rmt_objects[ch].data_size; + // will the remaining data occupy the entire halfbuffer + if (remaining_size > half_tx_nr) { + for (i = 0; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i]; + } + g_rmt_objects[ch].data_size -= half_tx_nr; + g_rmt_objects[ch].data_ptr += half_tx_nr; + } else { + for (i = 0; i < half_tx_nr; i++) { + if (i < remaining_size) { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i]; + } else { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F; + } + } + g_rmt_objects[ch].data_ptr = NULL; + + } + } else if ((!(g_rmt_objects[ch].tx_state & E_LAST_DATA)) && + (!(g_rmt_objects[ch].tx_state & E_END_TRANS))) { + for (i = 0; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F; + } + RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0; + g_rmt_objects[ch].tx_state |= E_LAST_DATA; + RMT.conf_ch[ch].conf1.tx_conti_mode = 0; + } else { + log_d("RMT Tx finished %d!\n", ch); + RMT.conf_ch[ch].conf1.tx_conti_mode = 0; + RMT.int_ena.val &= ~_INT_TX_END(ch); + RMT.int_ena.val &= ~_INT_THR_EVNT(ch); + g_rmt_objects[ch].intr_mode = E_NO_INTR; + g_rmt_objects[ch].tx_state = E_INACTIVE; + } + DEBUG_INTERRUPT_END(4); +} + +static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch) +{ + DEBUG_INTERRUPT_START(2); + uint32_t* data = g_rmt_objects[ch].data_ptr; + int half_tx_nr = MAX_DATA_PER_ITTERATION/2; + int i; + RMT.int_ena.val &= ~_INT_THR_EVNT(ch); + RMT.tx_lim_ch[ch].limit = 0; + + if (data) { + int remaining_size = g_rmt_objects[ch].data_size; + + // will the remaining data occupy the entire halfbuffer + if (remaining_size > half_tx_nr) { + RMTMEM.chan[ch].data32[0].val = data[0] - 1; + for (i = 1; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[i].val = data[i]; + } + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + // turn off the treshold interrupt + RMT.int_ena.val &= ~_INT_THR_EVNT(ch); + RMT.tx_lim_ch[ch].limit = 0; + g_rmt_objects[ch].data_size -= half_tx_nr; + g_rmt_objects[ch].data_ptr += half_tx_nr; + } else { + RMTMEM.chan[ch].data32[0].val = data[0] - 1; + for (i = 1; i < half_tx_nr; i++) { + if (i < remaining_size) { + RMTMEM.chan[ch].data32[i].val = data[i]; + } else { + RMTMEM.chan[ch].data32[i].val = 0x000F000F; + } + } + + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + g_rmt_objects[ch].data_ptr = NULL; + } + } else { + for (i = 0; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[i].val = 0x000F000F; + } + RMTMEM.chan[ch].data32[i].val = 0; + + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + RMT.tx_lim_ch[ch].limit = 0; + g_rmt_objects[ch].tx_state |= E_LAST_DATA; + RMT.conf_ch[ch].conf1.tx_conti_mode = 0; + } + DEBUG_INTERRUPT_END(2); +} + +static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel) +{ + int block_num = RMT.conf_ch[channel].conf0.mem_size; + int item_block_len = block_num * 64; + volatile rmt_item32_t* data = RMTMEM.chan[channel].data32; + int idx; + for(idx = 0; idx < item_block_len; idx++) { + if(data[idx].duration0 == 0) { + return idx; + } else if(data[idx].duration1 == 0) { + return idx + 1; + } + } + return idx; +} diff --git a/components/FastLED-idf/hal/esp32-hal-rmt.h b/components/FastLED-idf/hal/esp32-hal-rmt.h new file mode 100644 index 0000000..ff6da39 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-rmt.h @@ -0,0 +1,147 @@ +// Copyright 2018 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 MAIN_ESP32_HAL_RMT_H_ +#define MAIN_ESP32_HAL_RMT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// notification flags +#define RMT_FLAG_TX_DONE (1) +#define RMT_FLAG_RX_DONE (2) +#define RMT_FLAG_ERROR (4) +#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR) + +struct rmt_obj_s; + +typedef enum { + RMT_MEM_64 = 1, + RMT_MEM_128 = 2, + RMT_MEM_192 = 3, + RMT_MEM_256 = 4, + RMT_MEM_320 = 5, + RMT_MEM_384 = 6, + RMT_MEM_448 = 7, + RMT_MEM_512 = 8, +} rmt_reserve_memsize_t; + +typedef struct rmt_obj_s rmt_obj_t; + +typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len); + +typedef struct { + union { + struct { + uint32_t duration0 :15; + uint32_t level0 :1; + uint32_t duration1 :15; + uint32_t level1 :1; + }; + uint32_t val; + }; +} rmt_data_t; + +/** +* Initialize the object +* +*/ +rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize); + +/** +* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds +* return the real actual tick value in ns +*/ +float rmtSetTick(rmt_obj_t* rmt, float tick); + +/** +* Sending data in one-go mode or continual mode +* (more data being send while updating buffers in interrupts) +* +*/ +bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +/** +* Loop data up to the reserved memsize continuously +* +*/ +bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +/** +* Initiates async receive, event flag indicates data received +* +*/ +bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout); + +/** +* Initiates async receive with automatic buffering +* and callback with data from ISR +* +*/ +bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb); + + +/* Additional interface */ + +/** +* Start reception +* +*/ +bool rmtBeginReceive(rmt_obj_t* rmt); + +/** +* Checks if reception completes +* +*/ +bool rmtReceiveCompleted(rmt_obj_t* rmt); + +/** +* Reads the data for particular channel +* +*/ +bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size); + +/** + * Setting threshold for Rx completed + */ +bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value); + +/** + * Setting carrier + */ +bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high); + +/** + * Setting input filter + */ +bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level); + +/** + * Deinitialize the driver + */ +bool rmtDeinit(rmt_obj_t *rmt); + +// TODO: +// * uninstall interrupt when all channels are deinit +// * send only-conti mode with circular-buffer +// * put sanity checks to some macro or inlines +// * doxy comments +// * error reporting + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_RMT_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-sigmadelta.c b/components/FastLED-idf/hal/esp32-hal-sigmadelta.c new file mode 100644 index 0000000..098181c --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-sigmadelta.c @@ -0,0 +1,115 @@ +// 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 "esp32-hal.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "rom/ets_sys.h" +#include "esp32-hal-matrix.h" +#include "soc/gpio_sd_reg.h" +#include "soc/gpio_sd_struct.h" + + +#if CONFIG_DISABLE_HAL_LOCKS +#define SD_MUTEX_LOCK() +#define SD_MUTEX_UNLOCK() +#else +#define SD_MUTEX_LOCK() do {} while (xSemaphoreTake(_sd_sys_lock, portMAX_DELAY) != pdPASS) +#define SD_MUTEX_UNLOCK() xSemaphoreGive(_sd_sys_lock) +xSemaphoreHandle _sd_sys_lock; +#endif + +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + if(old_apb == new_apb){ + return; + } + uint32_t iarg = (uint32_t)arg; + uint8_t channel = iarg; + if(ev_type == APB_BEFORE_CHANGE){ + SIGMADELTA.cg.clk_en = 0; + } else { + old_apb /= 1000000; + new_apb /= 1000000; + SD_MUTEX_LOCK(); + uint32_t old_prescale = SIGMADELTA.channel[channel].prescale + 1; + SIGMADELTA.channel[channel].prescale = ((new_apb * old_prescale) / old_apb) - 1; + SIGMADELTA.cg.clk_en = 0; + SIGMADELTA.cg.clk_en = 1; + SD_MUTEX_UNLOCK(); + } +} + +uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500 +{ + if(channel > 7) { + return 0; + } +#if !CONFIG_DISABLE_HAL_LOCKS + static bool tHasStarted = false; + if(!tHasStarted) { + tHasStarted = true; + _sd_sys_lock = xSemaphoreCreateMutex(); + } +#endif + uint32_t apb_freq = getApbFrequency(); + uint32_t prescale = (apb_freq/(freq*256)) - 1; + if(prescale > 0xFF) { + prescale = 0xFF; + } + SD_MUTEX_LOCK(); + SIGMADELTA.channel[channel].prescale = prescale; + SIGMADELTA.cg.clk_en = 0; + SIGMADELTA.cg.clk_en = 1; + SD_MUTEX_UNLOCK(); + uint32_t iarg = channel; + addApbChangeCallback((void*)iarg, _on_apb_change); + return apb_freq/((prescale + 1) * 256); +} + +void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-7 duty 8 bit +{ + if(channel > 7) { + return; + } + duty -= 128; + SD_MUTEX_LOCK(); + SIGMADELTA.channel[channel].duty = duty; + SD_MUTEX_UNLOCK(); +} + +uint8_t sigmaDeltaRead(uint8_t channel) //chan 0-7 +{ + if(channel > 7) { + return 0; + } + SD_MUTEX_LOCK(); + uint8_t duty = SIGMADELTA.channel[channel].duty + 128; + SD_MUTEX_UNLOCK(); + return duty; +} + +void sigmaDeltaAttachPin(uint8_t pin, uint8_t channel) //channel 0-7 +{ + if(channel > 7) { + return; + } + pinMode(pin, OUTPUT); + pinMatrixOutAttach(pin, GPIO_SD0_OUT_IDX + channel, false, false); +} + +void sigmaDeltaDetachPin(uint8_t pin) +{ + pinMatrixOutDetach(pin, false, false); +} diff --git a/components/FastLED-idf/hal/esp32-hal-sigmadelta.h b/components/FastLED-idf/hal/esp32-hal-sigmadelta.h new file mode 100644 index 0000000..95bcb17 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-sigmadelta.h @@ -0,0 +1,37 @@ +// 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_SD_H_ +#define _ESP32_HAL_SD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +//channel 0-7 freq 1220-312500 duty 0-255 +uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq); +void sigmaDeltaWrite(uint8_t channel, uint8_t duty); +uint8_t sigmaDeltaRead(uint8_t channel); +void sigmaDeltaAttachPin(uint8_t pin, uint8_t channel); +void sigmaDeltaDetachPin(uint8_t pin); + + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_SD_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-spi.c b/components/FastLED-idf/hal/esp32-hal-spi.c new file mode 100644 index 0000000..b3b703c --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-spi.c @@ -0,0 +1,1095 @@ +// 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 "esp32-hal-spi.h" +#include "esp32-hal.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "rom/gpio.h" +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/dport_reg.h" +#include "soc/rtc.h" + +#define SPI_CLK_IDX(p) ((p==0)?SPICLK_OUT_IDX:((p==1)?SPICLK_OUT_IDX:((p==2)?HSPICLK_OUT_IDX:((p==3)?VSPICLK_OUT_IDX:0)))) +#define SPI_MISO_IDX(p) ((p==0)?SPIQ_OUT_IDX:((p==1)?SPIQ_OUT_IDX:((p==2)?HSPIQ_OUT_IDX:((p==3)?VSPIQ_OUT_IDX:0)))) +#define SPI_MOSI_IDX(p) ((p==0)?SPID_IN_IDX:((p==1)?SPID_IN_IDX:((p==2)?HSPID_IN_IDX:((p==3)?VSPID_IN_IDX:0)))) + +#define SPI_SPI_SS_IDX(n) ((n==0)?SPICS0_OUT_IDX:((n==1)?SPICS1_OUT_IDX:((n==2)?SPICS2_OUT_IDX:SPICS0_OUT_IDX))) +#define SPI_HSPI_SS_IDX(n) ((n==0)?HSPICS0_OUT_IDX:((n==1)?HSPICS1_OUT_IDX:((n==2)?HSPICS2_OUT_IDX:HSPICS0_OUT_IDX))) +#define SPI_VSPI_SS_IDX(n) ((n==0)?VSPICS0_OUT_IDX:((n==1)?VSPICS1_OUT_IDX:((n==2)?VSPICS2_OUT_IDX:VSPICS0_OUT_IDX))) +#define SPI_SS_IDX(p, n) ((p==0)?SPI_SPI_SS_IDX(n):((p==1)?SPI_SPI_SS_IDX(n):((p==2)?SPI_HSPI_SS_IDX(n):((p==3)?SPI_VSPI_SS_IDX(n):0)))) + +#define SPI_INUM(u) (2) +#define SPI_INTR_SOURCE(u) ((u==0)?ETS_SPI0_INTR_SOURCE:((u==1)?ETS_SPI1_INTR_SOURCE:((u==2)?ETS_SPI2_INTR_SOURCE:((p==3)?ETS_SPI3_INTR_SOURCE:0)))) + +struct spi_struct_t { + spi_dev_t * dev; +#if !CONFIG_DISABLE_HAL_LOCKS + xSemaphoreHandle lock; +#endif + uint8_t num; +}; + +#if CONFIG_DISABLE_HAL_LOCKS +#define SPI_MUTEX_LOCK() +#define SPI_MUTEX_UNLOCK() + +static spi_t _spi_bus_array[4] = { + {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), 0}, + {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), 1}, + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 2}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 3} +}; +#else +#define SPI_MUTEX_LOCK() do {} while (xSemaphoreTake(spi->lock, portMAX_DELAY) != pdPASS) +#define SPI_MUTEX_UNLOCK() xSemaphoreGive(spi->lock) + +static spi_t _spi_bus_array[4] = { + {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), NULL, 0}, + {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), NULL, 1}, + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 2}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 3} +}; +#endif + +void spiAttachSCK(spi_t * spi, int8_t sck) +{ + if(!spi) { + return; + } + if(sck < 0) { + if(spi->num == HSPI) { + sck = 14; + } else if(spi->num == VSPI) { + sck = 18; + } else { + sck = 6; + } + } + pinMode(sck, OUTPUT); + pinMatrixOutAttach(sck, SPI_CLK_IDX(spi->num), false, false); +} + +void spiAttachMISO(spi_t * spi, int8_t miso) +{ + if(!spi) { + return; + } + if(miso < 0) { + if(spi->num == HSPI) { + miso = 12; + } else if(spi->num == VSPI) { + miso = 19; + } else { + miso = 7; + } + } + SPI_MUTEX_LOCK(); + pinMode(miso, INPUT); + pinMatrixInAttach(miso, SPI_MISO_IDX(spi->num), false); + SPI_MUTEX_UNLOCK(); +} + +void spiAttachMOSI(spi_t * spi, int8_t mosi) +{ + if(!spi) { + return; + } + if(mosi < 0) { + if(spi->num == HSPI) { + mosi = 13; + } else if(spi->num == VSPI) { + mosi = 23; + } else { + mosi = 8; + } + } + pinMode(mosi, OUTPUT); + pinMatrixOutAttach(mosi, SPI_MOSI_IDX(spi->num), false, false); +} + +void spiDetachSCK(spi_t * spi, int8_t sck) +{ + if(!spi) { + return; + } + if(sck < 0) { + if(spi->num == HSPI) { + sck = 14; + } else if(spi->num == VSPI) { + sck = 18; + } else { + sck = 6; + } + } + pinMatrixOutDetach(sck, false, false); + pinMode(sck, INPUT); +} + +void spiDetachMISO(spi_t * spi, int8_t miso) +{ + if(!spi) { + return; + } + if(miso < 0) { + if(spi->num == HSPI) { + miso = 12; + } else if(spi->num == VSPI) { + miso = 19; + } else { + miso = 7; + } + } + pinMatrixInDetach(SPI_MISO_IDX(spi->num), false, false); + pinMode(miso, INPUT); +} + +void spiDetachMOSI(spi_t * spi, int8_t mosi) +{ + if(!spi) { + return; + } + if(mosi < 0) { + if(spi->num == HSPI) { + mosi = 13; + } else if(spi->num == VSPI) { + mosi = 23; + } else { + mosi = 8; + } + } + pinMatrixOutDetach(mosi, false, false); + pinMode(mosi, INPUT); +} + +void spiAttachSS(spi_t * spi, uint8_t cs_num, int8_t ss) +{ + if(!spi) { + return; + } + if(cs_num > 2) { + return; + } + if(ss < 0) { + cs_num = 0; + if(spi->num == HSPI) { + ss = 15; + } else if(spi->num == VSPI) { + ss = 5; + } else { + ss = 11; + } + } + pinMode(ss, OUTPUT); + pinMatrixOutAttach(ss, SPI_SS_IDX(spi->num, cs_num), false, false); + spiEnableSSPins(spi, (1 << cs_num)); +} + +void spiDetachSS(spi_t * spi, int8_t ss) +{ + if(!spi) { + return; + } + if(ss < 0) { + if(spi->num == HSPI) { + ss = 15; + } else if(spi->num == VSPI) { + ss = 5; + } else { + ss = 11; + } + } + pinMatrixOutDetach(ss, false, false); + pinMode(ss, INPUT); +} + +void spiEnableSSPins(spi_t * spi, uint8_t cs_mask) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->pin.val &= ~(cs_mask & SPI_CS_MASK_ALL); + SPI_MUTEX_UNLOCK(); +} + +void spiDisableSSPins(spi_t * spi, uint8_t cs_mask) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->pin.val |= (cs_mask & SPI_CS_MASK_ALL); + SPI_MUTEX_UNLOCK(); +} + +void spiSSEnable(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->user.cs_setup = 1; + spi->dev->user.cs_hold = 1; + SPI_MUTEX_UNLOCK(); +} + +void spiSSDisable(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->user.cs_setup = 0; + spi->dev->user.cs_hold = 0; + SPI_MUTEX_UNLOCK(); +} + +void spiSSSet(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->pin.cs_keep_active = 1; + SPI_MUTEX_UNLOCK(); +} + +void spiSSClear(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->pin.cs_keep_active = 0; + SPI_MUTEX_UNLOCK(); +} + +uint32_t spiGetClockDiv(spi_t * spi) +{ + if(!spi) { + return 0; + } + return spi->dev->clock.val; +} + +void spiSetClockDiv(spi_t * spi, uint32_t clockDiv) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->clock.val = clockDiv; + SPI_MUTEX_UNLOCK(); +} + +uint8_t spiGetDataMode(spi_t * spi) +{ + if(!spi) { + return 0; + } + bool idleEdge = spi->dev->pin.ck_idle_edge; + bool outEdge = spi->dev->user.ck_out_edge; + if(idleEdge) { + if(outEdge) { + return SPI_MODE2; + } + return SPI_MODE3; + } + if(outEdge) { + return SPI_MODE1; + } + return SPI_MODE0; +} + +void spiSetDataMode(spi_t * spi, uint8_t dataMode) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + switch (dataMode) { + case SPI_MODE1: + spi->dev->pin.ck_idle_edge = 0; + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE2: + spi->dev->pin.ck_idle_edge = 1; + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE3: + spi->dev->pin.ck_idle_edge = 1; + spi->dev->user.ck_out_edge = 0; + break; + case SPI_MODE0: + default: + spi->dev->pin.ck_idle_edge = 0; + spi->dev->user.ck_out_edge = 0; + break; + } + SPI_MUTEX_UNLOCK(); +} + +uint8_t spiGetBitOrder(spi_t * spi) +{ + if(!spi) { + return 0; + } + return (spi->dev->ctrl.wr_bit_order | spi->dev->ctrl.rd_bit_order) == 0; +} + +void spiSetBitOrder(spi_t * spi, uint8_t bitOrder) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + if (SPI_MSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 0; + spi->dev->ctrl.rd_bit_order = 0; + } else if (SPI_LSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 1; + spi->dev->ctrl.rd_bit_order = 1; + } + 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(); + } +} + +static void spiInitBus(spi_t * spi) +{ + spi->dev->slave.trans_done = 0; + spi->dev->slave.slave_mode = 0; + spi->dev->pin.val = 0; + spi->dev->user.val = 0; + spi->dev->user1.val = 0; + spi->dev->ctrl.val = 0; + spi->dev->ctrl1.val = 0; + spi->dev->ctrl2.val = 0; + spi->dev->clock.val = 0; +} + +void spiStopBus(spi_t * spi) +{ + if(!spi) { + return; + } + + removeApbChangeCallback(spi, _on_apb_change); + + SPI_MUTEX_LOCK(); + spiInitBus(spi); + SPI_MUTEX_UNLOCK(); +} + +spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) +{ + if(spi_num > 3){ + return NULL; + } + + spi_t * spi = &_spi_bus_array[spi_num]; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(spi->lock == NULL){ + spi->lock = xSemaphoreCreateMutex(); + if(spi->lock == NULL) { + return NULL; + } + } +#endif + + if(spi_num == HSPI) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST); + } else if(spi_num == VSPI) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2); + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1); + } + + SPI_MUTEX_LOCK(); + spiInitBus(spi); + spi->dev->user.usr_mosi = 1; + spi->dev->user.usr_miso = 1; + spi->dev->user.doutdin = 1; + + int i; + for(i=0; i<16; i++) { + spi->dev->data_buf[i] = 0x00000000; + } + SPI_MUTEX_UNLOCK(); + + spiSetDataMode(spi, dataMode); + spiSetBitOrder(spi, bitOrder); + spiSetClockDiv(spi, clockDiv); + + addApbChangeCallback(spi, _on_apb_change); + return spi; +} + +void spiWaitReady(spi_t * spi) +{ + if(!spi) { + return; + } + while(spi->dev->cmd.usr); +} + +void spiWrite(spi_t * spi, const uint32_t *data, uint8_t len) +{ + if(!spi) { + return; + } + int i; + if(len > 16) { + len = 16; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + for(i=0; idev->data_buf[i] = data[i]; + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +void spiTransfer(spi_t * spi, uint32_t *data, uint8_t len) +{ + if(!spi) { + return; + } + int i; + if(len > 16) { + len = 16; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1; + spi->dev->miso_dlen.usr_miso_dbitlen = (len * 32) - 1; + for(i=0; idev->data_buf[i] = data[i]; + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + for(i=0; idev->data_buf[i]; + } + SPI_MUTEX_UNLOCK(); +} + +void spiWriteByte(spi_t * spi, uint8_t data) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +uint8_t spiTransferByte(spi_t * spi, uint8_t data) +{ + if(!spi) { + return 0; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 7; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFF; + SPI_MUTEX_UNLOCK(); + return data; +} + +static uint32_t __spiTranslate32(uint32_t data) +{ + union { + uint32_t l; + uint8_t b[4]; + } out; + out.l = data; + return out.b[3] | (out.b[2] << 8) | (out.b[1] << 16) | (out.b[0] << 24); +} + +void spiWriteWord(spi_t * spi, uint16_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = (data >> 8) | (data << 8); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +uint16_t spiTransferWord(spi_t * spi, uint16_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = (data >> 8) | (data << 8); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 15; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + SPI_MUTEX_UNLOCK(); + if(!spi->dev->ctrl.rd_bit_order){ + data = (data >> 8) | (data << 8); + } + return data; +} + +void spiWriteLong(spi_t * spi, uint32_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = __spiTranslate32(data); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +uint32_t spiTransferLong(spi_t * spi, uint32_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = __spiTranslate32(data); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 31; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + SPI_MUTEX_UNLOCK(); + if(!spi->dev->ctrl.rd_bit_order){ + data = __spiTranslate32(data); + } + return data; +} + +static void __spiTransferBytes(spi_t * spi, const uint8_t * data, uint8_t * out, uint32_t bytes) +{ + if(!spi) { + return; + } + int i; + + if(bytes > 64) { + bytes = 64; + } + + uint32_t words = (bytes + 3) / 4;//16 max + + uint32_t wordsBuf[16] = {0,}; + uint8_t * bytesBuf = (uint8_t *) wordsBuf; + + if(data) { + memcpy(bytesBuf, data, bytes);//copy data to buffer + } else { + memset(bytesBuf, 0xFF, bytes); + } + + spi->dev->mosi_dlen.usr_mosi_dbitlen = ((bytes * 8) - 1); + spi->dev->miso_dlen.usr_miso_dbitlen = ((bytes * 8) - 1); + + for(i=0; idev->data_buf[i] = wordsBuf[i]; //copy buffer to spi fifo + } + + spi->dev->cmd.usr = 1; + + while(spi->dev->cmd.usr); + + if(out) { + for(i=0; idev->data_buf[i];//copy spi fifo to buffer + } + memcpy(out, bytesBuf, bytes);//copy buffer to output + } +} + +void spiTransferBytes(spi_t * spi, const uint8_t * data, uint8_t * out, uint32_t size) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + while(size) { + if(size > 64) { + __spiTransferBytes(spi, data, out, 64); + size -= 64; + if(data) { + data += 64; + } + if(out) { + out += 64; + } + } else { + __spiTransferBytes(spi, data, out, size); + size = 0; + } + } + SPI_MUTEX_UNLOCK(); +} + +void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spiTransferBitsNL(spi, data, out, bits); + SPI_MUTEX_UNLOCK(); +} + +/* + * Manual Lock Management + * */ + +#define MSB_32_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[3] | (d[2] << 8) | (d[1] << 16) | (d[0] << 24); } +#define MSB_24_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[2] | (d[1] << 8) | (d[0] << 16); } +#define MSB_16_SET(var, val) { (var) = (((val) & 0xFF00) >> 8) | (((val) & 0xFF) << 8); } +#define MSB_PIX_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[1] | (d[0] << 8) | (d[3] << 16) | (d[2] << 24); } + +void spiTransaction(spi_t * spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->clock.val = clockDiv; + switch (dataMode) { + case SPI_MODE1: + spi->dev->pin.ck_idle_edge = 0; + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE2: + spi->dev->pin.ck_idle_edge = 1; + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE3: + spi->dev->pin.ck_idle_edge = 1; + spi->dev->user.ck_out_edge = 0; + break; + case SPI_MODE0: + default: + spi->dev->pin.ck_idle_edge = 0; + spi->dev->user.ck_out_edge = 0; + break; + } + if (SPI_MSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 0; + spi->dev->ctrl.rd_bit_order = 0; + } else if (SPI_LSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 1; + spi->dev->ctrl.rd_bit_order = 1; + } +} + +void spiSimpleTransaction(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +} + +void spiEndTransaction(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_UNLOCK(); +} + +void IRAM_ATTR spiWriteByteNL(spi_t * spi, uint8_t data) +{ + if(!spi) { + return; + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint8_t spiTransferByteNL(spi_t * spi, uint8_t data) +{ + if(!spi) { + return 0; + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 7; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFF; + return data; +} + +void IRAM_ATTR spiWriteShortNL(spi_t * spi, uint16_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_16_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint16_t spiTransferShortNL(spi_t * spi, uint16_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_16_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 15; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFFFF; + if(!spi->dev->ctrl.rd_bit_order){ + MSB_16_SET(data, data); + } + return data; +} + +void IRAM_ATTR spiWriteLongNL(spi_t * spi, uint32_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_32_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint32_t spiTransferLongNL(spi_t * spi, uint32_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_32_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 31; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + if(!spi->dev->ctrl.rd_bit_order){ + MSB_32_SET(data, data); + } + return data; +} + +void spiWriteNL(spi_t * spi, const void * data_in, uint32_t len){ + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + uint32_t * data = (uint32_t*)data_in; + size_t c_len = 0, c_longs = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + for (int i=0; idev->data_buf[i] = data[i]; + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + + data += c_longs; + longs -= c_longs; + len -= c_len; + } +} + +void spiTransferBytesNL(spi_t * spi, const void * data_in, uint8_t * data_out, uint32_t len){ + if(!spi) { + return; + } + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + uint32_t * data = (uint32_t*)data_in; + uint32_t * result = (uint32_t*)data_out; + size_t c_len = 0, c_longs = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = (c_len*8)-1; + if(data){ + for (int i=0; idev->data_buf[i] = data[i]; + } + } else { + for (int i=0; idev->data_buf[i] = 0xFFFFFFFF; + } + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + if(result){ + for (int i=0; idev->data_buf[i]; + } + } + if(data){ + data += c_longs; + } + if(result){ + result += c_longs; + } + longs -= c_longs; + len -= c_len; + } +} + +void spiTransferBitsNL(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits) +{ + if(!spi) { + return; + } + + if(bits > 32) { + bits = 32; + } + uint32_t bytes = (bits + 7) / 8;//64 max + uint32_t mask = (((uint64_t)1 << bits) - 1) & 0xFFFFFFFF; + data = data & mask; + if(!spi->dev->ctrl.wr_bit_order){ + if(bytes == 2) { + MSB_16_SET(data, data); + } else if(bytes == 3) { + MSB_24_SET(data, data); + } else { + MSB_32_SET(data, data); + } + } + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1); + spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1); + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + if(out) { + *out = data; + if(!spi->dev->ctrl.rd_bit_order){ + if(bytes == 2) { + MSB_16_SET(*out, data); + } else if(bytes == 3) { + MSB_24_SET(*out, data); + } else { + MSB_32_SET(*out, data); + } + } + } +} + +void IRAM_ATTR spiWritePixelsNL(spi_t * spi, const void * data_in, uint32_t len){ + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + bool msb = !spi->dev->ctrl.wr_bit_order; + uint32_t * data = (uint32_t*)data_in; + size_t c_len = 0, c_longs = 0, l_bytes = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + l_bytes = (c_len & 3); + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + for (int i=0; idev->data_buf[i], data[i]); + } else { + spi->dev->data_buf[i] = data[i] & 0xFF; + } + } else { + MSB_PIX_SET(spi->dev->data_buf[i], data[i]); + } + } else { + spi->dev->data_buf[i] = data[i]; + } + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + + data += c_longs; + longs -= c_longs; + len -= c_len; + } +} + + + +/* + * Clock Calculators + * + * */ + +typedef union { + uint32_t value; + struct { + uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/ + uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/ + uint32_t clkcnt_n: 6; /*it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/ + uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/ + uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/ + }; +} spiClk_t; + +#define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1))) + +uint32_t spiClockDivToFrequency(uint32_t clockDiv) +{ + uint32_t apb_freq = getApbFrequency(); + spiClk_t reg = { clockDiv }; + return ClkRegToFreq(®); +} + +uint32_t spiFrequencyToClockDiv(uint32_t freq) +{ + uint32_t apb_freq = getApbFrequency(); + + if(freq >= apb_freq) { + return SPI_CLK_EQU_SYSCLK; + } + + const spiClk_t minFreqReg = { 0x7FFFF000 }; + uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); + if(freq < minFreq) { + return minFreqReg.value; + } + + uint8_t calN = 1; + spiClk_t bestReg = { 0 }; + int32_t bestFreq = 0; + + while(calN <= 0x3F) { + spiClk_t reg = { 0 }; + int32_t calFreq; + int32_t calPre; + int8_t calPreVari = -2; + + reg.clkcnt_n = calN; + + while(calPreVari++ <= 1) { + calPre = (((apb_freq / (reg.clkcnt_n + 1)) / freq) - 1) + calPreVari; + if(calPre > 0x1FFF) { + reg.clkdiv_pre = 0x1FFF; + } else if(calPre <= 0) { + reg.clkdiv_pre = 0; + } else { + reg.clkdiv_pre = calPre; + } + reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2); + calFreq = ClkRegToFreq(®); + if(calFreq == (int32_t) freq) { + memcpy(&bestReg, ®, sizeof(bestReg)); + break; + } else if(calFreq < (int32_t) freq) { + if(abs(freq - calFreq) < abs(freq - bestFreq)) { + bestFreq = calFreq; + memcpy(&bestReg, ®, sizeof(bestReg)); + } + } + } + if(calFreq == (int32_t) freq) { + break; + } + calN++; + } + return bestReg.value; +} + diff --git a/components/FastLED-idf/hal/esp32-hal-spi.h b/components/FastLED-idf/hal/esp32-hal-spi.h new file mode 100644 index 0000000..1317f38 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-spi.h @@ -0,0 +1,141 @@ +// 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 MAIN_ESP32_HAL_SPI_H_ +#define MAIN_ESP32_HAL_SPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SPI_HAS_TRANSACTION + +#define FSPI 1 //SPI bus attached to the flash (can use the same data lines but different SS) +#define HSPI 2 //SPI bus normally mapped to pins 12 - 15, but can be matrixed to any pins +#define VSPI 3 //SPI bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins + +// This defines are not representing the real Divider of the ESP32 +// the Defines match to an AVR Arduino on 16MHz for better compatibility +#define SPI_CLOCK_DIV2 0x00101001 //8 MHz +#define SPI_CLOCK_DIV4 0x00241001 //4 MHz +#define SPI_CLOCK_DIV8 0x004c1001 //2 MHz +#define SPI_CLOCK_DIV16 0x009c1001 //1 MHz +#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz +#define SPI_CLOCK_DIV64 0x027c1001 //250 KHz +#define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz + +#define SPI_MODE0 0 +#define SPI_MODE1 1 +#define SPI_MODE2 2 +#define SPI_MODE3 3 + +#define SPI_CS0 0 +#define SPI_CS1 1 +#define SPI_CS2 2 +#define SPI_CS_MASK_ALL 0x7 + +#define SPI_LSBFIRST 0 +#define SPI_MSBFIRST 1 + +struct spi_struct_t; +typedef struct spi_struct_t spi_t; + +spi_t * spiStartBus(uint8_t spi_num, uint32_t freq, uint8_t dataMode, uint8_t bitOrder); +void spiStopBus(spi_t * spi); + +//Attach/Detach Signal Pins +void spiAttachSCK(spi_t * spi, int8_t sck); +void spiAttachMISO(spi_t * spi, int8_t miso); +void spiAttachMOSI(spi_t * spi, int8_t mosi); +void spiDetachSCK(spi_t * spi, int8_t sck); +void spiDetachMISO(spi_t * spi, int8_t miso); +void spiDetachMOSI(spi_t * spi, int8_t mosi); + +//Attach/Detach SS pin to SPI_CSx signal +void spiAttachSS(spi_t * spi, uint8_t cs_num, int8_t ss); +void spiDetachSS(spi_t * spi, int8_t ss); + +//Enable/Disable SPI_CSx pins +void spiEnableSSPins(spi_t * spi, uint8_t cs_mask); +void spiDisableSSPins(spi_t * spi, uint8_t cs_mask); + +//Enable/Disable hardware control of SPI_CSx pins +void spiSSEnable(spi_t * spi); +void spiSSDisable(spi_t * spi); + +//Activate enabled SPI_CSx pins +void spiSSSet(spi_t * spi); +//Deactivate enabled SPI_CSx pins +void spiSSClear(spi_t * spi); + +void spiWaitReady(spi_t * spi); + +uint32_t spiGetClockDiv(spi_t * spi); +uint8_t spiGetDataMode(spi_t * spi); +uint8_t spiGetBitOrder(spi_t * spi); + + +/* + * Non transaction based lock methods (each locks and unlocks when called) + * */ +void spiSetClockDiv(spi_t * spi, uint32_t clockDiv); +void spiSetDataMode(spi_t * spi, uint8_t dataMode); +void spiSetBitOrder(spi_t * spi, uint8_t bitOrder); + +void spiWrite(spi_t * spi, const uint32_t *data, uint8_t len); +void spiWriteByte(spi_t * spi, uint8_t data); +void spiWriteWord(spi_t * spi, uint16_t data); +void spiWriteLong(spi_t * spi, uint32_t data); + +void spiTransfer(spi_t * spi, uint32_t *out, uint8_t len); +uint8_t spiTransferByte(spi_t * spi, uint8_t data); +uint16_t spiTransferWord(spi_t * spi, uint16_t data); +uint32_t spiTransferLong(spi_t * spi, uint32_t data); +void spiTransferBytes(spi_t * spi, const uint8_t * data, uint8_t * out, uint32_t size); +void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits); + +/* + * New (EXPERIMENTAL) Transaction lock based API (lock once until endTransaction) + * */ +void spiTransaction(spi_t * spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder); +void spiSimpleTransaction(spi_t * spi); +void spiEndTransaction(spi_t * spi); + +void spiWriteNL(spi_t * spi, const void * data_in, uint32_t len); +void spiWriteByteNL(spi_t * spi, uint8_t data); +void spiWriteShortNL(spi_t * spi, uint16_t data); +void spiWriteLongNL(spi_t * spi, uint32_t data); +void spiWritePixelsNL(spi_t * spi, const void * data_in, uint32_t len); + +#define spiTransferNL(spi, data, len) spiTransferBytesNL(spi, data, data, len) +uint8_t spiTransferByteNL(spi_t * spi, uint8_t data); +uint16_t spiTransferShortNL(spi_t * spi, uint16_t data); +uint32_t spiTransferLongNL(spi_t * spi, uint32_t data); +void spiTransferBytesNL(spi_t * spi, const void * data_in, uint8_t * data_out, uint32_t len); +void spiTransferBitsNL(spi_t * spi, uint32_t data_in, uint32_t * data_out, uint8_t bits); + +/* + * Helper functions to translate frequency to clock divider and back + * */ +uint32_t spiFrequencyToClockDiv(uint32_t freq); +uint32_t spiClockDivToFrequency(uint32_t freq); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_SPI_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-time.c b/components/FastLED-idf/hal/esp32-hal-time.c new file mode 100644 index 0000000..5647f1f --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-time.c @@ -0,0 +1,94 @@ +// 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 "esp32-hal.h" +#include "lwip/apps/sntp.h" +#include "tcpip_adapter.h" + +static void setTimeZone(long offset, int daylight) +{ + char cst[17] = {0}; + char cdt[17] = "DST"; + char tz[33] = {0}; + + if(offset % 3600){ + sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); + } else { + sprintf(cst, "UTC%ld", offset / 3600); + } + if(daylight != 3600){ + long tz_dst = offset - daylight; + if(tz_dst % 3600){ + sprintf(cdt, "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60)); + } else { + sprintf(cdt, "DST%ld", tz_dst / 3600); + } + } + sprintf(tz, "%s%s", cst, cdt); + setenv("TZ", tz, 1); + tzset(); +} + +/* + * configTime + * Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c + * */ +void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3) +{ + tcpip_adapter_init(); // Should not hurt anything if already inited + if(sntp_enabled()){ + sntp_stop(); + } + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, (char*)server1); + sntp_setservername(1, (char*)server2); + sntp_setservername(2, (char*)server3); + sntp_init(); + setTimeZone(-gmtOffset_sec, daylightOffset_sec); +} + +/* + * configTzTime + * sntp setup using TZ environment variable + * */ +void configTzTime(const char* tz, const char* server1, const char* server2, const char* server3) +{ + tcpip_adapter_init(); // Should not hurt anything if already inited + if(sntp_enabled()){ + sntp_stop(); + } + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, (char*)server1); + sntp_setservername(1, (char*)server2); + sntp_setservername(2, (char*)server3); + sntp_init(); + setenv("TZ", tz, 1); + tzset(); +} + +bool getLocalTime(struct tm * info, uint32_t ms) +{ + uint32_t start = millis(); + time_t now; + while((millis()-start) <= ms) { + time(&now); + localtime_r(&now, info); + if(info->tm_year > (2016 - 1900)){ + return true; + } + delay(10); + } + return false; +} + diff --git a/components/FastLED-idf/hal/esp32-hal-timer.c b/components/FastLED-idf/hal/esp32-hal-timer.c new file mode 100644 index 0000000..ed804d4 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-timer.c @@ -0,0 +1,308 @@ +// 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 "esp32-hal-timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "soc/timer_group_struct.h" +#include "soc/dport_reg.h" +#include "esp_attr.h" +#include "esp_intr.h" + +#define HWTIMER_LOCK() portENTER_CRITICAL(timer->lock) +#define HWTIMER_UNLOCK() portEXIT_CRITICAL(timer->lock) + +typedef struct { + union { + struct { + uint32_t reserved0: 10; + uint32_t alarm_en: 1; /*When set alarm is enabled*/ + uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/ + uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; + } config; + uint32_t cnt_low; /*Register to store timer 0/1 time-base counter current value lower 32 bits.*/ + uint32_t cnt_high; /*Register to store timer 0 time-base counter current value higher 32 bits.*/ + uint32_t update; /*Write any value will trigger a timer 0 time-base counter value update (timer 0 current value will be stored in registers above)*/ + uint32_t alarm_low; /*Timer 0 time-base counter value lower 32 bits that will trigger the alarm*/ + uint32_t alarm_high; /*Timer 0 time-base counter value higher 32 bits that will trigger the alarm*/ + uint32_t load_low; /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t load_high; /*higher 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t reload; /*Write any value will trigger timer 0 time-base counter reload*/ +} hw_timer_reg_t; + +typedef struct hw_timer_s { + hw_timer_reg_t * dev; + uint8_t num; + uint8_t group; + uint8_t timer; + portMUX_TYPE lock; +} hw_timer_t; + +static hw_timer_t hw_timer[4] = { + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE),0,0,0,portMUX_INITIALIZER_UNLOCKED}, + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x0024),1,0,1,portMUX_INITIALIZER_UNLOCKED}, + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1000),2,1,0,portMUX_INITIALIZER_UNLOCKED}, + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1024),3,1,1,portMUX_INITIALIZER_UNLOCKED} +}; + +typedef void (*voidFuncPtr)(void); +static voidFuncPtr __timerInterruptHandlers[4] = {0,0,0,0}; + +void IRAM_ATTR __timerISR(void * arg){ + uint32_t s0 = TIMERG0.int_st_timers.val; + uint32_t s1 = TIMERG1.int_st_timers.val; + TIMERG0.int_clr_timers.val = s0; + TIMERG1.int_clr_timers.val = s1; + uint8_t status = (s1 & 3) << 2 | (s0 & 3); + uint8_t i = 4; + //restart the timers that should autoreload + while(i--){ + hw_timer_reg_t * dev = hw_timer[i].dev; + if((status & (1 << i)) && dev->config.autoreload){ + dev->config.alarm_en = 1; + } + } + i = 4; + //call callbacks + while(i--){ + if(__timerInterruptHandlers[i] && (status & (1 << i))){ + __timerInterruptHandlers[i](); + } + } +} + +uint64_t timerRead(hw_timer_t *timer){ + timer->dev->update = 1; + uint64_t h = timer->dev->cnt_high; + uint64_t l = timer->dev->cnt_low; + return (h << 32) | l; +} + +uint64_t timerAlarmRead(hw_timer_t *timer){ + uint64_t h = timer->dev->alarm_high; + uint64_t l = timer->dev->alarm_low; + return (h << 32) | l; +} + +void timerWrite(hw_timer_t *timer, uint64_t val){ + timer->dev->load_high = (uint32_t) (val >> 32); + timer->dev->load_low = (uint32_t) (val); + timer->dev->reload = 1; +} + +void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){ + timer->dev->alarm_high = (uint32_t) (alarm_value >> 32); + timer->dev->alarm_low = (uint32_t) alarm_value; + timer->dev->config.autoreload = autoreload; +} + +void timerSetConfig(hw_timer_t *timer, uint32_t config){ + timer->dev->config.val = config; +} + +uint32_t timerGetConfig(hw_timer_t *timer){ + return timer->dev->config.val; +} + +void timerSetCountUp(hw_timer_t *timer, bool countUp){ + timer->dev->config.increase = countUp; +} + +bool timerGetCountUp(hw_timer_t *timer){ + return timer->dev->config.increase; +} + +void timerSetAutoReload(hw_timer_t *timer, bool autoreload){ + timer->dev->config.autoreload = autoreload; +} + +bool timerGetAutoReload(hw_timer_t *timer){ + return timer->dev->config.autoreload; +} + +void timerSetDivider(hw_timer_t *timer, uint16_t divider){//2 to 65536 + if(!divider){ + divider = 0xFFFF; + } else if(divider == 1){ + divider = 2; + } + int timer_en = timer->dev->config.enable; + timer->dev->config.enable = 0; + timer->dev->config.divider = divider; + timer->dev->config.enable = timer_en; +} + +uint16_t timerGetDivider(hw_timer_t *timer){ + return timer->dev->config.divider; +} + +void timerStart(hw_timer_t *timer){ + timer->dev->config.enable = 1; +} + +void timerStop(hw_timer_t *timer){ + timer->dev->config.enable = 0; +} + +void timerRestart(hw_timer_t *timer){ + timer->dev->config.enable = 0; + timer->dev->reload = 1; + timer->dev->config.enable = 1; +} + +bool timerStarted(hw_timer_t *timer){ + return timer->dev->config.enable; +} + +void timerAlarmEnable(hw_timer_t *timer){ + timer->dev->config.alarm_en = 1; +} + +void timerAlarmDisable(hw_timer_t *timer){ + timer->dev->config.alarm_en = 0; +} + +bool timerAlarmEnabled(hw_timer_t *timer){ + return timer->dev->config.alarm_en; +} + +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + hw_timer_t * timer = (hw_timer_t *)arg; + if(ev_type == APB_BEFORE_CHANGE){ + timer->dev->config.enable = 0; + } else { + old_apb /= 1000000; + new_apb /= 1000000; + timer->dev->config.divider = (new_apb * timer->dev->config.divider) / old_apb; + timer->dev->config.enable = 1; + } +} + +hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ + if(num > 3){ + return NULL; + } + hw_timer_t * timer = &hw_timer[num]; + if(timer->group) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_TIMERGROUP1_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_TIMERGROUP1_RST); + TIMERG1.int_ena.val &= ~BIT(timer->timer); + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_TIMERGROUP_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_TIMERGROUP_RST); + TIMERG0.int_ena.val &= ~BIT(timer->timer); + } + timer->dev->config.enable = 0; + timerSetDivider(timer, divider); + timerSetCountUp(timer, countUp); + timerSetAutoReload(timer, false); + timerAttachInterrupt(timer, NULL, false); + timerWrite(timer, 0); + timer->dev->config.enable = 1; + addApbChangeCallback(timer, _on_apb_change); + return timer; +} + +void timerEnd(hw_timer_t *timer){ + timer->dev->config.enable = 0; + timerAttachInterrupt(timer, NULL, false); + removeApbChangeCallback(timer, _on_apb_change); +} + +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){ + static bool initialized = false; + static intr_handle_t intr_handle = NULL; + if(intr_handle){ + esp_intr_disable(intr_handle); + } + if(fn == NULL){ + timer->dev->config.level_int_en = 0; + timer->dev->config.edge_int_en = 0; + timer->dev->config.alarm_en = 0; + if(timer->num & 2){ + TIMERG1.int_ena.val &= ~BIT(timer->timer); + } else { + TIMERG0.int_ena.val &= ~BIT(timer->timer); + } + __timerInterruptHandlers[timer->num] = NULL; + } else { + __timerInterruptHandlers[timer->num] = fn; + timer->dev->config.level_int_en = edge?0:1;//When set, an alarm will generate a level type interrupt. + timer->dev->config.edge_int_en = edge?1:0;//When set, an alarm will generate an edge type interrupt. + int intr_source = 0; + if(!edge){ + if(timer->group){ + intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer->timer; + } else { + intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer->timer; + } + } else { + if(timer->group){ + intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer->timer; + } else { + intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer->timer; + } + } + if(!initialized){ + initialized = true; + esp_intr_alloc(intr_source, (int)(ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LOWMED|ESP_INTR_FLAG_EDGE), __timerISR, NULL, &intr_handle); + } else { + intr_matrix_set(esp_intr_get_cpu(intr_handle), intr_source, esp_intr_get_intno(intr_handle)); + } + if(timer->group){ + TIMERG1.int_ena.val |= BIT(timer->timer); + } else { + TIMERG0.int_ena.val |= BIT(timer->timer); + } + } + if(intr_handle){ + esp_intr_enable(intr_handle); + } +} + +void timerDetachInterrupt(hw_timer_t *timer){ + timerAttachInterrupt(timer, NULL, false); +} + +uint64_t timerReadMicros(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / (getApbFrequency() / 1000000); +} + +double timerReadSeconds(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return (double)timer_val * div / getApbFrequency(); +} + +uint64_t timerAlarmReadMicros(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / (getApbFrequency() / 1000000); +} + +double timerAlarmReadSeconds(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return (double)timer_val * div / getApbFrequency(); +} diff --git a/components/FastLED-idf/hal/esp32-hal-timer.h b/components/FastLED-idf/hal/esp32-hal-timer.h new file mode 100644 index 0000000..7624fc5 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-timer.h @@ -0,0 +1,72 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_TIMER_H_ +#define MAIN_ESP32_HAL_TIMER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "freertos/FreeRTOS.h" + +struct hw_timer_s; +typedef struct hw_timer_s hw_timer_t; + +hw_timer_t * timerBegin(uint8_t timer, uint16_t divider, bool countUp); +void timerEnd(hw_timer_t *timer); + +void timerSetConfig(hw_timer_t *timer, uint32_t config); +uint32_t timerGetConfig(hw_timer_t *timer); + +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge); +void timerDetachInterrupt(hw_timer_t *timer); + +void timerStart(hw_timer_t *timer); +void timerStop(hw_timer_t *timer); +void timerRestart(hw_timer_t *timer); +void timerWrite(hw_timer_t *timer, uint64_t val); +void timerSetDivider(hw_timer_t *timer, uint16_t divider); +void timerSetCountUp(hw_timer_t *timer, bool countUp); +void timerSetAutoReload(hw_timer_t *timer, bool autoreload); + +bool timerStarted(hw_timer_t *timer); +uint64_t timerRead(hw_timer_t *timer); +uint64_t timerReadMicros(hw_timer_t *timer); +double timerReadSeconds(hw_timer_t *timer); +uint16_t timerGetDivider(hw_timer_t *timer); +bool timerGetCountUp(hw_timer_t *timer); +bool timerGetAutoReload(hw_timer_t *timer); + +void timerAlarmEnable(hw_timer_t *timer); +void timerAlarmDisable(hw_timer_t *timer); +void timerAlarmWrite(hw_timer_t *timer, uint64_t interruptAt, bool autoreload); + +bool timerAlarmEnabled(hw_timer_t *timer); +uint64_t timerAlarmRead(hw_timer_t *timer); +uint64_t timerAlarmReadMicros(hw_timer_t *timer); +double timerAlarmReadSeconds(hw_timer_t *timer); + + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_TIMER_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-touch.c b/components/FastLED-idf/hal/esp32-hal-touch.c new file mode 100644 index 0000000..94e8a8b --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-touch.c @@ -0,0 +1,168 @@ +// 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 "esp32-hal-touch.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "soc/rtc_io_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +static uint16_t __touchSleepCycles = 0x1000; +static uint16_t __touchMeasureCycles = 0x1000; + +typedef void (*voidFuncPtr)(void); +static voidFuncPtr __touchInterruptHandlers[10] = {0,}; +static intr_handle_t touch_intr_handle = NULL; + +void IRAM_ATTR __touchISR(void * arg) +{ + uint32_t pad_intr = READ_PERI_REG(SENS_SAR_TOUCH_CTRL2_REG) & 0x3ff; + uint32_t rtc_intr = READ_PERI_REG(RTC_CNTL_INT_ST_REG); + uint8_t i = 0; + //clear interrupt + WRITE_PERI_REG(RTC_CNTL_INT_CLR_REG, rtc_intr); + SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN_CLR); + + if (rtc_intr & RTC_CNTL_TOUCH_INT_ST) { + for (i = 0; i < 10; ++i) { + if ((pad_intr >> i) & 0x01) { + if(__touchInterruptHandlers[i]){ + __touchInterruptHandlers[i](); + } + } + } + } +} + +void __touchSetCycles(uint16_t measure, uint16_t sleep) +{ + __touchSleepCycles = sleep; + __touchMeasureCycles = measure; + //Touch pad SleepCycle Time + SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_SLEEP_CYCLES, __touchSleepCycles, SENS_TOUCH_SLEEP_CYCLES_S); + //Touch Pad Measure Time + SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_MEAS_DELAY, __touchMeasureCycles, SENS_TOUCH_MEAS_DELAY_S); +} + +void __touchInit() +{ + static bool initialized = false; + if(initialized){ + return; + } + initialized = true; + SET_PERI_REG_BITS(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_XPD_BIAS, 1, RTC_IO_TOUCH_XPD_BIAS_S); + SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN_CLR); + //clear touch enable + WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, 0x0); + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_TOUCH_SLP_TIMER_EN); + + __touchSetCycles(__touchMeasureCycles, __touchSleepCycles); + + esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __touchISR, NULL, &touch_intr_handle); +} + +uint16_t __touchRead(uint8_t pin) +{ + int8_t pad = digitalPinToTouchChannel(pin); + if(pad < 0){ + return 0; + } + + pinMode(pin, ANALOG); + + __touchInit(); + + uint32_t v0 = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG); + //Disable Intr & enable touch pad + WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, + (v0 & ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S)))) + | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S))); + + SET_PERI_REG_MASK(SENS_SAR_TOUCH_ENABLE_REG, (1 << (pad + SENS_TOUCH_PAD_WORKEN_S))); + + uint32_t rtc_tio_reg = RTC_IO_TOUCH_PAD0_REG + pad * 4; + WRITE_PERI_REG(rtc_tio_reg, (READ_PERI_REG(rtc_tio_reg) + & ~(RTC_IO_TOUCH_PAD0_DAC_M)) + | (7 << RTC_IO_TOUCH_PAD0_DAC_S)//Touch Set Slope + | RTC_IO_TOUCH_PAD0_TIE_OPT_M //Enable Tie,Init Level + | RTC_IO_TOUCH_PAD0_START_M //Enable Touch Pad IO + | RTC_IO_TOUCH_PAD0_XPD_M); //Enable Touch Pad Power on + + //force oneTime test start + SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M|SENS_TOUCH_START_FORCE_M); + + SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_XPD_WAIT, 10, SENS_TOUCH_XPD_WAIT_S); + + while (GET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_DONE) == 0) {}; + + uint16_t touch_value = READ_PERI_REG(SENS_SAR_TOUCH_OUT1_REG + (pad / 2) * 4) >> ((pad & 1) ? SENS_TOUCH_MEAS_OUT1_S : SENS_TOUCH_MEAS_OUT0_S); + + //clear touch force ,select the Touch mode is Timer + CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M|SENS_TOUCH_START_FORCE_M); + + //restore previous value + WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, v0); + return touch_value; +} + +void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), uint16_t threshold) +{ + int8_t pad = digitalPinToTouchChannel(pin); + if(pad < 0){ + return; + } + + pinMode(pin, ANALOG); + + __touchInit(); + + __touchInterruptHandlers[pad] = userFunc; + + //clear touch force ,select the Touch mode is Timer + CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M|SENS_TOUCH_START_FORCE_M); + + //interrupt when touch value < threshold + CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_OUT_SEL); + //Intr will give ,when SET0 < threshold + SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_OUT_1EN); + //Enable Rtc Touch Module Intr,the Interrupt need Rtc out Enable + SET_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, RTC_CNTL_TOUCH_INT_ENA); + + //set threshold + uint8_t shift = (pad & 1) ? SENS_TOUCH_OUT_TH1_S : SENS_TOUCH_OUT_TH0_S; + SET_PERI_REG_BITS((SENS_SAR_TOUCH_THRES1_REG + (pad / 2) * 4), SENS_TOUCH_OUT_TH0, threshold, shift); + + uint32_t rtc_tio_reg = RTC_IO_TOUCH_PAD0_REG + pad * 4; + WRITE_PERI_REG(rtc_tio_reg, (READ_PERI_REG(rtc_tio_reg) + & ~(RTC_IO_TOUCH_PAD0_DAC_M)) + | (7 << RTC_IO_TOUCH_PAD0_DAC_S)//Touch Set Slope + | RTC_IO_TOUCH_PAD0_TIE_OPT_M //Enable Tie,Init Level + | RTC_IO_TOUCH_PAD0_START_M //Enable Touch Pad IO + | RTC_IO_TOUCH_PAD0_XPD_M); //Enable Touch Pad Power on + + //Enable Digital rtc control :work mode and out mode + SET_PERI_REG_MASK(SENS_SAR_TOUCH_ENABLE_REG, + (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)) | \ + (1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) | \ + (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))); +} + +extern uint16_t touchRead(uint8_t pin) __attribute__ ((weak, alias("__touchRead"))); +extern void touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), uint16_t threshold) __attribute__ ((weak, alias("__touchAttachInterrupt"))); +extern void touchSetCycles(uint16_t measure, uint16_t sleep) __attribute__ ((weak, alias("__touchSetCycles"))); diff --git a/components/FastLED-idf/hal/esp32-hal-touch.h b/components/FastLED-idf/hal/esp32-hal-touch.h new file mode 100644 index 0000000..6f3b0fd --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-touch.h @@ -0,0 +1,56 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_TOUCH_H_ +#define MAIN_ESP32_HAL_TOUCH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" + +/* + * Set cycles that measurement operation takes + * The result from touchRead, threshold and detection + * accuracy depend on these values. Defaults are + * 0x1000 for measure and 0x1000 for sleep. + * With default values touchRead takes 0.5ms + * */ +void touchSetCycles(uint16_t measure, uint16_t sleep); + +/* + * Read touch pad (values close to 0 mean touch detected) + * You can use this method to chose a good threshold value + * to use as value for touchAttachInterrupt + * */ +uint16_t touchRead(uint8_t pin); + +/* + * Set function to be called if touch pad value falls + * below the given threshold. Use touchRead to determine + * a proper threshold between touched and untouched state + * */ +void touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), uint16_t threshold); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_TOUCH_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal-uart.c b/components/FastLED-idf/hal/esp32-hal-uart.c new file mode 100644 index 0000000..8ec1fe6 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-uart.c @@ -0,0 +1,615 @@ +// 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 "esp32-hal-uart.h" +#include "esp32-hal.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "rom/uart.h" +#include "soc/uart_reg.h" +#include "soc/uart_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/dport_reg.h" +#include "soc/rtc.h" +#include "esp_intr_alloc.h" + +#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0))) +#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:( (u==2)?U2RXD_IN_IDX:0))) +#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:( (u==2)?U2TXD_OUT_IDX:0))) +#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:((u==2)?ETS_UART2_INTR_SOURCE:0))) + +static int s_uart_debug_nr = 0; + +struct uart_struct_t { + uart_dev_t * dev; +#if !CONFIG_DISABLE_HAL_LOCKS + xSemaphoreHandle lock; +#endif + uint8_t num; + xQueueHandle queue; + intr_handle_t intr_handle; +}; + +#if CONFIG_DISABLE_HAL_LOCKS +#define UART_MUTEX_LOCK() +#define UART_MUTEX_UNLOCK() + +static uart_t _uart_bus_array[3] = { + {(volatile uart_dev_t *)(DR_REG_UART_BASE), 0, NULL, NULL}, + {(volatile uart_dev_t *)(DR_REG_UART1_BASE), 1, NULL, NULL}, + {(volatile uart_dev_t *)(DR_REG_UART2_BASE), 2, NULL, NULL} +}; +#else +#define UART_MUTEX_LOCK() do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS) +#define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock) + +static uart_t _uart_bus_array[3] = { + {(volatile uart_dev_t *)(DR_REG_UART_BASE), NULL, 0, NULL, NULL}, + {(volatile uart_dev_t *)(DR_REG_UART1_BASE), NULL, 1, NULL, NULL}, + {(volatile uart_dev_t *)(DR_REG_UART2_BASE), NULL, 2, NULL, NULL} +}; +#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; + BaseType_t xHigherPriorityTaskWoken; + uart_t* uart; + + for(i=0;i<3;i++){ + uart = &_uart_bus_array[i]; + if(uart->intr_handle == NULL){ + continue; + } + uart->dev->int_clr.rxfifo_full = 1; + uart->dev->int_clr.frm_err = 1; + uart->dev->int_clr.rxfifo_tout = 1; + while(uart->dev->status.rxfifo_cnt || (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); + } + } + } + + if (xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR(); + } +} + +void uartEnableInterrupt(uart_t* uart) +{ + UART_MUTEX_LOCK(); + uart->dev->conf1.rxfifo_full_thrhd = 112; + uart->dev->conf1.rx_tout_thrhd = 2; + uart->dev->conf1.rx_tout_en = 1; + 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; + + esp_intr_alloc(UART_INTR_SOURCE(uart->num), (int)ESP_INTR_FLAG_IRAM, _uart_isr, NULL, &uart->intr_handle); + UART_MUTEX_UNLOCK(); +} + +void uartDisableInterrupt(uart_t* uart) +{ + UART_MUTEX_LOCK(); + uart->dev->conf1.val = 0; + uart->dev->int_ena.val = 0; + uart->dev->int_clr.val = 0xffffffff; + + esp_intr_free(uart->intr_handle); + uart->intr_handle = NULL; + + UART_MUTEX_UNLOCK(); +} + +void uartDetachRx(uart_t* uart) +{ + if(uart == NULL) { + return; + } + pinMatrixInDetach(UART_RXD_IDX(uart->num), false, false); + uartDisableInterrupt(uart); +} + +void uartDetachTx(uart_t* uart) +{ + if(uart == NULL) { + return; + } + pinMatrixOutDetach(UART_TXD_IDX(uart->num), false, false); +} + +void uartAttachRx(uart_t* uart, uint8_t rxPin, bool inverted) +{ + if(uart == NULL || rxPin > 39) { + return; + } + pinMode(rxPin, INPUT); + pinMatrixInAttach(rxPin, UART_RXD_IDX(uart->num), inverted); + uartEnableInterrupt(uart); +} + +void uartAttachTx(uart_t* uart, uint8_t txPin, bool inverted) +{ + if(uart == NULL || txPin > 39) { + return; + } + pinMode(txPin, OUTPUT); + pinMatrixOutAttach(txPin, UART_TXD_IDX(uart->num), inverted, false); +} + +uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted) +{ + if(uart_nr > 2) { + return NULL; + } + + if(rxPin == -1 && txPin == -1) { + return NULL; + } + + uart_t* uart = &_uart_bus_array[uart_nr]; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(uart->lock == NULL) { + uart->lock = xSemaphoreCreateMutex(); + if(uart->lock == NULL) { + return NULL; + } + } +#endif + + if(queueLen && uart->queue == NULL) { + uart->queue = xQueueCreate(queueLen, sizeof(uint8_t)); //initialize the queue + if(uart->queue == NULL) { + return NULL; + } + } + if(uart_nr == 1){ + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART1_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART1_RST); + } else if(uart_nr == 2){ + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST); + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART_RST); + } + uartFlush(uart); + uartSetBaudRate(uart, baudrate); + UART_MUTEX_LOCK(); + uart->dev->conf0.val = config; + #define TWO_STOP_BITS_CONF 0x3 + #define ONE_STOP_BITS_CONF 0x1 + + if ( uart->dev->conf0.stop_bit_num == TWO_STOP_BITS_CONF) { + uart->dev->conf0.stop_bit_num = ONE_STOP_BITS_CONF; + uart->dev->rs485_conf.dl1_en = 1; + } + + // tx_idle_num : idle interval after tx FIFO is empty(unit: the time it takes to send one bit under current baudrate) + // Setting it to 0 prevents line idle time/delays when sending messages with small intervals + uart->dev->idle_conf.tx_idle_num = 0; // + + UART_MUTEX_UNLOCK(); + + if(rxPin != -1) { + uartAttachRx(uart, rxPin, inverted); + } + + if(txPin != -1) { + uartAttachTx(uart, txPin, inverted); + } + addApbChangeCallback(uart, uart_on_apb_change); + return uart; +} + +void uartEnd(uart_t* uart) +{ + if(uart == NULL) { + return; + } + removeApbChangeCallback(uart, uart_on_apb_change); + + UART_MUTEX_LOCK(); + if(uart->queue != NULL) { + vQueueDelete(uart->queue); + uart->queue = NULL; + } + + uart->dev->conf0.val = 0; + + UART_MUTEX_UNLOCK(); + + uartDetachRx(uart); + uartDetachTx(uart); +} + +size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) { + if(uart == NULL) { + return 0; + } + + UART_MUTEX_LOCK(); + if(uart->queue != NULL) { + vQueueDelete(uart->queue); + uart->queue = xQueueCreate(new_size, sizeof(uint8_t)); + if(uart->queue == NULL) { + return NULL; + } + } + UART_MUTEX_UNLOCK(); + + return new_size; +} + +uint32_t uartAvailable(uart_t* uart) +{ + if(uart == NULL || uart->queue == NULL) { + return 0; + } + return (uxQueueMessagesWaiting(uart->queue) + uart->dev->status.rxfifo_cnt) ; +} + +uint32_t uartAvailableForWrite(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + return 0x7f - uart->dev->status.txfifo_cnt; +} + +void uartRxFifoToQueue(uart_t* uart) +{ + uint8_t c; + UART_MUTEX_LOCK(); + while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) { + c = uart->dev->fifo.rw_byte; + xQueueSend(uart->queue, &c, 0); + } + UART_MUTEX_UNLOCK(); +} + +uint8_t uartRead(uart_t* uart) +{ + if(uart == NULL || uart->queue == NULL) { + return 0; + } + uint8_t c; + if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0)) + { + uartRxFifoToQueue(uart); + } + if(xQueueReceive(uart->queue, &c, 0)) { + return c; + } + return 0; +} + +uint8_t uartPeek(uart_t* uart) +{ + if(uart == NULL || uart->queue == NULL) { + return 0; + } + uint8_t c; + if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0)) + { + uartRxFifoToQueue(uart); + } + if(xQueuePeek(uart->queue, &c, 0)) { + return c; + } + return 0; +} + +void uartWrite(uart_t* uart, uint8_t c) +{ + if(uart == NULL) { + return; + } + UART_MUTEX_LOCK(); + while(uart->dev->status.txfifo_cnt == 0x7F); + uart->dev->fifo.rw_byte = c; + UART_MUTEX_UNLOCK(); +} + +void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len) +{ + if(uart == NULL) { + return; + } + UART_MUTEX_LOCK(); + while(len) { + while(uart->dev->status.txfifo_cnt == 0x7F); + uart->dev->fifo.rw_byte = *data++; + len--; + } + UART_MUTEX_UNLOCK(); +} + +void uartFlush(uart_t* uart) +{ + uartFlushTxOnly(uart,false); +} + +void uartFlushTxOnly(uart_t* uart, bool txOnly) +{ + if(uart == NULL) { + return; + } + + UART_MUTEX_LOCK(); + while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out); + + if( !txOnly ){ + //Due to hardware issue, we can not use fifo_rst to reset uart fifo. + //See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <> v2.6 or later. + + // we read the data out and make `fifo_len == 0 && rd_addr == wr_addr`. + while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) { + READ_PERI_REG(UART_FIFO_REG(uart->num)); + } + + xQueueReset(uart->queue); + } + + UART_MUTEX_UNLOCK(); +} + +void uartSetBaudRate(uart_t* uart, uint32_t baud_rate) +{ + if(uart == NULL) { + return; + } + UART_MUTEX_LOCK(); + uint32_t clk_div = ((getApbFrequency()<<4)/baud_rate); + uart->dev->clk_div.div_int = clk_div>>4 ; + uart->dev->clk_div.div_frag = clk_div & 0xf; + 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 ) { + xQueueSend(uart->queue, &c, 1); //&xHigherPriorityTaskWoken); + } + } + UART_MUTEX_UNLOCK(); + + // wait TX empty + while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out); + } else { + //todo: + // set baudrate + UART_MUTEX_LOCK(); + uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F); + uint32_t baud_rate = ((old_apb<<4)/clk_div); + clk_div = ((new_apb<<4)/baud_rate); + uart->dev->clk_div.div_int = clk_div>>4 ; + uart->dev->clk_div.div_frag = clk_div & 0xf; + //enable interrupts + uart->dev->int_ena.rxfifo_full = 1; + uart->dev->int_ena.frm_err = 1; + uart->dev->int_ena.rxfifo_tout = 1; + uart->dev->int_clr.val = 0xffffffff; + UART_MUTEX_UNLOCK(); + } +} + +uint32_t uartGetBaudRate(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + + uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F); + if(!clk_div) { + return 0; + } + + return ((getApbFrequency()<<4)/clk_div); +} + +static void IRAM_ATTR uart0_write_char(char c) +{ + while(((ESP_REG(0x01C+DR_REG_UART_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) == 0x7F); + ESP_REG(DR_REG_UART_BASE) = c; +} + +static void IRAM_ATTR uart1_write_char(char c) +{ + while(((ESP_REG(0x01C+DR_REG_UART1_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) == 0x7F); + ESP_REG(DR_REG_UART1_BASE) = c; +} + +static void IRAM_ATTR uart2_write_char(char c) +{ + while(((ESP_REG(0x01C+DR_REG_UART2_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) == 0x7F); + ESP_REG(DR_REG_UART2_BASE) = c; +} + +void uart_install_putc() +{ + switch(s_uart_debug_nr) { + case 0: + ets_install_putc1((void (*)(char)) &uart0_write_char); + break; + case 1: + ets_install_putc1((void (*)(char)) &uart1_write_char); + break; + case 2: + ets_install_putc1((void (*)(char)) &uart2_write_char); + break; + default: + ets_install_putc1(NULL); + break; + } +} + +void uartSetDebug(uart_t* uart) +{ + if(uart == NULL || uart->num > 2) { + s_uart_debug_nr = -1; + //ets_install_putc1(NULL); + //return; + } else + if(s_uart_debug_nr == uart->num) { + return; + } else + s_uart_debug_nr = uart->num; + uart_install_putc(); +} + +int uartGetDebug() +{ + return s_uart_debug_nr; +} + +int log_printf(const char *format, ...) +{ + if(s_uart_debug_nr < 0){ + return 0; + } + static char loc_buf[64]; + char * temp = loc_buf; + int len; + va_list arg; + va_list copy; + va_start(arg, format); + va_copy(copy, arg); + len = vsnprintf(NULL, 0, format, arg); + va_end(copy); + if(len >= sizeof(loc_buf)){ + temp = (char*)malloc(len+1); + if(temp == NULL) { + return 0; + } + } + vsnprintf(temp, len+1, format, arg); +#if !CONFIG_DISABLE_HAL_LOCKS + if(_uart_bus_array[s_uart_debug_nr].lock){ + xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY); + ets_printf("%s", temp); + xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock); + } else { + ets_printf("%s", temp); + } +#else + ets_printf("%s", temp); +#endif + va_end(arg); + if(len >= sizeof(loc_buf)){ + free(temp); + } + return len; +} + +/* + * if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two. + * This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses. + */ +unsigned long uartBaudrateDetect(uart_t *uart, bool flg) +{ + while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num) + if(flg) return 0; + ets_delay_us(1000); + } + + UART_MUTEX_LOCK(); + unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12; + UART_MUTEX_UNLOCK(); + + return ret; +} + +/* + * To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is + * detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is + * rounded to the closed real baudrate. +*/ +void uartStartDetectBaudrate(uart_t *uart) { + if(!uart) return; + + uart->dev->auto_baud.glitch_filt = 0x08; + uart->dev->auto_baud.en = 0; + uart->dev->auto_baud.en = 1; +} + +unsigned long +uartDetectBaudrate(uart_t *uart) +{ + static bool uartStateDetectingBaudrate = false; + + if(!uartStateDetectingBaudrate) { + uart->dev->auto_baud.glitch_filt = 0x08; + uart->dev->auto_baud.en = 0; + uart->dev->auto_baud.en = 1; + uartStateDetectingBaudrate = true; + } + + unsigned long divisor = uartBaudrateDetect(uart, true); + if (!divisor) { + return 0; + } + + uart->dev->auto_baud.en = 0; + uartStateDetectingBaudrate = false; // Initialize for the next round + + 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}; + + size_t i; + for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate + { + if (baudrate <= default_rates[i]) + { + if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) { + i--; + } + break; + } + } + + return default_rates[i]; +} + +/* + * Returns the status of the RX state machine, if the value is non-zero the state machine is active. + */ +bool uartRxActive(uart_t* uart) { + return uart->dev->status.st_urx_out != 0; +} diff --git a/components/FastLED-idf/hal/esp32-hal-uart.h b/components/FastLED-idf/hal/esp32-hal-uart.h new file mode 100644 index 0000000..e44d25b --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal-uart.h @@ -0,0 +1,85 @@ +// 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 MAIN_ESP32_HAL_UART_H_ +#define MAIN_ESP32_HAL_UART_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define SERIAL_5N1 0x8000010 +#define SERIAL_6N1 0x8000014 +#define SERIAL_7N1 0x8000018 +#define SERIAL_8N1 0x800001c +#define SERIAL_5N2 0x8000030 +#define SERIAL_6N2 0x8000034 +#define SERIAL_7N2 0x8000038 +#define SERIAL_8N2 0x800003c +#define SERIAL_5E1 0x8000012 +#define SERIAL_6E1 0x8000016 +#define SERIAL_7E1 0x800001a +#define SERIAL_8E1 0x800001e +#define SERIAL_5E2 0x8000032 +#define SERIAL_6E2 0x8000036 +#define SERIAL_7E2 0x800003a +#define SERIAL_8E2 0x800003e +#define SERIAL_5O1 0x8000013 +#define SERIAL_6O1 0x8000017 +#define SERIAL_7O1 0x800001b +#define SERIAL_8O1 0x800001f +#define SERIAL_5O2 0x8000033 +#define SERIAL_6O2 0x8000037 +#define SERIAL_7O2 0x800003b +#define SERIAL_8O2 0x800003f + +struct uart_struct_t; +typedef struct uart_struct_t uart_t; + +uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted); +void uartEnd(uart_t* uart); + +uint32_t uartAvailable(uart_t* uart); +uint32_t uartAvailableForWrite(uart_t* uart); +uint8_t uartRead(uart_t* uart); +uint8_t uartPeek(uart_t* uart); + +void uartWrite(uart_t* uart, uint8_t c); +void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len); + +void uartFlush(uart_t* uart); +void uartFlushTxOnly(uart_t* uart, bool txOnly ); + +void uartSetBaudRate(uart_t* uart, uint32_t baud_rate); +uint32_t uartGetBaudRate(uart_t* uart); + +size_t uartResizeRxBuffer(uart_t* uart, size_t new_size); + +void uartSetDebug(uart_t* uart); +int uartGetDebug(); + +void uartStartDetectBaudrate(uart_t *uart); +unsigned long uartDetectBaudrate(uart_t *uart); + +bool uartRxActive(uart_t* uart); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_UART_H_ */ diff --git a/components/FastLED-idf/hal/esp32-hal.h b/components/FastLED-idf/hal/esp32-hal.h new file mode 100644 index 0000000..f805273 --- /dev/null +++ b/components/FastLED-idf/hal/esp32-hal.h @@ -0,0 +1,128 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HAL_ESP32_HAL_H_ +#define HAL_ESP32_HAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_system.h" + +#ifndef F_CPU +#define F_CPU (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000U) +#endif + +//forward declaration from freertos/portmacro.h +void vPortYield(void); +void yield(void); +#define optimistic_yield(u) + +#define ESP_REG(addr) *((volatile uint32_t *)(addr)) +#define NOP() asm volatile ("nop") + +// BB: see how many we can remove + +#include "esp32-hal-log.h" +// #include "esp32-hal-matrix.h" +// #include "esp32-hal-uart.h" +#include "esp32-hal-gpio.h" +// #include "esp32-hal-touch.h" +// #include "esp32-hal-dac.h" +// #include "esp32-hal-adc.h" +#include "esp32-hal-spi.h" +#include "esp32-hal-i2c.h" +// #include "esp32-hal-ledc.h" +#include "esp32-hal-rmt.h" +// #include "esp32-hal-sigmadelta.h" +#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 +#undef CONFIG_SPIRAM_SUPPORT +#endif +#endif + +// BB a few key functions from esp32-hal-misc, which are defined +// bar Arduino and thus therea re no headers for here +// +unsigned long micros(void); +unsigned long millis(void); + +//returns chip temperature in Celsius +float temperatureRead(void); + +#if CONFIG_AUTOSTART_ARDUINO +//enable/disable WDT for Arduino's setup and loop functions +void enableLoopWDT(void); +void disableLoopWDT(void); +//feed WDT for the loop task +void feedLoopWDT(void); +#endif + +//enable/disable WDT for the IDLE task on Core 0 (SYSTEM) +void enableCore0WDT(); +void disableCore0WDT(); +#ifndef CONFIG_FREERTOS_UNICORE +//enable/disable WDT for the IDLE task on Core 1 (Arduino) +void enableCore1WDT(); +void disableCore1WDT(); +#endif + +//if xCoreID < 0 or CPU is unicore, it will use xTaskCreate, else xTaskCreatePinnedToCore +//allows to easily handle all possible situations without repetitive code +BaseType_t xTaskCreateUniversal( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID ); + +unsigned long micros(); +unsigned long millis(); +void delay(uint32_t); +void delayMicroseconds(uint32_t us); + +#if !CONFIG_ESP32_PHY_AUTO_INIT +void arduino_phy_init(); +#endif + +#if !CONFIG_AUTOSTART_ARDUINO +void initArduino(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_ESP32_HAL_H_ */