Преглед изворни кода

Merge branch 'bugfix/add_rf_test_lib' into 'master'

esp_phy: Update esp32c3/s3 phy lib and add test lib

Closes WIFI-5097

See merge request espressif/esp-idf!22058
Jiang Jiang Jian пре 2 година
родитељ
комит
266a58f85b

+ 9 - 0
components/esp_phy/CMakeLists.txt

@@ -20,6 +20,10 @@ if(CONFIG_SOC_BT_SUPPORTED OR CONFIG_SOC_IEEE802154_SUPPORTED OR CONFIG_SOC_IEEE
     list(APPEND srcs "src/btbb_init.c")
 endif()
 
+if(CONFIG_ESP_PHY_ENABLE_CERT_TEST)
+    list(APPEND srcs "src/phy_callback.c")
+endif()
+
 idf_build_get_property(build_dir BUILD_DIR)
 
 if(CONFIG_SOC_WIFI_SUPPORTED)
@@ -74,6 +78,11 @@ if(link_binary_libs)
         target_link_libraries(${COMPONENT_LIB} INTERFACE $<TARGET_FILE:${esp_phy_lib}> libphy.a libbtbb.a
         $<TARGET_FILE:${esp_phy_lib}>)
     endif()
+
+    if(CONFIG_ESP_PHY_ENABLE_CERT_TEST)
+        target_link_libraries(${COMPONENT_LIB} INTERFACE $<TARGET_FILE:${esp_phy_lib}> libbttestmode.a
+        librfate.a librftest.a $<TARGET_FILE:${esp_phy_lib}>)
+    endif()
 endif()
 
 if(CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION)

+ 7 - 0
components/esp_phy/Kconfig

@@ -114,4 +114,11 @@ menu "PHY"
             When using USB Serial/JTAG/OTG/CDC, PHY should enable USB, otherwise USB module
             can not work properly. Notice: Enabling this configuration option will slightly impact wifi performance.
 
+    config ESP_PHY_ENABLE_CERT_TEST
+        bool "Enable RF certification test functions"
+        default n
+        depends on IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3
+        help
+            If enabled, you can use RF certification test APIs.
+
 endmenu  # PHY

+ 193 - 0
components/esp_phy/include/esp_phy_cert_test.h

@@ -0,0 +1,193 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef enum {
+    //11b
+    PHY_RATE_1M  = 0x0,
+    PHY_RATE_2M  = 0x1,
+    PHY_RATE_5M5 = 0x2,
+    PHY_RATE_11M = 0x3,
+    //11g
+    PHY_RATE_6M  = 0xb,
+    PHY_RATE_9M  = 0xf,
+    PHY_RATE_12M = 0xa,
+    PHY_RATE_18M = 0xe,
+    PHY_RATE_24M = 0x9,
+    PHY_RATE_36M = 0xd,
+    PHY_RATE_48M = 0x8,
+    PHY_RATE_54M = 0xc,
+    //11n
+    PHY_RATE_MCS0 = 0x10,
+    PHY_RATE_MCS1 = 0x11,
+    PHY_RATE_MCS2 = 0x12,
+    PHY_RATE_MCS3 = 0x13,
+    PHY_RATE_MCS4 = 0x14,
+    PHY_RATE_MCS5 = 0x15,
+    PHY_RATE_MCS6 = 0x16,
+    PHY_RATE_MCS7 = 0x17,
+    PHY_WIFI_RATE_MAX
+} esp_phy_wifi_rate_t;
+
+typedef enum {
+    PHY_BLE_RATE_1M = 0,
+    PHY_BLE_RATE_2M,
+    PHY_BLE_RATE_125K,
+    PHY_BLE_RATE_500k,
+    PHY_BLE_RATE_MAX
+} esp_phy_ble_rate_t;
+
+typedef enum {
+    PHY_BLE_TYPE_1010       = 0,
+    PHY_BLE_TYPE_00001111   = 1,
+    PHY_BLE_TYPE_prbs9      = 2,
+    PHY_BLE_TYPE_00111100   = 4,
+    PHY_BLE_TYPE_MAX
+} esp_phy_ble_type_t;
+
+/**
+ * @brief Structure holding PHY RX result
+ */
+typedef struct {
+    uint32_t phy_rx_correct_count; /*!< The number of desired packets received */
+    int phy_rx_rssi;               /*!< Average RSSI of desired packets */
+    uint32_t phy_rx_total_count;   /*!< The number of total packets received */
+    uint32_t phy_rx_result_flag;   /*!< 0 means no RX info; 1 means the lastest Wi-Fi RX info; 2 means the lastest BLE RX info. */
+} esp_phy_rx_result_t;
+
+/**
+ * @brief Wifi power domain power on
+ */
+void esp_wifi_power_domain_on(void);
+
+/**
+ * @brief Wifi power domain power off
+ */
+void esp_wifi_power_domain_off(void);
+
+/**
+ * @brief Environment variable configuration
+ *
+ * @param conf:
+ *     Set to 1 to enter RF test mode.
+ */
+void esp_phy_rftest_config(uint8_t conf);
+
+/**
+ * @brief RF initialization configuration
+ */
+void esp_phy_rftest_init(void);
+
+/**
+ * @brief TX Continuous mode
+ *
+ * @param contin_en:
+ *     Set to true for continuous packet sending, which can be used for certification testing;
+ *     Set to false to cancel continuous mode, which is the default mode and can be used for WLAN tester.
+*/
+void esp_phy_tx_contin_en(bool contin_en);
+
+/**
+ * @brief HT40/HT20 mode selection
+ *
+ * @param en:
+ *     Set to false to enter 11n HT20 mode;
+ *     Set to true to enter 11n HT40 mode;
+ **/
+void esp_phy_cbw40m_en(bool en);
+
+/**
+ * @brief Wi-Fi TX command
+ *
+ * @param chan: channel setting, 1~14;
+ * @param rate: rate setting;
+ * @param backoff: Transmit power attenuation, unit is 0.25dB. For example, 4 means that the power is attenuated by 1dB;
+ * @param length_byte: TX packet length configuration, indicating PSDU Length, unit is byte;
+ * @param packet_delay: TX packet interval configuration, unit is us;
+ * @param packet_num: The number of packets sent, 0 means sending packets continuously, other values represent the number of packets to send.
+ */
+void esp_phy_wifi_tx(uint32_t chan, esp_phy_wifi_rate_t rate, int8_t backoff, uint32_t length_byte, uint32_t packet_delay, uint32_t packet_num);
+
+/**
+ * @brief Test start/stop command, used to stop transmitting or reciving state.
+ *
+ * @param value:
+ *     Value should be set to 3 before TX/RX.
+ *     Set value to 0 to end TX/RX state.
+ */
+void esp_phy_test_start_stop(uint8_t value);
+
+/**
+ * @brief Wi-Fi RX command
+ *
+ * @param chan: channel setting, 1~14;
+ * @param rate: rate setting;
+ *
+ */
+void esp_phy_wifi_rx(uint32_t chan, esp_phy_wifi_rate_t rate);
+
+/**
+ * @brief Wi-Fi Carrier Wave(CW) TX command
+ *
+ * @param start: enable CW, 1 means transmit, 0 means stop transmitting;
+ * @param chan: CW channel setting, 1~14;
+ * @param backoff: CW power attenuation parameter, unit is 0.25dB. 4 indicates the power is attenuated by 1dB.
+ *
+ */
+void esp_phy_wifi_tx_tone(uint32_t start, uint32_t chan, uint32_t backoff);
+
+/**
+ * @brief BLE TX command
+ *
+ * @param txpwr: Transmit power level. Tx power is about (level-8)*3 dBm, step is 3dB. Level 8 is around 0 dBm;
+ * @param chan: channel setting, range is 0~39, corresponding frequency = 2402+chan*2;
+ * @param len: Payload length setting, range is 0-255, unit is byte, 37 bytes is employed generally;
+ * @param data_type: Data type setting;
+ * @param syncw: Packet identification (need to be provided by the packet generator or instrument manufacturer), 0x71764129 is employed generally;
+ * @param rate: rate setting;
+ * @param tx_num_in: The number of packets sent, 0 means sending packets continuously, other values represent the number of packets to send.
+ */
+void esp_phy_ble_tx(uint32_t txpwr, uint32_t chan, uint32_t len, esp_phy_ble_type_t data_type, uint32_t syncw, esp_phy_ble_rate_t rate, uint32_t tx_num_in);
+
+/**
+ * @brief BLE RX command
+ *
+ * @param chan: channel selection, range is 0-39;
+ *     Channels 0, 1, 2~10 correspond to 2404MHz, 2406MHz, 2408MHz~2424MHz respectively;
+ *     Channels 11, 12, 13~36 correspond to 2428MHz, 2430MHz, 2432MHz~2478MHz respectively;
+ *     Channel 37: 2402MHz, Channel 38: 2426MHz, Channel 39: 2480MHz;
+ * @param syncw: Packet identification (need to be provided by the packet generator or instrument manufacturer), 0x71764129 is employed generally;
+ * @param rate: rate setting;
+ */
+void esp_phy_ble_rx(uint32_t chan, uint32_t syncw, esp_phy_ble_rate_t rate);
+
+/**
+ * @brief BLE Carrier Wave(CW) TX command
+ *
+ * @param start: enable CW, 1 means transmit, 0 means stop transmitting;
+ * @param chan: Single carrier transmission channel selection, range is 0~39, corresponding frequency freq = 2402+chan*2;
+ * @param power: CW power attenuation parameter, unit is 0.25dB. 4 indicates the power is attenuated by 1dB.
+ */
+void esp_phy_bt_tx_tone(uint32_t start, uint32_t chan, uint32_t power);
+
+/**
+ * @brief Get some RX information
+ *
+ * @param rx_result: This struct for storing RX information;
+ */
+void esp_phy_get_rx_result(esp_phy_rx_result_t *rx_result);
+
+#ifdef __cplusplus
+}
+#endif

+ 1 - 1
components/esp_phy/lib

@@ -1 +1 @@
-Subproject commit 1b8e12d3e0e8b7bcd87c115f09ec0f385700579a
+Subproject commit c38381964b48fe53dac584b74eefec62fc86511b

+ 44 - 0
components/esp_phy/src/phy_callback.c

@@ -0,0 +1,44 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+
+typedef enum {
+    OK = 0,
+    FAIL,
+    PENDING,
+    BUSY,
+    CANCEL,
+} STATUS;
+
+extern int phy_printf(const char* format, ...);
+
+static uint8_t g_rf_cmdstop = 3;
+
+void esp_phy_test_start_stop(uint8_t value)
+{
+    g_rf_cmdstop = value;
+}
+
+int esp_phy_cmdstop_callback(void)
+{
+    return g_rf_cmdstop;
+}
+
+STATUS esp_phy_getstopcmd(void)
+{
+    uint8_t value = esp_phy_cmdstop_callback();
+    if (value == 0) {
+        return OK;
+    } else if (value == 1) {
+        return BUSY;
+    } else if (value == 2) {
+        phy_printf("Please run cmdstop to exit current cmd!\n");
+        return FAIL;
+    } else {
+        return FAIL;
+    }
+}

+ 1 - 0
docs/doxygen/Doxyfile

@@ -161,6 +161,7 @@ INPUT = \
     $(PROJECT_PATH)/components/esp_netif/include/esp_netif_sntp.h \
     $(PROJECT_PATH)/components/esp_partition/include/esp_partition.h \
     $(PROJECT_PATH)/components/esp_phy/include/esp_phy_init.h \
+    $(PROJECT_PATH)/components/esp_phy/include/esp_phy_cert_test.h \
     $(PROJECT_PATH)/components/esp_pm/include/esp_pm.h \
     $(PROJECT_PATH)/components/esp_ringbuf/include/freertos/ringbuf.h \
     $(PROJECT_PATH)/components/esp_rom/include/esp_rom_sys.h \

+ 1 - 0
docs/en/api-guides/RF_calibration.rst

@@ -63,3 +63,4 @@ API Reference
 -------------
 
 .. include-build-file:: inc/esp_phy_init.inc
+.. include-build-file:: inc/esp_phy_cert_test.inc

+ 7 - 0
examples/phy/.build-test-rules.yml

@@ -0,0 +1,7 @@
+# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
+
+examples/phy/cert_test:
+  enable:
+    - if: IDF_TARGET in ["esp32c3", "esp32s3"]
+      temporary: true
+      reason: the other targets are not tested yet

+ 8 - 0
examples/phy/cert_test/CMakeLists.txt

@@ -0,0 +1,8 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(cert_test)

+ 112 - 0
examples/phy/cert_test/README.md

@@ -0,0 +1,112 @@
+| Supported Targets | ESP32-C3 | ESP32-S3 |
+| ----------------- | -------- | -------- |
+
+# Certification Test Example
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+This example shows how to use the Certification test APIS.
+
+## How to use example
+
+Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
+
+### Hardware Required
+
+* A development board with ESP32-C3/ESP32-S3/ SoC.
+* A USB cable for Power supply and programming
+
+### Configure the project
+
+Open the project configuration menu (`idf.py menuconfig`).
+
+In the `Example Configuration` menu:
+
+* Enable RF certification test functions.
+* Disable Interrupt Watchdog.
+* Disable Task Watchdog Timer.
+
+### Build and Flash
+
+Build the project and flash it to the board, then run the monitor tool to view the serial output:
+
+Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
+
+* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html)
+* [ESP-IDF Getting Started Guide on ESP32-S3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html)
+
+## Example Output
+
+The command and output logs for each test are as follows:
+
+```
+ ==================================================
+ |            RF certification test               |
+ |                                                |
+ |  1. Print 'help' to gain overview of commands  |
+ |  2. Wi-Fi certification test                   |
+ |  3. Bluetooth certification test               |
+ |                                                |
+ =================================================
+
+
+Type 'help' to get the list of commands.
+Use UP/DOWN arrows to navigate through command history.
+Press TAB when typing command name to auto-complete.
+I (598) main_task: Returned from app_main()
+phy> 
+phy> tx_contin_en 1
+I (4788) phy:  Tx continuous test!
+phy> 
+phy> tx_contin_en 0
+I (7518) phy: Tx packet test!
+phy> 
+phy> cbw40m_en 1
+I (10668) phy: cbw40m_en: 1
+phy> 
+phy> cbw40m_en 0
+I (13238) phy: cbw40m_en: 0
+phy> 
+phy> cmdstop
+I (8828) phy: Tx Over 0x0
+phy> 
+phy> esp_rx -n 1 -r 0
+I (19348) phy: wifi rx start: channel is 1, rate is 0x0
+phy> 
+phy> cmdstop
+I (142881) phy: Total: 535 Correct: 474 RSSI: -650 noise: -960 gain: 531 para1: 5759 para2: 3
+phy>
+phy> get_rx_result
+I (145991) cmd_phy: Total: 535, Correct: 474, RSSI: -650, flag: 1
+phy> 
+phy> wifiscwout -e 1 -c 1 -p 0
+I (98308) phy:  wifi single carrier tx out, single carrier is in 1 channel, power is about (2
+phy> 
+phy> esp_ble_tx -p 8 -n 1 -l 37 -t 2 -s 0x71764129 -r 0 -m 0
+I (319138) phy: fcc_le_tx_syncw: txpwr=8,chan=1,len=37,data_type=2,syncw=0x71764129,rate=0,tx_
+phy> 
+phy> cmdstop
+I (321208) phy: rw done! ce9
+phy> 
+phy> esp_ble_rx -n 1 -s 0x71764129 -r 0
+I (2898) phy: RW LE V9 RX PER
+phy> 
+phy> cmdstop
+I (381241) phy: 3 0 0 0 3 0 0 0 0 0 p -263 -86 -279 a8 -100 -77
+phy>
+phy> get_rx_result
+I (383871) cmd_phy: Total: 3, Correct: 0, RSSI: 0, flag: 2
+phy> 
+phy> bt_tx_tone -e 1 -n 1 -p 0
+I (56008) phy: BT TX TONE START!
+phy>
+```
+
+## Troubleshooting
+
+For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
+

+ 3 - 0
examples/phy/cert_test/main/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "cert_test.c"
+                            "cmd_phy.c"
+                    INCLUDE_DIRS ".")

+ 57 - 0
examples/phy/cert_test/main/cert_test.c

@@ -0,0 +1,57 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "nvs_flash.h"
+
+#include "esp_console.h"
+#include "cmd_system.h"
+
+#include "cmd_phy.h"
+#include "esp_phy_cert_test.h"
+
+void app_main(void)
+{
+    ESP_ERROR_CHECK(nvs_flash_init());
+
+    esp_console_repl_t *repl = NULL;
+    esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
+    repl_config.prompt = "phy>";
+
+#if CONFIG_ESP_CONSOLE_UART
+    esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
+#elif CONFIG_ESP_CONSOLE_USB_CDC
+    esp_console_dev_usb_cdc_config_t cdc_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&cdc_config, &repl_config, &repl));
+#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
+    esp_console_dev_usb_serial_jtag_config_t usbjtag_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&usbjtag_config, &repl_config, &repl));
+#endif
+
+    /* Register commands */
+    register_system();
+
+#if CONFIG_ESP_PHY_ENABLE_CERT_TEST
+    register_phy_cmd();
+
+    /* rftest.a requirements */
+    esp_wifi_power_domain_on();
+
+    esp_phy_rftest_config(1);
+    esp_phy_rftest_init();
+#endif
+
+    printf("\n ==================================================\n");
+    printf(" |            RF certification test               |\n");
+    printf(" |                                                |\n");
+    printf(" |  1. Print 'help' to gain overview of commands  |\n");
+    printf(" |  2. Wi-Fi certification test                   |\n");
+    printf(" |  3. Bluetooth certification test               |\n");
+    printf(" |                                                |\n");
+    printf(" =================================================\n\n");
+
+    ESP_ERROR_CHECK(esp_console_start_repl(repl));
+}

+ 525 - 0
examples/phy/cert_test/main/cmd_phy.c

@@ -0,0 +1,525 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_log.h"
+#include "esp_console.h"
+#include "argtable3/argtable3.h"
+#include "esp_phy_cert_test.h"
+#include "cmd_phy.h"
+
+#define TAG "cmd_phy"
+
+#if CONFIG_ESP_PHY_ENABLE_CERT_TEST
+
+static phy_args_t       phy_args;
+#if SOC_WIFI_SUPPORTED
+static phy_wifi_tx_t    phy_wifi_tx_args;
+static phy_wifi_rx_t    phy_wifi_rx_args;
+static phy_wifiscwout_t phy_wifiscwout_args;
+#endif
+#if SOC_BT_SUPPORTED
+static phy_ble_tx_t     phy_ble_tx_args;
+static phy_ble_rx_t     phy_ble_rx_args;
+static phy_bt_tx_tone_t phy_bt_tx_tone_args;
+#endif
+
+static int esp_phy_tx_contin_en_func(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &phy_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_args.enable->count == 1) {
+        esp_phy_tx_contin_en(phy_args.enable->ival[0]);
+    } else {
+        ESP_LOGW(TAG, "Please enter the enable parameter");
+    }
+    return 0;
+}
+
+static int esp_phy_cmdstop_func(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &phy_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_args.end, argv[0]);
+        return 1;
+    }
+
+    esp_phy_test_start_stop(0);
+
+    return 0;
+}
+
+static int esp_phy_get_rx_result_func(int argc, char **argv)
+{
+    esp_phy_rx_result_t rx_result;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_args.end, argv[0]);
+        return 1;
+    }
+
+    esp_phy_get_rx_result(&rx_result);
+
+    ESP_LOGI(TAG, "Total: %lu, Correct: %lu, RSSI: %d, flag: %lu", rx_result.phy_rx_total_count,
+                rx_result.phy_rx_correct_count, rx_result.phy_rx_rssi, rx_result.phy_rx_result_flag);
+
+    return 0;
+}
+
+#if SOC_WIFI_SUPPORTED
+void cert_wifi_tx(void *arg)
+{
+    phy_wifi_tx_s *cmd  = (phy_wifi_tx_s*)arg;
+
+    esp_phy_test_start_stop(3);
+    esp_phy_wifi_tx(cmd->channel, cmd->rate, cmd->backoff, cmd->length_byte, cmd->packet_delay, cmd->packet_num);
+
+    vTaskDelete(NULL);
+}
+
+void cert_wifi_rx(void *arg)
+{
+    phy_wifi_rx_s *cmd  = (phy_wifi_rx_s*)arg;
+
+    esp_phy_test_start_stop(3);
+    esp_phy_wifi_rx(cmd->channel, cmd->rate);
+
+    vTaskDelete(NULL);
+}
+
+static int esp_phy_cbw40m_en_func(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &phy_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_args.end, argv[0]);
+        return 1;
+    }
+    if (phy_args.enable->count == 1) {
+        esp_phy_cbw40m_en(phy_args.enable->ival[0]);
+    } else {
+        ESP_LOGW(TAG, "Please enter the enable parameter");
+    }
+    return 0;
+}
+
+static int esp_phy_wifi_tx_func(int argc, char **argv)
+{
+    phy_wifi_tx_s cmd;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_wifi_tx_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_wifi_tx_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_wifi_tx_args.channel->count == 1) {
+        cmd.channel = phy_wifi_tx_args.channel->ival[0];
+    } else {
+        cmd.channel = 1;
+        ESP_LOGW(TAG, "Default channel is 1");
+    }
+
+    if (phy_wifi_tx_args.rate->count == 1) {
+        cmd.rate = phy_wifi_tx_args.rate->ival[0];
+    } else {
+        cmd.rate = PHY_RATE_1M;
+        ESP_LOGW(TAG, "Default rate is PHY_RATE_1M");
+    }
+
+    if (phy_wifi_tx_args.attenuation->count == 1) {
+        cmd.backoff = phy_wifi_tx_args.attenuation->ival[0];
+    } else {
+        cmd.backoff = 0;
+        ESP_LOGW(TAG, "Default backoff is 0");
+    }
+
+    if (phy_wifi_tx_args.length_byte->count == 1) {
+        cmd.length_byte = phy_wifi_tx_args.length_byte->ival[0];
+    } else {
+        cmd.length_byte = 1000;
+        ESP_LOGW(TAG, "Default length_byte is 1000");
+    }
+
+    if (phy_wifi_tx_args.packet_delay->count == 1) {
+        cmd.packet_delay = phy_wifi_tx_args.packet_delay->ival[0];
+    } else {
+        cmd.packet_delay = 1000;
+        ESP_LOGW(TAG, "Default packet_delay is 1000");
+    }
+
+    if (phy_wifi_tx_args.packet_num->count == 1) {
+        cmd.packet_num = phy_wifi_tx_args.packet_num->ival[0];
+    } else {
+        cmd.packet_num = 0;
+        ESP_LOGW(TAG, "Default packet_num is 0");
+    }
+
+    xTaskCreate(cert_wifi_tx, "cert_wifi_tx", 1024 * 10, (void *)&cmd, 10, NULL);
+
+    return 0;
+}
+
+static int esp_phy_wifi_rx_func(int argc, char **argv)
+{
+    phy_wifi_tx_s cmd;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_wifi_rx_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_wifi_rx_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_wifi_rx_args.channel->count == 1) {
+        cmd.channel = phy_wifi_rx_args.channel->ival[0];
+    } else {
+        cmd.channel = 1;
+        ESP_LOGW(TAG, "Default channel is 1");
+    }
+
+    if (phy_wifi_rx_args.rate->count == 1) {
+        cmd.rate = phy_wifi_rx_args.rate->ival[0];
+    } else {
+        cmd.rate = PHY_RATE_1M;
+        ESP_LOGW(TAG, "Default rate is PHY_RATE_1M");
+    }
+
+    xTaskCreate(cert_wifi_rx, "cert_wifi_rx", 1024 * 20, (void *)&cmd, 10, NULL);
+    return 0;
+}
+
+static int esp_phy_wifiscwout_func(int argc, char **argv)
+{
+    uint32_t enable;
+    uint32_t channel;
+    uint32_t attenuation;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_wifiscwout_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_wifiscwout_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_wifiscwout_args.enable->count == 1) {
+        enable = phy_wifiscwout_args.enable->ival[0];
+    } else {
+        enable = 1;
+        ESP_LOGW(TAG, "Default enable is 1");
+    }
+
+    if (phy_wifiscwout_args.channel->count == 1) {
+        channel = phy_wifiscwout_args.channel->ival[0];
+    } else {
+        channel = 1;
+        ESP_LOGW(TAG, "Default channel is 1");
+    }
+
+    if (phy_wifiscwout_args.attenuation->count == 1) {
+        attenuation = phy_wifiscwout_args.attenuation->ival[0];
+    } else {
+        attenuation = 0;
+        ESP_LOGW(TAG, "Default attenuation is 0");
+    }
+
+    esp_phy_wifi_tx_tone(enable, channel, attenuation);
+
+    return 0;
+}
+#endif
+
+#if SOC_BT_SUPPORTED
+void cert_ble_tx(void *arg)
+{
+    phy_ble_tx_s *cmd = (phy_ble_tx_s *)arg;
+
+    esp_phy_test_start_stop(3);
+    esp_phy_ble_tx(cmd->txpwr, cmd->channel, cmd->len, cmd->data_type, cmd->syncw, cmd->rate, cmd->tx_num_in);
+
+    vTaskDelete(NULL);
+}
+
+void cert_ble_rx(void *arg)
+{
+    phy_ble_rx_s *cmd = (phy_ble_rx_s *)arg;
+
+    esp_phy_test_start_stop(3);
+    esp_phy_ble_rx(cmd->channel, cmd->syncw, cmd->rate);
+
+    vTaskDelete(NULL);
+}
+
+static int esp_phy_ble_tx_func(int argc, char **argv)
+{
+    phy_ble_tx_s cmd;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_ble_tx_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_ble_tx_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_ble_tx_args.txpwr->count == 1) {
+        cmd.txpwr = phy_ble_tx_args.txpwr->ival[0];
+    } else {
+        cmd.txpwr = 8;
+        ESP_LOGW(TAG, "Default txpwr is 8");
+    }
+
+    if (phy_ble_tx_args.channel->count == 1) {
+        cmd.channel = phy_ble_tx_args.channel->ival[0];
+    } else {
+        cmd.channel = 1;
+        ESP_LOGW(TAG, "Default channel is 1");
+    }
+
+    if (phy_ble_tx_args.len->count == 1) {
+        cmd.len = phy_ble_tx_args.len->ival[0];
+    } else {
+        cmd.len = 37;
+        ESP_LOGW(TAG, "Default len is 37");
+    }
+
+    if (phy_ble_tx_args.data_type->count == 1) {
+        cmd.data_type = phy_ble_tx_args.data_type->ival[0];
+    } else {
+        cmd.data_type = PHY_BLE_TYPE_prbs9;
+        ESP_LOGW(TAG, "Default data_type is PHY_BLE_TYPE_prbs9");
+    }
+
+    if (phy_ble_tx_args.syncw->count == 1) {
+        cmd.syncw = phy_ble_tx_args.syncw->ival[0];
+    } else {
+        cmd.syncw = 0x71764129;
+        ESP_LOGW(TAG, "Default syncw is 0x71764129");
+    }
+
+    if (phy_ble_tx_args.rate->count == 1) {
+        cmd.rate = phy_ble_tx_args.rate->ival[0];
+    } else {
+        cmd.rate = PHY_BLE_RATE_1M;
+        ESP_LOGW(TAG, "Default rate is PHY_BLE_RATE_1M");
+    }
+
+    if (phy_ble_tx_args.tx_num_in->count == 1) {
+        cmd.tx_num_in = phy_ble_tx_args.tx_num_in->ival[0];
+    } else {
+        cmd.tx_num_in = 0;
+        ESP_LOGW(TAG, "Default tx_num_in is 0");
+    }
+
+    xTaskCreate(cert_ble_tx, "cert_ble_tx", 4096, (void *)&cmd, 10, NULL);
+
+    return 0;
+}
+
+static int esp_phy_ble_rx_func(int argc, char **argv)
+{
+    phy_ble_rx_s cmd;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_ble_rx_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_ble_rx_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_ble_rx_args.channel->count == 1) {
+        cmd.channel = phy_ble_tx_args.channel->ival[0];
+    } else {
+        cmd.channel = 1;
+        ESP_LOGW(TAG, "Default channel is 1");
+    }
+
+    if (phy_ble_rx_args.syncw->count == 1) {
+        cmd.syncw = phy_ble_rx_args.syncw->ival[0];
+    } else {
+        cmd.syncw = 0x71764129;
+        ESP_LOGW(TAG, "Default syncw is 0x71764129");
+    }
+
+    if (phy_ble_rx_args.rate->count == 1) {
+        cmd.rate = phy_ble_rx_args.rate->ival[0];
+    } else {
+        cmd.rate = PHY_BLE_RATE_1M;
+        ESP_LOGW(TAG, "Default rate is PHY_BLE_RATE_1M");
+    }
+
+    xTaskCreate(cert_ble_rx, "cert_ble_rx", 4096, (void *)&cmd, 10, NULL);
+
+    return 0;
+}
+
+static int esp_phy_bt_tx_tone_func(int argc, char **argv)
+{
+    uint32_t start;
+    uint32_t channel;
+    uint32_t attenuation;
+    int nerrors = arg_parse(argc, argv, (void **) &phy_bt_tx_tone_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, phy_bt_tx_tone_args.end, argv[0]);
+        return 1;
+    }
+
+    if (phy_bt_tx_tone_args.start->count == 1) {
+        start = phy_bt_tx_tone_args.start->ival[0];
+    } else {
+        start = 1;
+        ESP_LOGW(TAG, "Default start is 1");
+    }
+
+    if (phy_bt_tx_tone_args.channel->count == 1) {
+        channel = phy_bt_tx_tone_args.channel->ival[0];
+    } else {
+        channel = 1;
+        ESP_LOGW(TAG, "Default channel is 1");
+    }
+
+    if (phy_bt_tx_tone_args.attenuation->count == 1) {
+        attenuation = phy_bt_tx_tone_args.attenuation->ival[0];
+    } else {
+        attenuation = 0;
+        ESP_LOGW(TAG, "Default backoff is 0");
+    }
+
+    esp_phy_bt_tx_tone(start, channel, attenuation);
+
+    return 0;
+}
+#endif
+
+void register_phy_cmd(void)
+{
+    phy_args.enable  = arg_int0(NULL, NULL, "<enable>", "enable");
+    phy_args.end = arg_end(1);
+
+    const esp_console_cmd_t tx_contin_cmd = {
+        .command = "tx_contin_en",
+        .help = "TX Continuous mode, 1: enable, 0: disable",
+        .hint = NULL,
+        .func = &esp_phy_tx_contin_en_func,
+        .argtable = &phy_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&tx_contin_cmd) );
+
+    const esp_console_cmd_t cmdstop_cmd = {
+        .command = "cmdstop",
+        .help = "TX/RX test stop command",
+        .hint = NULL,
+        .func = &esp_phy_cmdstop_func,
+        .argtable = NULL
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&cmdstop_cmd) );
+
+    const esp_console_cmd_t get_rx_result = {
+        .command = "get_rx_result",
+        .help = "Get RX information",
+        .hint = NULL,
+        .func = &esp_phy_get_rx_result_func,
+        .argtable = NULL
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&get_rx_result) );
+
+#if SOC_WIFI_SUPPORTED
+    const esp_console_cmd_t cbw40m_cmd = {
+        .command = "cbw40m_en",
+        .help = "HT40/HT20 mode selection, 0: HT20, 1: HT40",
+        .hint = NULL,
+        .func = &esp_phy_cbw40m_en_func,
+        .argtable = &phy_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&cbw40m_cmd) );
+
+    phy_wifi_tx_args.channel     = arg_int0("n", "channel"     , "<channel>"     , "channel setting, 1~14");
+    phy_wifi_tx_args.rate        = arg_int0("r", "rate"        , "<rate>"        , "rate setting");
+    phy_wifi_tx_args.attenuation = arg_int0("p", "attenuation" , "<attenuation>" , "Transmit power attenuation");
+    phy_wifi_tx_args.length_byte = arg_int0("l", "length_byte" , "<length_byte>" , "TX packet length configuration");
+    phy_wifi_tx_args.packet_delay= arg_int0("d", "packet_delay", "<packet_delay>", "TX packet interval configuration");
+    phy_wifi_tx_args.packet_num  = arg_int0("c", "packet_num"  , "<packet_num>"  , "The number of packets to send");
+    phy_wifi_tx_args.end         = arg_end(1);
+
+    const esp_console_cmd_t esp_tx_cmd = {
+        .command = "esp_tx",
+        .help = "WiFi TX command",
+        .hint = NULL,
+        .func = &esp_phy_wifi_tx_func,
+        .argtable = &phy_wifi_tx_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&esp_tx_cmd) );
+
+    phy_wifi_rx_args.channel     = arg_int0("n", "channel", "<channel>", "channel setting, 1~14");
+    phy_wifi_rx_args.rate        = arg_int0("r", "rate"   , "<rate>"   , "rate setting");
+    phy_wifi_rx_args.end         = arg_end(1);
+
+    const esp_console_cmd_t esp_rx_cmd = {
+        .command = "esp_rx",
+        .help = "WiFi RX command",
+        .hint = NULL,
+        .func = &esp_phy_wifi_rx_func,
+        .argtable = &phy_wifi_rx_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&esp_rx_cmd) );
+
+    phy_wifiscwout_args.enable      = arg_int0("e", "start"      , "<start>"      , "enable CW");
+    phy_wifiscwout_args.channel     = arg_int0("c", "channel"    , "<channel>"    , "channel setting, 1~14");
+    phy_wifiscwout_args.attenuation = arg_int0("p", "attenuation", "<attenuation>", "Transmit power attenuation");
+    phy_wifiscwout_args.end         = arg_end(1);
+
+    const esp_console_cmd_t wifiscwout_cmd = {
+        .command = "wifiscwout",
+        .help = "Wi-Fi CW TX command",
+        .hint = NULL,
+        .func = &esp_phy_wifiscwout_func,
+        .argtable = &phy_wifiscwout_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&wifiscwout_cmd) );
+#endif
+
+#if SOC_BT_SUPPORTED
+    phy_ble_tx_args.txpwr     = arg_int0("p", "txpwr"    , "<txpwr>"    , "Transmit power level setting");
+    phy_ble_tx_args.channel   = arg_int0("n", "channel"  , "<channel>"  , "TX channel setting, range is 0~39");
+    phy_ble_tx_args.len       = arg_int0("l", "len"      , "<len>"      , "Payload length setting, range is 0-255");
+    phy_ble_tx_args.data_type = arg_int0("t", "data_type", "<data_type>", "Data type setting");
+    phy_ble_tx_args.syncw     = arg_int0("s", "syncw"    , "<syncw>"    , "Packet identification");
+    phy_ble_tx_args.rate      = arg_int0("r", "rate"     , "<rate>"     , "TX rate setting,0: 1M; 1: 2M;2: 125K;3: 500K");
+    phy_ble_tx_args.tx_num_in = arg_int0("m", "tx_num_in", "<tx_num_in>", "TX mode setting");
+    phy_ble_tx_args.end       = arg_end(1);
+
+    const esp_console_cmd_t esp_ble_tx_cmd = {
+        .command = "esp_ble_tx",
+        .help = "BLE TX command",
+        .hint = NULL,
+        .func = &esp_phy_ble_tx_func,
+        .argtable = &phy_ble_tx_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&esp_ble_tx_cmd) );
+
+    phy_ble_rx_args.channel = arg_int0("n", "channel", "<channel>", "RX channel setting, range is 0~39");
+    phy_ble_rx_args.syncw   = arg_int0("s", "syncw"  , "<syncw>"  , "Packet identification");
+    phy_ble_rx_args.rate    = arg_int0("r", "rate"   , "<rate>"   , "RX rate setting,0: 1M;1: 2M;2: 125K;3: 500K");
+    phy_ble_rx_args.end     = arg_end(1);
+
+    const esp_console_cmd_t esp_ble_rx_cmd = {
+        .command = "esp_ble_rx",
+        .help = "BLE RX command",
+        .hint = NULL,
+        .func = &esp_phy_ble_rx_func,
+        .argtable = &phy_ble_rx_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&esp_ble_rx_cmd) );
+
+    phy_bt_tx_tone_args.start       = arg_int0("e", "start"  , "<start>"  , "enable CW, 1 means transmit, 0 means stop transmitting");
+    phy_bt_tx_tone_args.channel     = arg_int0("n", "channel", "<channel>", "Single carrier transmission channel selection");
+    phy_bt_tx_tone_args.attenuation = arg_int0("p", "power"  , "<power>"  , "CW power attenuation parameter");
+    phy_bt_tx_tone_args.end         = arg_end(1);
+
+    const esp_console_cmd_t bt_tx_tone_cmd = {
+        .command = "bt_tx_tone",
+        .help = "Single carrier TX command",
+        .hint = NULL,
+        .func = &esp_phy_bt_tx_tone_func,
+        .argtable = &phy_bt_tx_tone_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&bt_tx_tone_cmd) );
+#endif
+}
+#endif

+ 105 - 0
examples/phy/cert_test/main/cmd_phy.h

@@ -0,0 +1,105 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_phy_cert_test.h"
+
+typedef struct {
+    struct arg_int *enable;
+    struct arg_end *end;
+} phy_args_t;
+
+#if SOC_WIFI_SUPPORTED
+typedef struct {
+    struct arg_int *channel;
+    struct arg_int *rate;
+    struct arg_int *attenuation;
+    struct arg_int *length_byte;
+    struct arg_int *packet_delay;
+    struct arg_int *packet_num;
+    struct arg_end *end;
+} phy_wifi_tx_t;
+
+typedef struct {
+    struct arg_int *channel;
+    struct arg_int *rate;
+    struct arg_end *end;
+} phy_wifi_rx_t;
+
+typedef struct {
+    struct arg_int *enable;
+    struct arg_int *channel;
+    struct arg_int *attenuation;
+    struct arg_end *end;
+} phy_wifiscwout_t;
+
+typedef struct {
+    uint32_t channel;
+    esp_phy_wifi_rate_t rate;
+    int8_t backoff;
+    uint32_t length_byte;
+    uint32_t packet_delay;
+    uint32_t packet_num;
+} phy_wifi_tx_s;
+
+typedef struct {
+    uint32_t channel;
+    esp_phy_wifi_rate_t rate;
+} phy_wifi_rx_s;
+#endif
+
+#if SOC_BT_SUPPORTED
+typedef struct {
+    struct arg_int *txpwr;
+    struct arg_int *channel;
+    struct arg_int *len;
+    struct arg_int *data_type;
+    struct arg_int *syncw;
+    struct arg_int *rate;
+    struct arg_int *tx_num_in;
+    struct arg_end *end;
+} phy_ble_tx_t;
+
+typedef struct {
+    struct arg_int *channel;
+    struct arg_int *syncw;
+    struct arg_int *rate;
+    struct arg_end *end;
+} phy_ble_rx_t;
+
+typedef struct {
+    struct arg_int *start;
+    struct arg_int *channel;
+    struct arg_int *attenuation;
+    struct arg_end *end;
+} phy_bt_tx_tone_t;
+
+typedef struct {
+    uint32_t txpwr;
+    uint32_t channel;
+    uint32_t len;
+    esp_phy_ble_type_t data_type;
+    uint32_t syncw;
+    esp_phy_ble_rate_t rate;
+    uint32_t tx_num_in;
+} phy_ble_tx_s;
+
+typedef struct {
+    uint32_t channel;
+    uint32_t syncw;
+    esp_phy_ble_rate_t rate;
+} phy_ble_rx_s;
+#endif
+
+void register_phy_cmd(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 0
examples/phy/cert_test/sdkconfig.ci

@@ -0,0 +1,5 @@
+CONFIG_ESP_INT_WDT=n
+CONFIG_ESP_TASK_WDT_EN=n
+
+CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION=y
+CONFIG_ESP_PHY_ENABLE_CERT_TEST=y

+ 4 - 0
examples/phy/cert_test/sdkconfig.defaults

@@ -0,0 +1,4 @@
+CONFIG_ESP_TASK_WDT_EN=n
+
+CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION=y
+CONFIG_ESP_PHY_ENABLE_CERT_TEST=y