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:
Darian Leung 2018-08-28 21:13:20 +08:00
parent ba17648aea
commit dc3fa14a26
5 changed files with 117 additions and 54 deletions

View file

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

View file

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

View file

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

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

View file

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