Merge branch 'master' into feature/btdm_avrc

This commit is contained in:
wangmengyang 2017-03-31 12:51:45 +08:00
commit 9d745be3f7
131 changed files with 4149 additions and 1515 deletions

View file

@ -14,16 +14,17 @@ before_script:
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
# if testing master branch, use github wifi and bt libs.
# if testing other branches, use gitlab wifi and bt libs (as maybe changes aren't merged to master yet)
- test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-wifi-lib%${GITLAB_SSH_SERVER}/idf/esp32-wifi-lib%" .gitmodules
- test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-bt-lib%${GITLAB_SSH_SERVER}/idf/esp32-bt-lib%" .gitmodules
# Set IS_PRIVATE or IS_PUBLIC depending on if our branch is public or not
#
# (the same regular expressions are used to set these are used in 'only:' sections below
- source make/configure_ci_environment.sh
# fetch all submodules
- git submodule update --init --recursive
build_template_app:
stage: build
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
tags:
- build
@ -57,7 +58,7 @@ build_template_app:
stage: build
tags:
- build
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
variables:
SDK_PATH: "$CI_PROJECT_DIR"
@ -127,14 +128,14 @@ build_examples:
build_docs:
stage: build
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
tags:
- build_docs
script:
- cd docs
- doxygen
# If not building master branch, and there are Doxygen warnings, print them and bail out
- test "${CI_BUILD_REF_NAME}" = "master" || test $(cat doxygen-warning-log.txt | wc -l) -eq 0 || ( echo "Doxygen pass had some warnings:" && cat doxygen-warning-log.txt && false )
- test -n $IS_PRIVATE && test $(cat doxygen-warning-log.txt | wc -l) -eq 0 || ( echo "Doxygen pass had some warnings:" && cat doxygen-warning-log.txt && false )
- make gh-linkcheck
- make html
artifacts:
@ -145,7 +146,7 @@ build_docs:
test_nvs_on_host:
stage: test
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
tags:
- nvs_host_test
script:
@ -154,21 +155,23 @@ test_nvs_on_host:
test_build_system:
stage: test
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
tags:
- build_test
variables:
IDF_PATH: "$CI_PROJECT_DIR"
script:
- ./make/test_configure_ci_environment.sh
- ./make/test_build_system.sh
test_report:
stage: test_report
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
only:
- master
- triggers
- /^release\/v.*$/
- /^release\/v/
- /^v\d+\.\d+(\.\d+)?($|-)/
tags:
- report
variables:
@ -218,11 +221,12 @@ push_master_to_github:
stage: deploy
only:
- master
- /^release\/v.*$/
- /^release\/v/
- /^v\d+\.\d+(\.\d+)?($|-)/
tags:
- deploy
when: on_success
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
variables:
GIT_STRATEGY: clone
GITHUB_PUSH_REFS: refs/remotes/origin/release refs/remotes/origin/master
@ -250,11 +254,12 @@ deploy_docs:
stage: deploy
only:
- master
- /^release\/v.*$/
- /^release\/v/
- /^v\d+\.\d+(\.\d+)?($|-)/
- triggers
tags:
- deploy
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
script:
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
@ -271,7 +276,7 @@ deploy_docs:
check_doc_links:
stage: test
image: espressif/esp32-ci-env
image: $CI_DOCKER_REGISTRY/esp32-ci-env
tags:
- check_doc_links
only:
@ -297,7 +302,8 @@ check_doc_links:
when: on_success
only:
- master
- /^release\/v.*$/
- /^release\/v/
- /^v\d+\.\d+(\.\d+)?($|-)/
- triggers
allow_failure: true

View file

@ -2,7 +2,7 @@
[![alt text](https://readthedocs.org/projects/docs/badge/?version=latest "Documentation Status")](http://esp-idf.readthedocs.io/en/latest/?badge=latest)
ESP-IDF is the official development framework for the [ESP32](https://espressif.com/en/products/hardware/esp32/overview>) chip.
ESP-IDF is the official development framework for the [ESP32](https://espressif.com/en/products/hardware/esp32/overview) chip.
# Developing With the ESP-IDF

View file

@ -1,30 +1,37 @@
menuconfig BT_ENABLED
bool "Bluetooth"
help
Select this option to enable Bluetooth stack and show the submenu with Bluetooth configuration choices.
Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices.
menuconfig BLUEDROID_ENABLED
bool "Bluedroid Bluetooth stack enabled"
depends on BT_ENABLED
default y
help
This enables the default Bluedroid Bluetooth stack
config BTC_TASK_STACK_SIZE
int "Bluetooth event (callback to application) task stack size"
depends on BT_ENABLED
depends on BLUEDROID_ENABLED
default 3072
help
This select btc task stack size
config BLUEDROID_MEM_DEBUG
bool "Bluedroid memory debug"
depends on BT_ENABLED
depends on BLUEDROID_ENABLED
default n
help
Bluedroid memory debug
config CLASSIC_BT_ENABLED
bool "Classic Bluetooth"
depends on BT_ENABLED
depends on BLUEDROID_ENABLED
default n
config BT_DRAM_RELEASE
bool "Release DRAM from Classic BT controller"
depends on BT_ENABLED && !CLASSIC_BT_ENABLED
depends on BT_ENABLED && (!BLUEDROID_ENABLED || (BLUEDROID_ENABLED && !CLASSIC_BT_ENABLED))
default n
help
This option should only be used when BLE only.

View file

@ -268,6 +268,7 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
blufi_env.conn_id = p_data->conn.conn_id;
blufi_env.is_connected = false;
blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.sec_mode = 0x0;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;

View file

@ -3,7 +3,26 @@
#
ifdef CONFIG_BT_ENABLED
COMPONENT_ADD_INCLUDEDIRS := bluedroid/bta/include \
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := include
LIBS := btdm_app
COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \
$(addprefix -l,$(LIBS))
# re-link program if BT binary libs change
COMPONENT_ADD_LINKER_DEPS := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
COMPONENT_SUBMODULES += lib
endif
ifdef CONFIG_BLUEDROID_ENABLED
COMPONENT_ADD_INCLUDEDIRS += bluedroid/bta/include \
bluedroid/bta/sys/include \
bluedroid/btcore/include \
bluedroid/device/include \
@ -37,17 +56,8 @@ COMPONENT_ADD_INCLUDEDIRS := bluedroid/bta/include \
bluedroid/utils/include \
bluedroid/api/include \
bluedroid/include \
include
LIBS := btdm_app
COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \
$(addprefix -l,$(LIBS))
# re-link program if BT binary libs change
COMPONENT_ADD_LINKER_DEPS := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
COMPONENT_SRCDIRS := bluedroid/bta/dm \
COMPONENT_SRCDIRS += bluedroid/bta/dm \
bluedroid/bta/gatt \
bluedroid/bta/hh \
bluedroid/bta/sdp \
@ -87,8 +97,5 @@ COMPONENT_SRCDIRS := bluedroid/bta/dm \
bluedroid/utils \
bluedroid/api \
bluedroid \
.
COMPONENT_SUBMODULES += lib
endif

View file

@ -27,7 +27,7 @@ extern "C" {
* @brief Bluetooth mode for controller enable/disable
*/
typedef enum {
ESP_BT_MODE_ILDE = 0x00, /*!< Bluetooth is not run */
ESP_BT_MODE_IDLE = 0x00, /*!< Bluetooth is not running */
ESP_BT_MODE_BLE = 0x01, /*!< Run BLE mode */
ESP_BT_MODE_CLASSIC_BT = 0x02, /*!< Run Classic BT mode */
ESP_BT_MODE_BTDM = 0x03, /*!< Run dual mode */

View file

@ -66,6 +66,7 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 };
#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin do not support internal pull-up"
#define I2C_FIFO_FULL_THRESH_VAL (28)
#define I2C_FIFO_EMPTY_THRESH_VAL (5)
#define I2C_IO_INIT_LEVEL (1)
typedef struct {
uint8_t byte_num; /*!< cmd byte number */
@ -626,6 +627,7 @@ esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_
break;
}
if (sda_io_num >= 0) {
gpio_set_level(sda_io_num, I2C_IO_INIT_LEVEL);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[sda_io_num], PIN_FUNC_GPIO);
gpio_set_direction(sda_io_num, GPIO_MODE_INPUT_OUTPUT_OD);
if (sda_pullup_en == GPIO_PULLUP_ENABLE) {
@ -638,6 +640,7 @@ esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_
}
if (scl_io_num >= 0) {
gpio_set_level(scl_io_num, I2C_IO_INIT_LEVEL);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[scl_io_num], PIN_FUNC_GPIO);
if (mode == I2C_MODE_MASTER) {
gpio_set_direction(scl_io_num, GPIO_MODE_INPUT_OUTPUT_OD);
@ -862,19 +865,23 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = cmd->byte_num;
I2C[i2c_num]->command[p_i2c->cmd_idx].op_code = cmd->op_code;
if (cmd->op_code == I2C_CMD_WRITE) {
uint32_t wr_filled = 0;
//TODO: to reduce interrupt number
if (cmd->data) {
while (p_i2c->tx_fifo_remain > 0 && cmd->byte_num > 0) {
WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), *cmd->data++);
p_i2c->tx_fifo_remain--;
cmd->byte_num--;
wr_filled++;
}
} else {
WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), cmd->byte_cmd);
p_i2c->tx_fifo_remain--;
cmd->byte_num--;
wr_filled ++;
}
I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num -= cmd->byte_num;
//Workaround for register field operation.
I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = wr_filled;
I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0;
I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END;
p_i2c->tx_fifo_remain = I2C_FIFO_LEN;

View file

@ -29,8 +29,7 @@ extern "C" {
typedef enum {
LEDC_HIGH_SPEED_MODE = 0, /*!< LEDC high speed speed_mode */
//in this version, we only support high speed speed_mode. We will access low speed speed_mode later
//LEDC_LOW_SPEED_MODE, /*!< LEDC low speed speed_mode */
LEDC_LOW_SPEED_MODE, /*!< LEDC low speed speed_mode */
LEDC_SPEED_MODE_MAX, /*!< LEDC speed limit */
} ledc_mode_t;
@ -137,8 +136,6 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf);
* After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings.
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode,
* now we only support high-speed mode.
* We will access low-speed mode in next version
* @param channel LEDC channel(0-7), select from ledc_channel_t
*
* @return
@ -152,7 +149,7 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
* @brief LEDC stop.
* Disable LEDC output, and set idle level
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param channel LEDC channel(0-7), select from ledc_channel_t
* @param idle_level Set output idle level after LEDC stops.
*
@ -165,7 +162,7 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl
/**
* @brief LEDC set channel frequency(Hz)
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param timer_num LEDC timer index(0-3), select from ledc_timer_t
* @param freq_hz Set the LEDC frequency
*
@ -179,7 +176,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
/**
* @brief LEDC get channel frequency(Hz)
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param timer_num LEDC timer index(0-3), select from ledc_timer_t
*
* @return
@ -192,7 +189,7 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num);
* @brief LEDC set duty
* Only after calling ledc_update_duty will the duty update.
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param channel LEDC channel(0-7), select from ledc_channel_t
* @param duty Set the LEDC duty, the duty range is [0, (2**bit_num) - 1]
*
@ -205,7 +202,7 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t
/**
* @brief LEDC get duty
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param channel LEDC channel(0-7), select from ledc_channel_t
*
* @return
@ -218,7 +215,7 @@ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
* @brief LEDC set gradient
* Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect.
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param channel LEDC channel(0-7), select from ledc_channel_t
* @param duty Set the start of the gradient duty, the duty range is [0, (2**bit_num) - 1]
* @param gradule_direction Set the direction of the gradient
@ -254,7 +251,7 @@ esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags,
/**
* @brief Configure LEDC settings
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param timer_sel Timer index(0-3), there are 4 timers in LEDC module
* @param div_num Timer clock divide number, the timer clock is divided from the selected clock source
* @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1)
@ -269,7 +266,7 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_
/**
* @brief Reset LEDC timer
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param timer_sel LEDC timer index(0-3), select from ledc_timer_t
*
* @return
@ -281,7 +278,7 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel);
/**
* @brief Pause LEDC timer counter
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param timer_sel LEDC timer index(0-3), select from ledc_timer_t
*
* @return
@ -294,7 +291,7 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel);
/**
* @brief Resume LEDC timer
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param timer_sel LEDC timer index(0-3), select from ledc_timer_t
*
* @return
@ -306,7 +303,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel);
/**
* @brief Bind LEDC channel with the selected timer
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param channel LEDC channel index(0-7), select from ledc_channel_t
* @param timer_idx LEDC timer index(0-3), select from ledc_timer_t
*
@ -321,7 +318,6 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint
* Call ledc_fade_start() after this to start fading.
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode,
* For now we only support high-speed mode. We will access low-speed mode soon.
* @param channel LEDC channel index(0-7), select from ledc_channel_t
* @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1)))
* @param scale Controls the increase or decrease step scale.
@ -331,6 +327,7 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Fade function not installed.
* - ESP_FAIL Fade function init error
*/
esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num);
@ -339,7 +336,6 @@ esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel
* Call ledc_fade_start() after this to start fading.
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode,
* For now we only support high-speed mode. We will access low-speed mode soon.
* @param channel LEDC channel index(0-7), select from ledc_channel_t
* @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1)))
* @param max_fade_time_ms The maximum time of the fading ( ms ).
@ -348,6 +344,7 @@ esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Fade function not installed.
* - ESP_FAIL Fade function init error
*/
esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms);
@ -358,7 +355,6 @@ esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
*
* @return
* - ESP_ERR_NO_MEM No enough memory
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Fade function already installed.
*/
@ -373,6 +369,7 @@ void ledc_fade_func_uninstall();
/**
* @brief Start LEDC fading.
*
* @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode
* @param channel LEDC channel number
* @param wait_done Whether to block until fading done.
*
@ -381,7 +378,7 @@ void ledc_fade_func_uninstall();
* - ESP_ERR_INVALID_STATE Fade function not installed.
* - ESP_ERR_INVALID_ARG Parameter error.
*/
esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done);
esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_fade_mode_t wait_done);
#ifdef __cplusplus
}

View file

@ -619,22 +619,17 @@ esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t
*
* @param channel RMT channel (0 - 7)
*
* @param rx_buf_size Size of RMT RX ringbuffer.
* @param rx_buf_size Size of RMT RX ringbuffer. Can be 0 if the RX ringbuffer is not used.
*
* @note
* If we do not need RX ringbuffer, just set rx_buf_size to 0.
*
* @note
* When we call rmt_driver_install function, it will register a driver ISR handler,
* DO NOT REGISTER ISR HANDLER AGAIN.
*
* @param rmt_intr_num RMT interrupt number.
* @param intr_alloc_flags Flags for the RMT driver interrupt handler. Pass 0 for default flags. See esp_intr_alloc.h for details.
*
* @return
* - ESP_ERR_INVALID_STATE Driver is already installed, call rmt_driver_uninstall first.
* - ESP_ERR_NO_MEM Memory allocation failure
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_OK Success
*/
esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num);
esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr_alloc_flags);
/**
* @brief Uninstall RMT driver.

View file

@ -38,7 +38,8 @@ typedef struct {
uint32_t pulldown; /*!< Mask of pulldown enable */
uint32_t slpsel; /*!< If slpsel bit is set, slpie will be used as pad input enabled signal in sleep mode */
uint32_t slpie; /*!< Mask of input enable in sleep mode */
uint32_t hold; /*!< Mask of hold_force bit for RTC IO in RTC_CNTL_HOLD_FORCE_REG */
uint32_t hold; /*!< Mask of hold enable */
uint32_t hold_force;/*!< Mask of hold_force bit for RTC IO in RTC_CNTL_HOLD_FORCE_REG */
int rtc_num; /*!< RTC IO number, or -1 if not an RTC GPIO */
} rtc_gpio_desc_t;
@ -192,15 +193,44 @@ esp_err_t rtc_gpio_pullup_dis(gpio_num_t gpio_num);
esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num);
/**
* @brief Disable "hold" signal for all RTC IOs
* @brief Enable hold function on an RTC IO pad
*
* Each RTC pad has a "hold" input signal from the RTC controller.
* If hold signal is set, pad latches current values of input enable,
* Enabling HOLD function will cause the pad to latch current values of
* input enable, output enable, output value, function, drive strength values.
* This function is useful when going into light or deep sleep mode to prevent
* the pin configuration from changing.
*
* @param gpio_num GPIO number (e.g. GPIO_NUM_12)
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO is not an RTC IO
*/
esp_err_t rtc_gpio_hold_en(gpio_num_t gpio_num);
/**
* @brief Disable hold function on an RTC IO pad
*
* Disabling hold function will allow the pad receive the values of
* input enable, output enable, output value, function, drive strength from
* RTC_IO peripheral.
*
* @param gpio_num GPIO number (e.g. GPIO_NUM_12)
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO is not an RTC IO
*/
esp_err_t rtc_gpio_hold_dis(gpio_num_t gpio_num);
/**
* @brief Disable force hold signal for all RTC IOs
*
* Each RTC pad has a "force hold" input signal from the RTC controller.
* If this signal is set, pad latches current values of input enable,
* function, output enable, and other signals which come from the RTC mux.
* Hold signal is enabled before going into deep sleep for pins which
* Force hold signal is enabled before going into deep sleep for pins which
* are used for EXT1 wakeup.
*/
void rtc_gpio_unhold_all();
void rtc_gpio_force_hold_dis_all();
#ifdef __cplusplus

View file

@ -153,6 +153,9 @@ esp_err_t spi_bus_free(spi_host_device_t host);
* peripheral and routes it to the indicated GPIO. All SPI master devices have three CS pins and can thus control
* up to three devices.
*
* @note While in general, speeds up to 80MHz on the dedicated SPI pins and 40MHz on GPIO-matrix-routed pins are
* supported, full-duplex transfers routed over the GPIO matrix only support speeds up to 26MHz.
*
* @param host SPI peripheral to allocate device on
* @param dev_config SPI interface protocol config for the device
* @param handle Pointer to variable to hold the device handle

View file

@ -43,10 +43,21 @@ typedef intr_handle_t touch_isr_handle_t;
*
*This function int touch pad module ,enable touch module
*
* @return None
* @return
* - ESP_OK Success
* - ESP_FAIL Touch pad init error
*
*/
void touch_pad_init();
esp_err_t touch_pad_init();
/**
* @brief Uninstall TouchPad driver.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Touch pad deinit error
*/
esp_err_t touch_pad_deinit();
/**
* @brief Configure touch pad interrupt threshold.
@ -60,6 +71,7 @@ void touch_pad_init();
*
* @return - ESP_OK Success
* - ESP_ERR_INVALID_ARG Touch pad error
* - ESP_FAIL Touch pad not initialized
*
*/
esp_err_t touch_pad_config(touch_pad_t touch_num, uint16_t threshold);
@ -76,6 +88,7 @@ esp_err_t touch_pad_config(touch_pad_t touch_num, uint16_t threshold);
*
* @return - ESP_OK Success
* - ESP_ERR_INVALID_ARG Touch pad error
* - ESP_FAIL Touch pad not initialized
*
*/
esp_err_t touch_pad_read(touch_pad_t touch_num, uint16_t * touch_value);

View file

@ -41,7 +41,8 @@ typedef struct {
xSemaphoreHandle ledc_fade_sem;
xSemaphoreHandle ledc_fade_mux;
} ledc_fade_t;
static ledc_fade_t* s_ledc_fade_rec = NULL;
static ledc_fade_t *s_ledc_fade_rec[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX];
static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL;
#define LEDC_VAL_NO_CHANGE (-1)
@ -57,9 +58,24 @@ static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL;
#define LEDC_FADE_TARGET_ERR_STR "LEDC fade target duty error"
#define LEDC_FADE_INSTALLED_ERR_STR "LEDC fade service already installed"
#define LEDC_FADE_MODE_ERR_STR "LEDC fade mode error"
#define LEDC_FADE_INIT_ERROR_STR "LEDC fade channel init error"
static void ledc_ls_timer_update(ledc_mode_t speed_mode, ledc_timer_t timer_sel)
{
if (speed_mode == LEDC_LOW_SPEED_MODE) {
LEDC.timer_group[speed_mode].timer[timer_sel].conf.low_speed_update = 1;
}
}
esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src)
static IRAM_ATTR void ledc_ls_channel_update(ledc_mode_t speed_mode, ledc_channel_t channel_num)
{
if (speed_mode == LEDC_LOW_SPEED_MODE) {
LEDC.channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
}
}
esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num,
ledc_clk_src_t clk_src)
{
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG);
@ -67,9 +83,7 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_
LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num = bit_num;
if (speed_mode != LEDC_HIGH_SPEED_MODE) {
LEDC.timer_group[speed_mode].timer[timer_sel].conf.low_speed_update = 1;
}
ledc_ls_timer_update(speed_mode, timer_sel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -86,6 +100,7 @@ static IRAM_ATTR esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel
((duty_num & LEDC_DUTY_NUM_HSCH0_V) << LEDC_DUTY_NUM_HSCH0_S) |
((duty_cycle & LEDC_DUTY_CYCLE_HSCH0_V) << LEDC_DUTY_CYCLE_HSCH0_S) |
((duty_scale & LEDC_DUTY_SCALE_HSCH0_V) << LEDC_DUTY_SCALE_HSCH0_S);
ledc_ls_channel_update(speed_mode, channel_num);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -96,6 +111,7 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel
LEDC_CHECK(timer_idx <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx;
ledc_ls_channel_update(speed_mode, channel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -107,6 +123,7 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel)
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0;
ledc_ls_timer_update(speed_mode, timer_sel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -117,6 +134,7 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel)
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1;
ledc_ls_timer_update(speed_mode, timer_sel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -127,6 +145,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel)
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0;
ledc_ls_timer_update(speed_mode, timer_sel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -138,10 +157,14 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel,
uint32_t intr_type = type;
portENTER_CRITICAL(&ledc_spinlock);
value = LEDC.int_ena.val;
uint8_t int_en_base = LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S;
if (speed_mode == LEDC_LOW_SPEED_MODE) {
int_en_base = LEDC_DUTY_CHNG_END_LSCH0_INT_ENA_S;
}
if (intr_type == LEDC_INTR_FADE_END) {
LEDC.int_ena.val = value | BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel);
LEDC.int_ena.val = value | BIT(int_en_base + channel);
} else {
LEDC.int_ena.val = ( value & ( ~( BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel) ) ) );
LEDC.int_ena.val = (value & (~(BIT(int_en_base + channel))));
}
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
@ -188,12 +211,17 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
if (div_param > LEDC_DIV_NUM_HSTIMER0_V) {
// APB_CLK results in divisor which too high. Try using REF_TICK as clock source.
timer_clk_src = LEDC_REF_TICK;
div_param = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision;
div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
if (div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) {
ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d",
(uint32_t ) div_param);
ret = ESP_FAIL;
}
} else {
if (speed_mode == LEDC_LOW_SPEED_MODE) {
//for now, we only select 80mhz for slow clk of LEDC low speed channels.
LEDC.conf.slow_clk_sel = 1;
}
}
// set timer parameters
ledc_timer_set(speed_mode, timer_num, div_param, bit_num, timer_clk_src);
@ -212,8 +240,7 @@ esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc
if (speed_mode == LEDC_HIGH_SPEED_MODE) {
gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0);
} else {
ESP_LOGE(LEDC_TAG, "low speed mode is not implemented");
return ESP_ERR_NOT_SUPPORTED;
gpio_matrix_out(gpio_num, LEDC_LS_SIG_OUT0_IDX + ledc_channel, 0, 0);
}
return ESP_OK;
}
@ -248,7 +275,11 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
/*set LEDC signal in gpio matrix*/
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);
gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0);
if (speed_mode == LEDC_HIGH_SPEED_MODE) {
gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0);
} else {
gpio_matrix_out(gpio_num, LEDC_LS_SIG_OUT0_IDX + ledc_channel, 0, 0);
}
return ret;
}
@ -259,6 +290,7 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1;
LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1;
ledc_ls_channel_update(speed_mode, channel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -271,6 +303,7 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl
LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1;
LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0;
LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 0;
ledc_ls_channel_update(speed_mode, channel);
portEXIT_CRITICAL(&ledc_spinlock);
return ESP_OK;
}
@ -285,7 +318,7 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t
ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale);
return ESP_ERR_INVALID_ARG;
}
if (s_ledc_fade_rec) {
if (s_ledc_fade_rec[speed_mode][channel]) {
ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE);
}
ledc_duty_config(speed_mode,
@ -304,7 +337,7 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t
{
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
if (s_ledc_fade_rec) {
if (s_ledc_fade_rec[speed_mode][channel]) {
ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE);
}
ledc_duty_config(speed_mode,
@ -334,11 +367,11 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
uint32_t div_num = 0;
uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num;
uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel;
uint32_t precision = ( 0x1 << bit_num );
uint32_t precision = (0x1 << bit_num);
if (timer_source_clk == LEDC_APB_CLK) {
div_num = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision;
div_num = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision;
} else {
div_num = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision;
div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
}
if (div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) {
ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num);
@ -351,81 +384,127 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num)
{
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ( 0 ));
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, (0));
portENTER_CRITICAL(&ledc_spinlock);
uint32_t freq = 0;
uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel;
uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num;
uint32_t div_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num;
uint32_t precision = ( 0x1 << bit_num );
uint32_t precision = (0x1 << bit_num);
if (timer_source_clk == LEDC_APB_CLK) {
freq = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / precision / div_num;
freq = ((uint64_t) LEDC_APB_CLK_HZ << 8) / precision / div_num;
} else {
freq = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / precision / div_num;
freq = ((uint64_t) LEDC_REF_CLK_HZ << 8) / precision / div_num;
}
portEXIT_CRITICAL(&ledc_spinlock);
return freq;
}
void IRAM_ATTR ledc_fade_isr()
void IRAM_ATTR ledc_fade_isr(void* arg)
{
int i;
int channel;
portBASE_TYPE HPTaskAwoken = pdFALSE;
uint32_t intr_status = LEDC.int_st.val; //read LEDC interrupt status.
LEDC.int_clr.val = intr_status; //clear LEDC interrupt status.
for (i = 0; i < 8; i++) {
if (intr_status & BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ST_S + i)) {
int speed_mode = s_ledc_fade_rec[i].speed_mode;
int duty_cur = LEDC.channel_group[speed_mode].channel[i].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM;
if (duty_cur == s_ledc_fade_rec[i].target_duty) {
if(s_ledc_fade_rec[i].mode == LEDC_FADE_WAIT_DONE) {
xSemaphoreGiveFromISR(s_ledc_fade_rec[i].ledc_fade_sem, &HPTaskAwoken);
if(HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR() ;
int speed_mode = LEDC_HIGH_SPEED_MODE;
for (channel = 0; channel < LEDC_CHANNEL_MAX; channel++) {
if (intr_status & (BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ST_S + channel) | BIT(LEDC_DUTY_CHNG_END_LSCH0_INT_ST_S + channel))) {
if (intr_status & BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ST_S + channel)) {
speed_mode = LEDC_HIGH_SPEED_MODE;
} else {
speed_mode = LEDC_LOW_SPEED_MODE;
}
if (s_ledc_fade_rec[speed_mode][channel] == NULL) {
//fade object not initialized yet.
continue;
}
int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM;
if (duty_cur == s_ledc_fade_rec[speed_mode][channel]->target_duty) {
if (s_ledc_fade_rec[speed_mode][channel]->mode == LEDC_FADE_WAIT_DONE) {
xSemaphoreGiveFromISR(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
continue;
}
int duty_tar = s_ledc_fade_rec[i].target_duty;
int scale = s_ledc_fade_rec[i].scale;
int duty_tar = s_ledc_fade_rec[speed_mode][channel]->target_duty;
int scale = s_ledc_fade_rec[speed_mode][channel]->scale;
if (scale == 0) {
continue;
}
int cycle = s_ledc_fade_rec[i].cycle_num;
int delta = s_ledc_fade_rec[i].direction == LEDC_DUTY_DIR_DECREASE ? duty_cur - duty_tar : duty_tar - duty_cur;
int cycle = s_ledc_fade_rec[speed_mode][channel]->cycle_num;
int delta = s_ledc_fade_rec[speed_mode][channel]->direction == LEDC_DUTY_DIR_DECREASE ? duty_cur - duty_tar : duty_tar - duty_cur;
int step = delta / scale > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : delta / scale;
if (delta > scale) {
ledc_duty_config(
speed_mode,
i,
channel,
LEDC_VAL_NO_CHANGE,
duty_cur << LEDC_DUTY_DECIMAL_BIT_NUM,
s_ledc_fade_rec[i].direction,
s_ledc_fade_rec[speed_mode][channel]->direction,
step,
cycle,
scale);
} else {
ledc_duty_config(
speed_mode,
i,
channel,
LEDC_VAL_NO_CHANGE,
duty_tar << LEDC_DUTY_DECIMAL_BIT_NUM,
s_ledc_fade_rec[i].direction,
s_ledc_fade_rec[speed_mode][channel]->direction,
1,
1,
0);
}
LEDC.channel_group[speed_mode].channel[i].conf1.duty_start = 1;
LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1;
}
}
LEDC.int_clr.val = intr_status; //clear LEDC interrupt status.
}
static esp_err_t ledc_fade_channel_deinit(ledc_mode_t speed_mode, ledc_channel_t channel)
{
if (s_ledc_fade_rec[speed_mode][channel]) {
if (s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux) {
vSemaphoreDelete(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux);
s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux = NULL;
}
if (s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem) {
vSemaphoreDelete(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem);
s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem = NULL;
}
free(s_ledc_fade_rec[speed_mode][channel]);
s_ledc_fade_rec[speed_mode][channel] = NULL;
}
return ESP_OK;
}
static esp_err_t ledc_fade_channel_init_check(ledc_mode_t speed_mode, ledc_channel_t channel)
{
if (s_ledc_fade_rec[speed_mode][channel] == NULL) {
s_ledc_fade_rec[speed_mode][channel] = (ledc_fade_t *) calloc(1, sizeof(ledc_fade_t));
s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux = xSemaphoreCreateMutex();
s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem = xSemaphoreCreateBinary();
}
if (s_ledc_fade_rec[speed_mode][channel]
&& s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux
&& s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem) {
return ESP_OK;
} else {
ledc_fade_channel_deinit(speed_mode, channel);
return ESP_FAIL;
}
}
esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms)
{
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel < LEDC_CHANNEL_MAX, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK , LEDC_FADE_INIT_ERROR_STR, ESP_FAIL);
int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel;
int max_duty = ( 1 << ( LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num ) ) - 1;
int max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num)) - 1;
LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG);
uint32_t freq = ledc_get_freq(speed_mode, timer_sel);
int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM;
@ -441,16 +520,17 @@ esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel
cycle_num = total_cycles / duty_delta;
} else {
cycle_num = 1;
scale = ( duty_delta + total_cycles - 1 ) / total_cycles;
scale = (duty_delta + total_cycles - 1) / total_cycles;
}
return ledc_set_fade_with_step(speed_mode, channel, target_duty, scale, cycle_num);
}
esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num)
{
LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE);
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel < LEDC_CHANNEL_MAX, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK , LEDC_FADE_INIT_ERROR_STR, ESP_FAIL);
int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel;
int max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num)) - 1;
LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG);
@ -463,18 +543,18 @@ esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel
if (duty_delta == 0) {
return ESP_OK;
}
s_ledc_fade_rec[channel].speed_mode = speed_mode;
s_ledc_fade_rec[channel].target_duty = target_duty;
s_ledc_fade_rec[channel].cycle_num = cycle_num;
s_ledc_fade_rec[channel].scale = scale;
s_ledc_fade_rec[speed_mode][channel]->speed_mode = speed_mode;
s_ledc_fade_rec[speed_mode][channel]->target_duty = target_duty;
s_ledc_fade_rec[speed_mode][channel]->cycle_num = cycle_num;
s_ledc_fade_rec[speed_mode][channel]->scale = scale;
int step_num;
if (duty_cur > target_duty) {
s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_DECREASE;
step_num = ( duty_cur - target_duty ) / scale;
s_ledc_fade_rec[speed_mode][channel]->direction = LEDC_DUTY_DIR_DECREASE;
step_num = (duty_cur - target_duty) / scale;
step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num;
} else {
s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_INCREASE;
step_num = ( target_duty - duty_cur ) / scale;
s_ledc_fade_rec[speed_mode][channel]->direction = LEDC_DUTY_DIR_INCREASE;
step_num = (target_duty - duty_cur) / scale;
step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num;
}
portEXIT_CRITICAL(&ledc_spinlock);
@ -483,42 +563,28 @@ esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel
speed_mode,
channel,
duty_cur,
s_ledc_fade_rec[channel].direction,
s_ledc_fade_rec[speed_mode][channel]->direction,
step_num,
s_ledc_fade_rec[channel].cycle_num,
s_ledc_fade_rec[channel].scale
s_ledc_fade_rec[speed_mode][channel]->cycle_num,
s_ledc_fade_rec[speed_mode][channel]->scale
);
ESP_LOGD(LEDC_TAG, "cur duty: %d; target: %d, step: %d, cycle: %d; scale: %d\n",
LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM,
target_duty,
step_num,
s_ledc_fade_rec[channel].cycle_num,
s_ledc_fade_rec[channel].scale
s_ledc_fade_rec[speed_mode][channel]->cycle_num,
s_ledc_fade_rec[speed_mode][channel]->scale
);
LEDC.int_clr.val |= BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel);
int bit_num_ch0 = (speed_mode == LEDC_HIGH_SPEED_MODE) ? LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S : LEDC_DUTY_CHNG_END_LSCH0_INT_ENA_S;
LEDC.int_clr.val |= BIT(bit_num_ch0 + channel);
ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_FADE_END);
return ESP_OK;
}
esp_err_t ledc_fade_func_install(int intr_alloc_flags)
{
LEDC_CHECK(s_ledc_fade_rec == NULL, LEDC_FADE_INSTALLED_ERR_STR, ESP_ERR_INVALID_STATE);
s_ledc_fade_rec = (ledc_fade_t*) calloc(LEDC_CHANNEL_MAX, sizeof(ledc_fade_t));
if (s_ledc_fade_rec == NULL) {
return ESP_ERR_NO_MEM;
}
int i = 0;
for (i = 0; i < LEDC_CHANNEL_MAX; i++) {
s_ledc_fade_rec[i].ledc_fade_sem = xSemaphoreCreateBinary();
s_ledc_fade_rec[i].ledc_fade_mux = xSemaphoreCreateMutex();
if (s_ledc_fade_rec[i].ledc_fade_sem == NULL || s_ledc_fade_rec[i].ledc_fade_mux == NULL) {
ledc_fade_func_uninstall();
return ESP_ERR_NO_MEM;
}
}
//OR intr_alloc_flags with ESP_INTR_FLAG_IRAM because the fade isr is in IRAM
ledc_isr_register(ledc_fade_isr, NULL, intr_alloc_flags | ESP_INTR_FLAG_IRAM, &s_ledc_fade_isr_handle);
return ESP_OK;
return ledc_isr_register(ledc_fade_isr, NULL, intr_alloc_flags | ESP_INTR_FLAG_IRAM, &s_ledc_fade_isr_handle);
}
void ledc_fade_func_uninstall()
@ -526,42 +592,32 @@ void ledc_fade_func_uninstall()
if (s_ledc_fade_rec == NULL) {
return;
}
if(s_ledc_fade_isr_handle) {
if (s_ledc_fade_isr_handle) {
esp_intr_free(s_ledc_fade_isr_handle);
s_ledc_fade_isr_handle = NULL;
}
int i;
for (i = 0; i < LEDC_CHANNEL_MAX; i++) {
if (s_ledc_fade_rec[i].ledc_fade_sem) {
xSemaphoreHandle sem_tmp = s_ledc_fade_rec[i].ledc_fade_sem;
s_ledc_fade_rec[i].ledc_fade_sem = NULL;
vSemaphoreDelete(sem_tmp);
}
if (s_ledc_fade_rec[i].ledc_fade_mux) {
xSemaphoreHandle mux_tmp = s_ledc_fade_rec[i].ledc_fade_mux;
s_ledc_fade_rec[i].ledc_fade_mux = NULL;
vSemaphoreDelete(mux_tmp);
int channel, mode;
for (mode = 0; mode < LEDC_SPEED_MODE_MAX; mode++) {
for (channel = 0; channel < LEDC_CHANNEL_MAX; channel++) {
ledc_fade_channel_deinit(mode, channel);
}
}
free(s_ledc_fade_rec);
s_ledc_fade_rec = NULL;
return;
}
esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done)
esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_fade_mode_t wait_done)
{
LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE);
LEDC_CHECK(wait_done < LEDC_FADE_MAX, LEDC_FADE_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
int speed_mode = s_ledc_fade_rec[channel].speed_mode;
xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_mux, portMAX_DELAY);
xSemaphoreTake(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux, portMAX_DELAY);
if (wait_done == LEDC_FADE_WAIT_DONE) {
s_ledc_fade_rec[channel].mode = LEDC_FADE_WAIT_DONE;
s_ledc_fade_rec[speed_mode][channel]->mode = LEDC_FADE_WAIT_DONE;
ledc_update_duty(speed_mode, channel);
xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_sem, portMAX_DELAY);
xSemaphoreTake(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem, portMAX_DELAY);
} else {
s_ledc_fade_rec[channel].mode = LEDC_FADE_NO_WAIT;
s_ledc_fade_rec[speed_mode][channel]->mode = LEDC_FADE_NO_WAIT;
ledc_update_duty(speed_mode, channel);
}
xSemaphoreGive(s_ledc_fade_rec[channel].ledc_fade_mux);
xSemaphoreGive(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_mux);
return ESP_OK;
}

View file

@ -555,7 +555,7 @@ static void IRAM_ATTR rmt_driver_isr_default(void* arg)
if(p_rmt->rx_buf) {
BaseType_t res = xRingbufferSendFromISR(p_rmt->rx_buf, (void*) RMTMEM.chan[channel].data32, item_len * 4, &HPTaskAwoken);
if(res == pdFALSE) {
ESP_LOGE(RMT_TAG, "RMT RX BUFFER FULL");
ESP_EARLY_LOGE(RMT_TAG, "RMT RX BUFFER FULL");
} else {
}
@ -643,15 +643,15 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr
{
RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
if(p_rmt_obj[channel] != NULL) {
ESP_LOGD(RMT_TAG, "RMT DRIVER ALREADY INSTALLED");
return ESP_FAIL;
ESP_LOGD(RMT_TAG, "RMT driver already installed");
return ESP_ERR_INVALID_STATE;
}
p_rmt_obj[channel] = (rmt_obj_t*) malloc(sizeof(rmt_obj_t));
if(p_rmt_obj[channel] == NULL) {
ESP_LOGE(RMT_TAG, "RMT driver malloc error");
return ESP_FAIL;
return ESP_ERR_NO_MEM;
}
memset(p_rmt_obj[channel], 0, sizeof(rmt_obj_t));

View file

@ -23,6 +23,7 @@
#include "dac.h"
#include "freertos/FreeRTOS.h"
#include "freertos/xtensa_api.h"
#include "freertos/semphr.h"
static const char *RTC_MODULE_TAG = "RTC_MODULE";
@ -37,49 +38,50 @@ static const char *RTC_MODULE_TAG = "RTC_MODULE";
}
portMUX_TYPE rtc_spinlock = portMUX_INITIALIZER_UNLOCKED;
static xSemaphoreHandle rtc_touch_sem = NULL;
//Reg,Mux,Fun,IE,Up,Down,Rtc_number
const rtc_gpio_desc_t rtc_gpio_desc[GPIO_PIN_COUNT] = {
{RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, RTC_IO_TOUCH_PAD1_SLP_SEL_M, RTC_IO_TOUCH_PAD1_SLP_IE_M, RTC_CNTL_TOUCH_PAD1_HOLD_FORCE_M, 11}, //0
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //1
{RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, RTC_IO_TOUCH_PAD2_FUN_SEL_S, RTC_IO_TOUCH_PAD2_FUN_IE_M, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M, RTC_IO_TOUCH_PAD2_SLP_SEL_M, RTC_IO_TOUCH_PAD2_SLP_IE_M, RTC_CNTL_TOUCH_PAD2_HOLD_FORCE_M, 12}, //2
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //3
{RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, RTC_IO_TOUCH_PAD0_FUN_SEL_S, RTC_IO_TOUCH_PAD0_FUN_IE_M, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M, RTC_IO_TOUCH_PAD0_SLP_SEL_M, RTC_IO_TOUCH_PAD0_SLP_IE_M, RTC_CNTL_TOUCH_PAD0_HOLD_FORCE_M, 10}, //4
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //5
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //6
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //7
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //8
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //9
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //10
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //11
{RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_MUX_SEL_M, RTC_IO_TOUCH_PAD5_FUN_SEL_S, RTC_IO_TOUCH_PAD5_FUN_IE_M, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M, RTC_IO_TOUCH_PAD5_SLP_SEL_M, RTC_IO_TOUCH_PAD5_SLP_IE_M, RTC_CNTL_TOUCH_PAD5_HOLD_FORCE_M, 15}, //12
{RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, RTC_IO_TOUCH_PAD4_FUN_SEL_S, RTC_IO_TOUCH_PAD4_FUN_IE_M, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M, RTC_IO_TOUCH_PAD4_SLP_SEL_M, RTC_IO_TOUCH_PAD4_SLP_IE_M, RTC_CNTL_TOUCH_PAD4_HOLD_FORCE_M, 14}, //13
{RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_MUX_SEL_M, RTC_IO_TOUCH_PAD6_FUN_SEL_S, RTC_IO_TOUCH_PAD6_FUN_IE_M, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M, RTC_IO_TOUCH_PAD6_SLP_SEL_M, RTC_IO_TOUCH_PAD6_SLP_IE_M, RTC_CNTL_TOUCH_PAD6_HOLD_FORCE_M, 16}, //14
{RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_MUX_SEL_M, RTC_IO_TOUCH_PAD3_FUN_SEL_S, RTC_IO_TOUCH_PAD3_FUN_IE_M, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M, RTC_IO_TOUCH_PAD3_SLP_SEL_M, RTC_IO_TOUCH_PAD3_SLP_IE_M, RTC_CNTL_TOUCH_PAD3_HOLD_FORCE_M, 13}, //15
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //16
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //17
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //18
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //19
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //20
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //21
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //22
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //23
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //24
{RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, RTC_IO_PDAC1_SLP_SEL_M, RTC_IO_PDAC1_SLP_IE_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, 6}, //25
{RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, 7}, //26
{RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, RTC_IO_TOUCH_PAD7_SLP_SEL_M, RTC_IO_TOUCH_PAD7_SLP_IE_M, RTC_CNTL_TOUCH_PAD7_HOLD_FORCE_M, 17}, //27
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //28
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //29
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //30
{0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //31
{RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_MUX_SEL_M, RTC_IO_X32P_FUN_SEL_S, RTC_IO_X32P_FUN_IE_M, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M, RTC_IO_X32P_SLP_SEL_M, RTC_IO_X32P_SLP_IE_M, RTC_CNTL_X32P_HOLD_FORCE_M, 9}, //32
{RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL_M, RTC_IO_X32N_FUN_SEL_S, RTC_IO_X32N_FUN_IE_M, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M, RTC_IO_X32N_SLP_SEL_M, RTC_IO_X32N_SLP_IE_M, RTC_CNTL_X32N_HOLD_FORCE_M, 8}, //33
{RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_MUX_SEL_M, RTC_IO_ADC1_FUN_SEL_S, RTC_IO_ADC1_FUN_IE_M, 0, 0, RTC_IO_ADC1_SLP_SEL_M, RTC_IO_ADC1_SLP_IE_M, RTC_CNTL_ADC1_HOLD_FORCE_M, 4}, //34
{RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_MUX_SEL_M, RTC_IO_ADC2_FUN_SEL_S, RTC_IO_ADC2_FUN_IE_M, 0, 0, RTC_IO_ADC2_SLP_SEL_M, RTC_IO_ADC2_SLP_IE_M, RTC_CNTL_ADC2_HOLD_FORCE_M, 5}, //35
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE1_MUX_SEL_M, RTC_IO_SENSE1_FUN_SEL_S, RTC_IO_SENSE1_FUN_IE_M, 0, 0, RTC_IO_SENSE1_SLP_SEL_M, RTC_IO_SENSE1_SLP_IE_M, RTC_CNTL_SENSE1_HOLD_FORCE_M, 0}, //36
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE2_MUX_SEL_M, RTC_IO_SENSE2_FUN_SEL_S, RTC_IO_SENSE2_FUN_IE_M, 0, 0, RTC_IO_SENSE2_SLP_SEL_M, RTC_IO_SENSE2_SLP_IE_M, RTC_CNTL_SENSE2_HOLD_FORCE_M, 1}, //37
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE3_MUX_SEL_M, RTC_IO_SENSE3_FUN_SEL_S, RTC_IO_SENSE3_FUN_IE_M, 0, 0, RTC_IO_SENSE3_SLP_SEL_M, RTC_IO_SENSE3_SLP_IE_M, RTC_CNTL_SENSE3_HOLD_FORCE_M, 2}, //38
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE4_MUX_SEL_M, RTC_IO_SENSE4_FUN_SEL_S, RTC_IO_SENSE4_FUN_IE_M, 0, 0, RTC_IO_SENSE4_SLP_SEL_M, RTC_IO_SENSE4_SLP_IE_M, RTC_CNTL_SENSE4_HOLD_FORCE_M, 3}, //39
{RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, RTC_IO_TOUCH_PAD1_SLP_SEL_M, RTC_IO_TOUCH_PAD1_SLP_IE_M, RTC_IO_TOUCH_PAD1_HOLD_M, RTC_CNTL_TOUCH_PAD1_HOLD_FORCE_M, 11}, //0
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //1
{RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, RTC_IO_TOUCH_PAD2_FUN_SEL_S, RTC_IO_TOUCH_PAD2_FUN_IE_M, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M, RTC_IO_TOUCH_PAD2_SLP_SEL_M, RTC_IO_TOUCH_PAD2_SLP_IE_M, RTC_IO_TOUCH_PAD2_HOLD_M, RTC_CNTL_TOUCH_PAD2_HOLD_FORCE_M, 12}, //2
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //3
{RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, RTC_IO_TOUCH_PAD0_FUN_SEL_S, RTC_IO_TOUCH_PAD0_FUN_IE_M, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M, RTC_IO_TOUCH_PAD0_SLP_SEL_M, RTC_IO_TOUCH_PAD0_SLP_IE_M, RTC_IO_TOUCH_PAD0_HOLD_M, RTC_CNTL_TOUCH_PAD0_HOLD_FORCE_M, 10}, //4
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //5
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //6
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //7
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //8
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //9
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //10
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //11
{RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_MUX_SEL_M, RTC_IO_TOUCH_PAD5_FUN_SEL_S, RTC_IO_TOUCH_PAD5_FUN_IE_M, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M, RTC_IO_TOUCH_PAD5_SLP_SEL_M, RTC_IO_TOUCH_PAD5_SLP_IE_M, RTC_IO_TOUCH_PAD5_HOLD_M, RTC_CNTL_TOUCH_PAD5_HOLD_FORCE_M, 15}, //12
{RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, RTC_IO_TOUCH_PAD4_FUN_SEL_S, RTC_IO_TOUCH_PAD4_FUN_IE_M, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M, RTC_IO_TOUCH_PAD4_SLP_SEL_M, RTC_IO_TOUCH_PAD4_SLP_IE_M, RTC_IO_TOUCH_PAD4_HOLD_M, RTC_CNTL_TOUCH_PAD4_HOLD_FORCE_M, 14}, //13
{RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_MUX_SEL_M, RTC_IO_TOUCH_PAD6_FUN_SEL_S, RTC_IO_TOUCH_PAD6_FUN_IE_M, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M, RTC_IO_TOUCH_PAD6_SLP_SEL_M, RTC_IO_TOUCH_PAD6_SLP_IE_M, RTC_IO_TOUCH_PAD6_HOLD_M, RTC_CNTL_TOUCH_PAD6_HOLD_FORCE_M, 16}, //14
{RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_MUX_SEL_M, RTC_IO_TOUCH_PAD3_FUN_SEL_S, RTC_IO_TOUCH_PAD3_FUN_IE_M, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M, RTC_IO_TOUCH_PAD3_SLP_SEL_M, RTC_IO_TOUCH_PAD3_SLP_IE_M, RTC_IO_TOUCH_PAD3_HOLD_M, RTC_CNTL_TOUCH_PAD3_HOLD_FORCE_M, 13}, //15
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //16
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //17
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //18
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //19
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //20
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //21
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //22
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //23
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //24
{RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, RTC_IO_PDAC1_SLP_SEL_M, RTC_IO_PDAC1_SLP_IE_M, RTC_IO_PDAC1_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, 6}, //25
{RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, RTC_IO_PDAC2_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, 7}, //26
{RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, RTC_IO_TOUCH_PAD7_SLP_SEL_M, RTC_IO_TOUCH_PAD7_SLP_IE_M, RTC_IO_TOUCH_PAD7_HOLD_M, RTC_CNTL_TOUCH_PAD7_HOLD_FORCE_M, 17}, //27
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //28
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //29
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //30
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //31
{RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_MUX_SEL_M, RTC_IO_X32P_FUN_SEL_S, RTC_IO_X32P_FUN_IE_M, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M, RTC_IO_X32P_SLP_SEL_M, RTC_IO_X32P_SLP_IE_M, RTC_IO_X32P_HOLD_M, RTC_CNTL_X32P_HOLD_FORCE_M, 9}, //32
{RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL_M, RTC_IO_X32N_FUN_SEL_S, RTC_IO_X32N_FUN_IE_M, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M, RTC_IO_X32N_SLP_SEL_M, RTC_IO_X32N_SLP_IE_M, RTC_IO_X32N_HOLD_M, RTC_CNTL_X32N_HOLD_FORCE_M, 8}, //33
{RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_MUX_SEL_M, RTC_IO_ADC1_FUN_SEL_S, RTC_IO_ADC1_FUN_IE_M, 0, 0, RTC_IO_ADC1_SLP_SEL_M, RTC_IO_ADC1_SLP_IE_M, RTC_IO_ADC1_HOLD_M, RTC_CNTL_ADC1_HOLD_FORCE_M, 4}, //34
{RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_MUX_SEL_M, RTC_IO_ADC2_FUN_SEL_S, RTC_IO_ADC2_FUN_IE_M, 0, 0, RTC_IO_ADC2_SLP_SEL_M, RTC_IO_ADC2_SLP_IE_M, RTC_IO_ADC1_HOLD_M, RTC_CNTL_ADC2_HOLD_FORCE_M, 5}, //35
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE1_MUX_SEL_M, RTC_IO_SENSE1_FUN_SEL_S, RTC_IO_SENSE1_FUN_IE_M, 0, 0, RTC_IO_SENSE1_SLP_SEL_M, RTC_IO_SENSE1_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE1_HOLD_FORCE_M, 0}, //36
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE2_MUX_SEL_M, RTC_IO_SENSE2_FUN_SEL_S, RTC_IO_SENSE2_FUN_IE_M, 0, 0, RTC_IO_SENSE2_SLP_SEL_M, RTC_IO_SENSE2_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE2_HOLD_FORCE_M, 1}, //37
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE3_MUX_SEL_M, RTC_IO_SENSE3_FUN_SEL_S, RTC_IO_SENSE3_FUN_IE_M, 0, 0, RTC_IO_SENSE3_SLP_SEL_M, RTC_IO_SENSE3_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE3_HOLD_FORCE_M, 2}, //38
{RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE4_MUX_SEL_M, RTC_IO_SENSE4_FUN_SEL_S, RTC_IO_SENSE4_FUN_IE_M, 0, 0, RTC_IO_SENSE4_SLP_SEL_M, RTC_IO_SENSE4_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE4_HOLD_FORCE_M, 3}, //39
};
/*---------------------------------------------------------------
@ -205,7 +207,7 @@ esp_err_t rtc_gpio_pullup_en(gpio_num_t gpio_num)
{
//this is a digital pad
if (rtc_gpio_desc[gpio_num].pullup == 0) {
return ESP_FAIL;
return ESP_ERR_INVALID_ARG;
}
//this is a rtc pad
@ -220,7 +222,7 @@ esp_err_t rtc_gpio_pulldown_en(gpio_num_t gpio_num)
{
//this is a digital pad
if (rtc_gpio_desc[gpio_num].pulldown == 0) {
return ESP_FAIL;
return ESP_ERR_INVALID_ARG;
}
//this is a rtc pad
@ -235,7 +237,7 @@ esp_err_t rtc_gpio_pullup_dis(gpio_num_t gpio_num)
{
//this is a digital pad
if ( rtc_gpio_desc[gpio_num].pullup == 0 ) {
return ESP_FAIL;
return ESP_ERR_INVALID_ARG;
}
//this is a rtc pad
@ -250,7 +252,7 @@ esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num)
{
//this is a digital pad
if (rtc_gpio_desc[gpio_num].pulldown == 0) {
return ESP_FAIL;
return ESP_ERR_INVALID_ARG;
}
//this is a rtc pad
@ -261,12 +263,37 @@ esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num)
return ESP_OK;
}
void rtc_gpio_unhold_all()
esp_err_t rtc_gpio_hold_en(gpio_num_t gpio_num)
{
// check if an RTC IO
if (rtc_gpio_desc[gpio_num].pullup == 0) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&rtc_spinlock);
SET_PERI_REG_MASK(rtc_gpio_desc[gpio_num].reg, rtc_gpio_desc[gpio_num].hold);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t rtc_gpio_hold_dis(gpio_num_t gpio_num)
{
// check if an RTC IO
if (rtc_gpio_desc[gpio_num].pullup == 0) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&rtc_spinlock);
CLEAR_PERI_REG_MASK(rtc_gpio_desc[gpio_num].reg, rtc_gpio_desc[gpio_num].hold);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
void rtc_gpio_force_hold_dis_all()
{
for (int gpio = 0; gpio < GPIO_PIN_COUNT; ++gpio) {
const rtc_gpio_desc_t* desc = &rtc_gpio_desc[gpio];
if (desc->hold != 0) {
REG_CLR_BIT(RTC_CNTL_HOLD_FORCE_REG, desc->hold);
if (desc->hold_force != 0) {
REG_CLR_BIT(RTC_CNTL_HOLD_FORCE_REG, desc->hold_force);
}
}
}
@ -323,6 +350,7 @@ static esp_err_t touch_pad_get_io_num(touch_pad_t touch_num, gpio_num_t *gpio_nu
static esp_err_t touch_pad_init_config(uint16_t sleep_cycle, uint16_t sample_cycle_num)
{
xSemaphoreTake(rtc_touch_sem, portMAX_DELAY);
portENTER_CRITICAL(&rtc_spinlock);
SET_PERI_REG_BITS(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_XPD_BIAS, 1, RTC_IO_TOUCH_XPD_BIAS_S);
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN_CLR);
@ -336,13 +364,30 @@ static esp_err_t touch_pad_init_config(uint16_t sleep_cycle, uint16_t sample_cyc
//Touch Pad Measure Time= 8Mhz
SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_MEAS_DELAY, sample_cycle_num, SENS_TOUCH_MEAS_DELAY_S); //8Mhz
portEXIT_CRITICAL(&rtc_spinlock);
xSemaphoreGive(rtc_touch_sem);
return ESP_OK;
}
void touch_pad_init()
esp_err_t touch_pad_init()
{
touch_pad_init_config(TOUCH_PAD_SLEEP_CYCLE_CONFIG, TOUCH_PAD_MEASURE_CYCLE_CONFIG);
if(rtc_touch_sem == NULL) {
rtc_touch_sem = xSemaphoreCreateMutex();
}
if(rtc_touch_sem == NULL) {
return ESP_FAIL;
}
return touch_pad_init_config(TOUCH_PAD_SLEEP_CYCLE_CONFIG, TOUCH_PAD_MEASURE_CYCLE_CONFIG);
}
esp_err_t touch_pad_deinit()
{
if(rtc_touch_sem == NULL) {
return ESP_FAIL;
}
vSemaphoreDelete(rtc_touch_sem);
rtc_touch_sem=NULL;
return ESP_OK;
}
static void touch_pad_counter_init(touch_pad_t touch_num)
@ -391,7 +436,9 @@ static esp_err_t touch_start(touch_pad_t touch_num)
esp_err_t touch_pad_config(touch_pad_t touch_num, uint16_t threshold)
{
RTC_MODULE_CHECK(rtc_touch_sem != NULL, "Touch pad not initialized", ESP_FAIL);
RTC_MODULE_CHECK(touch_num < TOUCH_PAD_MAX, "Touch_Pad Num Err", ESP_ERR_INVALID_ARG);
xSemaphoreTake(rtc_touch_sem, portMAX_DELAY);
portENTER_CRITICAL(&rtc_spinlock);
//clear touch force ,select the Touch mode is Timer
CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M);
@ -407,18 +454,20 @@ esp_err_t touch_pad_config(touch_pad_t touch_num, uint16_t threshold)
//Enable Rtc Touch Module Intr,the Interrupt need Rtc out Enable
SET_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, RTC_CNTL_TOUCH_INT_ENA);
portEXIT_CRITICAL(&rtc_spinlock);
xSemaphoreGive(rtc_touch_sem);
touch_pad_power_on(touch_num);
toch_pad_io_init(touch_num);
touch_pad_counter_init(touch_num);
touch_start(touch_num);
return ESP_OK;
}
esp_err_t touch_pad_read(touch_pad_t touch_num, uint16_t *touch_value)
{
RTC_MODULE_CHECK(touch_num < TOUCH_PAD_MAX, "Touch_Pad Num Err", ESP_ERR_INVALID_ARG);
RTC_MODULE_CHECK(touch_value!=NULL, "touch_value", ESP_ERR_INVALID_ARG);
RTC_MODULE_CHECK(touch_value != NULL, "touch_value", ESP_ERR_INVALID_ARG);
RTC_MODULE_CHECK(rtc_touch_sem != NULL, "Touch pad not initialized", ESP_FAIL);
xSemaphoreTake(rtc_touch_sem, portMAX_DELAY);
uint32_t v0 = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG);
portENTER_CRITICAL(&rtc_spinlock);
SET_PERI_REG_MASK(SENS_SAR_TOUCH_ENABLE_REG, (1 << (touch_num)));
@ -432,16 +481,18 @@ esp_err_t touch_pad_read(touch_pad_t touch_num, uint16_t *touch_value)
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M);
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_FORCE_M);
SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_XPD_WAIT, 10, SENS_TOUCH_XPD_WAIT_S);
portEXIT_CRITICAL(&rtc_spinlock);
while (GET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_DONE) == 0) {};
uint8_t shift = (touch_num & 1) ? SENS_TOUCH_MEAS_OUT1_S : SENS_TOUCH_MEAS_OUT0_S;
*touch_value = READ_PERI_REG(SENS_SAR_TOUCH_OUT1_REG + (touch_num / 2) * 4) >> shift;
WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, v0);
//force oneTime test end
//clear touch force ,select the Touch mode is Timer
portENTER_CRITICAL(&rtc_spinlock);
CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M);
CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_FORCE_M);
portEXIT_CRITICAL(&rtc_spinlock);
xSemaphoreGive(rtc_touch_sem);
return ESP_OK;
}

View file

@ -268,7 +268,9 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_confi
spihost[host]->hw->dma_out_link.start=0;
spihost[host]->hw->dma_in_link.start=0;
spihost[host]->hw->dma_conf.val&=~(SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
//Reset timing
spihost[host]->hw->ctrl2.val=0;
//Disable unneeded ints
spihost[host]->hw->slave.rd_buf_done=0;
spihost[host]->hw->slave.wr_buf_done=0;
@ -315,6 +317,7 @@ esp_err_t spi_bus_free(spi_host_device_t host)
esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config_t *dev_config, spi_device_handle_t *handle)
{
int freecs;
int apbclk=APB_CLK_FREQ;
SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE);
SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG);
@ -327,6 +330,9 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config
//The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
//duplex mode does absolutely nothing on the ESP32.
SPI_CHECK(dev_config->cs_ena_pretrans==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG);
//Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
SPI_CHECK(!( ((dev_config->flags & SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)),
"No speeds >26MHz supported for full-duplex, GPIO-matrix SPI transfers", ESP_ERR_INVALID_ARG);
//Allocate memory for device
spi_device_t *dev=malloc(sizeof(spi_device_t));
@ -394,8 +400,12 @@ static int spi_freq_for_pre_n(int fapb, int pre, int n) {
return (fapb / (pre * n));
}
static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
int pre, n, h, l;
/*
* Set the SPI clock to a certain frequency. Returns the effective frequency set, which may be slightly
* different from the requested frequency.
*/
static int spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
int pre, n, h, l, eff_clk;
//In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value.
if (hz>((fapb/4)*3)) {
@ -405,6 +415,7 @@ static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
hw->clock.clkcnt_n=0;
hw->clock.clkdiv_pre=0;
hw->clock.clk_equ_sysclk=1;
eff_clk=fapb;
} else {
//For best duty cycle resolution, we want n to be as close to 32 as possible, but
//we also need a pre/n combo that gets us as close as possible to the intended freq.
@ -440,7 +451,9 @@ static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
hw->clock.clkdiv_pre=pre-1;
hw->clock.clkcnt_h=h-1;
hw->clock.clkcnt_l=l-1;
eff_clk=spi_freq_for_pre_n(fapb, pre, n);
}
return eff_clk;
}
@ -516,14 +529,28 @@ static void IRAM_ATTR spi_intr(void *arg)
//Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have
//clock scaling working.
int apbclk=APB_CLK_FREQ;
spi_set_clock(host->hw, apbclk, dev->cfg.clock_speed_hz, dev->cfg.duty_cycle_pos);
int effclk=spi_set_clock(host->hw, apbclk, dev->cfg.clock_speed_hz, dev->cfg.duty_cycle_pos);
//Configure bit order
host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0;
host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0;
//Configure polarity
//SPI iface needs to be configured for a delay unless it is not routed through GPIO and clock is >=apb/2
int nodelay=(host->no_gpio_matrix && dev->cfg.clock_speed_hz >= (apbclk/2));
//SPI iface needs to be configured for a delay in some cases.
int nodelay=0;
int extra_dummy=0;
if (host->no_gpio_matrix) {
if (effclk >= apbclk/2) {
nodelay=1;
}
} else {
if (effclk >= apbclk/2) {
nodelay=1;
extra_dummy=1; //Note: This only works on half-duplex connections. spi_bus_add_device checks for this.
} else if (effclk >= apbclk/4) {
nodelay=1;
}
}
if (dev->cfg.mode==0) {
host->hw->pin.ck_idle_edge=0;
host->hw->user.ck_out_edge=0;
@ -543,11 +570,11 @@ static void IRAM_ATTR spi_intr(void *arg)
}
//Configure bit sizes, load addr and command
host->hw->user.usr_dummy=(dev->cfg.dummy_bits)?1:0;
host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0;
host->hw->user.usr_addr=(dev->cfg.address_bits)?1:0;
host->hw->user.usr_command=(dev->cfg.command_bits)?1:0;
host->hw->user1.usr_addr_bitlen=dev->cfg.address_bits-1;
host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits-1;
host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1;
host->hw->user2.usr_command_bitlen=dev->cfg.command_bits-1;
//Configure misc stuff
host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1;

View file

@ -80,41 +80,29 @@ TEST_CASE("SPI Master clockdiv calculation routines", "[spi]")
}
TEST_CASE("SPI Master test", "[spi][ignore]")
{
spi_bus_config_t buscfg={
.mosi_io_num=4,
.miso_io_num=16,
.sclk_io_num=25,
.quadwp_io_num=-1,
.quadhd_io_num=-1
};
static void test_spi_bus_speed(int hz) {
esp_err_t ret;
spi_device_handle_t handle;
spi_device_interface_config_t devcfg={
.command_bits=8,
.address_bits=64,
.dummy_bits=0,
.clock_speed_hz=8000,
.clock_speed_hz=hz,
.duty_cycle_pos=128,
.mode=0,
.spics_io_num=21,
.queue_size=3
.queue_size=3,
};
esp_err_t ret;
spi_device_handle_t handle;
printf("THIS TEST NEEDS A JUMPER BETWEEN IO4 AND IO16\n");
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
TEST_ASSERT(ret==ESP_OK);
ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle);
TEST_ASSERT(ret==ESP_OK);
printf("Bus/dev inited.\n");
spi_transaction_t t;
char sendbuf[16]="Hello World!";
char recvbuf[16]="UUUUUUUUUUUUUUU";
char sendbuf[64]="Hello World!";
char recvbuf[64]="UUUUUUUUUUUUUUU";
memset(&t, 0, sizeof(t));
t.length=16*8;
t.length=64*8;
t.tx_buffer=sendbuf;
t.rx_buffer=recvbuf;
t.address=0xA00000000000000FL;
@ -130,11 +118,32 @@ TEST_CASE("SPI Master test", "[spi][ignore]")
ret=spi_bus_remove_device(handle);
TEST_ASSERT(ret==ESP_OK);
ret=spi_bus_free(HSPI_HOST);
TEST_ASSERT(ret==ESP_OK);
TEST_ASSERT_EQUAL_INT8_ARRAY(sendbuf, recvbuf, 16);
TEST_ASSERT_EQUAL_INT8_ARRAY(sendbuf, recvbuf, 64);
}
TEST_CASE("SPI Master test", "[spi][ignore]")
{
spi_bus_config_t buscfg={
.mosi_io_num=4,
.miso_io_num=16,
.sclk_io_num=25,
.quadwp_io_num=-1,
.quadhd_io_num=-1
};
esp_err_t ret;
printf("THIS TEST NEEDS A JUMPER BETWEEN IO4 AND IO16\n");
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
TEST_ASSERT(ret==ESP_OK);
int freqs[]={8000, 1000000, 5000000, 10000000, 20000000, 26666666, 0};
for (int x=0; freqs[x]!=0; x++) {
printf("Testing clock speed of %dHz...\n", freqs[x]);
test_spi_bus_speed(freqs[x]);
}
ret=spi_bus_free(HSPI_HOST);
TEST_ASSERT(ret==ESP_OK);
}

View file

@ -541,9 +541,50 @@ config ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM
number. Generally the number of dynamic rx buffer should be no less than static
rx buffer number if it is not 0.
choice ESP32_WIFI_TX_BUFFER
prompt "Type of WiFi TX buffers"
depends on WIFI_ENABLED
default ESP32_WIFI_DYNAMIC_TX_BUFFER
help
Select type of WiFi tx buffers and show the submenu with the number of WiFi tx buffers choice.
If "STATIC" is selected, WiFi tx buffers are allocated when WiFi is initialized and released
when WiFi is de-initialized. If "DYNAMIC" is selected, WiFi tx buffer is allocated when tx
data is delivered from LWIP to WiFi and released when tx data is sent out by WiFi.
The size of each static tx buffers is fixed to about 1.6KB and the size of dynamic tx buffers is
depend on the length of the data delivered from LWIP.
If PSRAM is enabled, "STATIC" should be selected to guarantee enough WiFi tx buffers.
If PSRAM is disabled, "DYNAMIC" should be selected to improve the utilization of RAM.
config ESP32_WIFI_STATIC_TX_BUFFER
bool "STATIC"
config ESP32_WIFI_DYNAMIC_TX_BUFFER
bool "DYNAMIC"
endchoice
config ESP32_WIFI_TX_BUFFER_TYPE
int
depends on WIFI_ENABLED
default 0 if ESP32_WIFI_STATIC_TX_BUFFER
default 1 if ESP32_WIFI_DYNAMIC_TX_BUFFER
config ESP32_WIFI_STATIC_TX_BUFFER_NUM
int "Max number of WiFi static TX buffers"
depends on WIFI_ENABLED
depends on ESP32_WIFI_STATIC_TX_BUFFER
range 16 64
default 32
help
Set the number of WiFi static tx buffers. Each buffer takes approximately 1.6KB of RAM.
The static rx buffers are allocated when esp_wifi_init is called, they are not released
until esp_wifi_deinit is called.
For each tx packet from high layer stack, WiFi driver make a copy of it. For some applications,
especially the UDP application, the high layer deliver speed is faster than the WiFi tx
speed, we may run out of static tx buffers.
config ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM
int "Max number of WiFi dynamic TX buffers"
depends on WIFI_ENABLED
depends on WIFI_ENABLED
depends on ESP32_WIFI_DYNAMIC_TX_BUFFER
range 16 64
default 32
help
@ -553,7 +594,6 @@ config ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM
especially the UDP application, the high layer deliver speed is faster than the WiFi tx
speed, we may run out of memory if no limitation for the dynamic tx buffer number.
config ESP32_WIFI_AMPDU_ENABLED
bool "WiFi AMPDU"
depends on WIFI_ENABLED

View file

@ -3,7 +3,7 @@
#
COMPONENT_SRCDIRS := . hwcrypto
LIBS := core rtc rtc_clk
LIBS := core rtc rtc_clk rtc_pm
ifdef CONFIG_PHY_ENABLED # BT || WIFI
LIBS += phy coexist
endif

View file

@ -185,7 +185,7 @@ void start_cpu0_default(void)
#if CONFIG_BROWNOUT_DET
esp_brownout_init();
#endif
rtc_gpio_unhold_all();
rtc_gpio_force_hold_dis_all();
esp_setup_time_syscalls();
esp_vfs_dev_uart_register();
esp_reent_init(_GLOBAL_REENT);

View file

@ -291,7 +291,7 @@ static void ext1_wakeup_prepare()
REG_SET_BIT(desc->reg, desc->ie);
REG_CLR_BIT(desc->reg, desc->pulldown);
REG_CLR_BIT(desc->reg, desc->pullup);
REG_SET_BIT(RTC_CNTL_HOLD_FORCE_REG, desc->hold);
REG_SET_BIT(RTC_CNTL_HOLD_FORCE_REG, desc->hold_force);
}
// Keep track of pins which are processed to bail out early
rtc_gpio_mask &= ~BIT(rtc_pin);

View file

@ -232,12 +232,11 @@ esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_da
* function.
* @param mode Calibration mode (Full, partial, or no calibration)
* @param[inout] calibration_data
* @param is_sleep WiFi wakes up from sleep or not
* @return ESP_OK on success.
* @return ESP_FAIL on fail.
*/
esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data, bool is_sleep);
esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data);
/**
* @brief De-initialize PHY and RF module

View file

@ -97,6 +97,8 @@ typedef struct {
system_event_handler_t event_handler; /**< WiFi event handler */
int static_rx_buf_num; /**< WiFi static RX buffer number */
int dynamic_rx_buf_num; /**< WiFi dynamic RX buffer number */
int tx_buf_type; /**< WiFi TX buffer type */
int static_tx_buf_num; /**< WiFi static TX buffer number */
int dynamic_tx_buf_num; /**< WiFi dynamic TX buffer number */
int ampdu_enable; /**< WiFi AMPDU feature enable flag */
int nvs_enable; /**< WiFi NVS flash enable flag */
@ -104,6 +106,18 @@ typedef struct {
int magic; /**< WiFi init magic number, it should be the last field */
} wifi_init_config_t;
#ifdef CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM
#define WIFI_STATIC_TX_BUFFER_NUM CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM
#else
#define WIFI_STATIC_TX_BUFFER_NUM 0
#endif
#ifdef CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM
#define WIFI_DYNAMIC_TX_BUFFER_NUM CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM
#else
#define WIFI_DYNAMIC_TX_BUFFER_NUM 0
#endif
#if CONFIG_ESP32_WIFI_AMPDU_ENABLED
#define WIFI_AMPDU_ENABLED 1
#else
@ -128,14 +142,16 @@ typedef struct {
.event_handler = &esp_event_send, \
.static_rx_buf_num = CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM,\
.dynamic_rx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM,\
.dynamic_tx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM,\
.tx_buf_type = CONFIG_ESP32_WIFI_TX_BUFFER_TYPE,\
.static_tx_buf_num = WIFI_STATIC_TX_BUFFER_NUM,\
.dynamic_tx_buf_num = WIFI_DYNAMIC_TX_BUFFER_NUM,\
.ampdu_enable = WIFI_AMPDU_ENABLED,\
.nvs_enable = WIFI_NVS_ENABLED,\
.nano_enable = WIFI_NANO_FORMAT_ENABLED,\
.magic = WIFI_INIT_CONFIG_MAGIC\
};
#else
#define WIFI_INIT_CONFIG_DEFAULT #error Wifi is disabled in config, WIFI_INIT_CONFIG_DEFAULT will not work
#define WIFI_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable wifi in menuconfig to use esp_wifi.h");
#endif
/**
@ -296,6 +312,8 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid);
* @attention If this API is called, the found APs are stored in WiFi driver dynamic allocated memory and the
* will be freed in esp_wifi_get_ap_list, so generally, call esp_wifi_get_ap_list to cause
* the memory to be freed once the scan is done
* @attention The values of maximum active scan time and passive scan time per channel are limited to 1500 milliseconds.
* Values above 1500ms may cause station to disconnect from AP and are not recommended.
*
* @param config configuration of scanning
* @param block if block is true, this API will block the caller until the scan is done, otherwise

View file

@ -49,11 +49,12 @@ typedef enum {
} wifi_country_t;
typedef enum {
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */
WIFI_AUTH_MAX
} wifi_auth_mode_t;
@ -95,11 +96,30 @@ typedef enum {
WIFI_SECOND_CHAN_BELOW, /**< the channel width is HT40 and the second channel is below the primary channel */
} wifi_second_chan_t;
typedef enum {
WIFI_SCAN_TYPE_ACTIVE = 0, /**< active scan */
WIFI_SCAN_TYPE_PASSIVE, /**< passive scan */
} wifi_scan_type_t;
typedef struct {
uint8_t *ssid; /**< SSID of AP */
uint8_t *bssid; /**< MAC address of AP */
uint8_t channel; /**< channel, scan the specific channel */
bool show_hidden; /**< enable to scan AP whose SSID is hidden */
uint32_t min; /**< minimum active scan time per channel, units: millisecond */
uint32_t max; /**< maximum active scan time per channel, units: millisecond, values above 1500ms may
cause station to disconnect from AP and are not recommended. */
} wifi_active_scan_time_t;
typedef union {
wifi_active_scan_time_t active; /**< active scan time per channel */
uint32_t passive; /**< passive scan time per channel, units: millisecond, values above 1500ms may
cause station to disconnect from AP and are not recommended. */
} wifi_scan_time_t;
typedef struct {
uint8_t *ssid; /**< SSID of AP */
uint8_t *bssid; /**< MAC address of AP */
uint8_t channel; /**< channel, scan the specific channel */
bool show_hidden; /**< enable to scan AP whose SSID is hidden */
wifi_scan_type_t scan_type; /**< scan type, active or passive */
wifi_scan_time_t scan_time; /**< scan time per channel */
} wifi_scan_config_t;
typedef struct {

View file

@ -21,7 +21,8 @@ typedef volatile struct {
uint32_t timer_sel: 2; /*There are four high speed timers the two bits are used to select one of them for high speed channel. 2'b00: seletc hstimer0. 2'b01: select hstimer1. 2'b10: select hstimer2. 2'b11: select hstimer3.*/
uint32_t sig_out_en: 1; /*This is the output enable control bit for high speed channel*/
uint32_t idle_lv: 1; /*This bit is used to control the output value when high speed channel is off.*/
uint32_t reserved4: 27;
uint32_t low_speed_update: 1; /*This bit is only useful for low speed timer channels, reserved for high speed timers*/
uint32_t reserved4: 26;
uint32_t clk_en: 1; /*This bit is clock gating control signal. when software configure LED_PWM internal registers it controls the register clock.*/
};
uint32_t val;
@ -204,9 +205,13 @@ typedef volatile struct {
} int_clr;
union {
struct {
uint32_t apb_clk_sel: 1; /*This bit is used to set the frequency of slow_clk. 1'b1:80mhz 1'b0:8mhz*/
uint32_t apb_clk_sel: 1; /*This bit decides the slow clock for LEDC low speed channels, so we want to replace the field name with slow_clk_sel*/
uint32_t reserved1: 31;
};
struct {
uint32_t slow_clk_sel: 1; /*This bit is used to set the frequency of slow_clk. 1'b1:80mhz 1'b0:8mhz, (only used by LEDC low speed channels/timers)*/
uint32_t reserved: 31;
};
uint32_t val;
} conf;
uint32_t reserved_194;

View file

@ -15,7 +15,9 @@
#ifndef _ESP32_SOC_H_
#define _ESP32_SOC_H_
#ifndef __ASSEMBLER__
#include <stdint.h>
#endif
//Register Bits{{
#define BIT31 0x80000000
@ -59,7 +61,11 @@
#define ETS_UNCACHED_ADDR(addr) (addr)
#define ETS_CACHED_ADDR(addr) (addr)
#ifndef __ASSEMBLER__
#define BIT(nr) (1UL << (nr))
#else
#define BIT(nr) (1 << (nr))
#endif //__ASSEMBLER__
//write value to register
#define REG_WRITE(_r, _v) (*(volatile uint32_t *)(_r)) = (_v)

View file

@ -0,0 +1,46 @@
// Copyright 2010-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.
#pragma once
// This file contains various convenience macros to be used in ULP programs.
// Helper macros to calculate bit field width from mask, using the preprocessor.
// Used later in READ_RTC_FIELD and WRITE_RTC_FIELD.
#define IS_BIT_SET(m, i) (((m) >> (i)) & 1)
#define MASK_TO_WIDTH_HELPER1(m, i) IS_BIT_SET(m, i)
#define MASK_TO_WIDTH_HELPER2(m, i) (MASK_TO_WIDTH_HELPER1(m, i) + MASK_TO_WIDTH_HELPER1(m, i + 1))
#define MASK_TO_WIDTH_HELPER4(m, i) (MASK_TO_WIDTH_HELPER2(m, i) + MASK_TO_WIDTH_HELPER2(m, i + 2))
#define MASK_TO_WIDTH_HELPER8(m, i) (MASK_TO_WIDTH_HELPER4(m, i) + MASK_TO_WIDTH_HELPER4(m, i + 4))
#define MASK_TO_WIDTH_HELPER16(m, i) (MASK_TO_WIDTH_HELPER8(m, i) + MASK_TO_WIDTH_HELPER8(m, i + 8))
#define MASK_TO_WIDTH_HELPER32(m, i) (MASK_TO_WIDTH_HELPER16(m, i) + MASK_TO_WIDTH_HELPER16(m, i + 16))
// Peripheral register access macros, build around REG_RD and REG_WR instructions.
// Registers defined in rtc_cntl_reg.h, rtc_io_reg.h, and sens_reg.h are usable with these macros.
// Read from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0, bit_width <= 16
#define READ_RTC_REG(rtc_reg, low_bit, bit_width) \
REG_RD (((rtc_reg) - DR_REG_RTCCNTL_BASE) / 4), ((low_bit) + (bit_width) - 1), (low_bit)
// Write immediate value into rtc_reg[low_bit + bit_width - 1 : low_bit], bit_width <= 8
#define WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value) \
REG_WR (((rtc_reg) - DR_REG_RTCCNTL_BASE) / 4), ((low_bit) + (bit_width) - 1), (low_bit), ((value) & 0xff)
// Read from a field in rtc_reg into R0, up to 16 bits
#define READ_RTC_FIELD(rtc_reg, field) \
READ_RTC_REG(rtc_reg, field ## _S, MASK_TO_WIDTH_HELPER16(field ## _V, 0))
// Write immediate value into a field in rtc_reg, up to 8 bits
#define WRITE_RTC_FIELD(rtc_reg, field, value) \
WRITE_RTC_REG(rtc_reg, field ## _S, MASK_TO_WIDTH_HELPER8(field ## _V, 0), ((value) & field ## _V))

View file

@ -71,6 +71,10 @@ SECTIONS
*(.init.literal)
*(.init)
_init_end = ABSOLUTE(.);
/* This goes here, not at top of linker script, so addr2line finds it last,
and uses it in preference to the first symbol in IRAM */
_iram_start = ABSOLUTE(0);
} > iram0_0_seg
.iram0.text :
@ -81,9 +85,11 @@ SECTIONS
*libfreertos.a:(.literal .text .literal.* .text.*)
*libesp32.a:panic.o(.literal .text .literal.* .text.*)
*libesp32.a:core_dump.o(.literal .text .literal.* .text.*)
*libesp32.a:heap_alloc_caps.o(.literal .text .literal.* .text.*)
*libphy.a:(.literal .text .literal.* .text.*)
*librtc.a:(.literal .text .literal.* .text.*)
*librtc_clk.a:(.literal .text .literal.* .text.*)
*librtc_pm.a:(.literal .text .literal.* .text.*)
*libpp.a:pp.o(.literal .text .literal.* .text.*)
*libpp.a:lmac.o(.literal .text .literal.* .text.*)
*libpp.a:wdev.o(.literal .text .literal.* .text.*)
@ -193,5 +199,11 @@ SECTIONS
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
/* Similar to _iram_start, this symbol goes here so it is
resolved by addr2line in preference to the first symbol in
the flash.text segment.
*/
_flash_cache_start = ABSOLUTE(0);
} >iram0_2_seg
}

@ -1 +1 @@
Subproject commit c88869b1aca5a062d1ebc5e0199fd8720cb08710
Subproject commit ae20d8efce9c46dea9dc949b542d8dfaa3ea136c

View file

@ -41,24 +41,16 @@ static const char* TAG = "phy_init";
/* Count value to indicate if there is peripheral that has initialized PHY and RF */
static int s_phy_rf_init_count = 0;
static bool s_mac_rst_flag = false;
static _lock_t s_phy_rf_init_lock;
esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data, bool is_sleep)
esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data)
{
assert((s_phy_rf_init_count <= 1) && (s_phy_rf_init_count >= 0));
_lock_acquire(&s_phy_rf_init_lock);
if (s_phy_rf_init_count == 0) {
if (is_sleep == false) {
if (s_mac_rst_flag == false) {
s_mac_rst_flag = true;
REG_SET_BIT(DPORT_CORE_RST_EN_REG, DPORT_MAC_RST);
REG_CLR_BIT(DPORT_CORE_RST_EN_REG, DPORT_MAC_RST);
}
}
// Enable WiFi peripheral clock
SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN | DPORT_WIFI_CLK_RNG_EN);
ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d",
@ -118,7 +110,7 @@ const esp_phy_init_data_t* esp_phy_get_init_data()
}
esp_err_t err = esp_partition_read(partition, 0, init_data_store, init_data_store_length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to read PHY data partition (%d)", err);
ESP_LOGE(TAG, "failed to read PHY data partition (0x%x)", err);
return NULL;
}
if (memcmp(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre)) != 0 ||
@ -167,10 +159,15 @@ static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle,
esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data)
{
nvs_handle handle;
esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle);
esp_err_t err = nvs_flash_init();
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err);
ESP_LOGW(TAG, "%s: failed to initialize NVS (0x%x)", __func__, err);
return err;
}
nvs_handle handle;
err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to open NVS namespace (0x%x)", __func__, err);
return err;
}
else {
@ -185,7 +182,7 @@ esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_da
nvs_handle handle;
esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err);
ESP_LOGD(TAG, "%s: failed to open NVS namespace (0x%x)", __func__, err);
return err;
}
else {
@ -202,7 +199,7 @@ static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle,
uint32_t cal_data_version;
err = nvs_get_u32(handle, PHY_CAL_VERSION_KEY, &cal_data_version);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to get cal_version (%d)", __func__, err);
ESP_LOGD(TAG, "%s: failed to get cal_version (0x%x)", __func__, err);
return err;
}
uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
@ -216,7 +213,7 @@ static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle,
size_t length = sizeof(cal_data_mac);
err = nvs_get_blob(handle, PHY_CAL_MAC_KEY, cal_data_mac, &length);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to get cal_mac (%d)", __func__, err);
ESP_LOGD(TAG, "%s: failed to get cal_mac (0x%x)", __func__, err);
return err;
}
if (length != sizeof(cal_data_mac)) {
@ -234,7 +231,7 @@ static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle,
length = sizeof(*out_cal_data);
err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_cal_data, &length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: failed to get cal_data(%d)", __func__, err);
ESP_LOGE(TAG, "%s: failed to get cal_data(0x%x)", __func__, err);
return err;
}
if (length != sizeof(*out_cal_data)) {
@ -267,7 +264,6 @@ static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle,
void esp_phy_load_cal_and_init(void)
{
#ifdef CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE
nvs_flash_init();
esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL;
if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) {
calibration_mode = PHY_RF_CAL_NONE;
@ -285,11 +281,11 @@ void esp_phy_load_cal_and_init(void)
}
esp_err_t err = esp_phy_load_cal_data_from_nvs(cal_data);
if (err != ESP_OK) {
ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration");
ESP_LOGW(TAG, "failed to load RF calibration data (0x%x), falling back to full calibration", err);
calibration_mode = PHY_RF_CAL_FULL;
}
esp_phy_rf_init(init_data, calibration_mode, cal_data, false);
esp_phy_rf_init(init_data, calibration_mode, cal_data);
if (calibration_mode != PHY_RF_CAL_NONE && err != ESP_OK) {
err = esp_phy_store_cal_data_to_nvs(cal_data);
@ -299,7 +295,7 @@ void esp_phy_load_cal_and_init(void)
esp_phy_release_init_data(init_data);
free(cal_data); // PHY maintains a copy of calibration data, so we can free this
#else
esp_phy_rf_init(NULL, PHY_RF_CAL_NONE, NULL, false);
esp_phy_rf_init(NULL, PHY_RF_CAL_NONE, NULL);
#endif
}

View file

@ -695,7 +695,9 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
super(ESPCoreDumpFlashLoader, self).__init__()
if not tool_path:
self.path = esptool.__file__
self.path = self.path[:-1]
_,e = os.path.splitext(self.path)
if e == '.pyc':
self.path = self.path[:-1]
else:
self.path = tool_path
self.port = port
@ -776,7 +778,7 @@ class GDBMIOutRecordHandler(object):
"""Base method to execute GDB/MI output record handler function
"""
if self.verbose:
print "%s.execute '%s'" % (self.__class__.__name__, ln)
print "%s.execute: [[%s]]" % (self.__class__.__name__, ln)
class GDBMIOutStreamHandler(GDBMIOutRecordHandler):
@ -916,6 +918,37 @@ def info_corefile(args):
out_handlers[h].execute(ln)
break
def gdbmi_start(handlers):
p = subprocess.Popen(
bufsize = 0,
args = [args.gdb,
'--quiet', # inhibit dumping info at start-up
'--nx', # inhibit window interface
'--nw', # ignore .gdbinit
'--interpreter=mi2', # use GDB/MI v2
'--core=%s' % core_fname, # core file
args.prog],
stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT,
close_fds = CLOSE_FDS
)
gdbmi_read2prompt(p.stdout, handlers)
return p
def gdbmi_getinfo(p, handlers, gdb_cmd):
for t in handlers:
handlers[t].result_class = None
p.stdin.write("-interpreter-exec console \"%s\"\n" % gdb_cmd)
gdbmi_read2prompt(p.stdout, handlers)
if not handlers[GDBMIResultHandler.TAG].result_class or handlers[GDBMIResultHandler.TAG].result_class == GDBMIResultHandler.RC_EXIT:
print "GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p.wait()
print "Problem occured! GDB exited, restart it."
p = gdbmi_start(handlers)
elif handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
return p
loader = None
if not args.core:
loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
@ -934,22 +967,6 @@ def info_corefile(args):
loader.cleanup()
return
handlers = {}
handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False)
handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
p = subprocess.Popen(
bufsize = 0,
args = [args.gdb,
'--quiet', # inhibit dumping info at start-up
'--nx', # inhibit window interface
'--nw', # ignore .gdbinit
'--interpreter=mi2', # use GDB/MI v2
'--core=%s' % core_fname, # core file
args.prog],
stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT,
close_fds = CLOSE_FDS
)
gdbmi_read2prompt(p.stdout, handlers)
exe_elf = ESPCoreDumpElfFile(args.prog)
core_elf = ESPCoreDumpElfFile(core_fname)
merged_segs = []
@ -995,26 +1012,22 @@ def info_corefile(args):
if not merged:
merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False))
handlers = {}
handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False)
handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
p = gdbmi_start(handlers)
print "==============================================================="
print "==================== ESP32 CORE DUMP START ===================="
handlers[GDBMIResultHandler.TAG].result_class = None
handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler
print "\n================== CURRENT THREAD REGISTERS ==================="
p.stdin.write("-interpreter-exec console \"info registers\"\n")
gdbmi_read2prompt(p.stdout, handlers)
if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p = gdbmi_getinfo(p, handlers, "info registers")
print "\n==================== CURRENT THREAD STACK ====================="
p.stdin.write("-interpreter-exec console \"bt\"\n")
gdbmi_read2prompt(p.stdout, handlers)
if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p = gdbmi_getinfo(p, handlers, "bt")
print "\n======================== THREADS INFO ========================="
p.stdin.write("-interpreter-exec console \"info threads\"\n")
gdbmi_read2prompt(p.stdout, handlers)
if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p = gdbmi_getinfo(p, handlers, "info threads")
print "\n======================= ALL MEMORY REGIONS ========================"
print "Name Address Size Attrs"
for ms in merged_segs:
@ -1025,10 +1038,7 @@ def info_corefile(args):
print "\n====================== CORE DUMP MEMORY CONTENTS ========================"
for cs in core_elf.program_segments:
print ".coredump.tasks 0x%x 0x%x %s" % (cs.addr, len(cs.data), cs.attr_str())
p.stdin.write("-interpreter-exec console \"x/%dx 0x%x\"\n" % (len(cs.data)/4, cs.addr))
gdbmi_read2prompt(p.stdout, handlers)
if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p = gdbmi_getinfo(p, handlers, "x/%dx 0x%x" % (len(cs.data)/4, cs.addr))
print "\n===================== ESP32 CORE DUMP END ====================="
print "==============================================================="

View file

@ -83,12 +83,14 @@ endif
simple_monitor: $(call prereq_if_explicit,%flash)
$(MONITOR_PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD)
MONITOR_OPTS := --baud $(MONITORBAUD) --port $(ESPPORT) --toolchain-prefix $(CONFIG_TOOLPREFIX) --make "$(MAKE)"
monitor: $(call prereq_if_explicit,%flash)
$(summary) MONITOR
[ -f $(APP_ELF) ] || echo "*** 'make monitor' target requires an app to be compiled and flashed first."
[ -f $(APP_ELF) ] || echo "*** Run 'make flash monitor' to build, flash and monitor"
[ -f $(APP_ELF) ] || echo "*** Or alternatively 'make simple_monitor' to view the serial port as-is."
[ -f $(APP_ELF) ] || exit 1
$(MONITOR_PYTHON) $(IDF_PATH)/tools/idf_monitor.py --port $(ESPPORT) --make "$(MAKE)" $(APP_ELF)
$(MONITOR_PYTHON) $(IDF_PATH)/tools/idf_monitor.py $(MONITOR_OPTS) $(APP_ELF)
.PHONY: erase_flash

View file

@ -86,7 +86,7 @@ void emac_reset(void)
while (REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1) {
//nothing to do ,if stop here,maybe emac have not clk input.
ESP_LOGI(TAG, "emac reseting ....");
ESP_LOGI(TAG, "emac resetting ....");
}
ESP_LOGI(TAG, "emac reset done");

View file

@ -948,14 +948,14 @@ esp_err_t esp_eth_init(eth_config_t *config)
emac_set_user_config_data(config);
}
emac_config.emac_phy_power_enable(true);
ret = emac_verify_args();
if (ret != ESP_OK) {
goto _exit;
}
emac_config.emac_phy_power_enable(true);
//before set emac reg must enable clk
emac_enable_clk(true);
REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL, EMAC_EX_PHY_INTF_RMII);

View file

@ -197,6 +197,81 @@ config FREERTOS_MAX_TASK_NAME_LEN
For most uses, the default of 16 is OK.
config SUPPORT_STATIC_ALLOCATION
bool "Enable FreeRTOS static allocation API"
default n
help
FreeRTOS gives the application writer the ability to instead provide the memory
themselves, allowing the following objects to optionally be created without any
memory being allocated dynamically:
- Tasks
- Software Timers
- Queues
- Event Groups
- Binary Semaphores
- Counting Semaphores
- Recursive Semaphores
- Mutexes
Whether it is preferable to use static or dynamic memory allocation is dependent on
the application, and the preference of the application writer. Both methods have pros
and cons, and both methods can be used within the same RTOS application.
Creating RTOS objects using statically allocated RAM has the benefit of providing the
application writer with more control: RTOS objects can be placed at specific memory locations.
The maximum RAM footprint can be determined at link time, rather than run time.
The application writer does not need to concern themselves with graceful handling of memory allocation failures.
It allows the RTOS to be used in applications that simply don't allow any dynamic memory allocation
(although FreeRTOS includes allocation schemes that can overcome most objections).
config ENABLE_STATIC_TASK_CLEAN_UP_HOOK
bool "Enable static task clean up hook"
depends on SUPPORT_STATIC_ALLOCATION
default n
help
Enable this option to make FreeRTOS call the static task clean up hook when a task is deleted.
Bear in mind that if this option is enabled you will need to implement the following function:
void vPortCleanUpTCB ( void *pxTCB ) {
// place clean up code here
}
config TIMER_TASK_PRIORITY
int "FreeRTOS timer task priority"
range 1 25
default 1
help
The timer service task (primarily) makes use of existing FreeRTOS features, allowing timer
functionality to be added to an application with minimal impact on the size of the application's
executable binary.
Use this constant to define the priority that the timer task will run at.
config TIMER_TASK_STACK_DEPTH
int "FreeRTOS timer task stack size"
range 1536 32768
default 2048
help
The timer service task (primarily) makes use of existing FreeRTOS features, allowing timer
functionality to be added to an application with minimal impact on the size of the application's
executable binary.
Use this constant to define the size (in bytes) of the stack allocated for the timer task.
config TIMER_QUEUE_LENGTH
int "FreeRTOS timer queue length"
range 5 20
default 10
help
FreeRTOS provides a set of timer related API functions. Many of these functions use a standard
FreeRTOS queue to send commands to the timer service task. The queue used for this purpose is
called the 'timer command queue'. The 'timer command queue' is private to the FreeRTOS timer
implementation, and cannot be accessed directly.
For most uses the default value of 10 is OK.
menuconfig FREERTOS_DEBUG_INTERNALS
bool "Debug FreeRTOS internals"
default n

View file

@ -248,13 +248,21 @@
#define configUSE_NEWLIB_REENTRANT 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configSUPPORT_STATIC_ALLOCATION CONFIG_SUPPORT_STATIC_ALLOCATION
#ifndef __ASSEMBLER__
#if CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK
extern void vPortCleanUpTCB ( void *pxTCB );
#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB )
#endif
#endif
/* Test FreeRTOS timers (with timer task) and more. */
/* Some files don't compile if this flag is disabled */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 1
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
#define configTIMER_TASK_PRIORITY CONFIG_TIMER_TASK_PRIORITY
#define configTIMER_QUEUE_LENGTH CONFIG_TIMER_QUEUE_LENGTH
#define configTIMER_TASK_STACK_DEPTH CONFIG_TIMER_TASK_STACK_DEPTH
#define INCLUDE_xTimerPendFunctionCall 1
#define INCLUDE_eTaskGetState 1

View file

@ -132,8 +132,8 @@ _frxt_int_enter:
l32i a2, a2, 0 /* a2 = current TCB */
beqz a2, 1f
s32i a1, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */
movi a1, port_IntStackTop /* a1 = top of intr stack */
movi a2, configISR_STACK_SIZE
movi a1, port_IntStack+configISR_STACK_SIZE /* a1 = top of intr stack for CPU 0 */
movi a2, configISR_STACK_SIZE /* add configISR_STACK_SIZE * cpu_num to arrive at top of stack for cpu_num */
mull a2, a4, a2
add a1, a1, a2 /* for current proc */

View file

@ -3763,11 +3763,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
static void prvDeleteTCB( TCB_t *pxTCB )
{
/* This call is required specifically for the TriCore port. It must be
above the vPortFree() calls. The call is also used by ports/demos that
want to allocate and clean RAM statically. */
portCLEAN_UP_TCB( pxTCB );
/* Free up the memory allocated by the scheduler for the task. It is up
to the task to free any memory allocated at the application level. */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
@ -3806,6 +3801,7 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
/* Neither the stack nor the TCB were allocated dynamically, so
nothing needs to be freed. */
configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB )
portCLEAN_UP_TCB( pxTCB );
mtCOVERAGE_TEST_MARKER();
}
}

View file

@ -46,6 +46,7 @@
#define MDNS_ANSWER_A 0x01
#define MDNS_ANSWER_AAAA 0x10
#define MDNS_ANSWER_NSEC 0x20
#define MDNS_ANSWER_SDPTR 0x80
#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on
#define MDNS_SERVICE_STACK_DEPTH 4096 // Stack size for the service thread
@ -796,6 +797,52 @@ static uint16_t _mdns_append_ptr_record(uint8_t * packet, uint16_t * index, mdns
return record_length;
}
/**
* @brief appends DNS-SD PTR record for service to a packet, incrementing the index
*
* @param packet MDNS packet
* @param index offset in the packet
* @param server the server that is hosting the service
* @param service the service to add record for
*
* @return length of added data: 0 on error or length on success
*/
static uint16_t _mdns_append_sdptr_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, mdns_service_t * service)
{
const char * str[3];
const char * sd_str[4];
uint16_t record_length = 0;
uint8_t part_length;
sd_str[0] = (char*)"_services";
sd_str[1] = (char*)"_dns-sd";
sd_str[2] = (char*)"_udp";
sd_str[3] = MDNS_DEFAULT_DOMAIN;
str[0] = service->service;
str[1] = service->proto;
str[2] = MDNS_DEFAULT_DOMAIN;
part_length = _mdns_append_fqdn(packet, index, sd_str, 4);
record_length += part_length;
part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, MDNS_ANSWER_PTR_TTL);
if (!part_length) {
return 0;
}
record_length += part_length;
uint16_t data_len_location = *index - 2;
part_length = _mdns_append_fqdn(packet, index, str, 3);
if (!part_length) {
return 0;
}
_mdns_set_u16(packet, data_len_location, part_length);
record_length += part_length;
return record_length;
}
/**
* @brief appends TXT record for service to a packet, incrementing the index
*
@ -1042,6 +1089,13 @@ static void _mdns_send_answers(mdns_server_t * server, mdns_answer_item_t * answ
}
answer_count += 1;
}
if (answers->answer & MDNS_ANSWER_SDPTR) {
if (!_mdns_append_sdptr_record(packet, &index, server, answers->service)) {
return;
}
answer_count += 1;
}
}
mdns_answer_item_t * a = answers;
answers = answers->next;
@ -1274,6 +1328,20 @@ static void _mdns_parse_packet(mdns_server_t * server, const uint8_t * data, siz
}
continue;
}
//is this a dns-sd service discovery meta query?
if (!strcmp(name->host, "_services") && !strcmp(name->service, "_dns-sd") && !strcmp(name->proto, "_udp") && !strcmp(name->domain, MDNS_DEFAULT_DOMAIN) && type == MDNS_TYPE_PTR)
{
//add answers for all services
mdns_srv_item_t * s = server->services;
while(s) {
if (s->service->service && s->service->proto) {
answers = _mdns_add_answer(answers, s->service, MDNS_ANSWER_SDPTR);
}
s = s->next;
}
continue;
}
if (name->sub) {
continue;

View file

@ -9,10 +9,12 @@ Non-volatile storage (NVS) library is designed to store key-value pairs in flash
Underlying storage
^^^^^^^^^^^^^^^^^^
Currently NVS uses a portion of main flash memory through ``spi_flash_{read|write|erase}`` APIs. The range of flash sectors to be used by the library is provided to ``nvs_flash_init`` function.
Currently NVS uses a portion of main flash memory through ``spi_flash_{read|write|erase}`` APIs. The library uses the first partition with ``data`` type and ``nvs`` subtype.
Future versions of this library may add other storage backends to keep data in another flash chip (SPI or I2C), RTC, FRAM, etc.
.. note:: if an NVS partition is truncated (for example, when the partition table layout is changed), its contents should be erased. ESP-IDF build system provides a ``make erase_flash`` target to erase all contents of the flash chip.
Keys and values
^^^^^^^^^^^^^^^

View file

@ -31,16 +31,17 @@ typedef uint32_t nvs_handle;
#define ESP_ERR_NVS_BASE 0x1100 /*!< Starting number of error codes */
#define ESP_ERR_NVS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x01) /*!< The storage driver is not initialized */
#define ESP_ERR_NVS_NOT_FOUND (ESP_ERR_NVS_BASE + 0x02) /*!< Id namespace doesnt exist yet and mode is NVS_READONLY */
#define ESP_ERR_NVS_TYPE_MISMATCH (ESP_ERR_NVS_BASE + 0x03) /*!< TBA */
#define ESP_ERR_NVS_TYPE_MISMATCH (ESP_ERR_NVS_BASE + 0x03) /*!< The type of set or get operation doesn't match the type of value stored in NVS */
#define ESP_ERR_NVS_READ_ONLY (ESP_ERR_NVS_BASE + 0x04) /*!< Storage handle was opened as read only */
#define ESP_ERR_NVS_NOT_ENOUGH_SPACE (ESP_ERR_NVS_BASE + 0x05) /*!< There is not enough space in the underlying storage to save the value */
#define ESP_ERR_NVS_INVALID_NAME (ESP_ERR_NVS_BASE + 0x06) /*!< Namespace name doesnt satisfy constraints */
#define ESP_ERR_NVS_INVALID_HANDLE (ESP_ERR_NVS_BASE + 0x07) /*!< Handle has been closed or is NULL */
#define ESP_ERR_NVS_REMOVE_FAILED (ESP_ERR_NVS_BASE + 0x08) /*!< The value wasnt updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesnt fail again. */
#define ESP_ERR_NVS_KEY_TOO_LONG (ESP_ERR_NVS_BASE + 0x09) /*!< TBA */
#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) /*!< TBA */
#define ESP_ERR_NVS_INVALID_STATE (ESP_ERR_NVS_BASE + 0x0b) /*!< TBA */
#define ESP_ERR_NVS_INVALID_LENGTH (ESP_ERR_NVS_BASE + 0x0c) /*!< TBA */
#define ESP_ERR_NVS_KEY_TOO_LONG (ESP_ERR_NVS_BASE + 0x09) /*!< Key name is too long */
#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) /*!< Internal error; never returned by nvs_ API functions */
#define ESP_ERR_NVS_INVALID_STATE (ESP_ERR_NVS_BASE + 0x0b) /*!< NVS is in an inconsistent state due to a previous error. Call nvs_flash_init and nvs_open again, then retry. */
#define ESP_ERR_NVS_INVALID_LENGTH (ESP_ERR_NVS_BASE + 0x0c) /*!< String or blob length is not sufficient to store data */
#define ESP_ERR_NVS_NO_FREE_PAGES (ESP_ERR_NVS_BASE + 0x0d) /*!< NVS partition doesn't contain any empty pages. This may happen if NVS partition was truncated. Erase the whole partition and call nvs_flash_init again. */
/**
* @brief Mode of opening the non-volatile storage

View file

@ -21,7 +21,11 @@ extern "C" {
/**
* @brief Initialize NVS flash storage with layout given in the partition table.
*
* @return ESP_OK if storage was successfully initialized.
* @return
* - ESP_OK if storage was successfully initialized.
* - ESP_ERR_NVS_NO_FREE_PAGES if the NVS storage contains no empty pages
* (which may happen if NVS partition was truncated)
* - one of the error codes from the underlying flash storage driver
*/
esp_err_t nvs_flash_init(void);

View file

@ -811,10 +811,37 @@ void Page::invalidateCache()
{
mFindInfo = CachedFindInfo();
}
const char* Page::pageStateToName(PageState ps)
{
switch (ps) {
case PageState::CORRUPT:
return "CORRUPT";
case PageState::ACTIVE:
return "ACTIVE";
case PageState::FREEING:
return "FREEING";
case PageState::FULL:
return "FULL";
case PageState::INVALID:
return "INVALID";
case PageState::UNINITIALIZED:
return "UNINITIALIZED";
default:
assert(0 && "invalid state value");
return "";
}
}
void Page::debugDump() const
{
printf("state=%x addr=%x seq=%d\nfirstUsed=%d nextFree=%d used=%d erased=%d\n", (int) mState, mBaseAddress, mSeqNumber, static_cast<int>(mFirstUsedEntry), static_cast<int>(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount);
printf("state=%x (%s) addr=%x seq=%d\nfirstUsed=%d nextFree=%d used=%d erased=%d\n", (uint32_t) mState, pageStateToName(mState), mBaseAddress, mSeqNumber, static_cast<int>(mFirstUsedEntry), static_cast<int>(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount);
size_t skip = 0;
for (size_t i = 0; i < ENTRY_COUNT; ++i) {
printf("%3d: ", static_cast<int>(i));

View file

@ -220,6 +220,8 @@ protected:
assert(entry < ENTRY_COUNT);
return mBaseAddress + ENTRY_DATA_OFFSET + static_cast<uint32_t>(entry) * ENTRY_SIZE;
}
static const char* pageStateToName(PageState ps);
protected:

View file

@ -105,6 +105,11 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
}
}
// partition should have at least one free page
if (mFreePageList.size() == 0) {
return ESP_ERR_NVS_NO_FREE_PAGES;
}
return ESP_OK;
}

View file

@ -6,14 +6,26 @@
#include "unity.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "esp_log.h"
#include <string.h>
static const char* TAG = "test_nvs";
TEST_CASE("various nvs tests", "[nvs]")
{
nvs_handle handle_1;
TEST_ESP_OK(nvs_flash_init());
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_LOGW(TAG, "nvs_flash_init failed (0x%x), erasing partition and retrying", err);
const esp_partition_t* nvs_partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
assert(nvs_partition && "partition table must have an NVS partition");
ESP_ERROR_CHECK( esp_partition_erase_range(nvs_partition, 0, nvs_partition->size) );
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
TEST_ESP_ERR(nvs_open("test_namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_set_i32(handle_1, "foo", 0x12345678), ESP_ERR_NVS_INVALID_HANDLE);

View file

@ -1108,6 +1108,23 @@ TEST_CASE("read/write failure (TW8406)", "[nvs]")
}
}
TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]")
{
const size_t blob_size = 2048; // big enough so that only one can fit into a page
uint8_t blob[blob_size] = {0};
SpiFlashEmulator emu(5);
TEST_ESP_OK( nvs_flash_init_custom(0, 5) );
nvs_handle handle;
TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) );
TEST_ESP_OK( nvs_set_blob(handle, "1", blob, blob_size) );
TEST_ESP_OK( nvs_set_blob(handle, "2", blob, blob_size) );
TEST_ESP_OK( nvs_set_blob(handle, "3", blob, blob_size) );
TEST_ESP_OK( nvs_commit(handle) );
nvs_close(handle);
// first two pages are now full, third one is writable, last two are empty
// init should fail
TEST_ESP_ERR( nvs_flash_init_custom(0, 3), ESP_ERR_NVS_NO_FREE_PAGES );
}
TEST_CASE("dump all performance data", "[nvs]")
{

View file

@ -35,6 +35,12 @@
/* bytes erased by SPIEraseBlock() ROM function */
#define BLOCK_ERASE_SIZE 65536
/* Limit number of bytes written/read in a single SPI operation,
as these operations disable all higher priority tasks from running.
*/
#define MAX_WRITE_CHUNK 8192
#define MAX_READ_CHUNK 16384
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
static const char* TAG = "spi_flash";
static spi_flash_counters_t s_flash_stats;
@ -94,19 +100,6 @@ size_t IRAM_ATTR spi_flash_get_chip_size()
return g_rom_flashchip.chip_size;
}
static SpiFlashOpResult IRAM_ATTR spi_flash_unlock()
{
static bool unlocked = false;
if (!unlocked) {
SpiFlashOpResult rc = SPIUnlock();
if (rc != SPI_FLASH_RESULT_OK) {
return rc;
}
unlocked = true;
}
return SPI_FLASH_RESULT_OK;
}
static inline void IRAM_ATTR spi_flash_guard_start()
{
if (s_flash_guard_ops && s_flash_guard_ops->start) {
@ -135,6 +128,21 @@ static inline void IRAM_ATTR spi_flash_guard_op_unlock()
}
}
static SpiFlashOpResult IRAM_ATTR spi_flash_unlock()
{
static bool unlocked = false;
if (!unlocked) {
spi_flash_guard_start();
SpiFlashOpResult rc = SPIUnlock();
spi_flash_guard_end();
if (rc != SPI_FLASH_RESULT_OK) {
return rc;
}
unlocked = true;
}
return SPI_FLASH_RESULT_OK;
}
esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
{
return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
@ -155,11 +163,10 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
size_t end = start + size / SPI_FLASH_SEC_SIZE;
const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE;
COUNTER_START();
spi_flash_guard_start();
SpiFlashOpResult rc;
rc = spi_flash_unlock();
SpiFlashOpResult rc = spi_flash_unlock();
if (rc == SPI_FLASH_RESULT_OK) {
for (size_t sector = start; sector != end && rc == SPI_FLASH_RESULT_OK; ) {
spi_flash_guard_start();
if (sector % sectors_per_block == 0 && end - sector > sectors_per_block) {
rc = SPIEraseBlock(sector / sectors_per_block);
sector += sectors_per_block;
@ -169,9 +176,9 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
++sector;
COUNTER_ADD_BYTES(erase, SPI_FLASH_SEC_SIZE);
}
spi_flash_guard_end();
}
}
spi_flash_guard_end();
COUNTER_STOP(erase);
return spi_flash_translate_rc(rc);
}
@ -202,9 +209,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
size_t mid_size = (size - left_size) & ~3U;
size_t right_off = left_size + mid_size;
size_t right_size = size - mid_size - left_size;
spi_flash_guard_start();
rc = spi_flash_unlock();
spi_flash_guard_end();
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
@ -220,43 +225,38 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
COUNTER_ADD_BYTES(write, 4);
}
if (mid_size > 0) {
/* If src buffer is 4-byte aligned as well and is not in a region that
* requires cache access to be enabled, we can write it all at once. */
/* If src buffer is 4-byte aligned as well and is not in a region that requires cache access to be enabled, we
* can write directly without buffering in RAM. */
#ifdef ESP_PLATFORM
bool in_dram = ((uintptr_t) srcc >= 0x3FFAE000 &&
(uintptr_t) srcc < 0x40000000);
bool direct_write = ( (uintptr_t) srcc >= 0x3FFAE000
&& (uintptr_t) srcc < 0x40000000
&& ((uintptr_t) srcc + mid_off) % 4 == 0 );
#else
bool in_dram = true;
bool direct_write = true;
#endif
if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) {
while(mid_size > 0 && rc == SPI_FLASH_RESULT_OK) {
uint32_t write_buf[8];
uint32_t write_size;
const uint32_t *write_src = (const uint32_t *) (srcc + mid_off);
if (direct_write) {
write_size = MIN(mid_size, MAX_WRITE_CHUNK); /* Write in chunks, to avoid starving other CPU/tasks */
} else {
write_size = MIN(mid_size, sizeof(write_buf));
memcpy(write_buf, write_src, write_size);
write_src = write_buf;
}
spi_flash_guard_start();
rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size);
rc = SPIWrite(dst + mid_off, write_src, write_size);
spi_flash_guard_end();
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
COUNTER_ADD_BYTES(write, mid_size);
} else {
/*
* Otherwise, unlike for read, we cannot manipulate data in the
* user-provided buffer, so we write in 32 byte blocks.
*/
while (mid_size > 0) {
uint32_t t[8];
uint32_t write_size = MIN(mid_size, sizeof(t));
memcpy(t, srcc + mid_off, write_size);
spi_flash_guard_start();
rc = SPIWrite(dst + mid_off, t, write_size);
spi_flash_guard_end();
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
COUNTER_ADD_BYTES(write, write_size);
mid_size -= write_size;
mid_off += write_size;
}
COUNTER_ADD_BYTES(write, write_size);
mid_size -= write_size;
mid_off += write_size;
}
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
}
if (right_size > 0) {
uint32_t t = 0xffffffff;
memcpy(&t, srcc + right_off, right_size);
@ -289,12 +289,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
}
COUNTER_START();
spi_flash_disable_interrupts_caches_and_other_cpu();
SpiFlashOpResult rc;
spi_flash_guard_start();
rc = spi_flash_unlock();
spi_flash_guard_end();
spi_flash_enable_interrupts_caches_and_other_cpu();
SpiFlashOpResult rc = spi_flash_unlock();
if (rc == SPI_FLASH_RESULT_OK) {
/* SPI_Encrypt_Write encrypts data in RAM as it writes,
@ -331,9 +326,9 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
memcpy(encrypt_buf, ssrc + i, 32);
}
spi_flash_disable_interrupts_caches_and_other_cpu();
spi_flash_guard_start();
rc = SPI_Encrypt_Write(row_addr, (uint32_t *)encrypt_buf, 32);
spi_flash_enable_interrupts_caches_and_other_cpu();
spi_flash_guard_end();
if (rc != SPI_FLASH_RESULT_OK) {
break;
}
@ -341,6 +336,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
bzero(encrypt_buf, sizeof(encrypt_buf));
}
COUNTER_ADD_BYTES(write, size);
COUNTER_STOP(write);
spi_flash_guard_op_lock();
spi_flash_mark_modified_region(dest_addr, size);
@ -402,9 +398,21 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
size_t pad_right_off = (pad_right_src - src);
size_t pad_right_size = (size - pad_right_off);
if (mid_size > 0) {
rc = SPIRead(src + src_mid_off, (uint32_t *) (dstc + dst_mid_off), mid_size);
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
uint32_t mid_remaining = mid_size;
uint32_t mid_read = 0;
while (mid_remaining > 0) {
uint32_t read_size = MIN(mid_remaining, MAX_READ_CHUNK);
rc = SPIRead(src + src_mid_off + mid_read, (uint32_t *) (dstc + dst_mid_off + mid_read), read_size);
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
mid_remaining -= read_size;
mid_read += read_size;
if (mid_remaining > 0) {
/* Drop guard momentarily, allows other tasks to preempt */
spi_flash_guard_end();
spi_flash_guard_start();
}
}
COUNTER_ADD_BYTES(read, mid_size);
/*
@ -483,7 +491,7 @@ static esp_err_t IRAM_ATTR spi_flash_translate_rc(SpiFlashOpResult rc)
static inline void dump_counter(spi_flash_counter_t* counter, const char* name)
{
ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name,
ESP_LOGI(TAG, "%s count=%8d time=%8dus bytes=%8d\n", name,
counter->count, counter->time, counter->bytes);
}

View file

@ -0,0 +1,68 @@
// Copyright 2010-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.
// Test for spi_flash_{read,write}.
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <unity.h>
#include <test_utils.h>
#include <esp_partition.h>
TEST_CASE("Test erase partition", "[spi_flash]")
{
const esp_partition_t *part = get_test_data_partition();
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
spi_flash_reset_counters();
#endif
// erase whole partition
ESP_ERROR_CHECK( esp_partition_erase_range(part, 0, part->size) );
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
spi_flash_dump_counters();
#endif
// put some dummy data on sector boundaries
const char *some_data = "abcdefghijklmn";
for (int i = 0; i < part->size; i+= 4096) {
ESP_ERROR_CHECK( esp_partition_write(part, i, some_data, strlen(some_data)) );
}
// check it's there!
char buf[strlen(some_data)];
for (int i = 0; i < part->size; i+= 4096) {
memset(buf, 0x00, sizeof(buf));
ESP_ERROR_CHECK( esp_partition_read(part, i, buf, sizeof(buf)) );
TEST_ASSERT_EQUAL_INT(0, strncmp(buf, some_data, sizeof(buf)));
}
// erase the whole thing again
ESP_ERROR_CHECK( esp_partition_erase_range(part, 0, part->size) );
// check it's gone
for (int i = 0; i < part->size; i+= 4096) {
memset(buf, 0x00, sizeof(buf));
ESP_ERROR_CHECK( esp_partition_read(part, i, buf, sizeof(buf)) );
for (int i = 0; i < sizeof(buf); i++) {
TEST_ASSERT_EQUAL_HEX8(0xFF, buf[i]);
}
}
}

View file

@ -87,7 +87,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len)
TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0);
}
TEST_CASE("Test spi_flash_read", "[spi_flash_read]")
TEST_CASE("Test spi_flash_read", "[spi_flash]")
{
setup_tests();
#if CONFIG_SPI_FLASH_MINIMAL_TEST
@ -165,7 +165,7 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len)
TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0);
}
TEST_CASE("Test spi_flash_write", "[spi_flash_write]")
TEST_CASE("Test spi_flash_write", "[spi_flash]")
{
setup_tests();
#if CONFIG_SPI_FLASH_MINIMAL_TEST

View file

@ -0,0 +1,8 @@
ULP_BINUTILS_PREFIX ?= esp32ulp-elf-
export ULP_AS := $(ULP_BINUTILS_PREFIX)as
export ULP_LD := $(ULP_BINUTILS_PREFIX)ld
export ULP_OBJCOPY := $(ULP_BINUTILS_PREFIX)objcopy
export ULP_OBJDUMP := $(ULP_BINUTILS_PREFIX)objdump
export ULP_NM := $(ULP_BINUTILS_PREFIX)nm
export ULP_LD_TEMPLATE := $(IDF_PATH)/components/ulp/ld/esp32.ulp.ld
export ULP_MAP_GEN := $(IDF_PATH)/components/ulp/esp32ulp_mapgen.py

View file

@ -1,12 +1,7 @@
ULP coprocessor programming
===========================
Programming ULP coprocessor using C macros
==========================================
.. warning:: ULP coprocessor programming approach described here is experimental. It is probable that once binutils support for ULP is done, this preprocessor-based approach may be deprecated. We welcome discussion about and contributions to ULP programming tools.
ULP coprocessor is a simple FSM which is designed to perform measurements using ADC, temperature sensor, and external I2C sensors, while main processors are in deep sleep mode. ULP coprocessor can access RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general purpose 16-bit registers.
ULP coprocessor doesn't have a dedicated binutils port yet. Programming ULP coprocessor is possible by embedding assembly-like macros into an ESP32 application.
Here is an example how this can be done::
In addition to the existing binutils port for the ESP32 ULP coprocessor, it is possible to generate programs for the ULP by embedding assembly-like macros into an ESP32 application. Here is an example how this can be done::
const ulp_insn_t program[] = {
I_MOVI(R3, 16), // R3 <- 16

View file

@ -0,0 +1,96 @@
# Extra make rules for components containing ULP coprocessor code.
#
# ULP program(s) gets built and linked into the application.
# Steps taken here are explained in docs/ulp.rst
# Define names for files generated at different stages
ULP_ELF := $(ULP_APP_NAME).elf
ULP_MAP := $(ULP_ELF:.elf=.map)
ULP_SYM := $(ULP_ELF:.elf=.sym)
ULP_BIN := $(ULP_ELF:.elf=.bin)
ULP_EXPORTS_LD := $(ULP_ELF:.elf=.ld)
ULP_EXPORTS_HEADER := $(ULP_ELF:.elf=.h)
ULP_LD_SCRIPT := $(ULP_ELF:.elf=.common.ld)
ULP_OBJECTS := $(notdir $(ULP_S_SOURCES:.S=.ulp.o))
ULP_DEP := $(notdir $(ULP_S_SOURCES:.S=.ulp.d)) $(ULP_LD_SCRIPT:.ld=.d)
ULP_PREPROCESSED := $(notdir $(ULP_S_SOURCES:.S=.ulp.pS))
ULP_LISTINGS := $(notdir $(ULP_S_SOURCES:.S=.ulp.lst))
ULP_PREPROCESSOR_ARGS := \
$(addprefix -I ,$(COMPONENT_INCLUDES)) \
$(addprefix -I ,$(COMPONENT_EXTRA_INCLUDES)) \
-I$(COMPONENT_PATH) -D__ASSEMBLER__
-include $(ULP_DEP)
# Preprocess LD script used to link ULP program
$(ULP_LD_SCRIPT): $(ULP_LD_TEMPLATE)
$(summary) CPP $(notdir $@)
$(CC) $(CPPFLAGS) -MT $(ULP_LD_SCRIPT) -E -P -xc -o $@ $(ULP_PREPROCESSOR_ARGS) $<
# Generate preprocessed assembly files.
# To inspect these preprocessed files, add a ".PRECIOUS: %.ulp.pS" rule.
%.ulp.pS: $(COMPONENT_PATH)/ulp/%.S
$(summary) CPP $(notdir $<)
$(CC) $(CPPFLAGS) -MT $(patsubst %.ulp.pS,%.ulp.o,$@) -E -P -xc -o $@ $(ULP_PREPROCESSOR_ARGS) $<
# Compiled preprocessed files into object files.
%.ulp.o: %.ulp.pS
$(summary) ULP_AS $(notdir $@)
$(ULP_AS) -al=$(patsubst %.ulp.o,%.ulp.lst,$@) -o $@ $<
# Link object files and generate map file
$(ULP_ELF): $(ULP_OBJECTS) $(ULP_LD_SCRIPT)
$(summary) ULP_LD $(notdir $@)
$(ULP_LD) -o $@ -A elf32-esp32ulp -Map=$(ULP_MAP) -T $(ULP_LD_SCRIPT) $<
# Dump the list of global symbols in a convenient format.
$(ULP_SYM): $(ULP_ELF)
$(ULP_NM) -g -f posix $< > $@
# Dump the binary for inclusion into the project
$(COMPONENT_BUILD_DIR)/$(ULP_BIN): $(ULP_ELF)
$(summary) ULP_BIN $(notdir $@)
$(ULP_OBJCOPY) -O binary $< $@
# Left and right side of the rule are the same, but the right side
# is given as an absolute path.
# (Make can not resolve such things automatically)
$(ULP_EXPORTS_HEADER): $(COMPONENT_BUILD_DIR)/$(ULP_EXPORTS_HEADER)
# Artificial intermediate target to trigger generation of .h and .ld files.
.INTERMEDIATE: $(COMPONENT_NAME)_ulp_mapgen_intermediate
$(COMPONENT_BUILD_DIR)/$(ULP_EXPORTS_HEADER)\
$(COMPONENT_BUILD_DIR)/$(ULP_EXPORTS_LD): $(COMPONENT_NAME)_ulp_mapgen_intermediate
# Convert the symbols list into a header file and linker export script.
$(COMPONENT_NAME)_ulp_mapgen_intermediate: $(ULP_SYM)
$(summary) ULP_MAPGEN $(notdir $<)
$(ULP_MAP_GEN) -s $(ULP_SYM) -o $(ULP_EXPORTS_LD:.ld=)
# Building the component separately from the project should result in
# ULP files being built.
build: $(COMPONENT_BUILD_DIR)/$(ULP_EXPORTS_HEADER) \
$(COMPONENT_BUILD_DIR)/$(ULP_EXPORTS_LD) \
$(COMPONENT_BUILD_DIR)/$(ULP_BIN)
# Objects listed as being dependent on $(ULP_EXPORTS_HEADER) must also
# depend on $(ULP_SYM), to order build steps correctly.
$(ULP_EXP_DEP_OBJECTS) : $(ULP_EXPORTS_HEADER) $(ULP_SYM)
# Finally, set all the variables processed by the build system.
COMPONENT_EXTRA_CLEAN := $(ULP_OBJECTS) \
$(ULP_LD_SCRIPT) \
$(ULP_PREPROCESSED) \
$(ULP_ELF) $(ULP_BIN) \
$(ULP_MAP) $(ULP_SYM) \
$(ULP_EXPORTS_LD) \
$(ULP_EXPORTS_HEADER) \
$(ULP_DEP) \
$(ULP_LISTINGS)
COMPONENT_EMBED_FILES := $(COMPONENT_BUILD_DIR)/$(ULP_BIN)
COMPONENT_ADD_LDFLAGS := -l$(COMPONENT_NAME) -T $(COMPONENT_BUILD_DIR)/$(ULP_EXPORTS_LD)
COMPONENT_EXTRA_INCLUDES := $(COMPONENT_BUILD_DIR)

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
# esp32ulp_mapgen utility converts a symbol list provided by nm into an export script
# for the linker and a header file.
#
# Copyright (c) 2016-2017 Espressif Systems (Shanghai) PTE LTD.
# Distributed under the terms of Apache License v2.0 found in the top-level LICENSE file.
from optparse import OptionParser
BASE_ADDR = 0x50000000;
def gen_ld_h_from_sym(f_sym, f_ld, f_h):
f_ld.write("/* Variable definitions for ESP32ULP linker\n");
f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n");
f_ld.write(" */\n\n");
f_h.write("// Variable definitions for ESP32ULP\n");
f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n");
f_h.write("#pragma once\n\n");
for line in f_sym:
name, _, addr_str = line.split()
addr = int(addr_str, 16) + BASE_ADDR;
f_h.write("extern uint32_t ulp_{0};\n".format(name));
f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr))
def main():
description = ( "This application generates .h and .ld files for symbols defined in input file. "
"The input symbols file can be generated using nm utility like this: "
"esp32-ulp-nm -g -f posix <elf_file> > <symbols_file>" );
parser = OptionParser(description=description)
parser.add_option("-s", "--symfile", dest="symfile",
help="symbols file name", metavar="SYMFILE")
parser.add_option("-o", "--outputfile", dest="outputfile",
help="destination .h and .ld files name prefix", metavar="OUTFILE")
(options, args) = parser.parse_args()
if options.symfile is None:
parser.print_help()
return 1
if options.outputfile is None:
parser.print_help()
return 1
with open(options.outputfile + ".h", 'w') as f_h, \
open(options.outputfile + ".ld", 'w') as f_ld, \
open(options.symfile) as f_sym: \
gen_ld_h_from_sym(f_sym, f_ld, f_h)
return 0
if __name__ == "__main__":
exit(main());

View file

@ -847,6 +847,34 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
*/
esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize);
/**
* @brief Load ULP program binary into RTC memory
*
* ULP program binary should have the following format (all values little-endian):
*
* 1. MAGIC, (value 0x00706c75, 4 bytes)
* 2. TEXT_OFFSET, offset of .text section from binary start (2 bytes)
* 3. TEXT_SIZE, size of .text section (2 bytes)
* 4. DATA_SIZE, size of .data section (2 bytes)
* 5. BSS_SIZE, size of .bss section (2 bytes)
* 6. (TEXT_OFFSET - 16) bytes of arbitrary data (will not be loaded into RTC memory)
* 7. .text section
* 8. .data section
*
* Linker script in components/ulp/ld/esp32.ulp.ld produces ELF files which
* correspond to this format. This linker script produces binaries with load_addr == 0.
*
* @param load_addr address where the program should be loaded, expressed in 32-bit words
* @param program_binary pointer to program binary
* @param program_size size of the program binary
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if load_addr is out of range
* - ESP_ERR_INVALID_SIZE if program_size doesn't match (TEXT_OFFSET + TEXT_SIZE + DATA_SIZE)
* - ESP_ERR_NOT_SUPPORTED if the magic number is incorrect
*/
esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t* program_binary, size_t program_size);
/**
* @brief Run the program loaded into RTC memory
* @param entry_point entry point, expressed in 32-bit words

View file

@ -0,0 +1,35 @@
#include "sdkconfig.h"
#define ULP_BIN_MAGIC 0x00706c75
#define HEADER_SIZE 12
MEMORY
{
ram(RW) : ORIGIN = 0, LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM
}
SECTIONS
{
.text : AT(HEADER_SIZE)
{
*(.text)
} >ram
.data :
{
. = ALIGN(4);
*(.data)
} >ram
.bss :
{
. = ALIGN(4);
*(.bss)
} >ram
.header : AT(0)
{
LONG(ULP_BIN_MAGIC)
SHORT(LOADADDR(.text))
SHORT(SIZEOF(.text))
SHORT(SIZEOF(.data))
SHORT(SIZEOF(.bss))
}
}

View file

@ -27,239 +27,17 @@
#include "sdkconfig.h"
static const char* TAG = "ulp";
typedef struct {
uint32_t label : 16;
uint32_t addr : 11;
uint32_t unused : 1;
uint32_t type : 4;
} reloc_info_t;
uint32_t magic;
uint16_t text_offset;
uint16_t text_size;
uint16_t data_size;
uint16_t bss_size;
} ulp_binary_header_t;
#define RELOC_TYPE_LABEL 0
#define RELOC_TYPE_BRANCH 1
#define ULP_BINARY_MAGIC_ESP32 (0x00706c75)
/* This record means: there is a label at address
* insn_addr, with number label_num.
*/
#define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_LABEL }
/* This record means: there is a branch instruction at
* insn_addr, it needs to be changed to point to address
* of label label_num.
*/
#define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_BRANCH }
/* Processing branch and label macros involves four steps:
*
* 1. Iterate over program and count all instructions
* with "macro" opcode. Allocate relocations array
* with number of entries equal to number of macro
* instructions.
*
* 2. Remove all fake instructions with "macro" opcode
* and record their locations into relocations array.
* Removal is done using two pointers. Instructions
* are read from read_ptr, and written to write_ptr.
* When a macro instruction is encountered,
* its contents are recorded into the appropriate
* table, and then read_ptr is advanced again.
* When a real instruction is encountered, it is
* read via read_ptr and written to write_ptr.
* In the end, all macro instructions are removed,
* size of the program (expressed in words) is
* reduced by the total number of macro instructions
* which were present.
*
* 3. Sort relocations array by label number, and then
* by type ("label" or "branch") if label numbers
* match. This is done to simplify lookup on the next
* step.
*
* 4. Iterate over entries of relocations table.
* For each label number, label entry comes first
* because the array was sorted at the previous step.
* Label address is recorded, and all subsequent
* "branch" entries which point to the same label number
* are processed. For each branch entry, correct offset
* or absolute address is calculated, depending on branch
* type, and written into the appropriate field of
* the instruction.
*
*/
static esp_err_t do_single_reloc(ulp_insn_t* program, uint32_t load_addr,
reloc_info_t label_info, reloc_info_t branch_info)
{
size_t insn_offset = branch_info.addr - load_addr;
ulp_insn_t* insn = &program[insn_offset];
// B and BX have the same layout of opcode/sub_opcode fields,
// and share the same opcode
assert(insn->b.opcode == OPCODE_BRANCH
&& "branch macro was applied to a non-branch instruction");
switch (insn->b.sub_opcode) {
case SUB_OPCODE_B: {
int32_t offset = ((int32_t) label_info.addr) - ((int32_t) branch_info.addr);
uint32_t abs_offset = abs(offset);
uint32_t sign = (offset >= 0) ? 0 : 1;
if (abs_offset > 127) {
ESP_LOGW(TAG, "target out of range: branch from %x to %x",
branch_info.addr, label_info.addr);
return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
}
insn->b.offset = abs_offset;
insn->b.sign = sign;
break;
}
case SUB_OPCODE_BX: {
assert(insn->bx.reg == 0 &&
"relocation applied to a jump with offset in register");
insn->bx.addr = label_info.addr;
break;
}
default:
assert(false && "unexpected sub-opcode");
}
return ESP_OK;
}
esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize)
{
const ulp_insn_t* read_ptr = program;
const ulp_insn_t* end = program + *psize;
size_t macro_count = 0;
// step 1: calculate number of macros
while (read_ptr < end) {
ulp_insn_t r_insn = *read_ptr;
if (r_insn.macro.opcode == OPCODE_MACRO) {
++macro_count;
}
++read_ptr;
}
size_t real_program_size = *psize - macro_count;
const size_t ulp_mem_end = CONFIG_ULP_COPROC_RESERVE_MEM / sizeof(ulp_insn_t);
if (load_addr > ulp_mem_end) {
ESP_LOGW(TAG, "invalid load address %x, max is %x",
load_addr, ulp_mem_end);
return ESP_ERR_ULP_INVALID_LOAD_ADDR;
}
if (real_program_size + load_addr > ulp_mem_end) {
ESP_LOGE(TAG, "program too big: %d words, max is %d words",
real_program_size, ulp_mem_end);
return ESP_ERR_ULP_SIZE_TOO_BIG;
}
// If no macros found, copy the program and return.
if (macro_count == 0) {
memcpy(((ulp_insn_t*) RTC_SLOW_MEM) + load_addr, program, *psize * sizeof(ulp_insn_t));
return ESP_OK;
}
reloc_info_t* reloc_info =
(reloc_info_t*) malloc(sizeof(reloc_info_t) * macro_count);
if (reloc_info == NULL) {
return ESP_ERR_NO_MEM;
}
// step 2: record macros into reloc_info array
// and remove them from then program
read_ptr = program;
ulp_insn_t* output_program = ((ulp_insn_t*) RTC_SLOW_MEM) + load_addr;
ulp_insn_t* write_ptr = output_program;
uint32_t cur_insn_addr = load_addr;
reloc_info_t* cur_reloc = reloc_info;
while (read_ptr < end) {
ulp_insn_t r_insn = *read_ptr;
if (r_insn.macro.opcode == OPCODE_MACRO) {
switch(r_insn.macro.sub_opcode) {
case SUB_OPCODE_MACRO_LABEL:
*cur_reloc = RELOC_INFO_LABEL(r_insn.macro.label,
cur_insn_addr);
break;
case SUB_OPCODE_MACRO_BRANCH:
*cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
cur_insn_addr);
break;
default:
assert(0 && "invalid sub_opcode for macro insn");
}
++read_ptr;
assert(read_ptr != end && "program can not end with macro insn");
++cur_reloc;
} else {
// normal instruction (not a macro)
*write_ptr = *read_ptr;
++read_ptr;
++write_ptr;
++cur_insn_addr;
}
}
// step 3: sort relocations array
int reloc_sort_func(const void* p_lhs, const void* p_rhs) {
const reloc_info_t lhs = *(const reloc_info_t*) p_lhs;
const reloc_info_t rhs = *(const reloc_info_t*) p_rhs;
if (lhs.label < rhs.label) {
return -1;
} else if (lhs.label > rhs.label) {
return 1;
}
// label numbers are equal
if (lhs.type < rhs.type) {
return -1;
} else if (lhs.type > rhs.type) {
return 1;
}
// both label number and type are equal
return 0;
}
qsort(reloc_info, macro_count, sizeof(reloc_info_t),
reloc_sort_func);
// step 4: walk relocations array and fix instructions
reloc_info_t* reloc_end = reloc_info + macro_count;
cur_reloc = reloc_info;
while(cur_reloc < reloc_end) {
reloc_info_t label_info = *cur_reloc;
assert(label_info.type == RELOC_TYPE_LABEL);
++cur_reloc;
while (cur_reloc < reloc_end) {
if (cur_reloc->type == RELOC_TYPE_LABEL) {
if(cur_reloc->label == label_info.label) {
ESP_LOGE(TAG, "duplicate label definition: %d",
label_info.label);
free(reloc_info);
return ESP_ERR_ULP_DUPLICATE_LABEL;
}
break;
}
if (cur_reloc->label != label_info.label) {
ESP_LOGE(TAG, "branch to an inexistent label: %d",
cur_reloc->label);
free(reloc_info);
return ESP_ERR_ULP_UNDEFINED_LABEL;
}
esp_err_t rc = do_single_reloc(output_program, load_addr,
label_info, *cur_reloc);
if (rc != ESP_OK) {
free(reloc_info);
return rc;
}
++cur_reloc;
}
}
free(reloc_info);
*psize = real_program_size;
return ESP_OK;
}
static const char* TAG = "ulp";
esp_err_t ulp_run(uint32_t entry_point)
{
@ -279,3 +57,46 @@ esp_err_t ulp_run(uint32_t entry_point)
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
return ESP_OK;
}
esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t* program_binary, size_t program_size)
{
size_t program_size_bytes = program_size * sizeof(uint32_t);
size_t load_addr_bytes = load_addr * sizeof(uint32_t);
if (program_size_bytes < sizeof(ulp_binary_header_t)) {
return ESP_ERR_INVALID_SIZE;
}
if (load_addr_bytes > CONFIG_ULP_COPROC_RESERVE_MEM) {
return ESP_ERR_INVALID_ARG;
}
if (load_addr_bytes + program_size_bytes > CONFIG_ULP_COPROC_RESERVE_MEM) {
return ESP_ERR_INVALID_SIZE;
}
// Make a copy of a header in case program_binary isn't aligned
ulp_binary_header_t header;
memcpy(&header, program_binary, sizeof(header));
if (header.magic != ULP_BINARY_MAGIC_ESP32) {
return ESP_ERR_NOT_SUPPORTED;
}
size_t total_size = (size_t) header.text_offset + (size_t) header.text_size +
(size_t) header.data_size;
ESP_LOGD(TAG, "program_size_bytes: %d total_size: %d offset: %d .text: %d, .data: %d, .bss: %d",
program_size_bytes, total_size, header.text_offset,
header.text_size, header.data_size, header.bss_size);
if (total_size != program_size_bytes) {
return ESP_ERR_INVALID_SIZE;
}
size_t text_data_size = header.text_size + header.data_size;
uint8_t* base = (uint8_t*) RTC_SLOW_MEM;
memcpy(base + load_addr_bytes, program_binary + header.text_offset, text_data_size);
memset(base + load_addr_bytes + text_data_size, 0, header.bss_size);
return ESP_OK;
}

262
components/ulp/ulp_macro.c Normal file
View file

@ -0,0 +1,262 @@
// Copyright 2010-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.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp32/ulp.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "sdkconfig.h"
static const char* TAG = "ulp";
typedef struct {
uint32_t label : 16;
uint32_t addr : 11;
uint32_t unused : 1;
uint32_t type : 4;
} reloc_info_t;
#define RELOC_TYPE_LABEL 0
#define RELOC_TYPE_BRANCH 1
/* This record means: there is a label at address
* insn_addr, with number label_num.
*/
#define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_LABEL }
/* This record means: there is a branch instruction at
* insn_addr, it needs to be changed to point to address
* of label label_num.
*/
#define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_BRANCH }
/* Processing branch and label macros involves four steps:
*
* 1. Iterate over program and count all instructions
* with "macro" opcode. Allocate relocations array
* with number of entries equal to number of macro
* instructions.
*
* 2. Remove all fake instructions with "macro" opcode
* and record their locations into relocations array.
* Removal is done using two pointers. Instructions
* are read from read_ptr, and written to write_ptr.
* When a macro instruction is encountered,
* its contents are recorded into the appropriate
* table, and then read_ptr is advanced again.
* When a real instruction is encountered, it is
* read via read_ptr and written to write_ptr.
* In the end, all macro instructions are removed,
* size of the program (expressed in words) is
* reduced by the total number of macro instructions
* which were present.
*
* 3. Sort relocations array by label number, and then
* by type ("label" or "branch") if label numbers
* match. This is done to simplify lookup on the next
* step.
*
* 4. Iterate over entries of relocations table.
* For each label number, label entry comes first
* because the array was sorted at the previous step.
* Label address is recorded, and all subsequent
* "branch" entries which point to the same label number
* are processed. For each branch entry, correct offset
* or absolute address is calculated, depending on branch
* type, and written into the appropriate field of
* the instruction.
*
*/
static esp_err_t do_single_reloc(ulp_insn_t* program, uint32_t load_addr,
reloc_info_t label_info, reloc_info_t branch_info)
{
size_t insn_offset = branch_info.addr - load_addr;
ulp_insn_t* insn = &program[insn_offset];
// B and BX have the same layout of opcode/sub_opcode fields,
// and share the same opcode
assert(insn->b.opcode == OPCODE_BRANCH
&& "branch macro was applied to a non-branch instruction");
switch (insn->b.sub_opcode) {
case SUB_OPCODE_B: {
int32_t offset = ((int32_t) label_info.addr) - ((int32_t) branch_info.addr);
uint32_t abs_offset = abs(offset);
uint32_t sign = (offset >= 0) ? 0 : 1;
if (abs_offset > 127) {
ESP_LOGW(TAG, "target out of range: branch from %x to %x",
branch_info.addr, label_info.addr);
return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
}
insn->b.offset = abs_offset;
insn->b.sign = sign;
break;
}
case SUB_OPCODE_BX: {
assert(insn->bx.reg == 0 &&
"relocation applied to a jump with offset in register");
insn->bx.addr = label_info.addr;
break;
}
default:
assert(false && "unexpected sub-opcode");
}
return ESP_OK;
}
esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize)
{
const ulp_insn_t* read_ptr = program;
const ulp_insn_t* end = program + *psize;
size_t macro_count = 0;
// step 1: calculate number of macros
while (read_ptr < end) {
ulp_insn_t r_insn = *read_ptr;
if (r_insn.macro.opcode == OPCODE_MACRO) {
++macro_count;
}
++read_ptr;
}
size_t real_program_size = *psize - macro_count;
const size_t ulp_mem_end = CONFIG_ULP_COPROC_RESERVE_MEM / sizeof(ulp_insn_t);
if (load_addr > ulp_mem_end) {
ESP_LOGW(TAG, "invalid load address %x, max is %x",
load_addr, ulp_mem_end);
return ESP_ERR_ULP_INVALID_LOAD_ADDR;
}
if (real_program_size + load_addr > ulp_mem_end) {
ESP_LOGE(TAG, "program too big: %d words, max is %d words",
real_program_size, ulp_mem_end);
return ESP_ERR_ULP_SIZE_TOO_BIG;
}
// If no macros found, copy the program and return.
if (macro_count == 0) {
memcpy(((ulp_insn_t*) RTC_SLOW_MEM) + load_addr, program, *psize * sizeof(ulp_insn_t));
return ESP_OK;
}
reloc_info_t* reloc_info =
(reloc_info_t*) malloc(sizeof(reloc_info_t) * macro_count);
if (reloc_info == NULL) {
return ESP_ERR_NO_MEM;
}
// step 2: record macros into reloc_info array
// and remove them from then program
read_ptr = program;
ulp_insn_t* output_program = ((ulp_insn_t*) RTC_SLOW_MEM) + load_addr;
ulp_insn_t* write_ptr = output_program;
uint32_t cur_insn_addr = load_addr;
reloc_info_t* cur_reloc = reloc_info;
while (read_ptr < end) {
ulp_insn_t r_insn = *read_ptr;
if (r_insn.macro.opcode == OPCODE_MACRO) {
switch(r_insn.macro.sub_opcode) {
case SUB_OPCODE_MACRO_LABEL:
*cur_reloc = RELOC_INFO_LABEL(r_insn.macro.label,
cur_insn_addr);
break;
case SUB_OPCODE_MACRO_BRANCH:
*cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
cur_insn_addr);
break;
default:
assert(0 && "invalid sub_opcode for macro insn");
}
++read_ptr;
assert(read_ptr != end && "program can not end with macro insn");
++cur_reloc;
} else {
// normal instruction (not a macro)
*write_ptr = *read_ptr;
++read_ptr;
++write_ptr;
++cur_insn_addr;
}
}
// step 3: sort relocations array
int reloc_sort_func(const void* p_lhs, const void* p_rhs) {
const reloc_info_t lhs = *(const reloc_info_t*) p_lhs;
const reloc_info_t rhs = *(const reloc_info_t*) p_rhs;
if (lhs.label < rhs.label) {
return -1;
} else if (lhs.label > rhs.label) {
return 1;
}
// label numbers are equal
if (lhs.type < rhs.type) {
return -1;
} else if (lhs.type > rhs.type) {
return 1;
}
// both label number and type are equal
return 0;
}
qsort(reloc_info, macro_count, sizeof(reloc_info_t),
reloc_sort_func);
// step 4: walk relocations array and fix instructions
reloc_info_t* reloc_end = reloc_info + macro_count;
cur_reloc = reloc_info;
while(cur_reloc < reloc_end) {
reloc_info_t label_info = *cur_reloc;
assert(label_info.type == RELOC_TYPE_LABEL);
++cur_reloc;
while (cur_reloc < reloc_end) {
if (cur_reloc->type == RELOC_TYPE_LABEL) {
if(cur_reloc->label == label_info.label) {
ESP_LOGE(TAG, "duplicate label definition: %d",
label_info.label);
free(reloc_info);
return ESP_ERR_ULP_DUPLICATE_LABEL;
}
break;
}
if (cur_reloc->label != label_info.label) {
ESP_LOGE(TAG, "branch to an inexistent label: %d",
cur_reloc->label);
free(reloc_info);
return ESP_ERR_ULP_UNDEFINED_LABEL;
}
esp_err_t rc = do_single_reloc(output_program, load_addr,
label_info, *cur_reloc);
if (rc != ESP_OK) {
free(reloc_info);
return rc;
}
++cur_reloc;
}
}
free(reloc_info);
*psize = real_program_size;
return ESP_OK;
}

View file

@ -48,6 +48,7 @@ Macros
.. doxygendefine:: ESP_ERR_NVS_PAGE_FULL
.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE
.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH
.. doxygendefine:: ESP_ERR_NVS_NO_FREE_PAGES
Type Definitions
^^^^^^^^^^^^^^^^
@ -61,6 +62,7 @@ Enumerations
Functions
^^^^^^^^^
.. doxygenfunction:: nvs_flash_init
.. doxygenfunction:: nvs_open
.. doxygenfunction:: nvs_set_i8
.. doxygenfunction:: nvs_set_u8
@ -86,5 +88,5 @@ Functions
.. doxygenfunction:: nvs_erase_all
.. doxygenfunction:: nvs_commit
.. doxygenfunction:: nvs_close
.. doxygenfunction:: nvs_flash_init

View file

@ -102,6 +102,7 @@ Known Issues with idf_monitor
Issues Observed on Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~
- If you are using the supported Windows environment and receive the error "winpty: command not found" then run ``pacman -S winpty`` to fix.
- Arrow keys and some other special keys in gdb don't work, due to Windows Console limitations.
- Occasionally when "make" exits, it may stall for up to 30 seconds before idf_monitor resumes.
- Occasionally when "gdb" is run, it may stall for a short time before it begins communicating with the gdbstub.

View file

@ -58,6 +58,7 @@ Contents:
Pin List and Functions (PDF) <http://espressif.com/sites/default/files/documentation/esp32_chip_pin_list_en.pdf>
Chip Pinout (PDF) <http://espressif.com/sites/default/files/documentation/esp32_pinout_v1_0.pdf>
Silicon Errata (PDF) <http://espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf>
Modules and Boards <modules-and-boards>
.. toctree::
:caption: Contribute

View file

@ -142,6 +142,9 @@ The easiest way to start a project is to download the template project from GitH
This will download ``esp-idf-template`` project into ``~/esp/myapp`` directory.
**IMPORTANT:** The esp-idf build system does not support spaces in paths to esp-idf or to projects.
You can also find a range of example projects under the "examples" directory in IDF. These example project directories can be copied to outside IDF in order to begin your own projects.
Step 4: Building and flashing the application
=============================================

View file

@ -130,6 +130,9 @@ This will download ``esp-idf-template`` project into ``~/esp/myapp`` directory.
**IMPORTANT:** The esp-idf build system does not support spaces in paths to esp-idf or to projects.
You can also find a range of example projects under the "examples" directory in IDF. These example project directories can be copied to outside IDF in order to begin your own projects.
Step 4: Building and flashing the application
=============================================

View file

@ -0,0 +1,87 @@
ESP32 Modules and Boards
========================
Espressif designed and manufactured several development modules and boards to help users evaluate functionality of ESP32 chip. Development boards, depending on intended functionality, have exposed GPIO pins headers, provide USB programming interface, JTAG interface as well as peripherals like touch pads, LCD screen, SD card slot, camera module header, etc.
For details please refer to documentation below, provided together with description of particular boards.
ESP-WROOM-32
------------
The smallest module intended for installation in final products. Can be also used for evaluation after adding extra components like programming interface, boot strapping resistors and break out headers.
.. image:: http://dl.espressif.com/dl/schematics/pictures/esp-wroom-32.jpg
:align: center
:width: 40%
:alt: ESP-WROOM-32 module (front and back)
* `Schematic <http://dl.espressif.com/dl/schematics/ESP-WROOM-32-v3.2_sch.pdf>`__ (PDF)
* `Datasheet <https://espressif.com/sites/default/files/documentation/esp_wroom_32_datasheet_en.pdf>`__ (PDF)
* `ESP32 Module Reference Design <https://espressif.com/sites/default/files/documentation/esp32_module_reference_design.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
ESP32 Core Board V2 / ESP32 DevKitC
-----------------------------------
Small and convenient development board with break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has press buttons to reset the board and put it in upload mode.
.. image:: http://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.jpg
:align: center
:width: 50%
:alt: ESP32 Core Board V2 / ESP32 DevKitC board
* `Schematic <http://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
* `ESP32 Development Board Reference Design <https://espressif.com/sites/default/files/documentation/esp32_development_board_reference_design.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
* `ESP32-DevKitC Getting Started Guide <https://espressif.com/sites/default/files/documentation/esp32-devkitc_getting_started_guide_en.pdf>`_ (PDF)
* `CP210x USB to UART Bridge VCP Drivers <http://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
ESP32 Demo Board V2
-------------------
One of first feature rich evaluation boards that contains several pin headers, dip switches, USB to serial programming interface, reset and boot mode press buttons, power switch, 10 touch pads and separate header to connect LCD screen.
.. image:: http://dl.espressif.com/dl/schematics/pictures/esp32-demo-board-v2.jpg
:align: center
:alt: ESP32 Demo Board V2 board
* `Schematic <http://dl.espressif.com/dl/schematics/ESP32-Demo-Board-V2_sch.pdf>`__ (PDF)
* `FTDI Virtual COM Port Drivers`_ Note: Drivers install automatically on most of OS / there is no need to install them manually
ESP32 WROVER KIT V1 / ESP32 DevKitJ V1
--------------------------------------
Development board that has dual port USB to serial converter for programming and JTAG interface for debugging. Power supply is provided by USB interface or from standard 5 mm power supply jack. Power supply selection is done with a jumper and may be put on/off with a separate switch. Has SD card slot, 3.2” SPI LCD screen and dedicated header to connect a camera. Provides RGB diode for diagnostics. Also includes 32.768KHz XTAL for internal RTC to operate it in low power modes.
.. image:: http://dl.espressif.com/dl/schematics/pictures/esp32-devkitj-v1.jpg
:align: center
:width: 90%
:alt: ESP32 WROVER KIT V1 / ESP32 DevKitJ V1 board
* `Schematic <http://dl.espressif.com/dl/schematics/ESP32-DevKitJ-v1_sch.pdf>`__ (PDF)
* `FTDI Virtual COM Port Drivers`_ Note: Drivers install automatically on most of OS / there is no need to install them manually
* `JTAG Debugging for ESP32`_ (PDF)
ESP32 WROVER KIT V2
-------------------
This is an updated version of ESP32 DevKitJ V1 described above with design improvements identified when DevKitJ was in use. Both V1 and V2 versions of this board are ready to accommodate existing ESP-WROOM-32 or the new ESP32-WROVER module.
.. image:: http://dl.espressif.com/dl/schematics/pictures/esp-wrover-kit-v2.jpg
:align: center
:width: 90%
:alt: ESP32 WROVER KIT V2 board
* `Schematic <http://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-2.pdf>`__ (PDF)
* `ESP-WROVER-KIT Getting Started Guide <https://espressif.com/sites/default/files/documentation/esp-wrover-kit_getting_started_guide_en.pdf>`_ (PDF)
* `FTDI Virtual COM Port Drivers`_ Note: Drivers install automatically on most of OS / there is no need to install them manually
* `JTAG Debugging for ESP32`_ (PDF)
.. _JTAG Debugging for ESP32: https://github.com/espressif/esp-idf/
.. _FTDI Virtual COM Port Drivers: http://www.ftdichip.com/Drivers/D2XX.htm

View file

@ -1 +1,161 @@
.. include:: ../components/ulp/README.rst
ULP coprocessor programming
===========================
.. toctree::
:maxdepth: 1
Instruction set reference <ulp_instruction_set>
Programming using macros (legacy) <ulp_macros>
ULP (Ultra Low Power) coprocessor is a simple FSM which is designed to perform measurements using ADC, temperature sensor, and external I2C sensors, while main processors are in deep sleep mode. ULP coprocessor can access RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general purpose 16-bit registers.
Installing the toolchain
------------------------
ULP coprocessor code is written in assembly and compiled using the `binutils-esp32ulp toolchain`_.
1. Download the toolchain using the links listed on this page:
https://github.com/espressif/binutils-esp32ulp/wiki#downloads
2. Extract the toolchain into a directory, and add the path to the ``bin/`` directory of the toolchain to the ``PATH`` environment variable.
Compiling ULP code
------------------
To compile ULP code as part of a component, the following steps must be taken:
1. ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside component directory, for instance `ulp/`.
.. note: This directory should not be added to the ``COMPONENT_SRCDIRS`` environment variable. The logic behind this is that the ESP-IDF build system will compile files found in ``COMPONENT_SRCDIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory.
2. Modify the component makefile, adding the following::
ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
ULP_S_SOURCES = $(COMPONENT_PATH)/ulp/ulp_source_file.S
ULP_EXP_DEP_OBJECTS := main.o
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
Here is each line explained:
ULP_APP_NAME
Name of the generated ULP application, without an extension. This name is used for build products of the ULP application: ELF file, map file, binary file, generated header file, and generated linker export file.
ULP_S_SOURCES
List of assembly files to be passed to the ULP assembler. These must be absolute paths, i.e. start with ``$(COMPONENT_PATH)``. Consider using ``$(addprefix)`` function if more than one file needs to be listed. Paths are relative to component build directory, so prefixing them is not necessary.
ULP_EXP_DEP_OBJECTS
List of object files names within the component which include the generated header file. This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled. See section below explaining the concept of generated header files for ULP applications.
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
Includes common definitions of ULP build steps. Defines build targets for ULP object files, ELF file, binary file, etc.
3. Build the application as usual (e.g. `make app`)
Inside, the build system will take the following steps to build ULP program:
1. **Run each assembly file (foo.S) through C preprocessor.** This step generates the preprocessed assembly files (foo.ulp.pS) in the component build directory. This step also generates dependency files (foo.ulp.d).
2. **Run preprocessed assembly sources through assembler.** This produces objects (foo.ulp.o) and listing (foo.ulp.lst) files. Listing files are generated for debugging purposes and are not used at later stages of build process.
3. **Run linker script template through C preprocessor.** The template is located in components/ulp/ld directory.
4. **Link object files into an output ELF file** (ulp_app_name.elf). Map file (ulp_app_name.map) generated at this stage may be useful for debugging purposes.
5. **Dump contents of the ELF file into binary** (ulp_app_name.bin) for embedding into the application.
6. **Generate list of global symbols** (ulp_app_name.sym) in the ELF file using esp32ulp-elf-nm.
7. **Create LD export script and header file** (ulp_app_name.ld and ulp_app_name.h) containing the symbols from ulp_app_name.sym. This is done using esp32ulp_mapgen.py utility.
8. **Add the generated binary to the list of binary files** to be emedded into the application.
Accessing ULP program variables
-------------------------------
Global symbols defined in the ULP program may be used inside the main program.
For example, ULP program may define a variable ``measurement_count`` which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep::
.global measurement_count
measurement_count: .long 0
/* later, use measurement_count */
move r3, measurement_count
ld r3, r3, 0
Main program needs to initialize this variable before ULP program is started. Build system makes this possible by generating a ``$(ULP_APP_NAME).h`` and ``$(ULP_APP_NAME).ld`` files which define global symbols present in the ULP program. This files include each global symbol defined in the ULP program, prefixed with ``ulp_``.
The header file contains declaration of the symbol::
extern uint32_t ulp_measurement_count;
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take address of the symbol and cast to the appropriate type.
The generated linker script file defines locations of symbols in RTC_SLOW_MEM::
PROVIDE ( ulp_measurement_count = 0x50000060 );
To access ULP program variables from the main program, include the generated header file and use variables as one normally would::
#include "ulp_app_name.h"
// later
void init_ulp_vars() {
ulp_measurement_count = 64;
}
Note that ULP program can only use lower 16 bits of each 32-bit word in RTC memory, because the registers are 16-bit, and there is no instruction to load from high part of the word.
Likewise, ULP store instruction writes register value into the lower 16 bit part of the 32-bit word. Upper 16 bits are written with a value which depends on the address of the store instruction, so when reading variables written by the ULP, main application needs to mask upper 16 bits, e.g.::
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
Starting the ULP program
------------------------
To run a ULP program, main application needs to load the ULP program into RTC memory using ``ulp_load_binary`` function, and then start it using ``ulp_run`` function.
Note that "Enable Ultra Low Power (ULP) Coprocessor" option must be enabled in menuconfig in order to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
Each ULP program is embedded into the ESP-IDF application as a binary blob. Application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``::
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
void start_ulp_program() {
ESP_ERROR_CHECK( ulp_load_binary(
0 /* load address, set to 0 when using default linker scripts */,
bin_start,
(bin_end - bin_start) / sizeof(uint32_t)) );
}
.. doxygenfunction:: ulp_load_binary
Once the program is loaded into RTC memory, application can start it, passing the address of the entry point to ``ulp_run`` function::
ESP_ERROR_CHECK( ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)) );
.. doxygenfunction:: ulp_run
Declaration of the entry point symbol comes from the above mentioned generated header file, ``$(ULP_APP_NAME).h``. In assembly source of the ULP application, this symbol must be marked as ``.global``::
.global entry
entry:
/* code starts here */
ULP program flow
----------------
ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to obtain the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using ``sleep`` instruction.
Once the timer counts the number of ticks set by the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, ULP coprocessor powers up and starts running the program from the entry point set in the call to ``ulp_run``.
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.
To disable the timer (effectively preventing the ULP program from running again), clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from ULP code and from the main program.
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp

768
docs/ulp_instruction_set.rst Executable file
View file

@ -0,0 +1,768 @@
ULP coprocessor instruction set
+++++++++++++++++++++++++++++++
This document provides details about the instructions used by ESP32 ULP coprocessor assembler.
ULP coprocessor has 4 16-bit general purpose registers, labeled R0, R1, R2, R3. It also has an 8-bit counter register (stage_cnt) which can be used to implement loops. Stage count regiter is accessed using special instructions.
ULP coprocessor can access 8k bytes of RTC_SLOW_MEM memory region. Memory is addressed in 32-bit word units. It can also access peripheral registers in RTC_CNTL, RTC_IO, and SENS peripherals.
All instructions are 32-bit. Jump instructions, ALU instructions, peripheral register and memory access instructions are executed in 1 cycle. Instructions which work with peripherals (TSENS, ADC, I2C) take variable number of cycles, depending on peripheral operation.
The instruction syntax is case insensitive. Upper and lower case letters can be used and intermixed arbitrarily. This is true both for register names and instruction names.
Note about addressing
---------------------
ESP32 ULP coprocessor's JUMP, ST, LD instructions which take register as an argument (jump address, store/load base address) expect the argument to be expressed in 32-bit words.
Consider the following example program::
entry:
NOP
NOP
NOP
NOP
loop:
MOVE R1, loop
JUMP R1
When this program is assembled and linked, address of label ``loop`` will be equal to 16 (expressed in bytes). However `JUMP` instruction expects the address stored in register to be expressed in 32-bit words. To account for this common use case, assembler will convert the address of label `loop` from bytes to words, when generating ``MOVE`` instruction, so the code generated code will be equivalent to::
0000 NOP
0004 NOP
0008 NOP
000c NOP
0010 MOVE R1, 4
0014 JUMP R1
The other case is when the argument of ``MOVE`` instruction is not a label but a constant. In this case assembler will use the value as is, without any conversion::
.set val, 0x10
MOVE R1, val
In this case, value loaded into R1 will be ``0x10``.
Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the following code::
.global array
array: .long 0
.long 0
.long 0
.long 0
MOVE R1, array
MOVE R2, 0x1234
ST R2, R1, 0 // write value of R2 into the first array element,
// i.e. array[0]
ST R2, R1, 4 // write value of R2 into the second array element
// (4 byte offset), i.e. array[1]
ADD R1, R1, 2 // this increments address by 2 words (8 bytes)
ST R2, R1, 0 // write value of R2 into the third array element,
// i.e. array[2]
**NOP** - no operation
----------------------
**Syntax:**
**NOP**
**Operands:**
None
**Description:**
No operation is performed. Only the PC is incremented.
**Example**::
1: NOP
**ADD** - Add to register
-------------------------
**Syntax:**
**ADD** *Rdst, Rsrc1, Rsrc2*
**ADD** *Rdst, Rsrc1, imm*
**Operands:**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Description:**
The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register.
**Examples**::
1: ADD R1, R2, R3 //R1 = R2 + R3
2: Add R1, R2, 0x1234 //R1 = R2 + 0x1234
3: .set value1, 0x03 //constant value1=0x03
Add R1, R2, value1 //R1 = R2 + value1
4: .global label //declaration of variable label
Add R1, R2, label //R1 = R2 + label
...
label: nop //definition of variable label
**SUB** - Subtract from register
--------------------------------
**Syntax:**
**SUB** *Rdst, Rsrc1, Rsrc2*
**SUB** *Rdst, Rsrc1, imm*
**Operands:**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Description:**
The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register.
**Examples:**::
1: SUB R1, R2, R3 //R1 = R2 - R3
2: sub R1, R2, 0x1234 //R1 = R2 - 0x1234
3: .set value1, 0x03 //constant value1=0x03
SUB R1, R2, value1 //R1 = R2 - value1
4: .global label //declaration of variable label
SUB R1, R2, label //R1 = R2 - label
....
label: nop //definition of variable label
**AND** - Logical AND of two operands
-------------------------------------
**Syntax:**
**AND** *Rdst, Rsrc1, Rsrc2*
**AND** *Rdst, Rsrc1, imm*
**Operands:**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Description:**
The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register.
**Example**::
1: AND R1, R2, R3 //R1 = R2 & R3
2: AND R1, R2, 0x1234 //R1 = R2 & 0x1234
3: .set value1, 0x03 //constant value1=0x03
AND R1, R2, value1 //R1 = R2 & value1
4: .global label //declaration of variable label
AND R1, R2, label //R1 = R2 & label
...
label: nop //definition of variable label
**OR** - Logical OR of two operands
-----------------------------------
**Syntax**
**OR** *Rdst, Rsrc1, Rsrc2*
**OR** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Description**
The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register.
**Examples**::
1: OR R1, R2, R3 //R1 = R2 \| R3
2: OR R1, R2, 0x1234 //R1 = R2 \| 0x1234
3: .set value1, 0x03 //constant value1=0x03
OR R1, R2, value1 //R1 = R2 \| value1
4: .global label //declaration of variable label
OR R1, R2, label //R1 = R2 \|label
...
label: nop //definition of variable label
**LSH** - Logical Shift Left
----------------------------
**Syntax**
**LSH** *Rdst, Rsrc1, Rsrc2*
**LSH** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Description**
The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
**Examples**::
1: LSH R1, R2, R3 //R1 = R2 << R3
2: LSH R1, R2, 0x03 //R1 = R2 << 0x03
3: .set value1, 0x03 //constant value1=0x03
LSH R1, R2, value1 //R1 = R2 << value1
4: .global label //declaration of variable label
LSH R1, R2, label //R1 = R2 << label
...
label: nop //definition of variable label
**RSH** - Logical Shift Right
----------------------------
**Syntax**
**RSH** *Rdst, Rsrc1, Rsrc2*
**RSH** *Rdst, Rsrc1, imm*
**Operands**
*Rdst* - Register R[0..3]
*Rsrc1* - Register R[0..3]
*Rsrc2* - Register R[0..3]
*Imm* - 16-bit signed value
**Description**
The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
**Examples**::
1: RSH R1, R2, R3 //R1 = R2 >> R3
2: RSH R1, R2, 0x03 //R1 = R2 >> 0x03
3: .set value1, 0x03 //constant value1=0x03
RSH R1, R2, value1 //R1 = R2 >> value1
4: .global label //declaration of variable label
RSH R1, R2, label //R1 = R2 >> label
label: nop //definition of variable label
**MOVE** Move to register
---------------------------
**Syntax**
**MOVE** *Rdst, Rsrc*
**MOVE** *Rdst, imm*
**Operands**
- *Rdst* Register R[0..3]
- *Rsrc* Register R[0..3]
- *Imm* 16-bit signed value
**Description**
The instruction move to destination register value from source register or 16-bit signed value.
Note that when a label is used as an immediate, the address of the label will be converted from bytes to words. This is because LD, ST, and JUMP instructions expect the address register value to be expressed in words rather than bytes. To avoid using an extra instruction
**Examples**::
1: MOVE R1, R2 //R1 = R2 >> R3
2: MOVE R1, 0x03 //R1 = R2 >> 0x03
3: .set value1, 0x03 //constant value1=0x03
MOVE R1, value1 //R1 = value1
4: .global label //declaration of label
MOVE R1, label //R1 = address_of(label) / 4
...
label: nop //definition of label
**ST** Store data to the memory
---------------------------------
**Syntax**
**ST** *Rsrc, Rdst, offset*
**Operands**
- *Rsrc* Register R[0..3], holds the 16-bit value to store
- *Rdst* Register R[0..3], address of the destination, in 32-bit words
- *Offset* 10-bit signed value, offset in bytes
**Description**
The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC), expressed in words, shifted left by 5 bits::
Mem[Rdst + offset / 4]{31:0} = {PC[10:0], 5'b0, Rsrc[15:0]}
The application can use higher 16 bits to determine which instruction in the ULP program has written any particular word into memory.
**Examples**::
1: ST R1, R2, 0x12 //MEM[R2+0x12] = R1
2: .data //Data section definition
Addr1: .word 123 // Define label Addr1 16 bit
.set offs, 0x00 // Define constant offs
.text //Text section definition
MOVE R1, 1 // R1 = 1
MOVE R2, Addr1 // R2 = Addr1
ST R1, R2, offs // MEM[R2 + 0] = R1
// MEM[Addr1 + 0] will be 32'h600001
**LD** Load data from the memory
----------------------------------
**Syntax**
**LD** *Rdst, Rsrc, offset*
**Operands**
*Rdst* Register R[0..3], destination
*Rsrc* Register R[0..3], holds address of destination, in 32-bit words
*Offset* 10-bit signed value, offset in bytes
**Description**
The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst::
Rdst[15:0] = Mem[Rsrc + offset / 4][15:0]
**Examples**::
1: LD R1, R2, 0x12 //R1 = MEM[R2+0x12]
2: .data //Data section definition
Addr1: .word 123 // Define label Addr1 16 bit
.set offs, 0x00 // Define constant offs
.text //Text section definition
MOVE R1, 1 // R1 = 1
MOVE R2, Addr1 // R2 = Addr1 / 4 (address of label is converted into words)
LD R1, R2, offs // R1 = MEM[R2 + 0]
// R1 will be 123
**JUMP** Jump to an absolute address
--------------------------------------
**Syntax**
**JUMP** *Rdst*
**JUMP** *ImmAddr*
**JUMP** *Rdst, Condition*
**JUMP** *ImmAddr, Condition*
**Operands**
- *Rdst* Register R[0..3] containing address to jump to (expressed in 32-bit words)
- *ImmAddr* 13 bits address (expressed in bytes), aligned to 4 bytes
- *Condition*:
- EQ jump if last ALU operation result was zero
- OV jump if last ALU has set overflow flag
**Description**
The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag.
**Examples**::
1: JUMP R1 // Jump to address in R1 (address in R1 is in 32-bit words)
2: JUMP 0x120, EQ // Jump to address 0x120 (in bytes) if ALU result is zero
3: JUMP label // Jump to label
...
label: nop // Definition of label
4: .global label // Declaration of global label
MOVE R1, label // R1 = label (value loaded into R1 is in words)
JUMP R1 // Jump to label
...
label: nop // Definition of label
**JUMPR** Jump to a relative offset (condition based on R0)
-------------------------------------------------------------
**Syntax**
**JUMPR** *Step, Threshold, Condition*
**Operands**
- *Step* relative shift from current position, in bytes
- *Threshold* threshold value for branch condition
- *Condition*:
- *GE* (greater or equal) jump if value in R0 >= threshold
- *LT* (less than) jump if value in R0 < threshold
**Description**
The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value.
**Examples**::
1:pos: JUMPR 16, 20, GE // Jump to address (position + 16 bytes) if value in R0 >= 20
2: // Down counting loop using R0 register
MOVE R0, 16 // load 16 into R0
label: SUB R0, R0, 1 // R0--
NOP // do something
JUMPR label, 1, GE // jump to label if R0 >= 1
**JUMPS** Jump to a relative address (condition based on stage count)
-----------------------------------------------------------------------
**Syntax**
**JUMPS** *Step, Threshold, Condition*
**Operands**
- *Step* relative shift from current position, in bytes
- *Threshold* threshold value for branch condition
- *Condition*:
- *EQ* (equal) jump if value in stage_cnt == threshold
- *LT* (less than) jump if value in stage_cnt < threshold
- *GT* (greater than) jump if value in stage_cnt > threshold
**Description**
The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value.
**Examples**::
1:pos: JUMPS 16, 20, EQ // Jump to (position + 16 bytes) if stage_cnt == 20
2: // Up counting loop using stage count register
STAGE_RST // set stage_cnt to 0
label: STAGE_INC 1 // stage_cnt++
NOP // do something
JUMPS label, 16, LT // jump to label if stage_cnt < 16
**STAGE_RST** Reset stage count register
------------------------------------------
**Syntax**
**STAGE_RST**
**Operands**
No operands
**Description**
The instruction sets the stage count register to 0
**Examples**::
1: STAGE_RST // Reset stage count register
**STAGE_INC** Increment stage count register
----------------------------------------------
**Syntax**
**STAGE_INC** *Value*
**Operands**
- *Value* 8 bits value
**Description**
The instruction increments stage count register by given value.
**Examples**::
1: STAGE_INC 10 // stage_cnt += 10
2: // Up counting loop example:
STAGE_RST // set stage_cnt to 0
label: STAGE_INC 1 // stage_cnt++
NOP // do something
JUMPS label, 16, LT // jump to label if stage_cnt < 16
**STAGE_DEC** Decrement stage count register
----------------------------------------------
**Syntax**
**STAGE_DEC** *Value*
**Operands**
- *Value* 8 bits value
**Description**
The instruction decrements stage count register by given value.
**Examples**::
1: STAGE_DEC 10 // stage_cnt -= 10;
2: // Down counting loop exaple
STAGE_RST // set stage_cnt to 0
STAGE_INC 16 // increment stage_cnt to 16
label: STAGE_DEC 1 // stage_cnt--;
NOP // do something
JUMPS label, 0, GT // jump to label if stage_cnt > 0
**HALT** End the program
--------------------------
**Syntax**
**HALT**
**Operands**
No operands
**Description**
The instruction halt the processor to the power down mode
**Examples**::
1: HALT // Move chip to powerdown
**WAKE** wakeup the chip
--------------------------
**Syntax**
**WAKE**
**Operands**
No operands
**Description**
The instruction sends an interrupt from ULP to RTC controller.
- If the SoC is in deep sleep mode, and ULP wakeup is enabled, this causes the SoC to wake up.
- If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered.
**Examples**::
1: WAKE // Trigger wake up
REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
HALT // Stop the ULP program
// After these instructions, SoC will wake up,
// and ULP will not run again until started by the main program.
**SLEEP** set ULP wakeup timer period
---------------------------------------
**Syntax**
**SLEEP** *sleep_reg*
**Operands**
- *sleep_reg* 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers.
**Description**
The instruction selects which of the ``SENS_ULP_CP_SLEEP_CYCx_REG`` (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from ``SENS_ULP_CP_SLEEP_CYC0_REG`` is used.
**Examples**::
1: SLEEP 1 // Use period set in SENS_ULP_CP_SLEEP_CYC1_REG
2: .set sleep_reg, 4 // Set constant
SLEEP sleep_reg // Use period set in SENS_ULP_CP_SLEEP_CYC4_REG
**WAIT** wait some number of cycles
-------------------------------------
**Syntax**
**WAIT** *Cycles*
**Operands**
- *Cycles* number of cycles for wait
**Description**
The instruction delays for given number of cycles.
**Examples**::
1: WAIT 10 // Do nothing for 10 cycles
2: .set wait_cnt, 10 // Set a constant
WAIT wait_cnt // wait for 10 cycles
**TSENS** do measurement with temperature sensor
--------------------------------------------------
**Syntax**
- **TSENS** *Rdst, Wait_Delay*
**Operands**
- *Rdst* Destination Register R[0..3], result will be stored to this register
- *Wait_Delay* number of cycles used to perform the measurement
**Description**
The instruction performs measurement using TSENS and stores the result into a general purpose register.
**Examples**::
1: TSENS R1, 1000 // Measure temperature sensor for 1000 cycles,
// and store result to R1
**ADC** do measurement with ADC
---------------------------------
**Syntax**
**ADC** *Rdst, Sar_sel, Mux, Cycles*
**Operands**
- *Rdst* Destination Register R[0..3], result will be stored to this register
- *Sar_sel* selected ADC : 0=SARADC0, 1=SARADC1
- *Mux* - selected PAD, SARADC Pad[Mux+1] is enabled
- *Cycle* number of cycles used to perform measurement
**Description**
The instruction makes measurements from ADC.
**Examples**::
1: ADC R1, 0, 1, 100 // Measure value using ADC1 pad 2,
// for 100 cycles and move result to R1
**REG_RD** read from peripheral register
------------------------------------------
**Syntax**
**REG_RD** *Addr, High, Low*
**Operands**
- *Addr* register address, in 32-bit words
- *High* High part of R0
- *Low* Low part of R0
**Description**
The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``.
This instruction can access registers in RTC_CNTL, RTC_IO, and SENS peripherals. Address of the the register, as seen from the ULP,
can be calculated from the address of the same register on the DPORT bus as follows::
addr_ulp = (addr_dport - DR_REG_RTCCNTL_BASE) / 4
**Examples**::
1: REG_RD 0x120, 2, 0 // load 4 bits: R0 = {12'b0, REG[0x120][7:4]}
**REG_WR** write to peripheral register
-----------------------------------------
**Syntax**
**REG_WR** *Addr, High, Low, Data*
**Operands**
- *Addr* register address, in 32-bit words.
- *High* High part of R0
- *Low* Low part of R0
- *Data* value to write, 8 bits
**Description**
The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data``
This instruction can access registers in RTC_CNTL, RTC_IO, and SENS peripherals. Address of the the register, as seen from the ULP,
can be calculated from the address of the same register on the DPORT bus as follows::
addr_ulp = (addr_dport - DR_REG_RTCCNTL_BASE) / 4
**Examples**::
1: REG_WR 0x120, 7, 0, 0x10 // set 8 bits: REG[0x120][7:0] = 0x10
Convenience macros for peripheral registers access
--------------------------------------------------
ULP source files are passed through C preprocessor before the assembler. This allows certain macros to be used to facilitate access to peripheral registers.
Some existing macros are defined in ``soc/soc_ulp.h`` header file. These macros allow access to the fields of peripheral registers by their names.
Peripheral registers names which can be used with these macros are the ones defined in ``soc/rtc_cntl_reg.h``, ``soc/rtc_io_reg.h``, and ``soc/sens_reg.h``.
READ_RTC_REG(rtc_reg, low_bit, bit_width)
Read up to 16 bits from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0. For example::
#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
/* Read 16 lower bits of RTC_CNTL_TIME0_REG into R0 */
READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)
READ_RTC_FIELD(rtc_reg, field)
Read from a field in rtc_reg into R0, up to 16 bits. For example::
#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
/* Read 8-bit SENS_TSENS_OUT field of SENS_SAR_SLAVE_ADDR3_REG into R0 */
READ_RTC_REG(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT)
WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value)
Write immediate value into rtc_reg[low_bit + bit_width - 1 : low_bit], bit_width <= 8. For example::
#include "soc/soc_ulp.h"
#include "soc/rtc_io_reg.h"
/* Set BIT(2) of RTC_GPIO_OUT_DATA_W1TS field in RTC_GPIO_OUT_W1TS_REG */
WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 2, 1, 1)
WRITE_RTC_FIELD(rtc_reg, field, value)
Write immediate value into a field in rtc_reg, up to 8 bits. For example::
#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
/* Set RTC_CNTL_ULP_CP_SLP_TIMER_EN field of RTC_CNTL_STATE0_REG to 0 */
READ_RTC_REG(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)

1
docs/ulp_macros.rst Normal file
View file

@ -0,0 +1 @@
.. include:: ../components/ulp/README.rst

View file

@ -0,0 +1,67 @@
Set up Windows Toolchain from Scratch
*************************************
This is an **optional alternative to the normal Windows Guide**.
:doc:`See the main Windows setup page for the quickest and simplest Windows setup guide </windows-setup>`.
Setting up the environment gives you some more control over the process, and also provides the information for advanced users to customise the install. The pre-built environment has been prepared by following these steps.
Configure toolchain & environment from scratch
==============================================
This process involves installing MSYS2_, then installing the MSYS2_ and Python packages which ESP-IDF uses, and finally downloading and installing the Xtensa toolchain.
* Navigate to the MSYS2_ installer page and download the ``msys2-i686-xxxxxxx.exe`` installer executable (we only support a 32-bit MSYS environment, it works on both 32-bit and 64-bit Windows.) At time of writing, the latest installer is ``msys2-i686-20161025.exe``.
* Run through the installer steps. **Uncheck the "Run MSYS2 32-bit now" checkbox at the end.**
* Once the installer exits, open Start Menu and find "MSYS2 MinGW 32-bit" to run the terminal.
*(Why launch this different terminal? MSYS2 has the concept of different kinds of environments. The default "MSYS" environment is Cygwin-like and uses a translation layer for all Windows API calls. We need the "MinGW" environment in order to have a native Python which supports COM ports.)*
* The ESP-IDF repository on github contains a script in the tools directory titled ``windows_install_prerequisites.sh``. If you haven't got a local copy of the ESP-IDF yet, that's OK - you can just download that one file in Raw format from here: :idf_raw:`tools/windows/windows_install_prerequisites.sh`. Save it somewhere on your computer.
* Type the path to the shell script into the MSYS2 terminal window. You can type it as a normal Windows path, but use forward-slashes instead of back-slashes. ie: ``C:/Users/myuser/Downloads/windows_install_prerequisites.sh``. You can read the script beforehand to check what it does.
* The ``windows_install_prerequisites.sh`` script will download and install packages for ESP-IDF support, and the ESP32 toolchain.
* During the initial update step, MSYS may update itself into a state where it can no longer operate. You may see errors like the following::
*** fatal error - cygheap base mismatch detected - 0x612E5408/0x612E4408. This problem is probably due to using incompatible versions of the cygwin DLL.
If you see errors like this, close the terminal window entirely (terminating the processes running there) and then re-open a new terminal. Re-run ``windows_install_prerequisites.sh`` (tip: use the up arrow key to see the last run command). The update process will resume after this step.
MSYS2 Mirrors in China
~~~~~~~~~~~~~~~~~~~~~~
There are some (unofficial) MSYS2 mirrors inside China, which substantially improves download speeds inside China.
To add these mirrors, edit the following two MSYS2 mirrorlist files before running the setup script. The mirrorlist files can be found in the ``/etc/pacman.d`` directory (ie ``c:\msys2\etc\pacman.d``).
Add these lines at the top of ``mirrorlist.mingw32``::
Server = http://mirrors.ustc.edu.cn/msys2/mingw/i686/
Server = http://mirror.bit.edu.cn/msys2/REPOS/MINGW/i686
Add these lines at the top of ``mirrorlist.msys``::
Server = http://mirrors.ustc.edu.cn/msys2/msys/$arch
Server = http://mirror.bit.edu.cn/msys2/REPOS/MSYS2/$arch
HTTP Proxy
~~~~~~~~~~
You can enable an HTTP proxy for MSYS and PIP downloads by setting the ``http_proxy`` variable in the terminal before running the setup script::
export http_proxy='http://http.proxy.server:PORT'
Or with credentials::
export http_proxy='http://user:password@http.proxy.server:PORT'
Add this line to ``/etc/profile`` in the MSYS directory in order to permanently enable the proxy when using MSYS.
.. _MSYS2: https://msys2.github.io/

View file

@ -4,34 +4,18 @@ Set up of Toolchain for Windows
Step 1: Quick Steps
===================
Windows doesn't have a built-in "make" environment, so as well as installing the toolchain you will need a GNU-compatible environment. We use the MSYS2_ environment to provide.
You don't need to use this environment all the time (you can use Eclipse_ or some other front-end), but it runs behind the scenes.
Windows doesn't have a built-in "make" environment, so as well as installing the toolchain you will need a GNU-compatible environment. We use the MSYS2_ environment to provide this. You don't need to use this environment all the time (you can use :doc:`Eclipse </eclipse-setup>` or some other front-end), but it runs behind the scenes.
The quick setup is to download the Windows all-in-one toolchain & MSYS zip file from dl.espressif.com:
https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20170111.zip
Unzip the zip file to C:\ and it will create an "msys32" directory with a pre-prepared environment.
https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20170330.zip
Unzip the zip file to ``C:\`` (or some other location, but this guide assumes ``C:\``) and it will create an "msys32" directory with a pre-prepared environment.
Alternative Step 1: Configure toolchain & environment from scratch
==================================================================
As an alternative to getting a pre-prepared environment, you can set up the environment from scratch:
* Navigate to the MSYS2_ installer page and download the ``msys2-i686-xxxxxxx.exe`` installer executable (we only support a 32-bit MSYS environment, it works on both 32-bit and 64-bit Windows.)
* Run through the installer steps, and accept the "Run MSYS2 now" option at the end. A window will open with a MSYS2 terminal.
* The ESP-IDF repository on github contains a script in the tools directory titled ``windows_install_prerequisites.sh``. If you haven't downloaded the ESP-IDF yet, that's OK - you can just download that one file in Raw format from here: :idf_raw:`tools/windows/windows_install_prerequisites.sh`. Save it somewhere on your computer.
* Type the path to the shell script into the MSYS2 terminal window. You can type it as a normal Windows path, but use forward-slashes instead of back-slashes. ie: ``C:/Users/myuser/Downloads/windows_install_prerequisites.sh``. You can read the script beforehand to check what it does.
* If you use the 201602 MSYS2 installer, the first time you run ``windows_install_prerequisites.sh`` it will update the MSYS2 core system. At the end of this update, you will be prompted to close the MSYS2 terminal and re-open. When you re-open after the update, re-run ``windows_install_prerequisites.sh``. The next version of MSYS2 (after 201602) will not need this interim step.
* The ``windows_install_prerequisites.sh`` script will download and install packages for ESP-IDF support, and the ESP32 toolchain.
Note: You may encounter a bug where svchost.exe uses 100% CPU in Windows after setup is finished, resulting in the ESP-IDF building very slowly. Terminating svchost.exe or restarting Windows will solve this problem.
Rather than use the pre-prepared environment, you can :doc:`alternatively follow this guide to set up the MSYS2 environment from scratch <windows-setup-scratch>`.
Another Alternative Step 1: Just download a toolchain
=====================================================
@ -40,14 +24,14 @@ If you already have an MSYS2 install or want to do things differently, you can d
https://dl.espressif.com/dl/xtensa-esp32-elf-win32-1.22.0-61-gab8375a-5.2.0.zip
If you followed one of the above options for Step 1, you won't need this download.
**If you followed one of the above options for Step 1, you already have the toolchain and you won't need this download.**
Important: Just having this toolchain is *not enough* to use ESP-IDF on Windows. You will need GNU make, bash, and sed at minimum. The above environments provide all this, plus a host compiler (required for menuconfig support).
**Important**: Just having this toolchain is *not enough* to use ESP-IDF on Windows. You will need GNU make, bash, and sed at minimum. The above environments provide all this, plus a host compiler (required for menuconfig support).
Step 2: Getting the esp-idf repository from github
==================================================
Open an MSYS2 terminal window by running ``C:\msys32\msys2_shell.cmd``. The environment in this window is a bash shell.
Open an MSYS2 terminal window by running ``C:\msys32\mingw32.exe``. The environment in this window is a bash shell.
Change to the directory you want to clone the SDK into by typing a command like this one: ``cd "C:/path/to/dir"`` (note the forward-slashes in the path). Then type ``git clone --recursive https://github.com/espressif/esp-idf.git``
@ -66,6 +50,9 @@ The process is the same as for checking out the ESP-IDF from github. Change to t
**IMPORTANT:** The esp-idf build system does not support spaces in paths to esp-idf or to projects.
You can also find a range of example projects under the "examples" directory in IDF. These example project directories can be copied to outside IDF in order to begin your own projects.
Step 4: Configuring the project
===============================
@ -75,10 +62,8 @@ Type a command like this to set the path to ESP-IDF directory: ``export IDF_PATH
Use ``cd`` to change to the project directory (not the ESP-IDF directory.) Type ``make menuconfig`` to configure your project, then ``make`` to build it, ``make clean`` to remove built files, and ``make flash`` to flash (use the menuconfig to set the serial port for flashing.)
If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory.
If you'd like to use the Eclipse IDE instead of running ``make``, check out the :doc:`Eclipse guide </eclipse-setup>`.
.. _Eclipse: eclipse-setup.rst
.. _MSYS2: https://msys2.github.io/
.. _github: https://github.com/espressif/esp-idf-template
.. _known issue: https://github.com/espressif/esp-idf/issues/11

View file

@ -32,4 +32,7 @@ In addition, here are some tips for creating good examples:
* A good example is documented and the basic options can be configured.
* A good example does not contain a lot of code. If there is a lot of generic code in the example, consider refactoring that code into a standalone component and then use the component's API in your example.
* Names (of files, functions, variables, etc.) inside examples should be distinguishable from names of other parts of IDF (ideally, use `example` in names.)
* Functions and variables used inside examples should be declared static where possible.
* Examples should demonstrate one distinct thing each. Avoid multi-purposed "demo" examples, split these into multiple examples instead.
* Examples must be licensed under the Apache License 2.0 or (preferably for examples) if possible you can declare the example to be Public Domain / Creative Commons Zero.

View file

@ -1,10 +1,8 @@
#ifndef __BLUFI_DEMO_H__
#define __BLUFI_DEMO_H__
#pragma once
#define BLUFI_DEMO_TAG "BLUFI_DEMO"
#define BLUFI_INFO(fmt, ...) ESP_LOGI(BLUFI_DEMO_TAG, fmt, ##__VA_ARGS__)
#define BLUFI_ERROR(fmt, ...) ESP_LOGE(BLUFI_DEMO_TAG, fmt, ##__VA_ARGS__)
#define BLUFI_EXAMPLE_TAG "BLUFI_EXAMPLE"
#define BLUFI_INFO(fmt, ...) ESP_LOGI(BLUFI_EXAMPLE_TAG, fmt, ##__VA_ARGS__)
#define BLUFI_ERROR(fmt, ...) ESP_LOGE(BLUFI_EXAMPLE_TAG, fmt, ##__VA_ARGS__)
void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free);
int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
@ -13,5 +11,3 @@ uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len);
int blufi_security_init(void);
void blufi_security_deinit(void);
#endif /* __BLUFI_DEMO_H__ */

View file

@ -30,19 +30,19 @@
#include "esp_gap_ble_api.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "blufi_demo.h"
#include "blufi_example.h"
static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
#define BLUFI_DEVICE_NAME "BLUFI_DEVICE"
static uint8_t blufi_service_uuid128[32] = {
static uint8_t example_service_uuid128[32] = {
/* LSB <--------------------------------------------------------------------------------> MSB */
//first uuid, 16bit, [12],[13] is the value
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
};
//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56};
static esp_ble_adv_data_t blufi_adv_data = {
static esp_ble_adv_data_t example_adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
@ -54,11 +54,11 @@ static esp_ble_adv_data_t blufi_adv_data = {
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = 16,
.p_service_uuid = blufi_service_uuid128,
.p_service_uuid = example_service_uuid128,
.flag = 0x6,
};
static esp_ble_adv_params_t blufi_adv_params = {
static esp_ble_adv_params_t example_adv_params = {
.adv_int_min = 0x100,
.adv_int_max = 0x100,
.adv_type = ADV_TYPE_IND,
@ -88,7 +88,7 @@ static uint8_t gl_sta_bssid[6];
static uint8_t gl_sta_ssid[32];
static int gl_sta_ssid_len;
static esp_err_t event_handler(void *ctx, system_event_t *event)
static esp_err_t example_net_event_handler(void *ctx, system_event_t *event)
{
wifi_mode_t mode;
@ -146,7 +146,7 @@ static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_loop_init(example_net_event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
@ -154,24 +154,24 @@ static void initialise_wifi(void)
ESP_ERROR_CHECK( esp_wifi_start() );
}
static esp_blufi_callbacks_t blufi_callbacks = {
.event_cb = blufi_event_callback,
static esp_blufi_callbacks_t example_callbacks = {
.event_cb = example_event_callback,
.negotiate_data_handler = blufi_dh_negotiate_data_handler,
.encrypt_func = blufi_aes_encrypt,
.decrypt_func = blufi_aes_decrypt,
.checksum_func = blufi_crc_checksum,
};
static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
{
/* actually, should post to blufi_task handle the procedure,
* now, as a demo, we do simplely */
* now, as a example, we do it more simply */
switch (event) {
case ESP_BLUFI_EVENT_INIT_FINISH:
BLUFI_INFO("BLUFI init finish\n");
esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME);
esp_ble_gap_config_adv_data(&blufi_adv_data);
esp_ble_gap_config_adv_data(&example_adv_data);
break;
case ESP_BLUFI_EVENT_DEINIT_FINISH:
BLUFI_INFO("BLUFI init finish\n");
@ -184,7 +184,7 @@ static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_
break;
case ESP_BLUFI_EVENT_BLE_DISCONNECT:
BLUFI_INFO("BLUFI ble disconnect\n");
esp_ble_gap_start_advertising(&blufi_adv_params);
esp_ble_gap_start_advertising(&example_adv_params);
break;
case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:
BLUFI_INFO("BLUFI Set WIFI opmode %d\n", param->wifi_mode.op_mode);
@ -297,11 +297,11 @@ static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_
}
}
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
static void example_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
esp_ble_gap_start_advertising(&blufi_adv_params);
esp_ble_gap_start_advertising(&example_adv_params);
break;
default:
break;
@ -312,7 +312,7 @@ void app_main()
{
esp_err_t ret;
nvs_flash_init();
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
esp_bt_controller_init();
@ -340,8 +340,8 @@ void app_main()
BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version());
blufi_security_init();
esp_ble_gap_register_callback(gap_event_handler);
esp_ble_gap_register_callback(example_gap_event_handler);
esp_blufi_register_callbacks(&blufi_callbacks);
esp_blufi_register_callbacks(&example_callbacks);
esp_blufi_profile_init();
}

View file

@ -29,7 +29,7 @@
#include "esp_bt_defs.h"
#include "esp_gap_ble_api.h"
#include "esp_bt_main.h"
#include "blufi_demo.h"
#include "blufi_example.h"
#include "mbedtls/aes.h"
#include "mbedtls/dhm.h"

View file

@ -34,7 +34,7 @@
#include "driver/gpio.h"
#include "tlk110_phy.h"
static const char *TAG = "eth_demo";
static const char *TAG = "eth_example";
#define DEFAULT_PHY_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG)
#define PIN_PHY_POWER 17

View file

@ -9,8 +9,6 @@
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
@ -42,6 +40,5 @@ void blink_task(void *pvParameter)
void app_main()
{
nvs_flash_init();
xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL);
}

View file

@ -10,7 +10,6 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
void hello_task(void *pvParameter)
{
@ -26,6 +25,5 @@ void hello_task(void *pvParameter)
void app_main()
{
nvs_flash_init();
xTaskCreate(&hello_task, "hello_task", 2048, NULL, 5, NULL);
}

View file

@ -3,7 +3,7 @@
# project subdirectory.
#
PROJECT_NAME := uart
PROJECT_NAME := adc
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,17 @@
# Example: ADC1
This test code shows how to configure ADC1 and how to use ADC1 get the voltage.
####ADC1 functions:
* ADC1,CHANNEL_4:GPIO32, voltage range [0v,3.1v],the Data range [0,4095]
####Test:
* Please connect the test voltage to GPIO32

View file

@ -0,0 +1,35 @@
/* ADC1 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#define ADC1_TEST_CHANNEL (4)
void adc1task(void* arg)
{
// initialize ADC
adc1_config_width(ADC_WIDTH_12Bit);
adc1_config_channel_atten(ADC1_TEST_CHANNEL,ADC_ATTEN_11db);
while(1){
printf("The adc1 value:%d\n",adc1_get_voltage(ADC1_TEST_CHANNEL));
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void app_main()
{
xTaskCreate(adc1task, "adc1task", 1024*3, NULL, 10, NULL);
}

View file

@ -41,13 +41,13 @@
static xQueueHandle gpio_evt_queue = NULL;
void IRAM_ATTR gpio_isr_handler(void* arg)
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
void gpio_task_example(void* arg)
static void gpio_task_example(void* arg)
{
uint32_t io_num;
for(;;) {
@ -62,7 +62,7 @@ void app_main()
gpio_config_t io_conf;
//disable interrupt
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
//set as output mode
//set as output mode
io_conf.mode = GPIO_MODE_OUTPUT;
//bit mask of the pins that you want to set,e.g.GPIO18/19
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;

View file

@ -47,18 +47,18 @@
#define RW_TEST_LENGTH 129 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/
#define DELAY_TIME_BETWEEN_ITEMS_MS 1234 /*!< delay time between different test items */
#define I2C_SLAVE_SCL_IO 26 /*!<gpio number for i2c slave clock */
#define I2C_SLAVE_SDA_IO 25 /*!<gpio number for i2c slave data */
#define I2C_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */
#define I2C_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */
#define I2C_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */
#define I2C_EXAMPLE_SLAVE_SCL_IO 26 /*!<gpio number for i2c slave clock */
#define I2C_EXAMPLE_SLAVE_SDA_IO 25 /*!<gpio number for i2c slave data */
#define I2C_EXAMPLE_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */
#define I2C_EXAMPLE_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */
#define I2C_EXAMPLE_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */
#define I2C_MASTER_SCL_IO 19 /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 18 /*!< gpio number for I2C master data */
#define I2C_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define I2C_EXAMPLE_MASTER_SCL_IO 19 /*!< gpio number for I2C master clock */
#define I2C_EXAMPLE_MASTER_SDA_IO 18 /*!< gpio number for I2C master data */
#define I2C_EXAMPLE_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */
#define I2C_EXAMPLE_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define BH1750_SENSOR_ADDR 0x23 /*!< slave address for BH1750 sensor */
#define BH1750_CMD_START 0x23 /*!< Command to set measure mode */
@ -81,7 +81,7 @@ xSemaphoreHandle print_mux;
* --------|--------------------------|----------------------|--------------------|------|
*
*/
esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t* data_rd, size_t size)
static esp_err_t i2c_example_master_read_slave(i2c_port_t i2c_num, uint8_t* data_rd, size_t size)
{
if (size == 0) {
return ESP_OK;
@ -110,7 +110,7 @@ esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t* data_rd, size_t siz
* --------|---------------------------|----------------------|------|
*
*/
esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size)
static esp_err_t i2c_example_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
@ -135,7 +135,7 @@ esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t si
* | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop |
* --------|---------------------------|--------------------|--------------------|------|
*/
esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
static esp_err_t i2c_example_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
@ -166,42 +166,46 @@ esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* d
/**
* @brief i2c master initialization
*/
void i2c_master_init()
static void i2c_example_master_init()
{
int i2c_master_port = I2C_MASTER_NUM;
int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_MASTER_SDA_IO;
conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_MASTER_SCL_IO;
conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
i2c_driver_install(i2c_master_port, conf.mode,
I2C_EXAMPLE_MASTER_RX_BUF_DISABLE,
I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0);
}
/**
* @brief i2c slave initialization
*/
void i2c_slave_init()
static void i2c_example_slave_init()
{
int i2c_slave_port = I2C_SLAVE_NUM;
int i2c_slave_port = I2C_EXAMPLE_SLAVE_NUM;
i2c_config_t conf_slave;
conf_slave.sda_io_num = I2C_SLAVE_SDA_IO;
conf_slave.sda_io_num = I2C_EXAMPLE_SLAVE_SDA_IO;
conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf_slave.scl_io_num = I2C_SLAVE_SCL_IO;
conf_slave.scl_io_num = I2C_EXAMPLE_SLAVE_SCL_IO;
conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf_slave.mode = I2C_MODE_SLAVE;
conf_slave.slave.addr_10bit_en = 0;
conf_slave.slave.slave_addr = ESP_SLAVE_ADDR;
i2c_param_config(i2c_slave_port, &conf_slave);
i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
i2c_driver_install(i2c_slave_port, conf_slave.mode,
I2C_EXAMPLE_SLAVE_RX_BUF_LEN,
I2C_EXAMPLE_SLAVE_TX_BUF_LEN, 0);
}
/**
* @brief test function to show buffer
*/
void disp_buf(uint8_t* buf, int len)
static void disp_buf(uint8_t* buf, int len)
{
int i;
for (i = 0; i < len; i++) {
@ -213,7 +217,7 @@ void disp_buf(uint8_t* buf, int len)
printf("\n");
}
void i2c_test_task(void* arg)
static void i2c_test_task(void* arg)
{
int i = 0;
int ret;
@ -224,7 +228,7 @@ void i2c_test_task(void* arg)
uint8_t sensor_data_h, sensor_data_l;
while (1) {
ret = i2c_master_sensor_test( I2C_MASTER_NUM, &sensor_data_h, &sensor_data_l);
ret = i2c_example_master_sensor_test( I2C_EXAMPLE_MASTER_NUM, &sensor_data_h, &sensor_data_l);
xSemaphoreTake(print_mux, portMAX_DELAY);
printf("*******************\n");
printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx);
@ -243,12 +247,12 @@ void i2c_test_task(void* arg)
for (i = 0; i < DATA_LENGTH; i++) {
data[i] = i;
}
size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
size_t d_size = i2c_slave_write_buffer(I2C_EXAMPLE_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
if (d_size == 0) {
printf("i2c slave tx buffer full\n");
ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH);
ret = i2c_example_master_read_slave(I2C_EXAMPLE_MASTER_NUM, data_rd, DATA_LENGTH);
} else {
ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH);
ret = i2c_example_master_read_slave(I2C_EXAMPLE_MASTER_NUM, data_rd, RW_TEST_LENGTH);
}
xSemaphoreTake(print_mux, portMAX_DELAY);
printf("*******************\n");
@ -270,9 +274,9 @@ void i2c_test_task(void* arg)
data_wr[i] = i + 10;
}
//we need to fill the slave buffer so that master can read later
ret = i2c_master_write_slave( I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH);
ret = i2c_example_master_write_slave( I2C_EXAMPLE_MASTER_NUM, data_wr, RW_TEST_LENGTH);
if (ret == ESP_OK) {
size = i2c_slave_read_buffer( I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
size = i2c_slave_read_buffer( I2C_EXAMPLE_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
}
xSemaphoreTake(print_mux, portMAX_DELAY);
printf("*******************\n");
@ -294,8 +298,8 @@ void i2c_test_task(void* arg)
void app_main()
{
print_mux = xSemaphoreCreateMutex();
i2c_slave_init();
i2c_master_init();
i2c_example_slave_init();
i2c_example_master_init();
xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL);
xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL);

View file

@ -11,8 +11,6 @@
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/i2s.h"
#include <math.h>
@ -48,7 +46,6 @@ void app_main()
.data_in_num = -1 //Not used
};
nvs_flash_init();
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);

View file

@ -14,3 +14,4 @@
* This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly.
* GPIO18/19 are from high speed channel group. GPIO4/5 are from low speed channel group.

View file

@ -0,0 +1,150 @@
/* Ledc fade 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 <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "freertos/queue.h"
#include "driver/ledc.h"
#include "esp_attr.h"
#include "esp_err.h"
/*
* About this example
* 1. init LEDC module:
* a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM.
* b. You need to set the LEDC channel you want to use, and bind with one of the timers.
*
* 2. You can install a default fade function, then you can use fade APIs.
*
* 3. You can also set a target duty directly without fading.
*
* 4. This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly.
*
* 5. GPIO18/19 are from high speed channel group. GPIO4/5 are from low speed channel group.
*
*/
#define LEDC_HS_TIMER LEDC_TIMER_0
#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE
#define LEDC_HS_CH0_GPIO (18)
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_HS_CH1_GPIO (19)
#define LEDC_HS_CH1_CHANNEL LEDC_CHANNEL_1
#define LEDC_LS_TIMER LEDC_TIMER_1
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
#define LEDC_LS_CH2_GPIO (4)
#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2
#define LEDC_LS_CH3_GPIO (5)
#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3
#define LEDC_TEST_CH_NUM (4)
typedef struct {
int channel;
int io;
int mode;
int timer_idx;
} ledc_info_t;
void app_main()
{
int ch;
ledc_info_t ledc_ch[LEDC_TEST_CH_NUM] = {
{
.channel = LEDC_HS_CH0_CHANNEL,
.io = LEDC_HS_CH0_GPIO,
.mode = LEDC_HS_MODE,
.timer_idx = LEDC_HS_TIMER
},
{
.channel = LEDC_HS_CH1_CHANNEL,
.io = LEDC_HS_CH1_GPIO,
.mode = LEDC_HS_MODE,
.timer_idx = LEDC_HS_TIMER
},
{
.channel = LEDC_LS_CH2_CHANNEL,
.io = LEDC_LS_CH2_GPIO,
.mode = LEDC_LS_MODE,
.timer_idx = LEDC_LS_TIMER
},
{
.channel = LEDC_LS_CH3_CHANNEL,
.io = LEDC_LS_CH3_GPIO,
.mode = LEDC_LS_MODE,
.timer_idx = LEDC_LS_TIMER
}
};
ledc_timer_config_t ledc_timer = {
.bit_num = LEDC_TIMER_13_BIT, //set timer counter bit number
.freq_hz = 5000, //set frequency of pwm
.speed_mode = LEDC_HS_MODE, //timer mode,
.timer_num = LEDC_HS_TIMER //timer index
};
//configure timer0 for high speed channels
ledc_timer_config(&ledc_timer);
//configure timer1 for low speed channels
ledc_timer.speed_mode = LEDC_LS_MODE;
ledc_timer.timer_num = LEDC_LS_TIMER;
ledc_timer_config(&ledc_timer);
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
ledc_channel_config_t ledc_channel = {
//set LEDC channel 0
.channel = ledc_ch[ch].channel,
//set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1)
.duty = 0,
//GPIO number
.gpio_num = ledc_ch[ch].io,
//GPIO INTR TYPE, as an example, we enable fade_end interrupt here.
.intr_type = LEDC_INTR_FADE_END,
//set LEDC mode, from ledc_mode_t
.speed_mode = ledc_ch[ch].mode,
//set LEDC timer source, if different channel use one timer,
//the frequency and bit_num of these channels should be the same
.timer_sel = ledc_ch[ch].timer_idx,
};
//set the configuration
ledc_channel_config(&ledc_channel);
}
//initialize fade service.
ledc_fade_func_install(0);
while (1) {
printf("LEDC fade up\n");
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
ledc_set_fade_with_time(ledc_ch[ch].mode, ledc_ch[ch].channel, 4000, 2000);
ledc_fade_start(ledc_ch[ch].mode, ledc_ch[ch].channel, LEDC_FADE_NO_WAIT);
}
vTaskDelay(3000 / portTICK_PERIOD_MS);
printf("LEDC fade down\n");
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
ledc_set_fade_with_time(ledc_ch[ch].mode, ledc_ch[ch].channel, 0, 2000);
ledc_fade_start(ledc_ch[ch].mode, ledc_ch[ch].channel, LEDC_FADE_NO_WAIT);
}
vTaskDelay(3000 / portTICK_PERIOD_MS);
printf("LEDC set duty without fade\n");
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
ledc_set_duty(ledc_ch[ch].mode, ledc_ch[ch].channel, 2000);
ledc_update_duty(ledc_ch[ch].mode, ledc_ch[ch].channel);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
printf("LEDC set duty without fade\n");
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
ledc_set_duty(ledc_ch[ch].mode, ledc_ch[ch].channel, 0);
ledc_update_duty(ledc_ch[ch].mode, ledc_ch[ch].channel);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}

View file

@ -1,131 +0,0 @@
/* Ledc fade 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 <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "freertos/queue.h"
#include "driver/ledc.h"
#include "esp_attr.h"
#include "esp_err.h"
/*
* About this example
* 1. init LEDC module:
* a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM.
* b. You need to set the LEDC channel you want to use, and bind with one of the timers.
*
* 2. You can install a default fade function, then you can use fade APIs.
*
* 3. You can also set a target duty directly without fading.
*
* 4. This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly.
*
*
*/
#define LEDC_IO_0 (18)
#define LEDC_IO_1 (19)
#define LEDC_IO_2 (4)
#define LEDC_IO_3 (5)
esp_err_t app_main()
{
ledc_timer_config_t ledc_timer = {
//set timer counter bit number
.bit_num = LEDC_TIMER_13_BIT,
//set frequency of pwm
.freq_hz = 5000,
//timer mode,
.speed_mode = LEDC_HIGH_SPEED_MODE,
//timer index
.timer_num = LEDC_TIMER_0
};
ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel = {
//set LEDC channel 0
.channel = LEDC_CHANNEL_0,
//set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1)
.duty = 100,
//GPIO number
.gpio_num = LEDC_IO_0,
//GPIO INTR TYPE, as an example, we enable fade_end interrupt here.
.intr_type = LEDC_INTR_FADE_END,
//set LEDC mode, from ledc_mode_t
.speed_mode = LEDC_HIGH_SPEED_MODE,
//set LEDC timer source, if different channel use one timer,
//the frequency and bit_num of these channels should be the same
.timer_sel = LEDC_TIMER_0
};
//set the configuration
ledc_channel_config(&ledc_channel);
//config ledc channel1
ledc_channel.channel = LEDC_CHANNEL_1;
ledc_channel.gpio_num = LEDC_IO_1;
ledc_channel_config(&ledc_channel);
//config ledc channel2
ledc_channel.channel = LEDC_CHANNEL_2;
ledc_channel.gpio_num = LEDC_IO_2;
ledc_channel_config(&ledc_channel);
//config ledc channel3
ledc_channel.channel = LEDC_CHANNEL_3;
ledc_channel.gpio_num = LEDC_IO_3;
ledc_channel_config(&ledc_channel);
//initialize fade service.
ledc_fade_func_install(0);
while(1) {
printf("LEDC fade up\n");
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000, 2000);
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000, 2000);
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000, 2000);
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000, 2000);
ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT);
ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT);
ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT);
vTaskDelay(3000 / portTICK_PERIOD_MS);
printf("LEDC fade down\n");
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 100, 2000);
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 300, 2000);
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 500, 2000);
ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 700, 2000);
ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT);
ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT);
ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT);
vTaskDelay(3000 / portTICK_PERIOD_MS);
printf("LEDC set duty without fade\n");
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3);
vTaskDelay(2000 / portTICK_PERIOD_MS);
printf("LEDC set duty without fade\n");
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 0);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 0);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 0);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 0);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}

View file

@ -53,7 +53,7 @@ typedef struct {
uint32_t status; /*pulse counter internal status*/
} pcnt_evt_t;
void IRAM_ATTR pcnt_intr_handler(void* arg)
static void IRAM_ATTR pcnt_example_intr_handler(void* arg)
{
uint32_t intr_status = PCNT.int_st.val;
int i;
@ -122,7 +122,7 @@ static void ledc_init(void)
ledc_timer_config(&ledc_timer);
}
static void pcnt_init(void)
static void pcnt_example_init(void)
{
pcnt_config_t pcnt_config = {
/*Set PCNT_INPUT_SIG_IO as pulse input gpio */
@ -175,7 +175,7 @@ static void pcnt_init(void)
/*Reset counter value*/
pcnt_counter_clear(PCNT_TEST_UNIT);
/*Register ISR handler*/
pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);
pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, NULL);
/*Enable interrupt for PCNT unit*/
pcnt_intr_enable(PCNT_TEST_UNIT);
/*Resume counting*/
@ -189,7 +189,7 @@ void app_main()
/*Init PCNT event queue */
pcnt_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));
/*Init PCNT functions*/
pcnt_init();
pcnt_example_init();
int16_t count = 0;
pcnt_evt_t evt;

View file

@ -2,5 +2,7 @@
This example uses the remote control (RMT) peripheral to transmit and receive codes for the NEC infrared remote protocol.
Configuration (pin numbers, etc.) can be modified in top of the main/infrared_nec.c file.
Configuration (pin numbers, etc.) can be modified in top of the main/infrared_nec_main.c file.
By default, this example runs a self test which assumes the TX and RX GPIO pins are connected together. To disable this, comment RMT_RX_SELF_TEST in infrared_nec_main.c.

View file

@ -1,358 +0,0 @@
/* NEC remote infrared RMT 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 <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/rmt.h"
#include "driver/periph_ctrl.h"
#include "soc/rmt_reg.h"
static const char* NEC_TAG = "NEC";
//CHOOSE SELF TEST OR NORMAL TEST
#define RMT_RX_SELF_TEST 1
/******************************************************/
/***** SELF TEST: *****/
/*Connect RMT_TX_GPIO_NUM with RMT_RX_GPIO_NUM */
/*TX task will send NEC data with carrier disabled */
/*RX task will print NEC data it receives. */
/******************************************************/
#if RMT_RX_SELF_TEST
#define RMT_RX_ACTIVE_LEVEL 1 /*!< Data bit is active high for self test mode */
#define RMT_TX_CARRIER_EN 0 /*!< Disable carrier for self test mode */
#else
//Test with infrared LED, we have to enable carrier for transmitter
//When testing via IR led, the receiver waveform is usually active-low.
#define RMT_RX_ACTIVE_LEVEL 0 /*!< If we connect with a IR receiver, the data is active low */
#define RMT_TX_CARRIER_EN 1 /*!< Enable carrier for IR transmitter test with IR led */
#endif
#define RMT_TX_CHANNEL 1 /*!< RMT channel for transmitter */
#define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */
#define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */
#define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */
#define RMT_CLK_DIV 100 /*!< RMT counter clock divider */
#define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */
#define NEC_HEADER_HIGH_US 9000 /*!< NEC protocol header: positive 9ms */
#define NEC_HEADER_LOW_US 4500 /*!< NEC protocol header: negative 4.5ms*/
#define NEC_BIT_ONE_HIGH_US 560 /*!< NEC protocol data bit 1: positive 0.56ms */
#define NEC_BIT_ONE_LOW_US (2250-NEC_BIT_ONE_HIGH_US) /*!< NEC protocol data bit 1: negative 1.69ms */
#define NEC_BIT_ZERO_HIGH_US 560 /*!< NEC protocol data bit 0: positive 0.56ms */
#define NEC_BIT_ZERO_LOW_US (1120-NEC_BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */
#define NEC_BIT_END 560 /*!< NEC protocol end: positive 0.56ms */
#define NEC_BIT_MARGIN 20 /*!< NEC parse margin time */
#define NEC_ITEM_DURATION(d) ((d & 0x7fff)*10/RMT_TICK_10_US) /*!< Parse duration time from memory register value */
#define NEC_DATA_ITEM_NUM 34 /*!< NEC code item number: header + 32bit data + end */
#define RMT_TX_DATA_NUM 100 /*!< NEC tx test data number */
#define rmt_item32_tIMEOUT_US 9500 /*!< RMT receiver timeout value(us) */
/*
* @brief Build register value of waveform for NEC one data bit
*/
inline void nec_fill_item_level(rmt_item32_t* item, int high_us, int low_us)
{
item->level0 = 1;
item->duration0 = (high_us) / 10 * RMT_TICK_10_US;
item->level1 = 0;
item->duration1 = (low_us) / 10 * RMT_TICK_10_US;
}
/*
* @brief Generate NEC header value: active 9ms + negative 4.5ms
*/
static void nec_fill_item_header(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_HEADER_HIGH_US, NEC_HEADER_LOW_US);
}
/*
* @brief Generate NEC data bit 1: positive 0.56ms + negative 1.69ms
*/
static void nec_fill_item_bit_one(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_ONE_HIGH_US, NEC_BIT_ONE_LOW_US);
}
/*
* @brief Generate NEC data bit 0: positive 0.56ms + negative 0.56ms
*/
static void nec_fill_item_bit_zero(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_ZERO_HIGH_US, NEC_BIT_ZERO_LOW_US);
}
/*
* @brief Generate NEC end signal: positive 0.56ms
*/
static void nec_fill_item_end(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_END, 0x7fff);
}
/*
* @brief Check whether duration is around target_us
*/
inline bool nec_check_in_range(int duration_ticks, int target_us, int margin_us)
{
if(( NEC_ITEM_DURATION(duration_ticks) < (target_us + margin_us))
&& ( NEC_ITEM_DURATION(duration_ticks) > (target_us - margin_us))) {
return true;
} else {
return false;
}
}
/*
* @brief Check whether this value represents an NEC header
*/
static bool nec_header_if(rmt_item32_t* item)
{
if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
&& nec_check_in_range(item->duration0, NEC_HEADER_HIGH_US, NEC_BIT_MARGIN)
&& nec_check_in_range(item->duration1, NEC_HEADER_LOW_US, NEC_BIT_MARGIN)) {
return true;
}
return false;
}
/*
* @brief Check whether this value represents an NEC data bit 1
*/
static bool nec_bit_one_if(rmt_item32_t* item)
{
if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
&& nec_check_in_range(item->duration0, NEC_BIT_ONE_HIGH_US, NEC_BIT_MARGIN)
&& nec_check_in_range(item->duration1, NEC_BIT_ONE_LOW_US, NEC_BIT_MARGIN)) {
return true;
}
return false;
}
/*
* @brief Check whether this value represents an NEC data bit 0
*/
static bool nec_bit_zero_if(rmt_item32_t* item)
{
if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
&& nec_check_in_range(item->duration0, NEC_BIT_ZERO_HIGH_US, NEC_BIT_MARGIN)
&& nec_check_in_range(item->duration1, NEC_BIT_ZERO_LOW_US, NEC_BIT_MARGIN)) {
return true;
}
return false;
}
/*
* @brief Parse NEC 32 bit waveform to address and command.
*/
static int nec_parse_items(rmt_item32_t* item, int item_num, uint16_t* addr, uint16_t* data)
{
int w_len = item_num;
if(w_len < NEC_DATA_ITEM_NUM) {
return -1;
}
int i = 0, j = 0;
if(!nec_header_if(item++)) {
return -1;
}
uint16_t addr_t = 0;
for(j = 0; j < 16; j++) {
if(nec_bit_one_if(item)) {
addr_t |= (1 << j);
} else if(nec_bit_zero_if(item)) {
addr_t |= (0 << j);
} else {
return -1;
}
item++;
i++;
}
uint16_t data_t = 0;
for(j = 0; j < 16; j++) {
if(nec_bit_one_if(item)) {
data_t |= (1 << j);
} else if(nec_bit_zero_if(item)) {
data_t |= (0 << j);
} else {
return -1;
}
item++;
i++;
}
*addr = addr_t;
*data = data_t;
return i;
}
/*
* @brief Build NEC 32bit waveform.
*/
static int nec_build_items(int channel, rmt_item32_t* item, int item_num, uint16_t addr, uint16_t cmd_data)
{
int i = 0, j = 0;
if(item_num < NEC_DATA_ITEM_NUM) {
return -1;
}
nec_fill_item_header(item++);
i++;
for(j = 0; j < 16; j++) {
if(addr & 0x1) {
nec_fill_item_bit_one(item);
} else {
nec_fill_item_bit_zero(item);
}
item++;
i++;
addr >>= 1;
}
for(j = 0; j < 16; j++) {
if(cmd_data & 0x1) {
nec_fill_item_bit_one(item);
} else {
nec_fill_item_bit_zero(item);
}
item++;
i++;
cmd_data >>= 1;
}
nec_fill_item_end(item);
i++;
return i;
}
/*
* @brief RMT transmitter initialization
*/
static void rmt_tx_init()
{
rmt_config_t rmt_tx;
rmt_tx.channel = RMT_TX_CHANNEL;
rmt_tx.gpio_num = RMT_TX_GPIO_NUM;
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = RMT_CLK_DIV;
rmt_tx.tx_config.loop_en = false;
rmt_tx.tx_config.carrier_duty_percent = 50;
rmt_tx.tx_config.carrier_freq_hz = 38000;
rmt_tx.tx_config.carrier_level = 1;
rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;
rmt_tx.tx_config.idle_level = 0;
rmt_tx.tx_config.idle_output_en = true;
rmt_tx.rmt_mode = 0;
rmt_config(&rmt_tx);
rmt_driver_install(rmt_tx.channel, 0, 0);
}
/*
* @brief RMT receiver initialization
*/
void rmt_rx_init()
{
rmt_config_t rmt_rx;
rmt_rx.channel = RMT_RX_CHANNEL;
rmt_rx.gpio_num = RMT_RX_GPIO_NUM;
rmt_rx.clk_div = RMT_CLK_DIV;
rmt_rx.mem_block_num = 1;
rmt_rx.rmt_mode = RMT_MODE_RX;
rmt_rx.rx_config.filter_en = true;
rmt_rx.rx_config.filter_ticks_thresh = 100;
rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US);
rmt_config(&rmt_rx);
rmt_driver_install(rmt_rx.channel, 1000, 0);
}
/**
* @brief RMT receiver demo, this task will print each received NEC data.
*
*/
void rmt_nec_rx_task()
{
int channel = RMT_RX_CHANNEL;
rmt_rx_init();
RingbufHandle_t rb = NULL;
//get RMT RX ringbuffer
rmt_get_ringbuf_handler(channel, &rb);
rmt_rx_start(channel, 1);
while(rb) {
size_t rx_size = 0;
//try to receive data from ringbuffer.
//RMT driver will push all the data it receives to its ringbuffer.
//We just need to parse the value and return the spaces of ringbuffer.
rmt_item32_t* item = (rmt_item32_t*) xRingbufferReceive(rb, &rx_size, 1000);
if(item) {
uint16_t rmt_addr;
uint16_t rmt_cmd;
int offset = 0;
while(1) {
//parse data value from ringbuffer.
int res = nec_parse_items(item + offset, rx_size / 4 - offset, &rmt_addr, &rmt_cmd);
if(res > 0) {
offset += res + 1;
ESP_LOGI(NEC_TAG, "RMT RCV --- addr: 0x%04x cmd: 0x%04x", rmt_addr, rmt_cmd);
} else {
break;
}
}
//after parsing the data, return spaces to ringbuffer.
vRingbufferReturnItem(rb, (void*) item);
} else {
break;
}
}
vTaskDelete(NULL);
}
/**
* @brief RMT transmitter demo, this task will periodically send NEC data. (100 * 32 bits each time.)
*
*/
void rmt_nec_tx_task()
{
vTaskDelay(10);
rmt_tx_init();
esp_log_level_set(NEC_TAG, ESP_LOG_INFO);
int channel = RMT_TX_CHANNEL;
uint16_t cmd = 0x0;
uint16_t addr = 0x11;
int nec_tx_num = RMT_TX_DATA_NUM;
for(;;) {
ESP_LOGI(NEC_TAG, "RMT TX DATA");
size_t size = (sizeof(rmt_item32_t) * NEC_DATA_ITEM_NUM * nec_tx_num);
//each item represent a cycle of waveform.
rmt_item32_t* item = (rmt_item32_t*) malloc(size);
int item_num = NEC_DATA_ITEM_NUM * nec_tx_num;
memset((void*) item, 0, size);
int i, offset = 0;
while(1) {
//To build a series of waveforms.
i = nec_build_items(channel, item + offset, item_num - offset, ((~addr) << 8) | addr, cmd);
if(i < 0) {
break;
}
cmd++;
addr++;
offset += i;
}
//To send data according to the waveform items.
rmt_write_items(channel, item, item_num, true);
//Wait until sending is done.
rmt_wait_tx_done(channel);
//before we free the data, make sure sending is already done.
free(item);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}

View file

@ -7,17 +7,358 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/rmt.h"
#include "driver/periph_ctrl.h"
extern void rmt_nec_tx_task();
extern void rmt_nec_rx_task();
#include "soc/rmt_reg.h"
static const char* NEC_TAG = "NEC";
//CHOOSE SELF TEST OR NORMAL TEST
#define RMT_RX_SELF_TEST 1
/******************************************************/
/***** SELF TEST: *****/
/*Connect RMT_TX_GPIO_NUM with RMT_RX_GPIO_NUM */
/*TX task will send NEC data with carrier disabled */
/*RX task will print NEC data it receives. */
/******************************************************/
#if RMT_RX_SELF_TEST
#define RMT_RX_ACTIVE_LEVEL 1 /*!< Data bit is active high for self test mode */
#define RMT_TX_CARRIER_EN 0 /*!< Disable carrier for self test mode */
#else
//Test with infrared LED, we have to enable carrier for transmitter
//When testing via IR led, the receiver waveform is usually active-low.
#define RMT_RX_ACTIVE_LEVEL 0 /*!< If we connect with a IR receiver, the data is active low */
#define RMT_TX_CARRIER_EN 1 /*!< Enable carrier for IR transmitter test with IR led */
#endif
#define RMT_TX_CHANNEL 1 /*!< RMT channel for transmitter */
#define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */
#define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */
#define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */
#define RMT_CLK_DIV 100 /*!< RMT counter clock divider */
#define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */
#define NEC_HEADER_HIGH_US 9000 /*!< NEC protocol header: positive 9ms */
#define NEC_HEADER_LOW_US 4500 /*!< NEC protocol header: negative 4.5ms*/
#define NEC_BIT_ONE_HIGH_US 560 /*!< NEC protocol data bit 1: positive 0.56ms */
#define NEC_BIT_ONE_LOW_US (2250-NEC_BIT_ONE_HIGH_US) /*!< NEC protocol data bit 1: negative 1.69ms */
#define NEC_BIT_ZERO_HIGH_US 560 /*!< NEC protocol data bit 0: positive 0.56ms */
#define NEC_BIT_ZERO_LOW_US (1120-NEC_BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */
#define NEC_BIT_END 560 /*!< NEC protocol end: positive 0.56ms */
#define NEC_BIT_MARGIN 20 /*!< NEC parse margin time */
#define NEC_ITEM_DURATION(d) ((d & 0x7fff)*10/RMT_TICK_10_US) /*!< Parse duration time from memory register value */
#define NEC_DATA_ITEM_NUM 34 /*!< NEC code item number: header + 32bit data + end */
#define RMT_TX_DATA_NUM 100 /*!< NEC tx test data number */
#define rmt_item32_tIMEOUT_US 9500 /*!< RMT receiver timeout value(us) */
/*
* @brief Build register value of waveform for NEC one data bit
*/
static inline void nec_fill_item_level(rmt_item32_t* item, int high_us, int low_us)
{
item->level0 = 1;
item->duration0 = (high_us) / 10 * RMT_TICK_10_US;
item->level1 = 0;
item->duration1 = (low_us) / 10 * RMT_TICK_10_US;
}
/*
* @brief Generate NEC header value: active 9ms + negative 4.5ms
*/
static void nec_fill_item_header(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_HEADER_HIGH_US, NEC_HEADER_LOW_US);
}
/*
* @brief Generate NEC data bit 1: positive 0.56ms + negative 1.69ms
*/
static void nec_fill_item_bit_one(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_ONE_HIGH_US, NEC_BIT_ONE_LOW_US);
}
/*
* @brief Generate NEC data bit 0: positive 0.56ms + negative 0.56ms
*/
static void nec_fill_item_bit_zero(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_ZERO_HIGH_US, NEC_BIT_ZERO_LOW_US);
}
/*
* @brief Generate NEC end signal: positive 0.56ms
*/
static void nec_fill_item_end(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_END, 0x7fff);
}
/*
* @brief Check whether duration is around target_us
*/
inline bool nec_check_in_range(int duration_ticks, int target_us, int margin_us)
{
if(( NEC_ITEM_DURATION(duration_ticks) < (target_us + margin_us))
&& ( NEC_ITEM_DURATION(duration_ticks) > (target_us - margin_us))) {
return true;
} else {
return false;
}
}
/*
* @brief Check whether this value represents an NEC header
*/
static bool nec_header_if(rmt_item32_t* item)
{
if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
&& nec_check_in_range(item->duration0, NEC_HEADER_HIGH_US, NEC_BIT_MARGIN)
&& nec_check_in_range(item->duration1, NEC_HEADER_LOW_US, NEC_BIT_MARGIN)) {
return true;
}
return false;
}
/*
* @brief Check whether this value represents an NEC data bit 1
*/
static bool nec_bit_one_if(rmt_item32_t* item)
{
if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
&& nec_check_in_range(item->duration0, NEC_BIT_ONE_HIGH_US, NEC_BIT_MARGIN)
&& nec_check_in_range(item->duration1, NEC_BIT_ONE_LOW_US, NEC_BIT_MARGIN)) {
return true;
}
return false;
}
/*
* @brief Check whether this value represents an NEC data bit 0
*/
static bool nec_bit_zero_if(rmt_item32_t* item)
{
if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
&& nec_check_in_range(item->duration0, NEC_BIT_ZERO_HIGH_US, NEC_BIT_MARGIN)
&& nec_check_in_range(item->duration1, NEC_BIT_ZERO_LOW_US, NEC_BIT_MARGIN)) {
return true;
}
return false;
}
/*
* @brief Parse NEC 32 bit waveform to address and command.
*/
static int nec_parse_items(rmt_item32_t* item, int item_num, uint16_t* addr, uint16_t* data)
{
int w_len = item_num;
if(w_len < NEC_DATA_ITEM_NUM) {
return -1;
}
int i = 0, j = 0;
if(!nec_header_if(item++)) {
return -1;
}
uint16_t addr_t = 0;
for(j = 0; j < 16; j++) {
if(nec_bit_one_if(item)) {
addr_t |= (1 << j);
} else if(nec_bit_zero_if(item)) {
addr_t |= (0 << j);
} else {
return -1;
}
item++;
i++;
}
uint16_t data_t = 0;
for(j = 0; j < 16; j++) {
if(nec_bit_one_if(item)) {
data_t |= (1 << j);
} else if(nec_bit_zero_if(item)) {
data_t |= (0 << j);
} else {
return -1;
}
item++;
i++;
}
*addr = addr_t;
*data = data_t;
return i;
}
/*
* @brief Build NEC 32bit waveform.
*/
static int nec_build_items(int channel, rmt_item32_t* item, int item_num, uint16_t addr, uint16_t cmd_data)
{
int i = 0, j = 0;
if(item_num < NEC_DATA_ITEM_NUM) {
return -1;
}
nec_fill_item_header(item++);
i++;
for(j = 0; j < 16; j++) {
if(addr & 0x1) {
nec_fill_item_bit_one(item);
} else {
nec_fill_item_bit_zero(item);
}
item++;
i++;
addr >>= 1;
}
for(j = 0; j < 16; j++) {
if(cmd_data & 0x1) {
nec_fill_item_bit_one(item);
} else {
nec_fill_item_bit_zero(item);
}
item++;
i++;
cmd_data >>= 1;
}
nec_fill_item_end(item);
i++;
return i;
}
/*
* @brief RMT transmitter initialization
*/
static void nec_tx_init()
{
rmt_config_t rmt_tx;
rmt_tx.channel = RMT_TX_CHANNEL;
rmt_tx.gpio_num = RMT_TX_GPIO_NUM;
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = RMT_CLK_DIV;
rmt_tx.tx_config.loop_en = false;
rmt_tx.tx_config.carrier_duty_percent = 50;
rmt_tx.tx_config.carrier_freq_hz = 38000;
rmt_tx.tx_config.carrier_level = 1;
rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;
rmt_tx.tx_config.idle_level = 0;
rmt_tx.tx_config.idle_output_en = true;
rmt_tx.rmt_mode = 0;
rmt_config(&rmt_tx);
rmt_driver_install(rmt_tx.channel, 0, 0);
}
/*
* @brief RMT receiver initialization
*/
static void nec_rx_init()
{
rmt_config_t rmt_rx;
rmt_rx.channel = RMT_RX_CHANNEL;
rmt_rx.gpio_num = RMT_RX_GPIO_NUM;
rmt_rx.clk_div = RMT_CLK_DIV;
rmt_rx.mem_block_num = 1;
rmt_rx.rmt_mode = RMT_MODE_RX;
rmt_rx.rx_config.filter_en = true;
rmt_rx.rx_config.filter_ticks_thresh = 100;
rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US);
rmt_config(&rmt_rx);
rmt_driver_install(rmt_rx.channel, 1000, 0);
}
/**
* @brief RMT receiver demo, this task will print each received NEC data.
*
*/
static void rmt_example_nec_rx_task()
{
int channel = RMT_RX_CHANNEL;
nec_rx_init();
RingbufHandle_t rb = NULL;
//get RMT RX ringbuffer
rmt_get_ringbuf_handler(channel, &rb);
rmt_rx_start(channel, 1);
while(rb) {
size_t rx_size = 0;
//try to receive data from ringbuffer.
//RMT driver will push all the data it receives to its ringbuffer.
//We just need to parse the value and return the spaces of ringbuffer.
rmt_item32_t* item = (rmt_item32_t*) xRingbufferReceive(rb, &rx_size, 1000);
if(item) {
uint16_t rmt_addr;
uint16_t rmt_cmd;
int offset = 0;
while(1) {
//parse data value from ringbuffer.
int res = nec_parse_items(item + offset, rx_size / 4 - offset, &rmt_addr, &rmt_cmd);
if(res > 0) {
offset += res + 1;
ESP_LOGI(NEC_TAG, "RMT RCV --- addr: 0x%04x cmd: 0x%04x", rmt_addr, rmt_cmd);
} else {
break;
}
}
//after parsing the data, return spaces to ringbuffer.
vRingbufferReturnItem(rb, (void*) item);
} else {
break;
}
}
vTaskDelete(NULL);
}
/**
* @brief RMT transmitter demo, this task will periodically send NEC data. (100 * 32 bits each time.)
*
*/
static void rmt_example_nec_tx_task()
{
vTaskDelay(10);
nec_tx_init();
esp_log_level_set(NEC_TAG, ESP_LOG_INFO);
int channel = RMT_TX_CHANNEL;
uint16_t cmd = 0x0;
uint16_t addr = 0x11;
int nec_tx_num = RMT_TX_DATA_NUM;
for(;;) {
ESP_LOGI(NEC_TAG, "RMT TX DATA");
size_t size = (sizeof(rmt_item32_t) * NEC_DATA_ITEM_NUM * nec_tx_num);
//each item represent a cycle of waveform.
rmt_item32_t* item = (rmt_item32_t*) malloc(size);
int item_num = NEC_DATA_ITEM_NUM * nec_tx_num;
memset((void*) item, 0, size);
int i, offset = 0;
while(1) {
//To build a series of waveforms.
i = nec_build_items(channel, item + offset, item_num - offset, ((~addr) << 8) | addr, cmd);
if(i < 0) {
break;
}
cmd++;
addr++;
offset += i;
}
//To send data according to the waveform items.
rmt_write_items(channel, item, item_num, true);
//Wait until sending is done.
rmt_wait_tx_done(channel);
//before we free the data, make sure sending is already done.
free(item);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main()
{
xTaskCreate(rmt_nec_rx_task, "rmt_nec_rx_task", 2048, NULL, 10, NULL);
xTaskCreate(rmt_nec_tx_task, "rmt_nec_tx_task", 2048, NULL, 10, NULL);
xTaskCreate(rmt_example_nec_rx_task, "rmt_nec_rx_task", 2048, NULL, 10, NULL);
xTaskCreate(rmt_example_nec_tx_task, "rmt_nec_tx_task", 2048, NULL, 10, NULL);
}

View file

@ -20,7 +20,7 @@
/**
* @brief Sigma-delta initialization.
*/
static void sigmadelta_init(void)
static void sigmadelta_example_init(void)
{
sigmadelta_config_t sigmadelta_cfg = {
/* Sigma-delta channel0*/
@ -40,7 +40,7 @@ static void sigmadelta_init(void)
*/
void app_main()
{
sigmadelta_init();
sigmadelta_example_init();
int8_t duty = 0;
int inc = 1;
while(1) {

View file

@ -147,7 +147,7 @@ void ili_init(spi_device_handle_t spi)
//before sending the line data itself; a total of 6 transactions. (We can't put all of this in just one transaction
//because the D/C line needs to be toggled in the middle.)
//This routine queues these commands up so they get sent as quickly as possible.
void send_line(spi_device_handle_t spi, int ypos, uint16_t *line)
static void send_line(spi_device_handle_t spi, int ypos, uint16_t *line)
{
esp_err_t ret;
int x;
@ -198,7 +198,7 @@ void send_line(spi_device_handle_t spi, int ypos, uint16_t *line)
}
void send_line_finish(spi_device_handle_t spi)
static void send_line_finish(spi_device_handle_t spi)
{
spi_transaction_t *rtrans;
esp_err_t ret;
@ -214,7 +214,7 @@ void send_line_finish(spi_device_handle_t spi)
//Simple routine to generate some patterns and send them to the LCD. Don't expect anything too
//impressive. Because the SPI driver handles transactions in the background, we can calculate the next line
//while the previous one is being sent.
void display_pretty_colors(spi_device_handle_t spi)
static void display_pretty_colors(spi_device_handle_t spi)
{
uint16_t line[2][320];
int x, y, frame=0;

View file

@ -44,7 +44,7 @@ static void inline print_u64(uint64_t val)
printf("0x%08x%08x\n", (uint32_t) (val >> 32), (uint32_t) (val));
}
void timer_evt_task(void *arg)
static void timer_example_evt_task(void *arg)
{
while(1) {
timer_event_t evt;
@ -135,7 +135,7 @@ void IRAM_ATTR timer_group0_isr(void *para)
/*
* @brief timer group0 hardware timer0 init
*/
void tg0_timer0_init()
static void example_tg0_timer0_init()
{
int timer_group = TIMER_GROUP_0;
int timer_idx = TIMER_0;
@ -165,7 +165,7 @@ void tg0_timer0_init()
/*
* @brief timer group0 hardware timer1 init
*/
void tg0_timer1_init()
static void example_tg0_timer1_init()
{
int timer_group = TIMER_GROUP_0;
int timer_idx = TIMER_1;
@ -198,8 +198,8 @@ void tg0_timer1_init()
void app_main()
{
timer_queue = xQueueCreate(10, sizeof(timer_event_t));
tg0_timer0_init();
tg0_timer1_init();
xTaskCreate(timer_evt_task, "timer_evt_task", 2048, NULL, 5, NULL);
example_tg0_timer0_init();
example_tg0_timer1_init();
xTaskCreate(timer_example_evt_task, "timer_evt_task", 2048, NULL, 5, NULL);
}

View file

@ -9,8 +9,6 @@
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "driver/touch_pad.h"
@ -19,7 +17,7 @@
static const char* TAG = "Touch pad";
static bool touch_pad_activated[TOUCH_PAD_MAX];
static bool s_pad_activated[TOUCH_PAD_MAX];
/*
@ -31,7 +29,7 @@ static bool touch_pad_activated[TOUCH_PAD_MAX];
Do not touch any pads when this routine
is running (on application start).
*/
static void touch_pad_set_thresholds(void)
static void tp_example_set_thresholds(void)
{
uint16_t touch_value;
for (int i=0; i<TOUCH_PAD_MAX; i++) {
@ -46,17 +44,17 @@ static void touch_pad_set_thresholds(void)
If so, then print it out on a serial monitor.
Clear related entry in the table afterwards
*/
static void touch_pad_read_task(void *pvParameter)
static void tp_example_read_task(void *pvParameter)
{
static int show_message;
while (1) {
for (int i=0; i<TOUCH_PAD_MAX; i++) {
if (touch_pad_activated[i] == true) {
if (s_pad_activated[i] == true) {
ESP_LOGI(TAG, "T%d activated!", i);
// Wait a while for the pad being released
vTaskDelay(200 / portTICK_PERIOD_MS);
// Clear information on pad activation
touch_pad_activated[i] = false;
s_pad_activated[i] = false;
// Reset the counter triggering a message
// that application is running
show_message = 1;
@ -75,7 +73,7 @@ static void touch_pad_read_task(void *pvParameter)
Handle an interrupt triggered when a pad is touched.
Recognize what pad has been touched and save it in a table.
*/
static void touch_pad_rtc_intr(void * arg)
static void tp_example_rtc_intr(void * arg)
{
uint32_t pad_intr = READ_PERI_REG(SENS_SAR_TOUCH_CTRL2_REG) & 0x3ff;
uint32_t rtc_intr = READ_PERI_REG(RTC_CNTL_INT_ST_REG);
@ -86,7 +84,7 @@ static void touch_pad_rtc_intr(void * arg)
if (rtc_intr & RTC_CNTL_TOUCH_INT_ST) {
for (int i = 0; i < TOUCH_PAD_MAX; i++) {
if ((pad_intr >> i) & 0x01) {
touch_pad_activated[i] = true;
s_pad_activated[i] = true;
}
}
}
@ -95,15 +93,12 @@ static void touch_pad_rtc_intr(void * arg)
void app_main()
{
ESP_LOGI(TAG, "Starting");
nvs_flash_init();
// Initialize touch pad peripheral
ESP_LOGI(TAG, "Initializing touch pad");
touch_pad_init();
touch_pad_set_thresholds();
touch_pad_isr_handler_register(touch_pad_rtc_intr, NULL, 0, NULL);
tp_example_set_thresholds();
touch_pad_isr_handler_register(tp_example_rtc_intr, NULL, 0, NULL);
// Start a task to show what pads have been touched
xTaskCreate(&touch_pad_read_task, "touch_pad_read_task", 2048, NULL, 5, NULL);
xTaskCreate(&tp_example_read_task, "touch_pad_read_task", 2048, NULL, 5, NULL);
}

View file

@ -9,8 +9,6 @@
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/touch_pad.h"
@ -19,7 +17,7 @@
Read values sensed at all available touch pads.
Print out values in a loop on a serial monitor.
*/
void touch_pad_read_task(void *pvParameter)
static void tp_example_read_task(void *pvParameter)
{
while (1) {
uint16_t touch_value;
@ -34,12 +32,10 @@ void touch_pad_read_task(void *pvParameter)
void app_main()
{
nvs_flash_init();
// Initialize touch pad peripheral
touch_pad_init();
// Start task to read values sensed by pads
xTaskCreate(&touch_pad_read_task, "touch_pad_read_task", 2048, NULL, 5, NULL);
xTaskCreate(&tp_example_read_task, "touch_pad_read_task", 2048, NULL, 5, NULL);
}

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := uart_echo
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View file

@ -0,0 +1,73 @@
/* Uart Events 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/uart.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "soc/uart_struct.h"
/**
* This is a example exaple which echos any data it receives on UART1 back to the sender, with hardware flow control
* turned on. It does not use UART driver event queue.
*
* - port: UART1
* - rx buffer: on
* - tx buffer: off
* - flow control: on
* - event queue: off
* - pin assignment: txd(io4), rxd(io5), rts(18), cts(19)
*/
#define ECHO_TEST_TXD (4)
#define ECHO_TEST_RXD (5)
#define ECHO_TEST_RTS (18)
#define ECHO_TEST_CTS (19)
#define BUF_SIZE (1024)
//an example of echo test with hardware flow control on UART1
static void echo_task()
{
const int uart_num = UART_NUM_1;
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
.rx_flow_ctrl_thresh = 122,
};
//Configure UART1 parameters
uart_param_config(uart_num, &uart_config);
//Set UART1 pins(TX: IO4, RX: I05, RTS: IO18, CTS: IO19)
uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);
//Install UART driver (we don't need an event queue here)
//In this example we don't even use a buffer for sending data.
uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0);
uint8_t* data = (uint8_t*) malloc(BUF_SIZE);
while(1) {
//Read data from UART
int len = uart_read_bytes(uart_num, data, BUF_SIZE, 20 / portTICK_RATE_MS);
//Write data back to UART
uart_write_bytes(uart_num, (const char*) data, len);
}
}
void app_main()
{
//A uart read/write example without event queue;
xTaskCreate(echo_task, "uart_echo_task", 1024, NULL, 10, NULL);
}

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := uart_events
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

Some files were not shown because too many files have changed in this diff Show more