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_log.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_pm.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/can_struct.h"
|
||||
#include "driver/gpio.h"
|
||||
|
@ -128,6 +129,10 @@ typedef struct {
|
|||
SemaphoreHandle_t alert_semphr;
|
||||
uint32_t alerts_enabled;
|
||||
uint32_t alerts_triggered;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
//Power Management
|
||||
esp_pm_lock_handle_t pm_lock;
|
||||
#endif
|
||||
} can_obj_t;
|
||||
|
||||
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_matrix_in(rx, CAN_RX_IDX, false);
|
||||
gpio_pad_select_gpio(rx);
|
||||
gpio_set_direction(rx, GPIO_MODE_INPUT);
|
||||
|
||||
//Configure output clock pin (Optional)
|
||||
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)
|
||||
{
|
||||
//Check arguments and state
|
||||
CAN_CHECK(p_can_obj == NULL, ESP_ERR_INVALID_STATE); //Check is driver is already installed
|
||||
//Check arguments
|
||||
CAN_CHECK(g_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(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->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
|
||||
//Initialize CAN object
|
||||
p_can_obj = calloc(1, sizeof(can_obj_t));
|
||||
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;
|
||||
p_can_obj->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_frame_t));
|
||||
p_can_obj->alert_semphr = xSemaphoreCreateBinary();
|
||||
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) {
|
||||
esp_err_t ret;
|
||||
can_obj_t *p_can_obj_dummy;
|
||||
|
||||
//Create a CAN object
|
||||
p_can_obj_dummy = calloc(1, sizeof(can_obj_t));
|
||||
CAN_CHECK(p_can_obj_dummy != NULL, ESP_ERR_NO_MEM);
|
||||
|
||||
//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;
|
||||
goto err;
|
||||
}
|
||||
p_can_obj->control_flags = CTRL_FLAG_STOPPED;
|
||||
p_can_obj->control_flags |= (g_config->mode == CAN_MODE_NO_ACK) ? CTRL_FLAG_SELF_TEST : 0;
|
||||
p_can_obj->control_flags |= (g_config->mode == CAN_MODE_LISTEN_ONLY) ? CTRL_FLAG_LISTEN_ONLY : 0;
|
||||
p_can_obj->tx_msg_count = 0;
|
||||
p_can_obj->rx_msg_count = 0;
|
||||
p_can_obj->tx_failed_count = 0;
|
||||
p_can_obj->rx_missed_count = 0;
|
||||
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;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "can", &(p_can_obj_dummy->pm_lock));
|
||||
if (pm_err != ESP_OK ) {
|
||||
ret = pm_err;
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
//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();
|
||||
//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
|
||||
configASSERT(can_enter_reset_mode() == ESP_OK); //Must enter reset mode to write to config registers
|
||||
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);
|
||||
(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));
|
||||
CAN_EXIT_CRITICAL();
|
||||
//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
|
||||
return ESP_OK;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
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:
|
||||
//Cleanup and return error
|
||||
if (p_can_obj != NULL) {
|
||||
if (p_can_obj->tx_queue != NULL) {
|
||||
vQueueDelete(p_can_obj->tx_queue);
|
||||
p_can_obj->tx_queue = NULL;
|
||||
//Cleanup CAN object and return error
|
||||
if (p_can_obj_dummy != NULL) {
|
||||
if (p_can_obj_dummy->tx_queue != NULL) {
|
||||
vQueueDelete(p_can_obj_dummy->tx_queue);
|
||||
p_can_obj_dummy->tx_queue = NULL;
|
||||
}
|
||||
if (p_can_obj->rx_queue != NULL) {
|
||||
vQueueDelete(p_can_obj->rx_queue);
|
||||
p_can_obj->rx_queue = NULL;
|
||||
if (p_can_obj_dummy->rx_queue != NULL) {
|
||||
vQueueDelete(p_can_obj_dummy->rx_queue);
|
||||
p_can_obj_dummy->rx_queue = NULL;
|
||||
}
|
||||
if (p_can_obj->alert_semphr != NULL) {
|
||||
vSemaphoreDelete(p_can_obj->alert_semphr);
|
||||
p_can_obj->alert_semphr = NULL;
|
||||
if (p_can_obj_dummy->alert_semphr != NULL) {
|
||||
vSemaphoreDelete(p_can_obj_dummy->alert_semphr);
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t can_driver_uninstall()
|
||||
{
|
||||
//Check state
|
||||
can_obj_t *p_can_obj_dummy;
|
||||
|
||||
CAN_ENTER_CRITICAL();
|
||||
//Check 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);
|
||||
|
||||
//Clear registers
|
||||
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_arbitration_lost_capture();
|
||||
(void) can_get_error_code_capture();
|
||||
|
||||
ESP_ERROR_CHECK(esp_intr_free(p_can_obj->isr_handle)); //Free interrupt
|
||||
periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral
|
||||
//Delete queues, semaphores
|
||||
if (p_can_obj->tx_queue != 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
|
||||
p_can_obj_dummy = p_can_obj; //Use dummy to shorten critical section
|
||||
p_can_obj = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
//Copied to TX Queue
|
||||
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
|
||||
configASSERT(xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0) == pdTRUE);
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
|
|
|
@ -42,6 +42,8 @@ extern "C" {
|
|||
* @brief Initializer macros for timing configuration structure
|
||||
*
|
||||
* 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_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;
|
||||
}
|
||||
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -581,19 +598,19 @@ Application Examples
|
|||
**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
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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 ----------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
||||
- 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`.
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
- 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:
|
||||
|
||||
- I2C
|
||||
|
|
|
@ -120,7 +120,7 @@ void app_main()
|
|||
xTaskCreatePinnedToCore(can_transmit_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
|
||||
|
||||
//Install CAN driver
|
||||
ESP_ERROR_CHECK(can_driver_install(&g_config, & t_config, &f_config));
|
||||
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, &f_config));
|
||||
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
|
||||
|
||||
//Start control task
|
||||
|
|
Loading…
Reference in a new issue