CAN: Add pm_lock, fix gpio bug, update docs
This commit does the following - Add power management lock to CAN driver so that APB frequency does not change whilst CAN driver is in use - Fix incorrect flag in can_transmit() - Refactor can_driver_install() and can_driver_uninstall() so that critical sections are shorter - Fix CAN gpio configuration bug on RX pin. Closes #2302 - Add docs about multiple ID configuration and fix example links
This commit is contained in:
parent
ba17648aea
commit
dc3fa14a26
5 changed files with 117 additions and 54 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include "esp_types.h"
|
#include "esp_types.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
|
#include "esp_pm.h"
|
||||||
#include "soc/dport_reg.h"
|
#include "soc/dport_reg.h"
|
||||||
#include "soc/can_struct.h"
|
#include "soc/can_struct.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
@ -128,6 +129,10 @@ typedef struct {
|
||||||
SemaphoreHandle_t alert_semphr;
|
SemaphoreHandle_t alert_semphr;
|
||||||
uint32_t alerts_enabled;
|
uint32_t alerts_enabled;
|
||||||
uint32_t alerts_triggered;
|
uint32_t alerts_triggered;
|
||||||
|
#ifdef CONFIG_PM_ENABLE
|
||||||
|
//Power Management
|
||||||
|
esp_pm_lock_handle_t pm_lock;
|
||||||
|
#endif
|
||||||
} can_obj_t;
|
} can_obj_t;
|
||||||
|
|
||||||
static can_obj_t *p_can_obj = NULL;
|
static can_obj_t *p_can_obj = NULL;
|
||||||
|
@ -591,6 +596,7 @@ static void can_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout,
|
||||||
gpio_set_pull_mode(rx, GPIO_FLOATING);
|
gpio_set_pull_mode(rx, GPIO_FLOATING);
|
||||||
gpio_matrix_in(rx, CAN_RX_IDX, false);
|
gpio_matrix_in(rx, CAN_RX_IDX, false);
|
||||||
gpio_pad_select_gpio(rx);
|
gpio_pad_select_gpio(rx);
|
||||||
|
gpio_set_direction(rx, GPIO_MODE_INPUT);
|
||||||
|
|
||||||
//Configure output clock pin (Optional)
|
//Configure output clock pin (Optional)
|
||||||
if (clkout >= 0 && clkout < GPIO_NUM_MAX) {
|
if (clkout >= 0 && clkout < GPIO_NUM_MAX) {
|
||||||
|
@ -611,41 +617,61 @@ static void can_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout,
|
||||||
|
|
||||||
esp_err_t can_driver_install(const can_general_config_t *g_config, const can_timing_config_t *t_config, const can_filter_config_t *f_config)
|
esp_err_t can_driver_install(const can_general_config_t *g_config, const can_timing_config_t *t_config, const can_filter_config_t *f_config)
|
||||||
{
|
{
|
||||||
//Check arguments and state
|
//Check arguments
|
||||||
CAN_CHECK(p_can_obj == NULL, ESP_ERR_INVALID_STATE); //Check is driver is already installed
|
|
||||||
CAN_CHECK(g_config != NULL, ESP_ERR_INVALID_ARG);
|
CAN_CHECK(g_config != NULL, ESP_ERR_INVALID_ARG);
|
||||||
CAN_CHECK(t_config != NULL, ESP_ERR_INVALID_ARG);
|
CAN_CHECK(t_config != NULL, ESP_ERR_INVALID_ARG);
|
||||||
CAN_CHECK(f_config != NULL, ESP_ERR_INVALID_ARG);
|
CAN_CHECK(f_config != NULL, ESP_ERR_INVALID_ARG);
|
||||||
CAN_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG);
|
CAN_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG);
|
||||||
CAN_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
|
CAN_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
|
||||||
CAN_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
|
CAN_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
|
||||||
esp_err_t ret;
|
|
||||||
|
|
||||||
//Initialize CAN object
|
esp_err_t ret;
|
||||||
p_can_obj = calloc(1, sizeof(can_obj_t));
|
can_obj_t *p_can_obj_dummy;
|
||||||
CAN_CHECK(p_can_obj != NULL, ESP_ERR_NO_MEM);
|
|
||||||
p_can_obj->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_frame_t)) : NULL;
|
//Create a CAN object
|
||||||
p_can_obj->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_frame_t));
|
p_can_obj_dummy = calloc(1, sizeof(can_obj_t));
|
||||||
p_can_obj->alert_semphr = xSemaphoreCreateBinary();
|
CAN_CHECK(p_can_obj_dummy != NULL, ESP_ERR_NO_MEM);
|
||||||
if ((g_config->tx_queue_len > 0 && p_can_obj->tx_queue == NULL) ||
|
|
||||||
p_can_obj->rx_queue == NULL || p_can_obj->alert_semphr == NULL) {
|
//Initialize queues, semaphores, and power management locks
|
||||||
|
p_can_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_frame_t)) : NULL;
|
||||||
|
p_can_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_frame_t));
|
||||||
|
p_can_obj_dummy->alert_semphr = xSemaphoreCreateBinary();
|
||||||
|
if ((g_config->tx_queue_len > 0 && p_can_obj_dummy->tx_queue == NULL) ||
|
||||||
|
p_can_obj_dummy->rx_queue == NULL || p_can_obj_dummy->alert_semphr == NULL) {
|
||||||
ret = ESP_ERR_NO_MEM;
|
ret = ESP_ERR_NO_MEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
p_can_obj->control_flags = CTRL_FLAG_STOPPED;
|
#ifdef CONFIG_PM_ENABLE
|
||||||
p_can_obj->control_flags |= (g_config->mode == CAN_MODE_NO_ACK) ? CTRL_FLAG_SELF_TEST : 0;
|
esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "can", &(p_can_obj_dummy->pm_lock));
|
||||||
p_can_obj->control_flags |= (g_config->mode == CAN_MODE_LISTEN_ONLY) ? CTRL_FLAG_LISTEN_ONLY : 0;
|
if (pm_err != ESP_OK ) {
|
||||||
p_can_obj->tx_msg_count = 0;
|
ret = pm_err;
|
||||||
p_can_obj->rx_msg_count = 0;
|
goto err;
|
||||||
p_can_obj->tx_failed_count = 0;
|
}
|
||||||
p_can_obj->rx_missed_count = 0;
|
#endif
|
||||||
p_can_obj->arb_lost_count = 0;
|
|
||||||
p_can_obj->bus_error_count = 0;
|
|
||||||
p_can_obj->alerts_enabled = g_config->alerts_enabled;
|
|
||||||
p_can_obj->alerts_triggered = 0;
|
|
||||||
|
|
||||||
|
//Initialize flags and variables
|
||||||
|
p_can_obj_dummy->control_flags = CTRL_FLAG_STOPPED;
|
||||||
|
p_can_obj_dummy->control_flags |= (g_config->mode == CAN_MODE_NO_ACK) ? CTRL_FLAG_SELF_TEST : 0;
|
||||||
|
p_can_obj_dummy->control_flags |= (g_config->mode == CAN_MODE_LISTEN_ONLY) ? CTRL_FLAG_LISTEN_ONLY : 0;
|
||||||
|
p_can_obj_dummy->tx_msg_count = 0;
|
||||||
|
p_can_obj_dummy->rx_msg_count = 0;
|
||||||
|
p_can_obj_dummy->tx_failed_count = 0;
|
||||||
|
p_can_obj_dummy->rx_missed_count = 0;
|
||||||
|
p_can_obj_dummy->arb_lost_count = 0;
|
||||||
|
p_can_obj_dummy->bus_error_count = 0;
|
||||||
|
p_can_obj_dummy->alerts_enabled = g_config->alerts_enabled;
|
||||||
|
p_can_obj_dummy->alerts_triggered = 0;
|
||||||
|
|
||||||
|
//Initialize CAN peripheral registers, and allocate interrupt
|
||||||
CAN_ENTER_CRITICAL();
|
CAN_ENTER_CRITICAL();
|
||||||
//Initialize CAN peripheral
|
if (p_can_obj == NULL) {
|
||||||
|
p_can_obj = p_can_obj_dummy;
|
||||||
|
} else {
|
||||||
|
//Check if driver is already installed
|
||||||
|
CAN_EXIT_CRITICAL();
|
||||||
|
ret = ESP_ERR_INVALID_STATE;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
periph_module_enable(PERIPH_CAN_MODULE); //Enable APB CLK to CAN peripheral
|
periph_module_enable(PERIPH_CAN_MODULE); //Enable APB CLK to CAN peripheral
|
||||||
configASSERT(can_enter_reset_mode() == ESP_OK); //Must enter reset mode to write to config registers
|
configASSERT(can_enter_reset_mode() == ESP_OK); //Must enter reset mode to write to config registers
|
||||||
can_config_pelican(); //Use PeliCAN addresses
|
can_config_pelican(); //Use PeliCAN addresses
|
||||||
|
@ -661,56 +687,72 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim
|
||||||
can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
|
can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
|
||||||
(void) can_get_interrupt_reason(); //Read interrupt reg to clear it before allocating ISR
|
(void) can_get_interrupt_reason(); //Read interrupt reg to clear it before allocating ISR
|
||||||
ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle));
|
ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle));
|
||||||
CAN_EXIT_CRITICAL();
|
|
||||||
//Todo: Allow interrupt to be registered to specified CPU
|
//Todo: Allow interrupt to be registered to specified CPU
|
||||||
|
CAN_EXIT_CRITICAL();
|
||||||
|
|
||||||
//CAN module is still in reset mode, users need to call can_start() afterwards
|
#ifdef CONFIG_PM_ENABLE
|
||||||
return ESP_OK;
|
ESP_ERROR_CHECK(esp_pm_lock_acquire(p_can_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz
|
||||||
|
#endif
|
||||||
|
return ESP_OK; //CAN module is still in reset mode, users need to call can_start() afterwards
|
||||||
|
|
||||||
err:
|
err:
|
||||||
//Cleanup and return error
|
//Cleanup CAN object and return error
|
||||||
if (p_can_obj != NULL) {
|
if (p_can_obj_dummy != NULL) {
|
||||||
if (p_can_obj->tx_queue != NULL) {
|
if (p_can_obj_dummy->tx_queue != NULL) {
|
||||||
vQueueDelete(p_can_obj->tx_queue);
|
vQueueDelete(p_can_obj_dummy->tx_queue);
|
||||||
p_can_obj->tx_queue = NULL;
|
p_can_obj_dummy->tx_queue = NULL;
|
||||||
}
|
}
|
||||||
if (p_can_obj->rx_queue != NULL) {
|
if (p_can_obj_dummy->rx_queue != NULL) {
|
||||||
vQueueDelete(p_can_obj->rx_queue);
|
vQueueDelete(p_can_obj_dummy->rx_queue);
|
||||||
p_can_obj->rx_queue = NULL;
|
p_can_obj_dummy->rx_queue = NULL;
|
||||||
}
|
}
|
||||||
if (p_can_obj->alert_semphr != NULL) {
|
if (p_can_obj_dummy->alert_semphr != NULL) {
|
||||||
vSemaphoreDelete(p_can_obj->alert_semphr);
|
vSemaphoreDelete(p_can_obj_dummy->alert_semphr);
|
||||||
p_can_obj->alert_semphr = NULL;
|
p_can_obj_dummy->alert_semphr = NULL;
|
||||||
}
|
}
|
||||||
free(p_can_obj);
|
#ifdef CONFIG_PM_ENABLE
|
||||||
|
if (p_can_obj_dummy->pm_lock != NULL) {
|
||||||
|
ESP_ERROR_CHECK(esp_pm_lock_delete(p_can_obj_dummy->pm_lock));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
free(p_can_obj_dummy);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t can_driver_uninstall()
|
esp_err_t can_driver_uninstall()
|
||||||
{
|
{
|
||||||
//Check state
|
can_obj_t *p_can_obj_dummy;
|
||||||
|
|
||||||
CAN_ENTER_CRITICAL();
|
CAN_ENTER_CRITICAL();
|
||||||
|
//Check state
|
||||||
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
|
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||||
CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
|
CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
|
||||||
|
|
||||||
//Clear registers
|
|
||||||
configASSERT(can_enter_reset_mode() == ESP_OK); //Enter reset mode to stop any CAN bus activity
|
configASSERT(can_enter_reset_mode() == ESP_OK); //Enter reset mode to stop any CAN bus activity
|
||||||
|
//Clear registers by reading
|
||||||
(void) can_get_interrupt_reason();
|
(void) can_get_interrupt_reason();
|
||||||
(void) can_get_arbitration_lost_capture();
|
(void) can_get_arbitration_lost_capture();
|
||||||
(void) can_get_error_code_capture();
|
(void) can_get_error_code_capture();
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_intr_free(p_can_obj->isr_handle)); //Free interrupt
|
ESP_ERROR_CHECK(esp_intr_free(p_can_obj->isr_handle)); //Free interrupt
|
||||||
periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral
|
periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral
|
||||||
//Delete queues, semaphores
|
p_can_obj_dummy = p_can_obj; //Use dummy to shorten critical section
|
||||||
if (p_can_obj->tx_queue != NULL) {
|
p_can_obj = NULL;
|
||||||
vQueueDelete(p_can_obj->tx_queue);
|
|
||||||
}
|
|
||||||
vQueueDelete(p_can_obj->rx_queue);
|
|
||||||
vSemaphoreDelete(p_can_obj->alert_semphr);
|
|
||||||
free(p_can_obj); //Free can driver object
|
|
||||||
CAN_EXIT_CRITICAL();
|
CAN_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
//Delete queues, semaphores, and power management locks
|
||||||
|
if (p_can_obj_dummy->tx_queue != NULL) {
|
||||||
|
vQueueDelete(p_can_obj_dummy->tx_queue);
|
||||||
|
}
|
||||||
|
vQueueDelete(p_can_obj_dummy->rx_queue);
|
||||||
|
vSemaphoreDelete(p_can_obj_dummy->alert_semphr);
|
||||||
|
#ifdef CONFIG_PM_ENABLE
|
||||||
|
//Release and delete power management lock
|
||||||
|
ESP_ERROR_CHECK(esp_pm_lock_release(p_can_obj_dummy->pm_lock));
|
||||||
|
ESP_ERROR_CHECK(esp_pm_lock_delete(p_can_obj_dummy->pm_lock));
|
||||||
|
#endif
|
||||||
|
free(p_can_obj_dummy); //Free can driver object
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,7 +843,7 @@ esp_err_t can_transmit(const can_message_t *message, TickType_t ticks_to_wait)
|
||||||
} else if (xQueueSend(p_can_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) {
|
} else if (xQueueSend(p_can_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) {
|
||||||
//Copied to TX Queue
|
//Copied to TX Queue
|
||||||
CAN_ENTER_CRITICAL();
|
CAN_ENTER_CRITICAL();
|
||||||
if (p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_STOPPED)) {
|
if (p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)) {
|
||||||
//TX queue was reset (due to stop/bus_off), remove copied frame from queue to prevent transmission
|
//TX queue was reset (due to stop/bus_off), remove copied frame from queue to prevent transmission
|
||||||
configASSERT(xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0) == pdTRUE);
|
configASSERT(xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0) == pdTRUE);
|
||||||
ret = ESP_ERR_INVALID_STATE;
|
ret = ESP_ERR_INVALID_STATE;
|
||||||
|
|
|
@ -42,6 +42,8 @@ extern "C" {
|
||||||
* @brief Initializer macros for timing configuration structure
|
* @brief Initializer macros for timing configuration structure
|
||||||
*
|
*
|
||||||
* The following initializer macros offer commonly found bit rates.
|
* The following initializer macros offer commonly found bit rates.
|
||||||
|
*
|
||||||
|
* @note These timing values are based on the assumption APB clock is at 80MHz
|
||||||
*/
|
*/
|
||||||
#define CAN_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
|
#define CAN_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
|
||||||
#define CAN_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
|
#define CAN_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
|
||||||
|
|
|
@ -574,6 +574,23 @@ use of the :cpp:func:`can_stop` and :cpp:func:`can_driver_uninstall` functions.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Multiple ID Filter Configuration
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The acceptance mask in :cpp:type:`can_filter_config_t` can be configured such that
|
||||||
|
two or more IDs will be accepted for a single filter. For a particular filter to
|
||||||
|
accept multiple IDs, the conflicting bit positions amongst the IDs must be set
|
||||||
|
in the acceptance mask. The acceptance code can be set to any one of the IDs.
|
||||||
|
|
||||||
|
The following example shows how the calculate the acceptance mask given multiple
|
||||||
|
IDs::
|
||||||
|
|
||||||
|
ID1 = 11'b101 1010 0000
|
||||||
|
ID2 = 11'b101 1010 0001
|
||||||
|
ID3 = 11'b101 1010 0100
|
||||||
|
ID4 = 11'b101 1010 1000
|
||||||
|
//Acceptance Mask
|
||||||
|
MASK = 11'b000 0000 1101
|
||||||
|
|
||||||
Application Examples
|
Application Examples
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -581,19 +598,19 @@ Application Examples
|
||||||
**Network Example:** The CAN Network example demonstrates communication between
|
**Network Example:** The CAN Network example demonstrates communication between
|
||||||
two ESP32s using the CAN driver API. One CAN node acts as a network master initiate
|
two ESP32s using the CAN driver API. One CAN node acts as a network master initiate
|
||||||
and ceasing the transfer of a data from another CAN node acting as a network slave.
|
and ceasing the transfer of a data from another CAN node acting as a network slave.
|
||||||
The example can be found via :example:`examples/peripheral/can/can_network`.
|
The example can be found via :example:`peripherals/can/can_network`.
|
||||||
|
|
||||||
**Alert and Recovery Example:** This example demonstrates how to use the CAN driver's
|
**Alert and Recovery Example:** This example demonstrates how to use the CAN driver's
|
||||||
alert and bus recovery API. The example purposely introduces errors on the CAN
|
alert and bus recovery API. The example purposely introduces errors on the CAN
|
||||||
bus to put the CAN controller into the Bus-Off state. An alert is used to detect
|
bus to put the CAN controller into the Bus-Off state. An alert is used to detect
|
||||||
the Bus-Off state and trigger the bus recovery process. The example can be found
|
the Bus-Off state and trigger the bus recovery process. The example can be found
|
||||||
via :example:`examples/peripheral/can/can_alert_and_recovery`.
|
via :example:`peripherals/can/can_alert_and_recovery`.
|
||||||
|
|
||||||
**Self Test Example:** This example uses the No Acknowledge Mode and Self Reception
|
**Self Test Example:** This example uses the No Acknowledge Mode and Self Reception
|
||||||
Request to cause the CAN controller to send and simultaneously receive a series
|
Request to cause the CAN controller to send and simultaneously receive a series
|
||||||
of messages. This example can be used to verify if the connections between the CAN
|
of messages. This example can be used to verify if the connections between the CAN
|
||||||
controller and the external transceiver are working correctly. The example can be
|
controller and the external transceiver are working correctly. The example can be
|
||||||
found via :example:`examples/peripheral/can/can_self_test`.
|
found via :example:`peripherals/can/can_self_test`.
|
||||||
|
|
||||||
|
|
||||||
.. ---------------------------- API Reference ----------------------------------
|
.. ---------------------------- API Reference ----------------------------------
|
||||||
|
|
|
@ -112,7 +112,7 @@ Currently, the following peripheral drivers are aware of DFS and will use ``ESP_
|
||||||
|
|
||||||
The following drivers will hold ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled:
|
The following drivers will hold ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled:
|
||||||
|
|
||||||
- SPI slave — between calls to :cpp:func:`spi_slave_initialize` and cpp:func:`spi_slave_free`.
|
- SPI slave — between calls to :cpp:func:`spi_slave_initialize` and :cpp:func:`spi_slave_free`.
|
||||||
|
|
||||||
- Ethernet — between calls to :cpp:func:`esp_eth_enable` and :cpp:func:`esp_eth_disable`.
|
- Ethernet — between calls to :cpp:func:`esp_eth_enable` and :cpp:func:`esp_eth_disable`.
|
||||||
|
|
||||||
|
@ -120,6 +120,8 @@ The following drivers will hold ``ESP_PM_APB_FREQ_MAX`` lock while the driver is
|
||||||
|
|
||||||
- Bluetooth — between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`.
|
- Bluetooth — between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`.
|
||||||
|
|
||||||
|
- CAN - between calls to :cpp:func:`can_driver_install` and :cpp:func:`can_driver_uninstall`
|
||||||
|
|
||||||
The following peripheral drivers are not aware of DFS yet. Applications need to acquire/release locks when necessary:
|
The following peripheral drivers are not aware of DFS yet. Applications need to acquire/release locks when necessary:
|
||||||
|
|
||||||
- I2C
|
- I2C
|
||||||
|
|
Loading…
Reference in a new issue