diff --git a/components/esp8266/Kconfig b/components/esp8266/Kconfig index 8233ad1f..fe7cb1b7 100644 --- a/components/esp8266/Kconfig +++ b/components/esp8266/Kconfig @@ -444,5 +444,16 @@ config ESP_PHY_INIT_DATA_IN_PARTITION If unsure, choose 'n'. +config ESP_PHY_INIT_DATA_VDD33_CONST + int "vdd33_const value" + range 0 255 + default 33 + help + vdd33_const provides ADC mode settings, i.e. selecting system voltage or external voltage measurements. + When measuring system voltage, it must be set to 255. + To read the external voltage on TOUT(ADC) pin, vdd33_const need less than 255 + When the ADC reference voltage is set to the actual VDD33 power supply voltage, the value range of vdd33_const is [18,36], the unit is 0.1V. + When the ADC reference voltage is set to the default value of 3.3V as the supply voltage, the range of vdd33_const is [0, 18] or (36, 255). + endmenu # PHY diff --git a/components/esp8266/driver/adc.c b/components/esp8266/driver/adc.c index 3c33be6c..df23e84b 100644 --- a/components/esp8266/driver/adc.c +++ b/components/esp8266/driver/adc.c @@ -12,19 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. - #include #include - +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" #include "esp_attr.h" #include "esp_err.h" #include "esp_log.h" - +#include "esp_phy_init.h" +#include "esp_heap_caps.h" #include "driver/adc.h" -#define ENTER_CRITICAL() portENTER_CRITICAL() -#define EXIT_CRITICAL() portEXIT_CRITICAL() - +static const char *TAG = "adc"; #define ADC_CHECK(a, str, ret_val) \ if (!(a)) { \ @@ -36,19 +36,112 @@ extern uint16_t test_tout(); extern void phy_adc_read_fast(uint16_t *adc_addr, uint16_t adc_num, uint8_t adc_clk_div); extern uint16_t phy_get_vdd33(); -uint16_t adc_read() +typedef struct { + adc_config_t config; + SemaphoreHandle_t adc_mux; +} adc_handle_t; + +adc_handle_t *adc_handle = NULL; + +esp_err_t adc_read(uint16_t *data) { - uint16_t ret = test_tout(0); + ADC_CHECK(data, "parameter pointer is empty", ESP_ERR_INVALID_ARG); + ADC_CHECK(adc_handle, "ADC has not been initialized yet.", ESP_FAIL); + uint16_t ret = 0; + xSemaphoreTake(adc_handle->adc_mux, portMAX_DELAY); - if (ret != 0xFFFF) { - // The working voltage of ADC is designed according to 1.1v. Later, the actual working voltage of ADC is increased to 1.2v, so this scale is added. - ret = ret * 12 / 11; + if (adc_handle->config.mode == ADC_READ_TOUT_MODE) { + ret = test_tout(0); - if (ret > 1023) { - // 10-bit precision ADC - ret = 1023; + if (ret != 0xFFFF) { + // The working voltage of ADC is designed according to 1.1v. Later, the actual working voltage of ADC is increased to 1.2v, so this scale is added. + ret = ret * 12 / 11; + + if (ret > 1023) { + // 10-bit precision ADC + ret = 1023; + } + } + } else if (adc_handle->config.mode == ADC_READ_VDD_MODE) { + ret = phy_get_vdd33(); + + if (ret != 0xFFFF) { + // The working voltage of ADC is designed according to 1.1v. Later, the actual working voltage of ADC is increased to 1.2v, so this scale is added. + ret = ret * 12 / 11; } } - return ret; + *data = ret; + xSemaphoreGive(adc_handle->adc_mux); + return ESP_OK; +} + +esp_err_t adc_read_fast(uint16_t *data, uint16_t len) +{ + ADC_CHECK(data && len > 0, "parameter pointer is empty", ESP_ERR_INVALID_ARG); + ADC_CHECK(adc_handle, "ADC has not been initialized yet.", ESP_FAIL); + ADC_CHECK(adc_handle->config.mode == ADC_READ_TOUT_MODE, "adc_read_fast can only be used in ADC_READ_TOUT_MODE mode", ESP_ERR_INVALID_ARG); + ADC_CHECK(adc_handle->config.clk_div >= 8 && adc_handle->config.clk_div <= 32, "ADC sample collection clock=80M/clk_div, range[8, 32]", ESP_FAIL); + uint16_t i; + uint16_t ret; + + xSemaphoreTake(adc_handle->adc_mux, portMAX_DELAY); + phy_adc_read_fast(data, len, adc_handle->config.clk_div); + + for (i = 0; i < len; i++) { + ret = data[i]; + + if (ret != 0xFFFF) { + // The working voltage of ADC is designed according to 1.1v. Later, the actual working voltage of ADC is increased to 1.2v, so this scale is added. + ret = ret * 12 / 11; + + if (ret > 1023) { + // 10-bit precision ADC + ret = 1023; + } + } + + data[i] = ret; + } + + xSemaphoreGive(adc_handle->adc_mux); + return ESP_OK; +} + +esp_err_t adc_deinit() +{ + ADC_CHECK(adc_handle, "ADC has not been initialized yet.", ESP_FAIL); + + if (adc_handle->adc_mux) { + vSemaphoreDelete(adc_handle->adc_mux); + } + + heap_caps_free(adc_handle); + adc_handle = NULL; + return ESP_OK; +} + +esp_err_t adc_init(adc_config_t *config) +{ + ADC_CHECK(config, "config error", ESP_ERR_INVALID_ARG); + ADC_CHECK(NULL == adc_handle, "adc has been initialized", ESP_FAIL); + uint8_t vdd33_const; + esp_phy_init_data_t *phy_init_data; + + phy_init_data = (esp_phy_init_data_t *)esp_phy_get_init_data(); + vdd33_const = phy_init_data->params[107]; + ADC_CHECK((config->mode == ADC_READ_TOUT_MODE) ? (vdd33_const <= 255) : true, "To read the external voltage on TOUT(ADC) pin, vdd33_const need less than 255", ESP_FAIL); + ADC_CHECK((config->mode == ADC_READ_VDD_MODE) ? (vdd33_const == 255) : true, "When adc measuring system voltage, vdd33_const must be set to 255,", ESP_FAIL); + + adc_handle = heap_caps_malloc(sizeof(adc_handle_t), MALLOC_CAP_8BIT); + ADC_CHECK(adc_handle, "adc handle malloc error", ESP_ERR_NO_MEM); + memcpy(&adc_handle->config, config, sizeof(adc_config_t)); + adc_handle->adc_mux = xSemaphoreCreateMutex(); + + if (NULL == adc_handle->adc_mux) { + adc_deinit(); + ADC_CHECK(false, "Semaphore create fail", ESP_ERR_NO_MEM); + } + + return ESP_OK; } \ No newline at end of file diff --git a/components/esp8266/include/driver/adc.h b/components/esp8266/include/driver/adc.h index d253b0a7..c7afbdfe 100644 --- a/components/esp8266/include/driver/adc.h +++ b/components/esp8266/include/driver/adc.h @@ -21,13 +21,79 @@ extern "C" { #endif +/** + * @brief ADC working mode enumeration + */ +typedef enum { + ADC_READ_TOUT_MODE, + ADC_READ_VDD_MODE +} adc_mode_t; /** - * @brief Measure the input voltage of TOUT pin 6, unit : 1/1023 V. + * @brief ADC initialization parameter structure type definition + */ +typedef struct { + adc_mode_t mode; + uint8_t clk_div; // ADC sample collection clock=80M/clk_div, range[8, 32] +} adc_config_t; + +/** + * @brief Single measurement of TOUT(ADC) pin, unit : 1/1023 V or VDD pin, uint: 1 mV * - * @return Input voltage of TOUT pin 6, unit : 1/1023 V + * @note When measuring VDD pin voltage, the TOUT(ADC) pin must be left floating. + * + * @param data Pointer to accept adc value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL adc has not been initialized yet */ -uint16_t adc_read(); +esp_err_t adc_read(uint16_t *data); + +/** + * @brief Measure the input voltage of TOUT(ADC) pin, unit : 1/1023 V. + * + * @note Wi-Fi and interrupts need to be turned off. + * + * @param data Pointer to accept adc value. Input voltage of TOUT(ADC) pin, unit : 1/1023 V + * @param len Receiving length of ADC value, range [1, 65535] + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL adc has not been initialized yet + */ +esp_err_t adc_read_fast(uint16_t *data, uint16_t len); + +/** + * @brief Deinit the adc + * + * @return + * - ESP_OK Success + * - ESP_FAIL adc has not been initialized yet + */ +esp_err_t adc_deinit(); + +/** + * @brief Initialize the adc + * + * @note First modify menuconfig->Component config->PHY->vdd33_const value, vdd33_const provides ADC mode settings, + * i.e. selecting system voltage or external voltage measurements. + * When measuring system voltage, it must be set to 255. + * To read the external voltage on TOUT(ADC) pin, vdd33_const need less than 255 + * When the ADC reference voltage is set to the actual VDD33 power supply voltage, the value range of vdd33_const is [18,36], the unit is 0.1V. + * When the ADC reference voltage is set to the default value of 3.3V as the supply voltage, the range of vdd33_const is [0, 18] or (36, 255). + * + * @param config Pointer to deliver initialize configuration parameter + * + * @return + * - ESP_OK Success + * - ESP_ERR_NO_MEM malloc fail + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL adc has been initialized + */ +esp_err_t adc_init(adc_config_t *config); #ifdef __cplusplus } diff --git a/components/esp8266/source/phy_init_data.h b/components/esp8266/source/phy_init_data.h index 4fc62494..55853b19 100644 --- a/components/esp8266/source/phy_init_data.h +++ b/components/esp8266/source/phy_init_data.h @@ -132,7 +132,11 @@ static const esp_phy_init_data_t phy_init_data= { { 0x00, 0x00, 0x00, +#ifdef CONFIG_ESP_PHY_INIT_DATA_VDD33_CONST + CONFIG_ESP_PHY_INIT_DATA_VDD33_CONST, +#else 0x00, +#endif 0x00, 0x00, 0x00, diff --git a/examples/peripherals/adc/Makefile b/examples/peripherals/adc/Makefile new file mode 100644 index 00000000..27b269e8 --- /dev/null +++ b/examples/peripherals/adc/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 := adc_example + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/peripherals/adc/README.md b/examples/peripherals/adc/README.md new file mode 100644 index 00000000..e5756705 --- /dev/null +++ b/examples/peripherals/adc/README.md @@ -0,0 +1,69 @@ +# _ADC Example_ + +_This is a example for reading ADC values._ + +1. An analog-to-digital converter (ADC) is used to convert analog signals into digital forms. ESP8266 has a built-in 10-bit ADC, only one ADC channel. + +2. ADC channel in ESP8266 is multiplexed with system voltage. Therefore, we can set it to measure system voltage or external voltage. When reading the external voltage, the input voltage range of TOUT(ADC) pin are 0-1.0V. + +3. ` menuconfig - > Component config - > PHY - > vdd33_const value ` provides the setting of ADC mode, that is, whether the system voltage or external voltage is being measured. + +4. ` Menuconfig - > Component config - > PHY - > vdd33_const value ` must be set to 255 to read the system voltage, that is, the voltage on the VDD pin of ESP8266. To read the external voltage on TOUT(ADC) pin, vdd33_const need less than 255 +When the ADC reference voltage is set to the actual VDD33 power supply voltage, the value range of vdd33_const is [18,36], the unit is 0.1V. +When the ADC reference voltage is set to the default value of 3.3V as the supply voltage, the range of vdd33_const is [0, 18] or (36, 255). + +## How to use example + +### Hardware Required + +1. To support external voltage range (0-3.3V), connect TOUT(ADC) pin to resistance divider network (R1 220K and R2 100K) + +![adc](adc.png) + +2. When measuring VDD pin voltage, the TOUT(ADC) pin must be left floating. At this point, resistance divider networks R1, R2 need to be removed. + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. +* `make monitor` baud rate set to what you set in the example. +* Modify `menuconfig->Component config->PHY->vdd33_const value`, vdd33_const provides ADC mode settings + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (482) adc example: adc read: 29 + +I (482) adc example: adc read fast: + +29 +29 +30 +29 +29 +29 +29 +29 +29 +29 +29 +29 +29 +29 +29 +``` \ No newline at end of file diff --git a/examples/peripherals/adc/adc.png b/examples/peripherals/adc/adc.png new file mode 100644 index 00000000..0d8c2de9 Binary files /dev/null and b/examples/peripherals/adc/adc.png differ diff --git a/examples/peripherals/adc/main/adc_example_main.c b/examples/peripherals/adc/main/adc_example_main.c new file mode 100644 index 00000000..d4ff81a4 --- /dev/null +++ b/examples/peripherals/adc/main/adc_example_main.c @@ -0,0 +1,55 @@ +/* adc 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 +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/adc.h" +#include "esp_log.h" + +static const char *TAG = "adc example"; + +static void adc_task() +{ + int x; + uint16_t adc_data[100]; + + while (1) { + if (ESP_OK == adc_read(&adc_data[0])) { + ESP_LOGI(TAG, "adc read: %d\r\n", adc_data[0]); + } + + ESP_LOGI(TAG, "adc read fast:\r\n"); + + if (ESP_OK == adc_read_fast(adc_data, 100)) { + for (x = 0; x < 100; x++) { + printf("%d\n", adc_data[x]); + } + } + + vTaskDelay(1000 / portTICK_RATE_MS); + } +} + +void app_main() +{ + // 1. init adc + adc_config_t adc_config; + + // Depend on menuconfig->Component config->PHY->vdd33_const value + // When measuring system voltage(ADC_READ_VDD_MODE), vdd33_const must be set to 255. + adc_config.mode = ADC_READ_TOUT_MODE; + adc_config.clk_div = 8; // ADC sample collection clock = 80MHz/clk_div = 10MHz + ESP_ERROR_CHECK(adc_init(&adc_config)); + + // 2. Create a adc task to read adc value + xTaskCreate(adc_task, "adc_task", 1024, NULL, 5, NULL); +} diff --git a/examples/peripherals/adc/main/component.mk b/examples/peripherals/adc/main/component.mk new file mode 100644 index 00000000..44bd2b52 --- /dev/null +++ b/examples/peripherals/adc/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +#