adc: support adc dma driver on all chips

This commit is contained in:
Armando
2021-12-15 14:15:32 +08:00
committed by Armando (Dou Yiwen)
parent 5ddce053ea
commit 4dc0d6b2fe
81 changed files with 4156 additions and 5420 deletions

View File

@@ -0,0 +1,67 @@
# ADC DMA Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example shows how to use ADC Continuous Read Mode (DMA Mode) to read from GPIO pins via on-chip ADC module(s).
## How to use example
### Hardware Required
* A development board with ESP32, ESP32-S and ESP32-C SoC
* A USB cable for power supply and programming
Please refer to the `channel` array defined in `adc_dma_example_main.c`. These GPIOs should be connected to voltage sources (0 ~ 3.3V). If other ADC unit(s) / channel(s) are selected in your application,
feel free to modify these definitions.
### Configure the project
```
idf.py menuconfig
```
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT 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
Running this example, you will see the following log output on the serial monitor:
```
I (338) ADC DMA: adc_pattern[0].atten is :0
I (348) ADC DMA: adc_pattern[0].channel is :2
I (348) ADC DMA: adc_pattern[0].unit is :0
I (358) ADC DMA: adc_pattern[1].atten is :0
I (358) ADC DMA: adc_pattern[1].channel is :3
I (368) ADC DMA: adc_pattern[1].unit is :0
I (368) ADC DMA: adc_pattern[2].atten is :0
I (378) ADC DMA: adc_pattern[2].channel is :0
I (378) ADC DMA: adc_pattern[2].unit is :1
I (388) TASK:: ret is 0, ret_num is 256
I (388) ADC DMA: Unit: 1,_Channel: 2, Value: bec
I (398) ADC DMA: Unit: 2,_Channel: 0, Value: 9cb
I (398) ADC DMA: Unit: 1,_Channel: 3, Value: acb
I (408) ADC DMA: Unit: 2,_Channel: 0, Value: 966
I (408) ADC DMA: Unit: 1,_Channel: 2, Value: b63
I (418) ADC DMA: Unit: 2,_Channel: 0, Value: 8ff
I (418) ADC DMA: Unit: 1,_Channel: 3, Value: a6b
I (428) ADC DMA: Unit: 2,_Channel: 0, Value: 8a2
...
```
## Troubleshooting
* program upload failure
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@@ -0,0 +1,176 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/adc.h"
#define TIMES 256
#define GET_UNIT(x) ((x>>3) & 0x1)
#if CONFIG_IDF_TARGET_ESP32
#define ADC_RESULT_BYTE 2
#define ADC_CONV_LIMIT_EN 1 //For ESP32, this should always be set to 1
#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 //ESP32 only supports ADC1 DMA mode
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_RESULT_BYTE 2
#define ADC_CONV_LIMIT_EN 0
#define ADC_CONV_MODE ADC_CONV_BOTH_UNIT
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP8684
#define ADC_RESULT_BYTE 4
#define ADC_CONV_LIMIT_EN 0
#define ADC_CONV_MODE ADC_CONV_ALTER_UNIT //ESP32C3 only supports alter mode
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#elif CONFIG_IDF_TARGET_ESP32S3
#define ADC_RESULT_BYTE 4
#define ADC_CONV_LIMIT_EN 0
#define ADC_CONV_MODE ADC_CONV_BOTH_UNIT
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#endif
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP8684
static uint16_t adc1_chan_mask = BIT(2) | BIT(3);
static uint16_t adc2_chan_mask = BIT(0);
static adc_channel_t channel[3] = {ADC1_CHANNEL_2, ADC1_CHANNEL_3, (ADC2_CHANNEL_0 | 1 << 3)};
#endif
#if CONFIG_IDF_TARGET_ESP32S2
static uint16_t adc1_chan_mask = BIT(2) | BIT(3);
static uint16_t adc2_chan_mask = BIT(0);
static adc_channel_t channel[3] = {ADC1_CHANNEL_2, ADC1_CHANNEL_3, (ADC2_CHANNEL_0 | 1 << 3)};
#endif
#if CONFIG_IDF_TARGET_ESP32
static uint16_t adc1_chan_mask = BIT(7);
static uint16_t adc2_chan_mask = 0;
static adc_channel_t channel[1] = {ADC1_CHANNEL_7};
#endif
static const char *TAG = "ADC DMA";
static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
{
adc_digi_init_config_t adc_dma_config = {
.max_store_buf_size = 1024,
.conv_num_each_intr = TIMES,
.adc1_chan_mask = adc1_chan_mask,
.adc2_chan_mask = adc2_chan_mask,
};
ESP_ERROR_CHECK(adc_digi_initialize(&adc_dma_config));
adc_digi_configuration_t dig_cfg = {
.conv_limit_en = ADC_CONV_LIMIT_EN,
.conv_limit_num = 250,
.sample_freq_hz = 10 * 1000,
.conv_mode = ADC_CONV_MODE,
.format = ADC_OUTPUT_TYPE,
};
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
dig_cfg.pattern_num = channel_num;
for (int i = 0; i < channel_num; i++) {
uint8_t unit = GET_UNIT(channel[i]);
uint8_t ch = channel[i] & 0x7;
adc_pattern[i].atten = ADC_ATTEN_DB_0;
adc_pattern[i].channel = ch;
adc_pattern[i].unit = unit;
adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
ESP_LOGI(TAG, "adc_pattern[%d].atten is :%x", i, adc_pattern[i].atten);
ESP_LOGI(TAG, "adc_pattern[%d].channel is :%x", i, adc_pattern[i].channel);
ESP_LOGI(TAG, "adc_pattern[%d].unit is :%x", i, adc_pattern[i].unit);
}
dig_cfg.adc_pattern = adc_pattern;
ESP_ERROR_CHECK(adc_digi_controller_configure(&dig_cfg));
}
#if !CONFIG_IDF_TARGET_ESP32
static bool check_valid_data(const adc_digi_output_data_t *data)
{
const unsigned int unit = data->type2.unit;
if (unit > 2) return false;
if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit)) return false;
return true;
}
#endif
void app_main(void)
{
esp_err_t ret;
uint32_t ret_num = 0;
uint8_t result[TIMES] = {0};
memset(result, 0xcc, TIMES);
continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));
adc_digi_start();
while(1) {
ret = adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);
if (ret == ESP_OK || ret == ESP_ERR_INVALID_STATE) {
if (ret == ESP_ERR_INVALID_STATE) {
/**
* @note 1
* Issue:
* As an example, we simply print the result out, which is super slow. Therefore the conversion is too
* fast for the task to handle. In this condition, some conversion results lost.
*
* Reason:
* When this error occurs, you will usually see the task watchdog timeout issue also.
* Because the conversion is too fast, whereas the task calling `adc_digi_read_bytes` is slow.
* So `adc_digi_read_bytes` will hardly block. Therefore Idle Task hardly has chance to run. In this
* example, we add a `vTaskDelay(1)` below, to prevent the task watchdog timeout.
*
* Solution:
* Either decrease the conversion speed, or increase the frequency you call `adc_digi_read_bytes`
*/
}
ESP_LOGI("TASK:", "ret is %x, ret_num is %d", ret, ret_num);
for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE) {
adc_digi_output_data_t *p = (void*)&result[i];
#if CONFIG_IDF_TARGET_ESP32
ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %x", 1, p->type1.channel, p->type1.data);
#else
if (ADC_CONV_MODE == ADC_CONV_BOTH_UNIT || ADC_CONV_MODE == ADC_CONV_ALTER_UNIT) {
if (check_valid_data(p)) {
ESP_LOGI(TAG, "Unit: %d,_Channel: %d, Value: %x", p->type2.unit+1, p->type2.channel, p->type2.data);
} else {
// abort();
ESP_LOGI(TAG, "Invalid data [%d_%d_%x]", p->type2.unit+1, p->type2.channel, p->type2.data);
}
}
#if CONFIG_IDF_TARGET_ESP32S2
else if (ADC_CONV_MODE == ADC_CONV_SINGLE_UNIT_2) {
ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %x", 2, p->type1.channel, p->type1.data);
} else if (ADC_CONV_MODE == ADC_CONV_SINGLE_UNIT_1) {
ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %x", 1, p->type1.channel, p->type1.data);
}
#endif //#if CONFIG_IDF_TARGET_ESP32S2
#endif
}
//See `note 1`
vTaskDelay(1);
} else if (ret == ESP_ERR_TIMEOUT) {
/**
* ``ESP_ERR_TIMEOUT``: If ADC conversion is not finished until Timeout, you'll get this return error.
* Here we set Timeout ``portMAX_DELAY``, so you'll never reach this branch.
*/
ESP_LOGW(TAG, "No data, increase timeout or reduce conv_num_each_intr");
vTaskDelay(1000);
}
}
adc_digi_stop();
ret = adc_digi_deinitialize();
assert(ret == ESP_OK);
}

View File

@@ -1,63 +0,0 @@
| Supported Targets | ESP32-C3 |
| ----------------- | -------- |
# ADC DMA Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example shows how to use DMA-Read-APIs and Single-Read-APIs to read voltage from GPIO pins via ADC controller.
## How to use example
### Hardware Required
* A development board with ESP32C3 SoC
* A USB cable for power supply and programming
For `single_read` (Single-Read-APIs example), we use `ADC1_CHANNEL_2`, `ADC1_CHANNEL_3`, `ADC1_CHANNEL_4`, `ADC2_CHANNEL_0`. Hence we need to connect voltage sources (0 ~ 3.3V) to GPIO2, GPIO3, GPIO4, GPIO5 respectively.
For `continuous_read` (DMA-Read-APIs example), we use `ADC1_CHANNEL_0`, `ADC1_CHANNEL_1` and `ADC2_CHANNEL_0`. Therefore, GPIO0, GPIO1 and GPIO5 should be connected to voltage sources (0 ~ 3.3V).
If other ADC units/channels are selected in your application, you need to change the GPIO pin (please refer to the `ESP32C3 Technical Reference Manual`).
### Configure the project
```
idf.py menuconfig
```
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT 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
Running this example, you will see the following log output on the serial monitor:
```
I (322) ADC1_CH2: 7c8
I (322) ADC1_CH3: 278
I (322) ADC1_CH4: d4b
I (322) ADC2_CH0: 48
```
```
ADC1_CH0: 61b
ADC1_CH1: 39b
ADC2_CH0: 4b
```
## Troubleshooting
* program upload failure
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@@ -1,124 +0,0 @@
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/adc.h"
#define TIMES 256
static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
{
esp_err_t ret = ESP_OK;
assert(ret == ESP_OK);
adc_digi_init_config_t adc_dma_config = {
.max_store_buf_size = 1024,
.conv_num_each_intr = 256,
.adc1_chan_mask = adc1_chan_mask,
.adc2_chan_mask = adc2_chan_mask,
};
ret = adc_digi_initialize(&adc_dma_config);
assert(ret == ESP_OK);
adc_digi_pattern_table_t adc_pattern[10] = {0};
//Do not set the sampling frequency out of the range between `SOC_ADC_SAMPLE_FREQ_THRES_LOW` and `SOC_ADC_SAMPLE_FREQ_THRES_HIGH`
adc_digi_config_t dig_cfg = {
.conv_limit_en = 0,
.conv_limit_num = 250,
.sample_freq_hz = 620,
};
dig_cfg.adc_pattern_len = channel_num;
for (int i = 0; i < channel_num; i++) {
uint8_t unit = ((channel[i] >> 3) & 0x1);
uint8_t ch = channel[i] & 0x7;
adc_pattern[i].atten = ADC_ATTEN_DB_0;
adc_pattern[i].channel = ch;
adc_pattern[i].unit = unit;
}
dig_cfg.adc_pattern = adc_pattern;
ret = adc_digi_controller_config(&dig_cfg);
assert(ret == ESP_OK);
}
static bool check_valid_data(const adc_digi_output_data_t *data)
{
const unsigned int unit = data->type2.unit;
if (unit > 2) return false;
if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit)) return false;
return true;
}
static void continuous_read(void *arg)
{
esp_err_t ret;
uint32_t ret_num = 0;
uint8_t result[TIMES] = {0};
memset(result, 0xcc, TIMES);
uint16_t adc1_chan_mask = BIT(0) | BIT(1);
uint16_t adc2_chan_mask = BIT(0);
adc_channel_t channel[3] = {ADC1_CHANNEL_0, ADC1_CHANNEL_1, (ADC2_CHANNEL_0 | 1 << 3)};
continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));
adc_digi_start();
int n = 20;
while(n--) {
ret = adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);
for (int i = 0; i < ret_num; i+=4) {
adc_digi_output_data_t *p = (void*)&result[i];
if (check_valid_data(p)) {
printf("ADC%d_CH%d: %x\n", p->type2.unit+1, p->type2.channel, p->type2.data);
} else {
printf("Invalid data [%d_%d_%x]\n", p->type2.unit+1, p->type2.channel, p->type2.data);
}
}
// If you see task WDT in this task, it means the conversion is too fast for the task to handle
}
adc_digi_stop();
ret = adc_digi_deinitialize();
assert(ret == ESP_OK);
}
static void single_read(void *arg)
{
esp_err_t ret;
int adc1_reading[3] = {0xcc};
int adc2_reading[1] = {0xcc};
const char TAG_CH[][10] = {"ADC1_CH2", "ADC1_CH3","ADC1_CH4", "ADC2_CH0"};
adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
adc1_config_channel_atten(ADC1_CHANNEL_2, ADC_ATTEN_DB_0);
adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_6);
adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_0);
adc2_config_channel_atten(ADC2_CHANNEL_0, ADC_ATTEN_DB_0);
int n = 20;
while (n--) {
adc1_reading[0] = adc1_get_raw(ADC1_CHANNEL_2);
adc1_reading[1] = adc1_get_raw(ADC1_CHANNEL_3);
adc1_reading[2] = adc1_get_raw(ADC1_CHANNEL_4);
for (int i = 0; i < 3; i++) {
ESP_LOGI(TAG_CH[i], "%x", adc1_reading[i]);
}
ret = adc2_get_raw(ADC2_CHANNEL_0, ADC_WIDTH_BIT_12, &adc2_reading[0]);
assert(ret == ESP_OK);
ESP_LOGI(TAG_CH[3], "%x", adc2_reading[0]);
}
}
void app_main(void)
{
single_read(NULL);
continuous_read(NULL);
}

View File

@@ -1,3 +1,9 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* ADC1 Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@@ -11,7 +17,7 @@
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc_common.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"