mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'bugfix/lp-adc-raw-read-snapshot' into 'master'
test(ulp): snapshot LP ADC raw reads after GPIO settle Closes IDFCI-5376 See merge request espressif/esp-idf!52022
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "ulp_lp_core_lp_adc_shared.h"
|
||||
|
||||
volatile int adc_raw[8];
|
||||
volatile uint32_t adc_scan_seq;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
@@ -14,6 +15,7 @@ int main(void)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
lp_core_lp_adc_read_channel_raw(ADC_UNIT_1, i, (int *)&adc_raw[i]);
|
||||
}
|
||||
adc_scan_seq++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
@@ -21,6 +22,10 @@ static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
#if SOC_LP_ADC_SUPPORTED
|
||||
void lp_adc_test_teardown(void);
|
||||
#endif
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
@@ -29,6 +34,9 @@ void setUp(void)
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
#if SOC_LP_ADC_SUPPORTED
|
||||
lp_adc_test_teardown();
|
||||
#endif
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "hal/adc_types.h"
|
||||
@@ -15,6 +18,7 @@
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "driver/temperature_sensor.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
@@ -31,6 +35,14 @@ extern const uint8_t lp_core_main_adc_bin_end[] asm("_binary_lp_core_test_app_
|
||||
|
||||
#define ADC_GET_IO_NUM(unit, channel) (adc_channel_io_map[unit][channel])
|
||||
|
||||
#define ADC_SETTLE_DELAY_MS 100 /* RC settle of the weak internal pulls */
|
||||
#define ADC_SCAN_TIMEOUT_MS 100 /* upper bound on LP scan completion */
|
||||
#define ADC_SCANS_TO_WAIT 2 /* completed LP scans before taking a snapshot */
|
||||
|
||||
static bool s_lp_adc_inited;
|
||||
static bool s_lp_firmware_started;
|
||||
static adc_unit_t s_lp_adc_unit;
|
||||
|
||||
static void test_adc_set_io_level(adc_unit_t unit, adc_channel_t channel, bool level)
|
||||
{
|
||||
TEST_ASSERT(channel < ADC_LL_CHANNEL_NUM(unit) && "invalid channel");
|
||||
@@ -50,28 +62,103 @@ static void test_adc_set_io_level(adc_unit_t unit, adc_channel_t channel, bool l
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_adc_set_all_io_level(adc_unit_t unit, bool level)
|
||||
{
|
||||
for (int ch = 0; ch < ADC_LL_CHANNEL_NUM(unit); ch++) {
|
||||
test_adc_set_io_level(unit, ch, level);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end)
|
||||
{
|
||||
TEST_ASSERT(ulp_lp_core_load_binary(firmware_start,
|
||||
(firmware_end - firmware_start)) == ESP_OK);
|
||||
|
||||
TEST_ASSERT(ulp_lp_core_run(cfg) == ESP_OK);
|
||||
s_lp_firmware_started = true;
|
||||
}
|
||||
|
||||
static uint32_t adc_scan_seq_get(void)
|
||||
{
|
||||
return *((volatile uint32_t *)&ulp_adc_scan_seq);
|
||||
}
|
||||
|
||||
static void snapshot_adc_raw(int *dst, int n)
|
||||
{
|
||||
volatile int *src = (volatile int *)&ulp_adc_raw;
|
||||
for (int i = 0; i < n; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void lp_adc_init_tracked(adc_unit_t unit_id)
|
||||
{
|
||||
ESP_ERROR_CHECK(lp_core_lp_adc_init(unit_id));
|
||||
s_lp_adc_unit = unit_id;
|
||||
s_lp_adc_inited = true;
|
||||
}
|
||||
|
||||
static esp_err_t lp_adc_hw_deinit(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (s_lp_adc_inited) {
|
||||
ret = lp_core_lp_adc_deinit(s_lp_adc_unit);
|
||||
s_lp_adc_inited = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lp_adc_test_teardown(void)
|
||||
{
|
||||
(void)lp_adc_hw_deinit();
|
||||
if (s_lp_firmware_started) {
|
||||
ulp_lp_core_stop();
|
||||
s_lp_firmware_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void delay_poll_tick(void)
|
||||
{
|
||||
/* pdMS_TO_TICKS(1) is 0 at a 10 ms FreeRTOS tick, so wait one tick. */
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
static void wait_for_adc_scans(uint32_t count)
|
||||
{
|
||||
uint32_t start_seq = adc_scan_seq_get();
|
||||
const int64_t deadline_us = esp_timer_get_time() + (int64_t)ADC_SCAN_TIMEOUT_MS * 1000;
|
||||
while ((adc_scan_seq_get() - start_seq) < count && esp_timer_get_time() < deadline_us) {
|
||||
delay_poll_tick();
|
||||
}
|
||||
TEST_ASSERT_MESSAGE((adc_scan_seq_get() - start_seq) >= count,
|
||||
"LP ADC did not complete the expected number of scans");
|
||||
}
|
||||
|
||||
/* Every sample is taken after the GPIO change. Values may span two adjacent
|
||||
* LP scans; that is fine because the pull state is stable. Do not search
|
||||
* for a passing window.
|
||||
*/
|
||||
static void snapshot_after_gpio_change(int *dst, int n)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(ADC_SETTLE_DELAY_MS));
|
||||
wait_for_adc_scans(ADC_SCANS_TO_WAIT);
|
||||
snapshot_adc_raw(dst, n);
|
||||
}
|
||||
|
||||
void test_lp_adc(adc_unit_t unit_id)
|
||||
{
|
||||
/* Load ULP firmware and start the coprocessor */
|
||||
int adc_raw[8];
|
||||
const int n = ADC_LL_CHANNEL_NUM(unit_id);
|
||||
TEST_ASSERT(n <= (int)(sizeof(adc_raw) / sizeof(adc_raw[0])));
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end);
|
||||
/* Init ADC and GPIO before starting the LP firmware so the first scan
|
||||
* is not taken on unconfigured channels / floating pins.
|
||||
*/
|
||||
lp_adc_init_tracked(unit_id);
|
||||
|
||||
/* LP ADC Init */
|
||||
ESP_ERROR_CHECK(lp_core_lp_adc_init(unit_id));
|
||||
|
||||
/* LP ADC channel config */
|
||||
const lp_core_lp_adc_chan_cfg_t config = {
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
@@ -92,46 +179,24 @@ void test_lp_adc(adc_unit_t unit_id)
|
||||
TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_7, &config) == ESP_OK);
|
||||
}
|
||||
|
||||
/* Set all the ADC channel IOs to low */
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_0, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_1, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_2, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_3, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_4, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_5, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_6, 0);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_7, 0);
|
||||
test_adc_set_all_io_level(unit_id, 0);
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end);
|
||||
snapshot_after_gpio_change(adc_raw, n);
|
||||
|
||||
vTaskDelay(10);
|
||||
|
||||
int *adc_raw = (int *)&ulp_adc_raw;
|
||||
|
||||
/* Verify that the LP ADC values reflect a low-state of the input pins */
|
||||
for (int i = 0; i < ADC_LL_CHANNEL_NUM(unit_id); i++) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
printf("LP ADC low[%d] = %d\n", i, adc_raw[i]);
|
||||
TEST_ASSERT_LESS_THAN_INT(ADC_TEST_LOW_VAL, adc_raw[i]);
|
||||
}
|
||||
|
||||
/* Set all the ADC channel IOs to high */
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_0, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_1, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_2, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_3, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_4, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_5, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_6, 1);
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_7, 1);
|
||||
test_adc_set_all_io_level(unit_id, 1);
|
||||
snapshot_after_gpio_change(adc_raw, n);
|
||||
|
||||
vTaskDelay(10);
|
||||
|
||||
/* Verify that the LP ADC values reflect a high-state of the input pins */
|
||||
for (int i = 0; i < ADC_LL_CHANNEL_NUM(unit_id); i++) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
printf("LP ADC high[%d] = %d\n", i, adc_raw[i]);
|
||||
TEST_ASSERT_GREATER_THAN_INT(ADC_TEST_HIGH_VAL, adc_raw[i]);
|
||||
}
|
||||
|
||||
/* Deinit LP ADC */
|
||||
ESP_ERROR_CHECK(lp_core_lp_adc_deinit(unit_id));
|
||||
TEST_ESP_OK(lp_adc_hw_deinit());
|
||||
}
|
||||
|
||||
TEST_CASE("LP ADC 1 raw read test", "[lp_core]")
|
||||
@@ -147,18 +212,17 @@ TEST_CASE("LP ADC 1 raw read test", "[lp_core]")
|
||||
|
||||
static void test_lp_adc_stress(adc_unit_t unit_id)
|
||||
{
|
||||
/* Load ULP firmware and start the coprocessor */
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
int adc_raw;
|
||||
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end);
|
||||
|
||||
/* Firmware is started once and left running across init/deinit cycles so
|
||||
* the LP core keeps converting while HP has released the ADC.
|
||||
*/
|
||||
for (int i = 0; i < 100; i++) {
|
||||
/* LP ADC Init */
|
||||
ESP_ERROR_CHECK(lp_core_lp_adc_init(unit_id));
|
||||
lp_adc_init_tracked(unit_id);
|
||||
|
||||
/* LP ADC channel config */
|
||||
const lp_core_lp_adc_chan_cfg_t config = {
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
@@ -166,14 +230,14 @@ static void test_lp_adc_stress(adc_unit_t unit_id)
|
||||
|
||||
TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_0, &config) == ESP_OK);
|
||||
|
||||
/* Set LP ADC channel IO and read raw value */
|
||||
test_adc_set_io_level(unit_id, ADC_CHANNEL_0, 1);
|
||||
vTaskDelay(10);
|
||||
int *adc_raw = (int *)&ulp_adc_raw;
|
||||
TEST_ASSERT_NOT_EQUAL(0, adc_raw[0]);
|
||||
if (!s_lp_firmware_started) {
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end);
|
||||
}
|
||||
snapshot_after_gpio_change(&adc_raw, 1);
|
||||
TEST_ASSERT_NOT_EQUAL(0, adc_raw);
|
||||
|
||||
/* De-init LP ADC */
|
||||
ESP_ERROR_CHECK(lp_core_lp_adc_deinit(unit_id));
|
||||
TEST_ESP_OK(lp_adc_hw_deinit());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,33 +268,31 @@ TEST_CASE("Test temperature sensor does not affect LP ADC", "[lp_core]")
|
||||
TEST_ESP_OK(temperature_sensor_disable(temp_sensor));
|
||||
}
|
||||
|
||||
/* Load ULP firmware and start the coprocessor */
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end);
|
||||
lp_adc_init_tracked(ADC_UNIT_1);
|
||||
|
||||
/* LP ADC Init */
|
||||
ESP_ERROR_CHECK(lp_core_lp_adc_init(ADC_UNIT_1));
|
||||
|
||||
/* LP ADC channel config */
|
||||
const lp_core_lp_adc_chan_cfg_t config = {
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
|
||||
/* Configure ADC channel 0 */
|
||||
TEST_ASSERT(lp_core_lp_adc_config_channel(ADC_UNIT_1, ADC_CHANNEL_0, &config) == ESP_OK);
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end);
|
||||
|
||||
int *adc_raw = (int *)&ulp_adc_raw;
|
||||
int adc_raw;
|
||||
cnt = 2;
|
||||
while (cnt--) {
|
||||
printf("LP ADC%d Channel[%d] Raw Data: %d\n", ADC_UNIT_1 + 1, 0, adc_raw[0]);
|
||||
wait_for_adc_scans(ADC_SCANS_TO_WAIT);
|
||||
snapshot_adc_raw(&adc_raw, 1);
|
||||
printf("LP ADC%d Channel[%d] Raw Data: %d\n", ADC_UNIT_1 + 1, 0, adc_raw);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
TEST_ESP_OK(lp_core_lp_adc_deinit(ADC_UNIT_1));
|
||||
TEST_ESP_OK(lp_adc_hw_deinit());
|
||||
lp_adc_test_teardown();
|
||||
|
||||
cnt = 2;
|
||||
while (cnt--) {
|
||||
|
||||
Reference in New Issue
Block a user