]> granicus.if.org Git - esp-idf/commitdiff
CAN: Add pm_lock, fix gpio bug, update docs
authorDarian Leung <darian@espressif.com>
Tue, 28 Aug 2018 13:13:20 +0000 (21:13 +0800)
committerDarian Leung <darian@espressif.com>
Fri, 31 Aug 2018 18:46:05 +0000 (02:46 +0800)
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

components/driver/can.c
components/driver/include/driver/can.h
docs/en/api-reference/peripherals/can.rst
docs/en/api-reference/system/power_management.rst
examples/peripherals/can/can_self_test/main/can_self_test_example_main.c

index 139d8a49a7e5ff98c59fb78e5de9d49138e64471..0c20288f97dea8faba97721c9056562a06d9333d 100644 (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;
index 5ec272ca47c64622655165e39862e97cde13bfac..af7b66e0b57e92c0098e9fe37a6bee4db4e2a6ea 100644 (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}
index ece926d88aa0e46ca215f9cdadf6f69f0da559e6..d6ba1423a7158483891ff20b53f8b374bf1cb196 100644 (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 ----------------------------------
index 618482441cb9f89ef0cecbdc71bcbfcc6d16ea28..116233393e745fb65598a80c9940aa6c67282c82 100644 (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
index 34af559430c66193d2a2d16f1054fb7d173e316f..a00a46ad85c5b273f79130e6011aff284a12aa12 100644 (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