Merge branch 'bugfix/can_driver_multiple_bugs' into 'master'

CAN: Add pm_lock, fix gpio bug, update docs

See merge request idf/esp-idf!3138
This commit is contained in:
Ivan Grokhotkov 2018-09-04 20:34:06 +08:00
commit 97b2281c49
5 changed files with 117 additions and 54 deletions

View file

@ -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;

View file

@ -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}

View file

@ -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 ----------------------------------

View file

@ -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

View file

@ -120,7 +120,7 @@ void app_main()
xTaskCreatePinnedToCore(can_transmit_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); xTaskCreatePinnedToCore(can_transmit_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver //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"); ESP_LOGI(EXAMPLE_TAG, "Driver installed");
//Start control task //Start control task