diff --git a/components/driver/include/driver/adc.h b/components/driver/include/driver/adc.h index 799001ddb..9d26c5390 100644 --- a/components/driver/include/driver/adc.h +++ b/components/driver/include/driver/adc.h @@ -112,6 +112,19 @@ typedef enum { ADC_I2S_DATA_SRC_MAX, } adc_i2s_source_t; +/** + * @brief Get the gpio number of a specific ADC1 channel. + * + * @param channel Channel to get the gpio number + * + * @param gpio_num output buffer to hold the gpio number + * + * @return + * - ESP_OK if success + * - ESP_ERR_INVALID_ARG if channal not valid + */ +esp_err_t adc1_pad_get_io_num(adc1_channel_t channel, gpio_num_t *gpio_num); + /** * @brief Configure ADC1 capture width, meanwhile enable output invert for ADC1. * The configuration is for all channels of ADC1 @@ -273,6 +286,70 @@ void adc1_ulp_enable(); */ int hall_sensor_read(); +/** + * @brief Get the gpio number of a specific ADC2 channel. + * + * @param channel Channel to get the gpio number + * + * @param gpio_num output buffer to hold the gpio number + * + * @return + * - ESP_OK if success + * - ESP_ERR_INVALID_ARG if channal not valid + */ +esp_err_t adc2_pad_get_io_num(adc2_channel_t channel, gpio_num_t *gpio_num); + +/** + * @brief Configure the ADC2 channel, including setting attenuation. + * + * @note This function also configures the input GPIO pin mux to + * connect it to the ADC2 channel. It must be called before calling + * ``adc2_get_raw()`` for this channel. + * + * The default ADC full-scale voltage is 1.1V. To read higher voltages (up to the pin maximum voltage, + * usually 3.3V) requires setting >0dB signal attenuation for that ADC channel. + * + * When VDD_A is 3.3V: + * + * - 0dB attenuaton (ADC_ATTEN_0db) gives full-scale voltage 1.1V + * - 2.5dB attenuation (ADC_ATTEN_2_5db) gives full-scale voltage 1.5V + * - 6dB attenuation (ADC_ATTEN_6db) gives full-scale voltage 2.2V + * - 11dB attenuation (ADC_ATTEN_11db) gives full-scale voltage 3.9V (see note below) + * + * @note The full-scale voltage is the voltage corresponding to a maximum reading + * (depending on ADC2 configured bit width, this value is: 4095 for 12-bits, 2047 + * for 11-bits, 1023 for 10-bits, 511 for 9 bits.) + * + * @note At 11dB attenuation the maximum voltage is limited by VDD_A, not the full scale voltage. + * + * @param channel ADC2 channel to configure + * @param atten Attenuation level + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten); + +/** + * @brief Take an ADC2 reading on a single channel + * + * @note For a given channel, ``adc2_config_channel_atten()`` + * must be called before the first time this function is called. If Wi-Fi is started via ``esp_wifi_start()``, this + * function will always fail with ``ESP_ERR_TIMEOUT``. + * + * @param channel ADC2 channel to read + * + * @param width_bit Bit capture width for ADC2 + * + * @param raw_out the variable to hold the output data. + * + * @return + * - ESP_OK if success + * - ESP_ERR_TIMEOUT the WIFI is started, using the ADC2 + */ +esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int* raw_out); + /** * @brief Output ADC2 reference voltage to gpio 25 or 26 or 27 * diff --git a/components/driver/include/driver/adc2_wifi_internal.h b/components/driver/include/driver/adc2_wifi_internal.h new file mode 100644 index 000000000..ba5c32ead --- /dev/null +++ b/components/driver/include/driver/adc2_wifi_internal.h @@ -0,0 +1,52 @@ +// 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 _DRIVER_ADC2_INTERNAL_H_ +#define _DRIVER_ADC2_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +/** + * @brief For WIFI module to claim the usage of ADC2. + * + * Other tasks will be forbidden to use ADC2 between ``adc2_wifi_acquire`` and ``adc2_wifi_release``. + * The WIFI module may have to wait for a short time for the current conversion (if exist) to finish. + * + * @return + * - ESP_OK success + * - ESP_ERR_TIMEOUT reserved for future use. Currently the function will wait until success. + */ +esp_err_t adc2_wifi_acquire(); + + +/** + * @brief For WIFI module to let other tasks use the ADC2 when WIFI is not work. + * + * Other tasks will be forbidden to use ADC2 between ``adc2_wifi_acquire`` and ``adc2_wifi_release``. + * Call this function to release the occupation of ADC2 by WIFI. + * + * @return always return ESP_OK. + */ +esp_err_t adc2_wifi_release(); + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_ADC2_INTERNAL_H_*/ + diff --git a/components/driver/include/driver/dac.h b/components/driver/include/driver/dac.h index a5563bb82..f921d98f1 100644 --- a/components/driver/include/driver/dac.h +++ b/components/driver/include/driver/dac.h @@ -29,6 +29,19 @@ typedef enum { DAC_CHANNEL_MAX, } dac_channel_t; +/** + * @brief Get the gpio number of a specific DAC channel. + * + * @param channel Channel to get the gpio number + * + * @param gpio_num output buffer to hold the gpio number + * + * @return + * - ESP_OK if success + * - ESP_ERR_INVALID_ARG if channal not valid + */ +esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num); + /** @cond */ /** * @brief Set DAC output voltage. diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index f31630221..fe3aea71a 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -73,8 +73,31 @@ static const char *RTC_MODULE_TAG = "RTC_MODULE"; return ESP_FAIL;\ } +#define ADC2_CHECK_FUNCTION_RET(fun_ret) do { if(fun_ret!=ESP_OK){\ + ESP_LOGE(RTC_MODULE_TAG,"%s:%d\n",__FUNCTION__,__LINE__);\ + return ESP_FAIL;\ +} }while (0) + portMUX_TYPE rtc_spinlock = portMUX_INITIALIZER_UNLOCKED; static SemaphoreHandle_t rtc_touch_mux = NULL; +/* +In ADC2, there're two locks used for different cases: +1. lock shared with app and WIFI: + when wifi using the ADC2, we assume it will never stop, + so app checks the lock and returns immediately if failed. + +2. lock shared between tasks: + when several tasks sharing the ADC2, we want to guarantee + all the requests will be handled. + Since conversions are short (about 31us), app returns the lock very soon, + we use a spinlock to stand there waiting to do conversions one by one. + +adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock. +*/ +//prevent ADC2 being used by wifi and other tasks at the same time. +static _lock_t adc2_wifi_lock = NULL; +//prevent ADC2 being used by tasks (regardless of WIFI) +portMUX_TYPE adc2_spinlock = portMUX_INITIALIZER_UNLOCKED; typedef struct { TimerHandle_t timer; @@ -903,44 +926,8 @@ esp_err_t touch_pad_filter_delete() } /*--------------------------------------------------------------- - ADC + ADC Common ---------------------------------------------------------------*/ -static esp_err_t adc1_pad_get_io_num(adc1_channel_t channel, gpio_num_t *gpio_num) -{ - RTC_MODULE_CHECK(channel < ADC1_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG); - - switch (channel) { - case ADC1_CHANNEL_0: - *gpio_num = ADC1_CHANNEL_0_GPIO_NUM; - break; - case ADC1_CHANNEL_1: - *gpio_num = ADC1_CHANNEL_1_GPIO_NUM; - break; - case ADC1_CHANNEL_2: - *gpio_num = ADC1_CHANNEL_2_GPIO_NUM; - break; - case ADC1_CHANNEL_3: - *gpio_num = ADC1_CHANNEL_3_GPIO_NUM; - break; - case ADC1_CHANNEL_4: - *gpio_num = ADC1_CHANNEL_4_GPIO_NUM; - break; - case ADC1_CHANNEL_5: - *gpio_num = ADC1_CHANNEL_5_GPIO_NUM; - break; - case ADC1_CHANNEL_6: - *gpio_num = ADC1_CHANNEL_6_GPIO_NUM; - break; - case ADC1_CHANNEL_7: - *gpio_num = ADC1_CHANNEL_7_GPIO_NUM; - break; - default: - return ESP_ERR_INVALID_ARG; - } - - return ESP_OK; -} - static esp_err_t adc_set_fsm_time(int rst_wait, int start_wait, int standby_wait, int sample_cycle) { portENTER_CRITICAL(&rtc_spinlock); @@ -1034,9 +1021,7 @@ static esp_err_t adc_set_atten(adc_unit_t adc_unit, adc_channel_t channel, adc_a void adc_power_on() { portENTER_CRITICAL(&rtc_spinlock); - //Bit1 0:Fsm 1: SW mode - //Bit0 0:SW mode power down 1: SW mode power on - SENS.sar_meas_wait2.force_xpd_sar = ADC_FORCE_ENABLE; + SENS.sar_meas_wait2.force_xpd_sar = ADC_FORCE_FSM; portEXIT_CRITICAL(&rtc_spinlock); } @@ -1115,6 +1100,9 @@ esp_err_t adc_set_data_width(adc_unit_t adc_unit, adc_bits_width_t bits) return ESP_OK; } +/*------------------------------------------------------------------------------------- + * ADC I2S + *------------------------------------------------------------------------------------*/ static esp_err_t adc_set_i2s_data_len(adc_unit_t adc_unit, int patt_len) { ADC_CHECK_UNIT(adc_unit); @@ -1195,6 +1183,45 @@ esp_err_t adc_i2s_mode_init(adc_unit_t adc_unit, adc_channel_t channel) return ESP_OK; } +/*------------------------------------------------------------------------------------- + * ADC1 + *------------------------------------------------------------------------------------*/ +esp_err_t adc1_pad_get_io_num(adc1_channel_t channel, gpio_num_t *gpio_num) +{ + RTC_MODULE_CHECK(channel < ADC1_CHANNEL_MAX, "ADC1 Channel Err", ESP_ERR_INVALID_ARG); + + switch (channel) { + case ADC1_CHANNEL_0: + *gpio_num = ADC1_CHANNEL_0_GPIO_NUM; + break; + case ADC1_CHANNEL_1: + *gpio_num = ADC1_CHANNEL_1_GPIO_NUM; + break; + case ADC1_CHANNEL_2: + *gpio_num = ADC1_CHANNEL_2_GPIO_NUM; + break; + case ADC1_CHANNEL_3: + *gpio_num = ADC1_CHANNEL_3_GPIO_NUM; + break; + case ADC1_CHANNEL_4: + *gpio_num = ADC1_CHANNEL_4_GPIO_NUM; + break; + case ADC1_CHANNEL_5: + *gpio_num = ADC1_CHANNEL_5_GPIO_NUM; + break; + case ADC1_CHANNEL_6: + *gpio_num = ADC1_CHANNEL_6_GPIO_NUM; + break; + case ADC1_CHANNEL_7: + *gpio_num = ADC1_CHANNEL_7_GPIO_NUM; + break; + default: + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten) { RTC_MODULE_CHECK(channel < ADC1_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG); @@ -1216,11 +1243,12 @@ int adc1_get_raw(adc1_channel_t channel) { uint16_t adc_value; RTC_MODULE_CHECK(channel < ADC1_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG); + + adc_power_on(); + portENTER_CRITICAL(&rtc_spinlock); //Adc Controler is Rtc module,not ulp coprocessor SENS.sar_meas_start1.meas1_start_force = 1; - //Bit1=0:Fsm Bit1=1(Bit0=0:PownDown Bit10=1:Powerup) - SENS.sar_meas_wait2.force_xpd_sar = 0; //Disable Amp Bit1=0:Fsm Bit1=1(Bit0=0:PownDown Bit10=1:Powerup) SENS.sar_meas_wait2.force_xpd_amp = 0x2; //Open the ADC1 Data port Not ulp coprocessor @@ -1249,20 +1277,170 @@ int adc1_get_voltage(adc1_channel_t channel) //Deprecated. Use adc1_get_raw() void adc1_ulp_enable(void) { + adc_power_on(); + portENTER_CRITICAL(&rtc_spinlock); SENS.sar_meas_start1.meas1_start_force = 0; SENS.sar_meas_start1.sar1_en_pad_force = 0; SENS.sar_meas_wait2.force_xpd_amp = 0x2; - SENS.sar_meas_wait2.force_xpd_sar = 0; SENS.sar_meas_ctrl.amp_rst_fb_fsm = 0; SENS.sar_meas_ctrl.amp_short_ref_fsm = 0; SENS.sar_meas_ctrl.amp_short_ref_gnd_fsm = 0; SENS.sar_meas_wait1.sar_amp_wait1 = 0x1; - SENS.sar_meas_wait1.sar_amp_wait2 = 1; + SENS.sar_meas_wait1.sar_amp_wait2 = 0x1; SENS.sar_meas_wait2.sar_amp_wait3 = 0x1; portEXIT_CRITICAL(&rtc_spinlock); } +/*--------------------------------------------------------------- + ADC2 +---------------------------------------------------------------*/ +esp_err_t adc2_pad_get_io_num(adc2_channel_t channel, gpio_num_t *gpio_num) +{ + RTC_MODULE_CHECK(channel < ADC2_CHANNEL_MAX, "ADC2 Channel Err", ESP_ERR_INVALID_ARG); + + switch (channel) { + case ADC2_CHANNEL_0: + *gpio_num = ADC2_CHANNEL_0_GPIO_NUM; + break; + case ADC2_CHANNEL_1: + *gpio_num = ADC2_CHANNEL_1_GPIO_NUM; + break; + case ADC2_CHANNEL_2: + *gpio_num = ADC2_CHANNEL_2_GPIO_NUM; + break; + case ADC2_CHANNEL_3: + *gpio_num = ADC2_CHANNEL_3_GPIO_NUM; + break; + case ADC2_CHANNEL_4: + *gpio_num = ADC2_CHANNEL_4_GPIO_NUM; + break; + case ADC2_CHANNEL_5: + *gpio_num = ADC2_CHANNEL_5_GPIO_NUM; + break; + case ADC2_CHANNEL_6: + *gpio_num = ADC2_CHANNEL_6_GPIO_NUM; + break; + case ADC2_CHANNEL_7: + *gpio_num = ADC2_CHANNEL_7_GPIO_NUM; + break; + case ADC2_CHANNEL_8: + *gpio_num = ADC2_CHANNEL_8_GPIO_NUM; + break; + case ADC2_CHANNEL_9: + *gpio_num = ADC2_CHANNEL_9_GPIO_NUM; + break; + default: + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + +esp_err_t adc2_wifi_acquire() +{ + //lazy initialization + //for wifi, block until acquire the lock + _lock_acquire( &adc2_wifi_lock ); + ESP_LOGD( RTC_MODULE_TAG, "Wi-Fi takes adc2 lock." ); + return ESP_OK; +} + +esp_err_t adc2_wifi_release() +{ + RTC_MODULE_CHECK((uint32_t*)adc2_wifi_lock != NULL, "wifi release called before acquire", ESP_ERR_INVALID_STATE ); + + _lock_release( &adc2_wifi_lock ); + ESP_LOGD( RTC_MODULE_TAG, "Wi-Fi returns adc2 lock." ); + return ESP_OK; +} + +static esp_err_t adc2_pad_init(adc2_channel_t channel) +{ + gpio_num_t gpio_num = 0; + ADC2_CHECK_FUNCTION_RET(adc2_pad_get_io_num(channel, &gpio_num)); + ADC2_CHECK_FUNCTION_RET(rtc_gpio_init(gpio_num)); + ADC2_CHECK_FUNCTION_RET(rtc_gpio_output_disable(gpio_num)); + ADC2_CHECK_FUNCTION_RET(rtc_gpio_input_disable(gpio_num)); + ADC2_CHECK_FUNCTION_RET(gpio_set_pull_mode(gpio_num, GPIO_FLOATING)); + return ESP_OK; +} + +esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten) +{ + RTC_MODULE_CHECK(channel < ADC2_CHANNEL_MAX, "ADC2 Channel Err", ESP_ERR_INVALID_ARG); + RTC_MODULE_CHECK(atten <= ADC_ATTEN_11db, "ADC2 Atten Err", ESP_ERR_INVALID_ARG); + + adc2_pad_init(channel); + portENTER_CRITICAL( &adc2_spinlock ); + + //lazy initialization + //avoid collision with other tasks + if ( _lock_try_acquire( &adc2_wifi_lock ) == -1 ) { + //try the lock, return if failed (wifi using). + portEXIT_CRITICAL( &adc2_spinlock ); + return ESP_ERR_TIMEOUT; + } + SENS.sar_atten2 = ( SENS.sar_atten2 & ~(3<<(channel*2)) ) | ((atten&3) << (channel*2)); + _lock_release( &adc2_wifi_lock ); + + portEXIT_CRITICAL( &adc2_spinlock ); + return ESP_OK; +} + +static inline void adc2_config_width(adc_bits_width_t width_bit) +{ + portENTER_CRITICAL(&rtc_spinlock); + //sar_start_force shared with ADC1 + SENS.sar_start_force.sar2_bit_width = width_bit; + portEXIT_CRITICAL(&rtc_spinlock); + + //Invert the adc value,the Output value is invert + SENS.sar_read_ctrl2.sar2_data_inv = 1; + //Set The adc sample width,invert adc value,must digital sar2_bit_width[1:0]=3 + SENS.sar_read_ctrl2.sar2_sample_bit = width_bit; + //Take the control from WIFI + SENS.sar_read_ctrl2.sar2_pwdet_force = 0; +} + +//registers in critical section with adc1: +//SENS_SAR_START_FORCE_REG, +esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int* raw_out) +{ + uint16_t adc_value = 0; + RTC_MODULE_CHECK(channel < ADC2_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG); + + //in critical section with whole rtc module + adc_power_on(); + + //avoid collision with other tasks + portENTER_CRITICAL(&adc2_spinlock); + //lazy initialization + //try the lock, return if failed (wifi using). + if ( _lock_try_acquire( &adc2_wifi_lock ) == -1 ) { + portEXIT_CRITICAL( &adc2_spinlock ); + return ESP_ERR_TIMEOUT; + } + //in critical section with whole rtc module + adc2_config_width( width_bit ); + + //Adc Controler is Rtc module,not ulp coprocessor + SENS.sar_meas_start2.meas2_start_force = 1; //force pad mux and force start + //Open the ADC2 Data port Not ulp coprocessor + SENS.sar_meas_start2.sar2_en_pad_force = 1; //open the ADC2 data port + //Select channel + SENS.sar_meas_start2.sar2_en_pad = 1 << channel; //pad enable + SENS.sar_meas_start2.meas2_start_sar = 0; //start force 0 + SENS.sar_meas_start2.meas2_start_sar = 1; //start force 1 + while (SENS.sar_meas_start2.meas2_done_sar == 0) {}; //read done + adc_value = SENS.sar_meas_start2.meas2_data_sar; + _lock_release( &adc2_wifi_lock ); + portEXIT_CRITICAL(&adc2_spinlock); + + *raw_out = (int)adc_value; + return ESP_OK; +} + esp_err_t adc2_vref_to_gpio(gpio_num_t gpio) { int channel; @@ -1283,19 +1461,19 @@ esp_err_t adc2_vref_to_gpio(gpio_num_t gpio) rtc_gpio_pullup_dis(gpio); rtc_gpio_pulldown_dis(gpio); - SET_PERI_REG_BITS(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0, RTC_CNTL_DBG_ATTEN_S); //Check DBG effect outside sleep mode + RTCCNTL.bias_conf.dbg_atten = 0; //Check DBG effect outside sleep mode //set dtest (MUX_SEL : 0 -> RTC; 1-> vdd_sar2) - SET_PERI_REG_BITS(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_DTEST_RTC, 1, RTC_CNTL_DTEST_RTC_S); //Config test mux to route v_ref to ADC2 Channels + RTCCNTL.test_mux.dtest_rtc = 1; //Config test mux to route v_ref to ADC2 Channels //set ent - SET_PERI_REG_MASK(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_ENT_RTC_M); + RTCCNTL.test_mux.ent_rtc = 1; //set sar2_en_test - SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST_M); + SENS.sar_start_force.sar2_en_test = 1; //force fsm - SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); //Select power source of ADC + SENS.sar_meas_wait2.force_xpd_sar = ADC_FORCE_ENABLE; //Select power source of ADC //set sar2 en force - SET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD_FORCE_M); //Pad bitmap controlled by SW + SENS.sar_meas_start2.sar2_en_pad_force = 1; //Pad bitmap controlled by SW //set en_pad for channels 7,8,9 (bits 0x380) - SET_PERI_REG_BITS(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD, 1<= DAC_CHANNEL_1) && (channel < DAC_CHANNEL_MAX), DAC_ERR_STR_CHANNEL_ERROR, ESP_ERR_INVALID_ARG); RTC_MODULE_CHECK(gpio_num, "Param null", ESP_ERR_INVALID_ARG); @@ -1446,20 +1624,23 @@ static int hall_sensor_get_value() //hall sensor without LNA int Sens_Vp1; int Sens_Vn1; int hall_value; + + adc_power_on(); portENTER_CRITICAL(&rtc_spinlock); - 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.sar_touch_ctrl1.xpd_hall_force = 1; // hall sens force enable + RTCIO.hall_sens.xpd_hall = 1; // xpd hall + SENS.sar_touch_ctrl1.hall_phase_force = 1; // phase force + + RTCIO.hall_sens.hall_phase = 0; // hall phase Sens_Vp0 = adc1_get_raw(ADC1_CHANNEL_0); Sens_Vn0 = adc1_get_raw(ADC1_CHANNEL_3); - SET_PERI_REG_MASK(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE); + RTCIO.hall_sens.hall_phase = 1; Sens_Vp1 = adc1_get_raw(ADC1_CHANNEL_0); Sens_Vn1 = adc1_get_raw(ADC1_CHANNEL_3); - 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); + + SENS.sar_touch_ctrl1.xpd_hall_force = 0; + SENS.sar_touch_ctrl1.hall_phase_force = 0; portEXIT_CRITICAL(&rtc_spinlock); hall_value = (Sens_Vp1 - Sens_Vp0) - (Sens_Vn1 - Sens_Vn0); diff --git a/components/driver/test/test_adc2.c b/components/driver/test/test_adc2.c new file mode 100644 index 000000000..815f5c79a --- /dev/null +++ b/components/driver/test/test_adc2.c @@ -0,0 +1,115 @@ +/* + Tests for the adc2 device driver +*/ +#include "esp_system.h" +#include "driver/adc.h" +#include "driver/dac.h" +#include "unity.h" +#include "esp_system.h" +#include "esp_event_loop.h" +#include "esp_wifi.h" +#include "esp_log.h" +#include "nvs_flash.h" + +static const char* TAG = "test_adc2"; + +#define DEFAULT_SSID "TEST_SSID" +#define DEFAULT_PWD "TEST_PASS" + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + printf("ev_handle_called.\n"); + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); + //do not actually connect in test case + //; + break; + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); + ESP_LOGI(TAG, "got ip:%s\n", + ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED"); + TEST_ESP_OK(esp_wifi_connect()); + break; + default: + break; + } + return ESP_OK; +} + +TEST_CASE("adc2 work with wifi","[adc]") +{ + int read_raw; + int target_value; + + //adc and dac init + TEST_ESP_OK( dac_output_enable( DAC_CHANNEL_1 )); + TEST_ESP_OK( dac_output_enable( DAC_CHANNEL_2 )); + TEST_ESP_OK( dac_output_voltage( DAC_CHANNEL_1, 30 )); + TEST_ESP_OK( dac_output_voltage( DAC_CHANNEL_2, 60 )); + TEST_ESP_OK( adc2_config_channel_atten( ADC2_CHANNEL_8, ADC_ATTEN_0db )); + TEST_ESP_OK( adc2_config_channel_atten( ADC2_CHANNEL_9, ADC_ATTEN_0db )); + + //init wifi + printf("nvs init\n"); + esp_err_t r = nvs_flash_init(); + if (r == ESP_ERR_NVS_NO_FREE_PAGES) { + printf("no free pages, erase..\n"); + TEST_ESP_OK(nvs_flash_erase()); + r = nvs_flash_init(); + } + TEST_ESP_OK( r); + tcpip_adapter_init(); + TEST_ESP_OK(esp_event_loop_init(event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + TEST_ESP_OK(esp_wifi_init(&cfg)); + wifi_config_t wifi_config = { + .sta = { + .ssid = DEFAULT_SSID, + .password = DEFAULT_PWD + }, + }; + TEST_ESP_OK(esp_wifi_set_mode(WIFI_MODE_STA)); + TEST_ESP_OK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + + //test read value + TEST_ESP_OK( adc2_get_raw( ADC2_CHANNEL_8, ADC_WIDTH_12Bit, &read_raw )); + target_value = 30*4096*3/256; //3 = 3.3/1.1 + printf("dac set: %d, adc read: %d (target_value: %d)\n", 30, read_raw, target_value ); + TEST_ASSERT_INT_WITHIN( 600, target_value, read_raw ); + TEST_ESP_OK( adc2_get_raw( ADC2_CHANNEL_9, ADC_WIDTH_12Bit, &read_raw )); + target_value = 60*4096*3/256; + printf("dac set: %d, adc read: %d (target_value: %d)\n", 60, read_raw, target_value ); + TEST_ASSERT_INT_WITHIN( 600, target_value, read_raw ); + + //now start wifi + printf("wifi start...\n"); + TEST_ESP_OK(esp_wifi_start()); + + //test reading during wifi on + TEST_ASSERT_EQUAL( adc2_get_raw( ADC2_CHANNEL_8, ADC_WIDTH_12Bit, &read_raw ), ESP_ERR_TIMEOUT ); + TEST_ASSERT_EQUAL( adc2_get_raw( ADC2_CHANNEL_9, ADC_WIDTH_12Bit, &read_raw ), ESP_ERR_TIMEOUT ); + + //wifi stop again + printf("wifi stop...\n"); + TEST_ESP_OK( esp_wifi_stop() ); + TEST_ESP_OK(esp_wifi_deinit()); + nvs_flash_deinit(); + + //test read value + TEST_ESP_OK( adc2_get_raw( ADC2_CHANNEL_8, ADC_WIDTH_12Bit, &read_raw )); + target_value = 30*4096*3/256; //3 = 3.3/1.1 + printf("dac set: %d, adc read: %d (target_value: %d)\n", 30, read_raw, target_value ); + TEST_ASSERT_INT_WITHIN( 600, target_value, read_raw ); + TEST_ESP_OK( adc2_get_raw( ADC2_CHANNEL_9, ADC_WIDTH_12Bit, &read_raw )); + target_value = 60*4096*3/256; + printf("dac set: %d, adc read: %d (target_value: %d)\n", 60, read_raw, target_value ); + TEST_ASSERT_INT_WITHIN( 600, target_value, read_raw ); + + printf("test passed...\n"); + + TEST_IGNORE_MESSAGE("this test case is ignored due to the critical memory leak of tcpip_adapter and event_loop."); +} diff --git a/components/soc/esp32/include/soc/sens_reg.h b/components/soc/esp32/include/soc/sens_reg.h index 6383ff62c..adc971905 100644 --- a/components/soc/esp32/include/soc/sens_reg.h +++ b/components/soc/esp32/include/soc/sens_reg.h @@ -96,12 +96,18 @@ #define SENS_FORCE_XPD_SAR_M ((SENS_FORCE_XPD_SAR_V)<<(SENS_FORCE_XPD_SAR_S)) #define SENS_FORCE_XPD_SAR_V 0x3 #define SENS_FORCE_XPD_SAR_S 18 +#define SENS_FORCE_XPD_SAR_FSM 0 // Use FSM to control power down +#define SENS_FORCE_XPD_SAR_PD 2 // Force power down +#define SENS_FORCE_XPD_SAR_PU 3 // Force power up /* SENS_FORCE_XPD_AMP : R/W ;bitpos:[17:16] ;default: 2'd0 ; */ /*description: */ #define SENS_FORCE_XPD_AMP 0x00000003 #define SENS_FORCE_XPD_AMP_M ((SENS_FORCE_XPD_AMP_V)<<(SENS_FORCE_XPD_AMP_S)) #define SENS_FORCE_XPD_AMP_V 0x3 #define SENS_FORCE_XPD_AMP_S 16 +#define SENS_FORCE_XPD_AMP_FSM 0 // Use FSM to control power down +#define SENS_FORCE_XPD_AMP_PD 2 // Force power down +#define SENS_FORCE_XPD_AMP_PU 3 // Force power up /* SENS_SAR_AMP_WAIT3 : R/W ;bitpos:[15:0] ;default: 16'd10 ; */ /*description: */ #define SENS_SAR_AMP_WAIT3 0x0000FFFF @@ -958,24 +964,36 @@ #define SENS_AMP_SHORT_REF_GND_FORCE_M ((SENS_AMP_SHORT_REF_GND_FORCE_V)<<(SENS_AMP_SHORT_REF_GND_FORCE_S)) #define SENS_AMP_SHORT_REF_GND_FORCE_V 0x3 #define SENS_AMP_SHORT_REF_GND_FORCE_S 17 +#define SENS_AMP_SHORT_REF_GND_FORCE_FSM 0 // Use FSM to control power down +#define SENS_AMP_SHORT_REF_GND_FORCE_PD 2 // Force power down +#define SENS_AMP_SHORT_REF_GND_FORCE_PU 3 // Force power up /* SENS_AMP_SHORT_REF_FORCE : R/W ;bitpos:[16:15] ;default: 2'b0 ; */ /*description: */ #define SENS_AMP_SHORT_REF_FORCE 0x00000003 #define SENS_AMP_SHORT_REF_FORCE_M ((SENS_AMP_SHORT_REF_FORCE_V)<<(SENS_AMP_SHORT_REF_FORCE_S)) #define SENS_AMP_SHORT_REF_FORCE_V 0x3 #define SENS_AMP_SHORT_REF_FORCE_S 15 +#define SENS_AMP_SHORT_REF_FORCE_FSM 0 // Use FSM to control power down +#define SENS_AMP_SHORT_REF_FORCE_PD 2 // Force power down +#define SENS_AMP_SHORT_REF_FORCE_PU 3 // Force power up /* SENS_AMP_RST_FB_FORCE : R/W ;bitpos:[14:13] ;default: 2'b0 ; */ /*description: */ #define SENS_AMP_RST_FB_FORCE 0x00000003 #define SENS_AMP_RST_FB_FORCE_M ((SENS_AMP_RST_FB_FORCE_V)<<(SENS_AMP_RST_FB_FORCE_S)) #define SENS_AMP_RST_FB_FORCE_V 0x3 #define SENS_AMP_RST_FB_FORCE_S 13 +#define SENS_AMP_RST_FB_FORCE_FSM 0 // Use FSM to control power down +#define SENS_AMP_RST_FB_FORCE_PD 2 // Force power down +#define SENS_AMP_RST_FB_FORCE_PU 3 // Force power up /* SENS_SAR2_RSTB_FORCE : R/W ;bitpos:[12:11] ;default: 2'b0 ; */ /*description: */ #define SENS_SAR2_RSTB_FORCE 0x00000003 #define SENS_SAR2_RSTB_FORCE_M ((SENS_SAR2_RSTB_FORCE_V)<<(SENS_SAR2_RSTB_FORCE_S)) #define SENS_SAR2_RSTB_FORCE_V 0x3 #define SENS_SAR2_RSTB_FORCE_S 11 +#define SENS_SAR2_RSTB_FORCE_FSM 0 // Use FSM to control power down +#define SENS_SAR2_RSTB_FORCE_PD 2 // Force power down +#define SENS_SAR2_RSTB_FORCE_PU 3 // Force power up /* SENS_SAR_RSTB_FSM_IDLE : R/W ;bitpos:[10] ;default: 1'b0 ; */ /*description: */ #define SENS_SAR_RSTB_FSM_IDLE (BIT(10)) diff --git a/docs/api-reference/peripherals/adc.rst b/docs/api-reference/peripherals/adc.rst index 165bea68d..3bad7b24b 100644 --- a/docs/api-reference/peripherals/adc.rst +++ b/docs/api-reference/peripherals/adc.rst @@ -6,16 +6,28 @@ Overview ESP32 integrates two 12-bit SAR (`Successive Approximation Register `_) ADCs (Analog to Digital Converters) and supports measurements on 18 channels (analog enabled pins). Some of these pins can be used to build a programmable gain amplifier which is used for the measurement of small analog signals. -The ADC driver API currently supports ADC1 (9 channels, attached to GPIOs 32 - 39). +The ADC driver API supports ADC1 (9 channels, attached to GPIOs 32 - 39), and ADC2 (10 channels, attached to GPIOs 0, 2, 4, 12 - 15 and 25 - 27). +However, there're some restrictions for the application to use ADC2: -API to support ADC2 is not available yet in ESP-IDF. The reason is that ADC2 is also used by Wi-Fi driver, and the application can only use ADC2 when Wi-Fi driver is not using it (and is not about to use it). This coordination mechanism is work in progress at the moment. +1. The application can use ADC2 only when Wi-Fi driver is not started, since the ADC is also used by the Wi-Fi driver, which has higher priority. +2. Some of the ADC2 pins are used as strapping pins (GPIO 0, 2, 15), so they cannot be used freely. For examples, for official Develop Kits: + + - `ESP32 Core Board V2 / ESP32 DevKitC `_: GPIO 0 cannot be used due to external auto program circuits. + - `ESP-WROVER-KIT V3 `_: GPIO 0, 2, 4 and 15 cannot be used due to external connections for different purposes. Configuration and Reading ADC ----------------------------- -Taking an ADC reading involves configuring the ADC with the desired precision and attenuation by calling functions :cpp:func:`adc1_config_width` and :cpp:func:`adc1_config_channel_atten`. Configuration is done per channel, see :cpp:type:`adc1_channel_t`, set as a parameter of above functions. +The ADC should be configured before reading is taken. -Then it is possible to read ADC conversion result with :cpp:func:`adc1_get_raw`. + - For ADC1, configure desired precision and attenuation by calling functions :cpp:func:`adc1_config_width` and :cpp:func:`adc1_config_channel_atten`. + - For ADC2, configure the attenuation by :cpp:func:`adc2_config_channel_atten`. The reading width of ADC2 is configured every time you take the reading. + +Attenuation configuration is done per channel, see :cpp:type:`adc1_channel_t` and :cpp:type:`adc2_channel_t`, set as a parameter of above functions. + +Then it is possible to read ADC conversion result with :cpp:func:`adc1_get_raw` and :cpp:func:`adc2_get_raw`. Reading width of ADC2 should be set as a parameter of :cpp:func:`adc2_get_raw` instead of in the configuration functions. + +.. note:: Since the ADC2 is shared with the WIFI module, which has higher priority, reading operation of :cpp:func:`adc2_get_raw` will fail between :cpp:func:`esp_wifi_start()` and :cpp:func:`esp_wifi_stop()`. Use the return code to see whether the reading is successful. It is also possible to read the internal hall effect sensor via ADC1 by calling dedicated function :cpp:func:`hall_sensor_read`. Note that even the hall sensor is internal to ESP32, reading from it uses channels 0 and 3 of ADC1 (GPIO 36 and 39). Do not connect anything else to these pins and do not change their configuration. Otherwise it may affect the measurement of low value signal from the sesnor. @@ -37,6 +49,26 @@ Reading voltage on ADC1 channel 0 (GPIO 36):: int val = adc1_get_raw(ADC1_CHANNEL_0); The input voltage in above example is from 0 to 1.1V (0 dB attenuation). The input range can be extended by setting higher attenuation, see :cpp:type:`adc_atten_t`. +An example using the ADC driver including calibration (discussed below) is available in esp-idf: :example:`peripherals/adc` + +Reading voltage on ADC2 channel 7 (GPIO 27):: + + #include + + ... + + int read_raw; + adc2_config_channel_atten( ADC2_CHANNEL_7, ADC_ATTEN_0db ); + + esp_err_t r = adc2_get_raw( ADC2_CHANNEL_7, ADC_WIDTH_12Bit, &read_raw); + if ( r == ESP_OK ) { + printf("%d\n", read_raw ); + } else if ( r == ESP_ERR_TIMEOUT ) { + printf("ADC2 used by Wi-Fi.\n"); + } + +The reading may fail due to collision with Wi-Fi, should check it. +An example using the ADC2 driver to read the output of DAC is available in esp-idf: :example:`peripherals/adc2` Reading the internal hall effect sensor:: @@ -51,8 +83,6 @@ Reading the internal hall effect sensor:: The value read in both these examples is 12 bits wide (range 0-4095). -An example of using the ADC driver including calibration (discussed below) is available in esp-idf: :example:`peripherals/adc` - .. _adc-api-adc-calibration: ADC Calibration diff --git a/examples/peripherals/adc2/Makefile b/examples/peripherals/adc2/Makefile new file mode 100644 index 000000000..2f322486e --- /dev/null +++ b/examples/peripherals/adc2/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := adc2 + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/peripherals/adc2/README.md b/examples/peripherals/adc2/README.md new file mode 100644 index 000000000..7b8871878 --- /dev/null +++ b/examples/peripherals/adc2/README.md @@ -0,0 +1,29 @@ +# Example: ADC2 + +This test code shows how to configure ADC2 and read the voltage connected to GPIO pin. + + +### ADC2 functions: + +ADC1_CHANNEL_7: GPIO27, voltage range [0V..3.1V], the data range [0..4095], +saturates at about 1.1V. + +DAC_CHANNEL_1: GPIO25, output range [0V..3.3V] + + +### Test: + +Please choose the channel (GPIO) to output DAC signals and the channel to input +signals to ADC by menuconfig: + + make menuconfig + +Then connect two pins by a wire directly. + +E.g. Choose GPIO27 for ADC and GPIO 25 for DAC in menuconfig (default option). +Then connect them up. + +Connection of ADC and DAC to the same pin is also allowed. In this case, you don't +have to connect by a wire externally. E.g. choose GPIO 26 for both ADC and DAC in +menuconfig. + diff --git a/examples/peripherals/adc2/main/Kconfig.projbuild b/examples/peripherals/adc2/main/Kconfig.projbuild new file mode 100644 index 000000000..4478e272d --- /dev/null +++ b/examples/peripherals/adc2/main/Kconfig.projbuild @@ -0,0 +1,61 @@ +menu "Example Configuration" + +choice ADC2_EXAMPLE_CHANNEL + bool "ADC2 Channel Num" + default ADC2_EXAMPLE_CHANNEL_7 + help + The channel of ADC2 used in this example. + +config ADC2_EXAMPLE_CHANNEL_0 + bool "ADC2 Channel 0 (GPIO 4)" +config ADC2_EXAMPLE_CHANNEL_1 + bool "ADC2 Channel 1 (GPIO 0)" +config ADC2_EXAMPLE_CHANNEL_2 + bool "ADC2 Channel 2 (GPIO 2)" +config ADC2_EXAMPLE_CHANNEL_3 + bool "ADC2 Channel 3 (GPIO 15)" +config ADC2_EXAMPLE_CHANNEL_4 + bool "ADC2 Channel 4 (GPIO 13)" +config ADC2_EXAMPLE_CHANNEL_5 + bool "ADC2 Channel 5 (GPIO 12)" +config ADC2_EXAMPLE_CHANNEL_6 + bool "ADC2 Channel 6 (GPIO 14)" +config ADC2_EXAMPLE_CHANNEL_7 + bool "ADC2 Channel 7 (GPIO 27)" +config ADC2_EXAMPLE_CHANNEL_8 + bool "ADC2 Channel 8 (GPIO 25)" +config ADC2_EXAMPLE_CHANNEL_9 + bool "ADC2 Channel 9 (GPIO 26)" +endchoice + +config ADC2_EXAMPLE_CHANNEL + int + default 0 if ADC2_EXAMPLE_CHANNEL_0 + default 1 if ADC2_EXAMPLE_CHANNEL_1 + default 2 if ADC2_EXAMPLE_CHANNEL_2 + default 3 if ADC2_EXAMPLE_CHANNEL_3 + default 4 if ADC2_EXAMPLE_CHANNEL_4 + default 5 if ADC2_EXAMPLE_CHANNEL_5 + default 6 if ADC2_EXAMPLE_CHANNEL_6 + default 7 if ADC2_EXAMPLE_CHANNEL_7 + default 8 if ADC2_EXAMPLE_CHANNEL_8 + default 9 if ADC2_EXAMPLE_CHANNEL_9 + +choice DAC_EXAMPLE_CHANNEL + bool "DAC Channel Num" + default DAC_EXAMPLE_CHANNEL_1 + help + The channel of DAC used in this example. + +config DAC_EXAMPLE_CHANNEL_1 + bool "DAC Channel 1 (GPIO25)" +config DAC_EXAMPLE_CHANNEL_2 + bool "DAC Channel 2 (GPIO26)" +endchoice + +config DAC_EXAMPLE_CHANNEL + int + default 1 if DAC_EXAMPLE_CHANNEL_1 + default 2 if DAC_EXAMPLE_CHANNEL_2 + +endmenu diff --git a/examples/peripherals/adc2/main/adc2_example_main.c b/examples/peripherals/adc2/main/adc2_example_main.c new file mode 100644 index 000000000..ed574be2b --- /dev/null +++ b/examples/peripherals/adc2/main/adc2_example_main.c @@ -0,0 +1,59 @@ +/* ADC2 Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "driver/adc.h" +#include "driver/dac.h" +#include "esp_system.h" +#include "esp_adc_cal.h" + +#define DAC_EXAMPLE_CHANNEL CONFIG_DAC_EXAMPLE_CHANNEL +#define ADC2_EXAMPLE_CHANNEL CONFIG_ADC2_EXAMPLE_CHANNEL + +void app_main(void) +{ + uint8_t output_data=0; + int read_raw; + esp_err_t r; + + gpio_num_t adc_gpio_num, dac_gpio_num; + + assert( adc2_pad_get_io_num( ADC2_EXAMPLE_CHANNEL, &adc_gpio_num ) == ESP_OK ); + assert( dac_pad_get_io_num( DAC_EXAMPLE_CHANNEL, &dac_gpio_num ) == ESP_OK ); + + printf("ADC channel %d @ GPIO %d, DAC channel %d @ GPIO %d.\n", ADC2_EXAMPLE_CHANNEL, adc_gpio_num, + DAC_EXAMPLE_CHANNEL, dac_gpio_num ); + + dac_output_enable( DAC_EXAMPLE_CHANNEL ); + + //be sure to do the init before using adc2. + printf("adc2_init...\n"); + adc2_config_channel_atten( ADC2_EXAMPLE_CHANNEL, ADC_ATTEN_0db ); + + vTaskDelay(2 * portTICK_PERIOD_MS); + + printf("start conversion.\n"); + while(1) { + dac_output_voltage( DAC_EXAMPLE_CHANNEL, output_data++ ); + r = adc2_get_raw( ADC2_EXAMPLE_CHANNEL, ADC_WIDTH_12Bit, &read_raw); + if ( r == ESP_OK ) { + printf("%d: %d\n", output_data, read_raw ); + } else if ( r == ESP_ERR_INVALID_STATE ) { + printf("ADC2 not initialized yet.\n"); + } else if ( r == ESP_ERR_TIMEOUT ) { + //This can not happen in this example. But if WiFi is in use, such error code could be returned. + printf("ADC2 is in use by Wi-Fi.\n"); + } + vTaskDelay( 2 * portTICK_PERIOD_MS ); + } +} diff --git a/examples/peripherals/adc2/main/component.mk b/examples/peripherals/adc2/main/component.mk new file mode 100644 index 000000000..44bd2b527 --- /dev/null +++ b/examples/peripherals/adc2/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +#