mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
TWAI: FIFO overrun handling and errata workarounds
This commit adds handling for FIFO overruns and adds workarounds for HW erratas on the ESP32. Closes https://github.com/espressif/esp-idf/issues/2519 Closes https://github.com/espressif/esp-idf/issues/4276
This commit is contained in:
parent
90ee294139
commit
a0666b9be8
@ -9,7 +9,7 @@ entries:
|
||||
SEGGER_SYSVIEW_Config_FreeRTOS (noflash)
|
||||
SEGGER_SYSVIEW_FreeRTOS (noflash)
|
||||
|
||||
[mapping:driver]
|
||||
[mapping:app_trace_driver]
|
||||
archive: libdriver.a
|
||||
entries:
|
||||
if SYSVIEW_TS_SOURCE_TIMER_00 = y || SYSVIEW_TS_SOURCE_TIMER_01 = y
|
||||
|
@ -51,7 +51,8 @@ idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ${includes}
|
||||
PRIV_INCLUDE_DIRS "include/driver"
|
||||
PRIV_REQUIRES efuse esp_timer esp_ipc
|
||||
REQUIRES esp_ringbuf freertos soc) #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent
|
||||
REQUIRES esp_ringbuf freertos soc #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent
|
||||
LDFRAGMENTS linker.lf)
|
||||
|
||||
# uses C11 atomic feature
|
||||
set_source_files_properties(spi_master.c PROPERTIES COMPILE_FLAGS -std=gnu11)
|
||||
|
@ -92,6 +92,48 @@ menu "Driver configurations"
|
||||
- Alert logging (i.e., setting of the TWAI_ALERT_AND_LOG flag)
|
||||
will have no effect.
|
||||
|
||||
config TWAI_ERRATA_FIX_BUS_OFF_REC
|
||||
bool "Add SW workaround for REC change during bus-off"
|
||||
depends on IDF_TARGET_ESP32
|
||||
default n
|
||||
help
|
||||
When the bus-off condition is reached, the REC should be reset to 0 and frozen (via LOM) by the
|
||||
driver's ISR. However on the ESP32, there is an edge case where the REC will increase before the
|
||||
driver's ISR can respond in time (e.g., due to the rapid occurrence of bus errors), thus causing the
|
||||
REC to be non-zero after bus-off. A non-zero REC can prevent bus-off recovery as the bus-off recovery
|
||||
condition is that both TEC and REC become 0. Enabling this option will add a workaround in the driver
|
||||
to forcibly reset REC to zero on reaching bus-off.
|
||||
|
||||
config TWAI_ERRATA_FIX_TX_INTR_LOST
|
||||
bool "Add SW workaround for TX interrupt lost errata"
|
||||
depends on IDF_TARGET_ESP32
|
||||
default n
|
||||
help
|
||||
On the ESP32, when a transmit interrupt occurs, and interrupt register is read on the same APB clock
|
||||
cycle, the transmit interrupt could be lost. Enabling this option will add a workaround that checks the
|
||||
transmit buffer status bit to recover any lost transmit interrupt.
|
||||
|
||||
config TWAI_ERRATA_FIX_RX_FRAME_INVALID
|
||||
bool "Add SW workaround for invalid RX frame errata"
|
||||
depends on IDF_TARGET_ESP32
|
||||
default n
|
||||
help
|
||||
On the ESP32, when receiving a data or remote frame, if a bus error occurs in the data or CRC field,
|
||||
the data of the next received frame could be invalid. Enabling this option will add a workaround that
|
||||
will reset the peripheral on detection of this errata condition. Note that if a frame is transmitted on
|
||||
the bus whilst the reset is ongoing, the message will not be receive by the peripheral sent on the bus
|
||||
during the reset, the message will be lost.
|
||||
|
||||
config TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
|
||||
bool "Add SW workaround for RX FIFO corruption errata"
|
||||
depends on IDF_TARGET_ESP32
|
||||
default n
|
||||
help
|
||||
On the ESP32, when the RX FIFO overruns and the RX message counter maxes out at 64 messages, the entire
|
||||
RX FIFO is no longer recoverable. Enabling this option will add a workaround that resets the peripheral
|
||||
on detection of this errata condition. Note that if a frame is being sent on the bus during the reset
|
||||
bus during the reset, the message will be lost.
|
||||
|
||||
endmenu # TWAI Configuration
|
||||
|
||||
menu "UART configuration"
|
||||
|
@ -56,22 +56,25 @@ extern "C" {
|
||||
* @note The TWAI_ALERT_AND_LOG flag is not an actual alert, but will configure
|
||||
* the TWAI driver to log to UART when an enabled alert occurs.
|
||||
*/
|
||||
#define TWAI_ALERT_TX_IDLE 0x0001 /**< Alert(1): No more messages to transmit */
|
||||
#define TWAI_ALERT_TX_SUCCESS 0x0002 /**< Alert(2): The previous transmission was successful */
|
||||
#define TWAI_ALERT_BELOW_ERR_WARN 0x0004 /**< Alert(4): Both error counters have dropped below error warning limit */
|
||||
#define TWAI_ALERT_ERR_ACTIVE 0x0008 /**< Alert(8): TWAI controller has become error active */
|
||||
#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x0010 /**< Alert(16): TWAI controller is undergoing bus recovery */
|
||||
#define TWAI_ALERT_BUS_RECOVERED 0x0020 /**< Alert(32): TWAI controller has successfully completed bus recovery */
|
||||
#define TWAI_ALERT_ARB_LOST 0x0040 /**< Alert(64): The previous transmission lost arbitration */
|
||||
#define TWAI_ALERT_ABOVE_ERR_WARN 0x0080 /**< Alert(128): One of the error counters have exceeded the error warning limit */
|
||||
#define TWAI_ALERT_BUS_ERROR 0x0100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */
|
||||
#define TWAI_ALERT_TX_FAILED 0x0200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */
|
||||
#define TWAI_ALERT_RX_QUEUE_FULL 0x0400 /**< Alert(1024): The RX queue is full causing a frame to be lost */
|
||||
#define TWAI_ALERT_ERR_PASS 0x0800 /**< Alert(2048): TWAI controller has become error passive */
|
||||
#define TWAI_ALERT_BUS_OFF 0x1000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */
|
||||
#define TWAI_ALERT_ALL 0x1FFF /**< Bit mask to enable all alerts during configuration */
|
||||
#define TWAI_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */
|
||||
#define TWAI_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur. Note that logging from the ISR is disabled if CONFIG_TWAI_ISR_IN_IRAM is enabled (see docs). */
|
||||
#define TWAI_ALERT_TX_IDLE 0x00000001 /**< Alert(1): No more messages to transmit */
|
||||
#define TWAI_ALERT_TX_SUCCESS 0x00000002 /**< Alert(2): The previous transmission was successful */
|
||||
#define TWAI_ALERT_BELOW_ERR_WARN 0x00000004 /**< Alert(4): Both error counters have dropped below error warning limit */
|
||||
#define TWAI_ALERT_ERR_ACTIVE 0x00000008 /**< Alert(8): TWAI controller has become error active */
|
||||
#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x00000010 /**< Alert(16): TWAI controller is undergoing bus recovery */
|
||||
#define TWAI_ALERT_BUS_RECOVERED 0x00000020 /**< Alert(32): TWAI controller has successfully completed bus recovery */
|
||||
#define TWAI_ALERT_ARB_LOST 0x00000040 /**< Alert(64): The previous transmission lost arbitration */
|
||||
#define TWAI_ALERT_ABOVE_ERR_WARN 0x00000080 /**< Alert(128): One of the error counters have exceeded the error warning limit */
|
||||
#define TWAI_ALERT_BUS_ERROR 0x00000100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */
|
||||
#define TWAI_ALERT_TX_FAILED 0x00000200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */
|
||||
#define TWAI_ALERT_RX_QUEUE_FULL 0x00000400 /**< Alert(1024): The RX queue is full causing a frame to be lost */
|
||||
#define TWAI_ALERT_ERR_PASS 0x00000800 /**< Alert(2048): TWAI controller has become error passive */
|
||||
#define TWAI_ALERT_BUS_OFF 0x00001000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */
|
||||
#define TWAI_ALERT_RX_FIFO_OVERRUN 0x00002000 /**< Alert(8192): An RX FIFO overrun has occurred */
|
||||
#define TWAI_ALERT_TX_RETRIED 0x00004000 /**< Alert(16384): An message transmission was cancelled and retried due to an errata workaround */
|
||||
#define TWAI_ALERT_PERIPH_RESET 0x00008000 /**< Alert(32768): The TWAI controller was reset */
|
||||
#define TWAI_ALERT_ALL 0x0000FFFF /**< Bit mask to enable all alerts during configuration */
|
||||
#define TWAI_ALERT_NONE 0x00000000 /**< Bit mask to disable all alerts during configuration */
|
||||
#define TWAI_ALERT_AND_LOG 0x00010000 /**< Bit mask to enable alerts to also be logged when they occur. Note that logging from the ISR is disabled if CONFIG_TWAI_ISR_IN_IRAM is enabled (see docs). */
|
||||
|
||||
/** @endcond */
|
||||
|
||||
@ -117,7 +120,8 @@ typedef struct {
|
||||
uint32_t tx_error_counter; /**< Current value of Transmit Error Counter */
|
||||
uint32_t rx_error_counter; /**< Current value of Receive Error Counter */
|
||||
uint32_t tx_failed_count; /**< Number of messages that failed transmissions */
|
||||
uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue */
|
||||
uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue (or errata workaround if enabled) */
|
||||
uint32_t rx_overrun_count; /**< Number of messages that were lost due to a RX FIFO overrun */
|
||||
uint32_t arb_lost_count; /**< Number of instances arbitration was lost */
|
||||
uint32_t bus_error_count; /**< Number of instances a bus error has occurred */
|
||||
} twai_status_info_t;
|
||||
|
9
components/driver/linker.lf
Normal file
9
components/driver/linker.lf
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
[mapping:driver]
|
||||
archive: libdriver.a
|
||||
entries:
|
||||
# TWAI workarounds that require periph_module_reset() won't work if cache is disabled due to the use of switch jump
|
||||
# tables in periph_module_reset(). We prevent any part of periph_module_reset() (either text or RO data) from being
|
||||
# placed in flash.
|
||||
if TWAI_ISR_IN_IRAM = y && (TWAI_ERRATA_FIX_RX_FRAME_INVALID = y || TWAI_ERRATA_FIX_RX_FIFO_CORRUPT = y):
|
||||
periph_ctrl: periph_module_reset (noflash)
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "hal/clk_gate_ll.h"
|
||||
#include "esp_attr.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
|
||||
static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -28,6 +27,7 @@
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "driver/twai.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/twai_periph.h"
|
||||
#include "hal/twai_hal.h"
|
||||
|
||||
@ -68,6 +68,7 @@ typedef struct {
|
||||
twai_state_t state;
|
||||
twai_mode_t mode;
|
||||
uint32_t rx_missed_count;
|
||||
uint32_t rx_overrun_count;
|
||||
uint32_t tx_failed_count;
|
||||
uint32_t arb_lost_count;
|
||||
uint32_t bus_error_count;
|
||||
@ -127,19 +128,49 @@ TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req
|
||||
|
||||
static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req)
|
||||
{
|
||||
#ifdef TWAI_SUPPORTS_RX_STATUS
|
||||
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
|
||||
|
||||
for (int i = 0; i < msg_count; i++) {
|
||||
for (uint32_t i = 0; i < msg_count; i++) {
|
||||
twai_hal_frame_t frame;
|
||||
twai_hal_read_rx_buffer_and_clear(&twai_context, &frame);
|
||||
//Copy frame into RX Queue
|
||||
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
|
||||
p_twai_obj->rx_msg_count++;
|
||||
} else {
|
||||
p_twai_obj->rx_missed_count++;
|
||||
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
|
||||
if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
|
||||
//Valid frame copied from RX buffer
|
||||
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
|
||||
p_twai_obj->rx_msg_count++;
|
||||
} else { //Failed to send to queue
|
||||
p_twai_obj->rx_missed_count++;
|
||||
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
|
||||
}
|
||||
} else { //Failed to read from RX buffer because message is overrun
|
||||
p_twai_obj->rx_overrun_count++;
|
||||
twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
|
||||
}
|
||||
}
|
||||
#else //TWAI_SUPPORTS_RX_STATUS
|
||||
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
|
||||
bool overrun = false;
|
||||
//Clear all valid RX frames
|
||||
for (int i = 0; i < msg_count; i++) {
|
||||
twai_hal_frame_t frame;
|
||||
if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
|
||||
//Valid frame copied from RX buffer
|
||||
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
|
||||
p_twai_obj->rx_msg_count++;
|
||||
} else {
|
||||
p_twai_obj->rx_missed_count++;
|
||||
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
|
||||
}
|
||||
} else {
|
||||
overrun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//All remaining frames are treated as overrun. Clear them all
|
||||
if (overrun) {
|
||||
p_twai_obj->rx_overrun_count += twai_hal_clear_rx_fifo_overrun(&twai_context);
|
||||
twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
|
||||
}
|
||||
#endif //TWAI_SUPPORTS_RX_STATUS
|
||||
}
|
||||
|
||||
static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
|
||||
@ -175,56 +206,65 @@ TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg)
|
||||
{
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
int alert_req = 0;
|
||||
uint32_t event;
|
||||
uint32_t events;
|
||||
TWAI_ENTER_CRITICAL_ISR();
|
||||
if (p_twai_obj == NULL) { //Incase intr occurs whilst driver is being uninstalled
|
||||
if (p_twai_obj == NULL) { //In case intr occurs whilst driver is being uninstalled
|
||||
TWAI_EXIT_CRITICAL_ISR();
|
||||
return;
|
||||
}
|
||||
event = twai_hal_decode_interrupt_events(&twai_context);
|
||||
if (event & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
|
||||
events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt
|
||||
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) {
|
||||
twai_hal_prepare_for_reset(&twai_context);
|
||||
periph_module_reset(PERIPH_TWAI_MODULE);
|
||||
twai_hal_recover_from_reset(&twai_context);
|
||||
p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&twai_context);
|
||||
twai_alert_handler(TWAI_ALERT_PERIPH_RESET, &alert_req);
|
||||
}
|
||||
#endif
|
||||
if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
|
||||
//Note: This event will never occur if there is a periph reset event
|
||||
twai_handle_rx_buffer_frames(&task_woken, &alert_req);
|
||||
}
|
||||
//TX command should be the last command related handler to be called, so that
|
||||
//other command register bits do not overwrite the TX command bit.
|
||||
if (event & TWAI_HAL_EVENT_TX_BUFF_FREE) {
|
||||
if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
|
||||
twai_handle_tx_buffer_frame(&task_woken, &alert_req);
|
||||
}
|
||||
|
||||
//Handle events that only require alerting (i.e. no handler)
|
||||
if (event & TWAI_HAL_EVENT_BUS_OFF) {
|
||||
if (events & TWAI_HAL_EVENT_BUS_OFF) {
|
||||
p_twai_obj->state = TWAI_STATE_BUS_OFF;
|
||||
twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
|
||||
if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
|
||||
p_twai_obj->state = TWAI_STATE_STOPPED;
|
||||
twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_BUS_ERR) {
|
||||
if (events & TWAI_HAL_EVENT_BUS_ERR) {
|
||||
p_twai_obj->bus_error_count++;
|
||||
twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_ARB_LOST) {
|
||||
if (events & TWAI_HAL_EVENT_ARB_LOST) {
|
||||
p_twai_obj->arb_lost_count++;
|
||||
twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
|
||||
if (events & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
|
||||
//Bus-recovery in progress. TEC has dropped below error warning limit
|
||||
twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_ERROR_PASSIVE) {
|
||||
if (events & TWAI_HAL_EVENT_ERROR_PASSIVE) {
|
||||
//Entered error passive
|
||||
twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_ERROR_ACTIVE) {
|
||||
if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) {
|
||||
//Returned to error active
|
||||
twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_ABOVE_EWL) {
|
||||
if (events & TWAI_HAL_EVENT_ABOVE_EWL) {
|
||||
//TEC or REC surpassed error warning limit
|
||||
twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, &alert_req);
|
||||
}
|
||||
if (event & TWAI_HAL_EVENT_BELOW_EWL) {
|
||||
if (events & TWAI_HAL_EVENT_BELOW_EWL) {
|
||||
//TEC and REC are both below error warning
|
||||
twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req);
|
||||
}
|
||||
@ -644,6 +684,7 @@ esp_err_t twai_get_status_info(twai_status_info_t *status_info)
|
||||
status_info->msgs_to_rx = p_twai_obj->rx_msg_count;
|
||||
status_info->tx_failed_count = p_twai_obj->tx_failed_count;
|
||||
status_info->rx_missed_count = p_twai_obj->rx_missed_count;
|
||||
status_info->rx_overrun_count = p_twai_obj->rx_overrun_count;
|
||||
status_info->arb_lost_count = p_twai_obj->arb_lost_count;
|
||||
status_info->bus_error_count = p_twai_obj->bus_error_count;
|
||||
status_info->state = p_twai_obj->state;
|
||||
|
@ -26,13 +26,14 @@ extern "C" {
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "hal/twai_types.h"
|
||||
#include "hal/twai_ll.h"
|
||||
|
||||
/* ------------------------- Defines and Typedefs --------------------------- */
|
||||
|
||||
#define TWAI_HAL_SET_FLAG(var, flag) ((var) |= (flag))
|
||||
#define TWAI_HAL_RESET_FLAG(var, flag) ((var) &= ~(flag))
|
||||
#define TWAI_HAL_SET_BITS(var, flag) ((var) |= (flag))
|
||||
#define TWAI_HAL_CLEAR_BITS(var, flag) ((var) &= ~(flag))
|
||||
|
||||
//HAL state flags
|
||||
#define TWAI_HAL_STATE_FLAG_RUNNING (1 << 0) //Controller is active (not in reset mode)
|
||||
@ -41,8 +42,11 @@ extern "C" {
|
||||
#define TWAI_HAL_STATE_FLAG_ERR_PASSIVE (1 << 3) //TEC or REC is >= 128
|
||||
#define TWAI_HAL_STATE_FLAG_BUS_OFF (1 << 4) //Bus-off due to TEC >= 256
|
||||
#define TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED (1 << 5) //Transmit buffer is occupied
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
#define TWAI_HAL_STATE_FLAG_TX_NEED_RETRY (1 << 7) //TX needs to be restarted due to errata workarounds
|
||||
#endif
|
||||
|
||||
//Error active interrupt related
|
||||
//Interrupt Events
|
||||
#define TWAI_HAL_EVENT_BUS_OFF (1 << 0)
|
||||
#define TWAI_HAL_EVENT_BUS_RECOV_CPLT (1 << 1)
|
||||
#define TWAI_HAL_EVENT_BUS_RECOV_PROGRESS (1 << 2)
|
||||
@ -54,14 +58,22 @@ extern "C" {
|
||||
#define TWAI_HAL_EVENT_ARB_LOST (1 << 8)
|
||||
#define TWAI_HAL_EVENT_RX_BUFF_FRAME (1 << 9)
|
||||
#define TWAI_HAL_EVENT_TX_BUFF_FREE (1 << 10)
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
#define TWAI_HAL_EVENT_NEED_PERIPH_RESET (1 << 11)
|
||||
#endif
|
||||
|
||||
typedef twai_ll_frame_buffer_t twai_hal_frame_t;
|
||||
|
||||
typedef struct {
|
||||
twai_dev_t *dev;
|
||||
uint32_t state_flags;
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
twai_hal_frame_t tx_frame_save;
|
||||
twai_ll_reg_save_t reg_save;
|
||||
uint8_t rx_msg_cnt_save;
|
||||
#endif
|
||||
} twai_hal_context_t;
|
||||
|
||||
typedef twai_ll_frame_buffer_t twai_hal_frame_t;
|
||||
|
||||
/* ---------------------------- Init and Config ----------------------------- */
|
||||
|
||||
/**
|
||||
@ -125,7 +137,7 @@ void twai_hal_stop(twai_hal_context_t *hal_ctx);
|
||||
*/
|
||||
static inline void twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING);
|
||||
TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING);
|
||||
twai_ll_exit_reset_mode(hal_ctx->dev);
|
||||
}
|
||||
|
||||
@ -193,22 +205,23 @@ static inline bool twai_hal_check_state_flags(twai_hal_context_t *hal_ctx, uint3
|
||||
/* ----------------------------- Event Handling ----------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Decode current events that triggered an interrupt
|
||||
* @brief Get a bit mask of the events that triggered that triggered an interrupt
|
||||
*
|
||||
* This function should be the called at the beginning of an ISR. This
|
||||
* function will do the following:
|
||||
* - Read and clear interrupts
|
||||
* - Decode current events that triggered an interrupt
|
||||
* This function should be called at the beginning of an interrupt. This function will do the following:
|
||||
* - Read and clear interrupt register
|
||||
* - Calculate what events have triggered the interrupt
|
||||
* - Respond to low latency interrupt events
|
||||
* - Bus off: Change to LOM to free TEC/REC
|
||||
* - Bus off: Change to LOM to freeze TEC/REC. Errata 1 Fix
|
||||
* - Recovery complete: Enter reset mode
|
||||
* - Clear ECC and ALC
|
||||
* - Update state flags based on events that have occurred.
|
||||
* - Clear ECC and ALC so that their interrupts are re-armed
|
||||
* - Update HAL state flags based on interrupts that have occurred.
|
||||
* - For the ESP32, check for errata conditions. If a HW reset is required, this function
|
||||
* will set the TWAI_HAL_EVENT_NEED_PERIPH_RESET event.
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
* @return Bit mask of events that have occurred
|
||||
*/
|
||||
uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx);
|
||||
uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx);
|
||||
|
||||
/* ------------------------------- TX and RX -------------------------------- */
|
||||
|
||||
@ -260,24 +273,105 @@ void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_f
|
||||
* @brief Copy a frame from the RX buffer and release
|
||||
*
|
||||
* This function copies a frame from the RX buffer, then release the buffer (so
|
||||
* that it loads the next frame in the RX FIFO).
|
||||
* that it loads the next frame in the RX FIFO). False is returned under the
|
||||
* following conditions:
|
||||
* - On the ESP32S2, false is returned if the RX buffer points to an overrun frame
|
||||
* - On the ESP32, false is returned if the RX buffer points to the first overrun
|
||||
* frame in the RX FIFO
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
* @param rx_frame Pointer to structure to store RX frame
|
||||
* @return True if a valid frame was copied and released. False if overrun.
|
||||
*/
|
||||
static inline void twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx, twai_hal_frame_t *rx_frame)
|
||||
static inline bool twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx, twai_hal_frame_t *rx_frame)
|
||||
{
|
||||
#ifdef TWAI_SUPPORTS_RX_STATUS
|
||||
if (twai_ll_get_status(hal_ctx->dev) & TWAI_LL_STATUS_MS) {
|
||||
//Release the buffer for this particular overrun frame
|
||||
twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (twai_ll_get_status(hal_ctx->dev) & TWAI_LL_STATUS_DOS) {
|
||||
//No need to release RX buffer as we'll be releaseing all RX frames in continuously later
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
twai_ll_get_rx_buffer(hal_ctx->dev, rx_frame);
|
||||
twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev);
|
||||
/*
|
||||
* Todo: Support overrun handling by:
|
||||
* - Check overrun status bit. Return false if overrun
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef TWAI_SUPPORTS_RX_STATUS
|
||||
/**
|
||||
* @brief Clear the RX FIFO of overrun frames
|
||||
*
|
||||
* This function will clear the RX FIFO of overrun frames. The RX message count
|
||||
* will return to 0 after calling this function.
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
* @return Number of overrun messages cleared from RX FIFO
|
||||
*/
|
||||
static inline uint32_t twai_hal_clear_rx_fifo_overrun(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
uint32_t msg_cnt = 0;
|
||||
//Note: Need to keep polling th rx message counter incase another message arrives whilst clearing
|
||||
while (twai_ll_get_rx_msg_count(hal_ctx->dev) > 0) {
|
||||
twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev);
|
||||
msg_cnt++;
|
||||
}
|
||||
//Set a clear data overrun command to clear the data overrun status bit
|
||||
twai_ll_set_cmd_clear_data_overrun(hal_ctx->dev);
|
||||
|
||||
//Todo: Decode ALC register
|
||||
//Todo: Decode error code capture
|
||||
return msg_cnt;
|
||||
}
|
||||
#endif //TWAI_SUPPORTS_RX_STATUS
|
||||
|
||||
/* --------------------------- Errata Workarounds --------------------------- */
|
||||
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
/**
|
||||
* @brief Prepare the peripheral for a HW reset
|
||||
*
|
||||
* Some HW erratas will require the peripheral be reset. This function should be
|
||||
* called if twai_hal_get_events() returns the TWAI_HAL_EVENT_NEED_PERIPH_RESET event.
|
||||
* Preparing for a reset involves the following:
|
||||
* - Checking if a reset will cancel a TX. If so, mark that we need to retry that message after the reset
|
||||
* - Save how many RX messages were lost due to this reset
|
||||
* - Enter reset mode to stop any the peripheral from receiving any bus activity
|
||||
* - Store the regsiter state of the peripheral
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
*/
|
||||
void twai_hal_prepare_for_reset(twai_hal_context_t *hal_ctx);
|
||||
|
||||
/**
|
||||
* @brief Recover the peripheral after a HW reset
|
||||
*
|
||||
* This should be called after calling twai_hal_prepare_for_reset() and then
|
||||
* executing the HW reset.
|
||||
* Recovering the peripheral from a HW reset involves the following:
|
||||
* - Restoring the previously saved register state
|
||||
* - Exiting reset mode to allow receiving of bus activity
|
||||
* - Retrying any TX message that was cancelled by the HW reset
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
*/
|
||||
void twai_hal_recover_from_reset(twai_hal_context_t *hal_ctx);
|
||||
|
||||
/**
|
||||
* @brief Get how many RX messages were lost due to HW reset
|
||||
*
|
||||
* @note The number of lost RX messages are saved during twai_hal_prepare_for_reset()
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
* @return uint32_t Number of RX messages lost due to HW reset
|
||||
*/
|
||||
static inline uint32_t twai_hal_get_reset_lost_rx_cnt(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
return hal_ctx->rx_msg_cnt_save;
|
||||
}
|
||||
#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ extern "C" {
|
||||
|
||||
#define TWAI_BRP_MIN 2
|
||||
#define TWAI_BRP_MAX 32768
|
||||
#define TWAI_SUPPORTS_RX_STATUS 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ static inline bool can_hal_check_state_flags(can_hal_context_t *hal_ctx, uint32_
|
||||
/* ----------------------------- Event Handling ----------------------------- */
|
||||
|
||||
static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx) {
|
||||
return twai_hal_decode_interrupt_events(hal_ctx);
|
||||
return twai_hal_decode_interrupt(hal_ctx);
|
||||
}
|
||||
|
||||
/* ------------------------------- TX and RX -------------------------------- */
|
||||
|
@ -86,6 +86,70 @@ typedef union {
|
||||
|
||||
_Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes");
|
||||
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
/**
|
||||
* Some errata workarounds will require a hardware reset of the peripheral. Thus
|
||||
* certain registers must be saved before the reset, and then restored after the
|
||||
* reset. This structure is used to hold some of those registers.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t mode_reg;
|
||||
uint8_t interrupt_enable_reg;
|
||||
uint8_t bus_timing_0_reg;
|
||||
uint8_t bus_timing_1_reg;
|
||||
uint8_t error_warning_limit_reg;
|
||||
uint8_t acr_reg[4];
|
||||
uint8_t amr_reg[4];
|
||||
uint8_t rx_error_counter_reg;
|
||||
uint8_t tx_error_counter_reg;
|
||||
uint8_t clock_divider_reg;
|
||||
} __attribute__((packed)) twai_ll_reg_save_t;
|
||||
#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID
|
||||
typedef enum {
|
||||
TWAI_LL_ERR_BIT = 0,
|
||||
TWAI_LL_ERR_FORM,
|
||||
TWAI_LL_ERR_STUFF,
|
||||
TWAI_LL_ERR_OTHER,
|
||||
TWAI_LL_ERR_MAX,
|
||||
} twai_ll_err_type_t;
|
||||
|
||||
typedef enum {
|
||||
TWAI_LL_ERR_DIR_TX = 0,
|
||||
TWAI_LL_ERR_DIR_RX,
|
||||
TWAI_LL_ERR_DIR_MAX,
|
||||
} twai_ll_err_dir_t;
|
||||
|
||||
typedef enum {
|
||||
TWAI_LL_ERR_SEG_SOF = 0,
|
||||
TWAI_LL_ERR_SEG_ID_28_21 = 2,
|
||||
TWAI_LL_ERR_SEG_SRTR = 4,
|
||||
TWAI_LL_ERR_SEG_IDE = 5,
|
||||
TWAI_LL_ERR_SEG_ID_20_18 = 6,
|
||||
TWAI_LL_ERR_SEG_ID_17_13 = 7,
|
||||
TWAI_LL_ERR_SEG_CRC_SEQ = 8,
|
||||
TWAI_LL_ERR_SEG_R0 = 9,
|
||||
TWAI_LL_ERR_SEG_DATA = 10,
|
||||
TWAI_LL_ERR_SEG_DLC = 11,
|
||||
TWAI_LL_ERR_SEG_RTR = 12,
|
||||
TWAI_LL_ERR_SEG_R1 = 13,
|
||||
TWAI_LL_ERR_SEG_ID_4_0 = 14,
|
||||
TWAI_LL_ERR_SEG_ID_12_5 = 15,
|
||||
TWAI_LL_ERR_SEG_ACT_FLAG = 17,
|
||||
TWAI_LL_ERR_SEG_INTER = 18,
|
||||
TWAI_LL_ERR_SEG_SUPERPOS = 19,
|
||||
TWAI_LL_ERR_SEG_PASS_FLAG = 22,
|
||||
TWAI_LL_ERR_SEG_ERR_DELIM = 23,
|
||||
TWAI_LL_ERR_SEG_CRC_DELIM = 24,
|
||||
TWAI_LL_ERR_SEG_ACK_SLOT = 25,
|
||||
TWAI_LL_ERR_SEG_EOF = 26,
|
||||
TWAI_LL_ERR_SEG_ACK_DELIM = 27,
|
||||
TWAI_LL_ERR_SEG_OVRLD_FLAG = 28,
|
||||
TWAI_LL_ERR_SEG_MAX = 29,
|
||||
} twai_ll_err_seg_t;
|
||||
#endif
|
||||
|
||||
/* ---------------------------- Mode Register ------------------------------- */
|
||||
|
||||
/**
|
||||
@ -402,6 +466,19 @@ static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
|
||||
(void)hw->error_code_capture_reg.val;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID
|
||||
static inline void twai_ll_parse_err_code_cap(twai_dev_t *hw,
|
||||
twai_ll_err_type_t *type,
|
||||
twai_ll_err_dir_t *dir,
|
||||
twai_ll_err_seg_t *seg)
|
||||
{
|
||||
uint32_t ecc = hw->error_code_capture_reg.val;
|
||||
*type = (twai_ll_err_type_t) ((ecc >> 6) & 0x3);
|
||||
*dir = (twai_ll_err_dir_t) ((ecc >> 5) & 0x1);
|
||||
*seg = (twai_ll_err_seg_t) (ecc & 0x1F);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ----------------------------- EWL Register ------------------------------- */
|
||||
|
||||
/**
|
||||
@ -694,6 +771,65 @@ static inline void twai_ll_enable_extended_reg_layout(twai_dev_t *hw)
|
||||
hw->clock_divider_reg.cm = 1;
|
||||
}
|
||||
|
||||
/* ------------------------- Register Save/Restore -------------------------- */
|
||||
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
/**
|
||||
* @brief Saves the current values of the TWAI controller's registers
|
||||
*
|
||||
* This function saves the current values of the some of the TWAI controller's
|
||||
* registers in preparation for a hardware reset of the controller.
|
||||
*
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param reg_save Pointer to structure to store register values
|
||||
* @note Must be called in reset mode so that config registers become accessible.
|
||||
* @note Some registers are cleared on entering reset mode so must be saved
|
||||
* separate from this function.
|
||||
*/
|
||||
static inline void twai_ll_save_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save)
|
||||
{
|
||||
reg_save->mode_reg = (uint8_t) hw->mode_reg.val;
|
||||
reg_save->interrupt_enable_reg = (uint8_t) hw->interrupt_enable_reg.val;
|
||||
reg_save->bus_timing_0_reg = (uint8_t) hw->bus_timing_0_reg.val;
|
||||
reg_save->bus_timing_1_reg = (uint8_t) hw->bus_timing_1_reg.val;
|
||||
reg_save->error_warning_limit_reg = (uint8_t) hw->error_warning_limit_reg.val;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
reg_save->acr_reg[i] = hw->acceptance_filter.acr[i].byte;
|
||||
reg_save->amr_reg[i] = hw->acceptance_filter.amr[i].byte;
|
||||
}
|
||||
reg_save->rx_error_counter_reg = (uint8_t) hw->rx_error_counter_reg.val;
|
||||
reg_save->tx_error_counter_reg = (uint8_t) hw->tx_error_counter_reg.val;
|
||||
reg_save->clock_divider_reg = (uint8_t) hw->clock_divider_reg.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores the previous values of the TWAI controller's registers
|
||||
*
|
||||
* This function restores the previous values of some of the TWAI controller's
|
||||
* registers following a hardware reset of the controller.
|
||||
*
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param reg_save Pointer to structure to storing register values to restore
|
||||
* @note Must be called in reset mode so that config registers become accessible
|
||||
* @note Some registers are read only thus cannot be restored
|
||||
*/
|
||||
static inline void twai_ll_restore_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save)
|
||||
{
|
||||
hw->mode_reg.val = reg_save->mode_reg;
|
||||
hw->interrupt_enable_reg.val = reg_save->interrupt_enable_reg;
|
||||
hw->bus_timing_0_reg.val = reg_save->bus_timing_0_reg;
|
||||
hw->bus_timing_1_reg.val = reg_save->bus_timing_1_reg;
|
||||
hw->error_warning_limit_reg.val = reg_save->error_warning_limit_reg;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
hw->acceptance_filter.acr[i].byte = reg_save->acr_reg[i];
|
||||
hw->acceptance_filter.amr[i].byte = reg_save->amr_reg[i];
|
||||
}
|
||||
hw->rx_error_counter_reg.val = reg_save->rx_error_counter_reg;
|
||||
hw->tx_error_counter_reg.val = reg_save->tx_error_counter_reg;
|
||||
hw->clock_divider_reg.val = reg_save->clock_divider_reg;
|
||||
}
|
||||
#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -42,6 +42,7 @@ extern "C" {
|
||||
#define TWAI_LL_STATUS_TS (0x1 << 5) //Transmit Status
|
||||
#define TWAI_LL_STATUS_ES (0x1 << 6) //Error Status
|
||||
#define TWAI_LL_STATUS_BS (0x1 << 7) //Bus Status
|
||||
#define TWAI_LL_STATUS_MS (0x1 << 8) //Miss Status
|
||||
|
||||
#define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt
|
||||
#define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt
|
||||
|
@ -13,7 +13,9 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <stddef.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "hal/twai_hal.h"
|
||||
#include "soc/twai_periph.h"
|
||||
|
||||
//Default values written to various registers on initialization
|
||||
#define TWAI_HAL_INIT_TEC 0
|
||||
@ -69,8 +71,8 @@ void twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode)
|
||||
{
|
||||
twai_ll_set_mode(hal_ctx->dev, mode); //Set operating mode
|
||||
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
|
||||
TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING);
|
||||
twai_ll_exit_reset_mode(hal_ctx->dev);
|
||||
TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING);
|
||||
twai_ll_exit_reset_mode(hal_ctx->dev);
|
||||
}
|
||||
|
||||
void twai_hal_stop(twai_hal_context_t *hal_ctx)
|
||||
@ -79,6 +81,5 @@ void twai_hal_stop(twai_hal_context_t *hal_ctx)
|
||||
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev);
|
||||
twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode
|
||||
//Any TX is immediately halted on entering reset mode
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING);
|
||||
}
|
||||
TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED | TWAI_HAL_STATE_FLAG_RUNNING);
|
||||
}
|
||||
|
@ -13,83 +13,173 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "hal/twai_hal.h"
|
||||
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
|
||||
//Errata condition occurs at 64 messages. Threshold set to 62 to prevent the chance of failing to detect errata condition.
|
||||
#define TWAI_RX_FIFO_CORRUPT_THRESH 62
|
||||
#endif
|
||||
|
||||
/* ----------------------------- Event Handling ----------------------------- */
|
||||
|
||||
uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx)
|
||||
/**
|
||||
* Helper functions that can decode what events have been triggered based on
|
||||
* the values of the interrupt, status, TEC and REC registers. The HAL context's
|
||||
* state flags are also updated based on the events that have triggered.
|
||||
*/
|
||||
static inline uint32_t twai_hal_decode_interrupt(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
uint32_t events = 0;
|
||||
//Read interrupt, status
|
||||
uint32_t interrupts = twai_ll_get_and_clear_intrs(hal_ctx->dev);
|
||||
uint32_t status = twai_ll_get_status(hal_ctx->dev);
|
||||
uint32_t tec = twai_ll_get_tec(hal_ctx->dev);
|
||||
uint32_t rec = twai_ll_get_rec(hal_ctx->dev);
|
||||
uint32_t state_flags = hal_ctx->state_flags;
|
||||
|
||||
//Error Warning Interrupt set whenever Error or Bus Status bit changes
|
||||
if (interrupts & TWAI_LL_INTR_EI) {
|
||||
if (status & TWAI_LL_STATUS_BS) {
|
||||
//Currently in BUS OFF state
|
||||
if (status & TWAI_LL_STATUS_BS) { //Currently in BUS OFF state
|
||||
if (status & TWAI_LL_STATUS_ES) { //EWL is exceeded, thus must have entered BUS OFF
|
||||
twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Set to listen only to freeze tec and rec
|
||||
events |= TWAI_HAL_EVENT_BUS_OFF;
|
||||
TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF);
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_OFF);
|
||||
TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF);
|
||||
//Any TX would have been halted by entering bus off. Reset its flag
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RUNNING | TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
} else {
|
||||
//Below EWL. Therefore TEC is counting down in bus recovery
|
||||
events |= TWAI_HAL_EVENT_BUS_RECOV_PROGRESS;
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_PROGRESS);
|
||||
}
|
||||
} else {
|
||||
//Not in BUS OFF
|
||||
} else { //Not in BUS OFF
|
||||
if (status & TWAI_LL_STATUS_ES) { //Just Exceeded EWL
|
||||
events |= TWAI_HAL_EVENT_ABOVE_EWL;
|
||||
TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ABOVE_EWL);
|
||||
TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN);
|
||||
} else if (hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_RECOVERING) {
|
||||
//Previously undergoing bus recovery. Thus means bus recovery complete
|
||||
twai_ll_enter_reset_mode(hal_ctx->dev); //Enter reset mode to stop the peripheral
|
||||
events |= TWAI_HAL_EVENT_BUS_RECOV_CPLT;
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING);
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_CPLT);
|
||||
TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RECOVERING | TWAI_HAL_STATE_FLAG_BUS_OFF);
|
||||
} else { //Just went below EWL
|
||||
events |= TWAI_HAL_EVENT_BELOW_EWL;
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BELOW_EWL);
|
||||
TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Receive Interrupt set whenever RX FIFO is not empty
|
||||
//Receive Interrupt set whenever RX FIFO is not empty
|
||||
if (interrupts & TWAI_LL_INTR_RI) {
|
||||
events |= TWAI_HAL_EVENT_RX_BUFF_FRAME;
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_RX_BUFF_FRAME);
|
||||
}
|
||||
//Transmit interrupt set whenever TX buffer becomes free
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST
|
||||
if ((interrupts & TWAI_LL_INTR_TI || hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) && status & TWAI_LL_STATUS_TBS) {
|
||||
#else
|
||||
if (interrupts & TWAI_LL_INTR_TI) {
|
||||
events |= TWAI_HAL_EVENT_TX_BUFF_FREE;
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
#endif
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX_BUFF_FREE);
|
||||
TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
}
|
||||
//Error Passive Interrupt on transition from error active to passive or vice versa
|
||||
if (interrupts & TWAI_LL_INTR_EPI) {
|
||||
if (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) {
|
||||
events |= TWAI_HAL_EVENT_ERROR_PASSIVE;
|
||||
TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_PASSIVE);
|
||||
TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
|
||||
} else {
|
||||
events |= TWAI_HAL_EVENT_ERROR_ACTIVE;
|
||||
TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_ACTIVE);
|
||||
TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
|
||||
}
|
||||
}
|
||||
//Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc)
|
||||
if (interrupts & TWAI_LL_INTR_BEI) {
|
||||
twai_ll_clear_err_code_cap(hal_ctx->dev);
|
||||
events |= TWAI_HAL_EVENT_BUS_ERR;
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_ERR);
|
||||
}
|
||||
//Arbitration Lost Interrupt triggered on losing arbitration
|
||||
if (interrupts & TWAI_LL_INTR_ALI) {
|
||||
twai_ll_clear_arb_lost_cap(hal_ctx->dev);
|
||||
events |= TWAI_HAL_EVENT_ARB_LOST;
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ARB_LOST);
|
||||
}
|
||||
hal_ctx->state_flags = state_flags;
|
||||
return events;
|
||||
}
|
||||
|
||||
uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
uint32_t events = twai_hal_decode_interrupt(hal_ctx);
|
||||
|
||||
//Handle low latency events
|
||||
if (events & TWAI_HAL_EVENT_BUS_OFF) {
|
||||
twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Freeze TEC/REC by entering LOM
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC
|
||||
//Errata workaround: Force REC to 0 by re-triggering bus-off (by setting TEC to 0 then 255)
|
||||
twai_ll_set_tec(hal_ctx->dev, 0);
|
||||
twai_ll_set_tec(hal_ctx->dev, 255);
|
||||
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear the re-triggered bus-off interrupt
|
||||
#endif
|
||||
}
|
||||
if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
|
||||
twai_ll_enter_reset_mode(hal_ctx->dev); //Enter reset mode to stop the controller
|
||||
}
|
||||
if (events & TWAI_HAL_EVENT_BUS_ERR) {
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID
|
||||
twai_ll_err_type_t type;
|
||||
twai_ll_err_dir_t dir;
|
||||
twai_ll_err_seg_t seg;
|
||||
twai_ll_parse_err_code_cap(hal_ctx->dev, &type, &dir, &seg); //Decode error interrupt
|
||||
//Check for errata condition (RX message has bus error at particular segments)
|
||||
if (dir == TWAI_LL_ERR_DIR_RX &&
|
||||
((seg == TWAI_LL_ERR_SEG_DATA || seg == TWAI_LL_ERR_SEG_CRC_SEQ) ||
|
||||
(seg == TWAI_LL_ERR_SEG_ACK_DELIM && type == TWAI_LL_ERR_OTHER))) {
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_NEED_PERIPH_RESET);
|
||||
}
|
||||
#endif
|
||||
twai_ll_clear_err_code_cap(hal_ctx->dev);
|
||||
}
|
||||
if (events & TWAI_HAL_EVENT_ARB_LOST) {
|
||||
twai_ll_clear_arb_lost_cap(hal_ctx->dev);
|
||||
}
|
||||
#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
|
||||
//Check for errata condition (rx_msg_count >= corruption_threshold)
|
||||
if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME && twai_ll_get_rx_msg_count(hal_ctx->dev) >= TWAI_RX_FIFO_CORRUPT_THRESH) {
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_NEED_PERIPH_RESET);
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) {
|
||||
//A peripheral reset will invalidate an RX event;
|
||||
TWAI_HAL_CLEAR_BITS(events, (TWAI_HAL_EVENT_RX_BUFF_FRAME));
|
||||
}
|
||||
#endif
|
||||
return events;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
void twai_hal_prepare_for_reset(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
uint32_t status = twai_ll_get_status(hal_ctx->dev);
|
||||
if (!(status & TWAI_LL_STATUS_TBS)) { //Transmit buffer is NOT free, indicating an Ongoing TX will be cancelled by the HW reset
|
||||
TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_NEED_RETRY);
|
||||
//Note: Even if the TX completes right after this, we still consider it will be retried.
|
||||
//Worst case the same message will get sent twice.
|
||||
}
|
||||
//Some register must saved before entering reset mode
|
||||
hal_ctx->rx_msg_cnt_save = (uint8_t) twai_ll_get_rx_msg_count(hal_ctx->dev);
|
||||
twai_ll_enter_reset_mode(hal_ctx->dev); //Enter reset mode to stop the controller
|
||||
twai_ll_save_reg(hal_ctx->dev, &hal_ctx->reg_save); //Save remaining registers after entering reset mode
|
||||
}
|
||||
|
||||
void twai_hal_recover_from_reset(twai_hal_context_t *hal_ctx)
|
||||
{
|
||||
twai_ll_enter_reset_mode(hal_ctx->dev);
|
||||
twai_ll_enable_extended_reg_layout(hal_ctx->dev);
|
||||
twai_ll_restore_reg(hal_ctx->dev, &hal_ctx->reg_save);
|
||||
twai_ll_exit_reset_mode(hal_ctx->dev);
|
||||
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev);
|
||||
|
||||
if (hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_TX_NEED_RETRY) {
|
||||
//HW reset has cancelled a TX. Re-transmit here
|
||||
twai_hal_set_tx_buffer_and_transmit(hal_ctx, &hal_ctx->tx_frame_save);
|
||||
TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_NEED_RETRY);
|
||||
}
|
||||
}
|
||||
#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
|
||||
void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_frame_t *tx_frame)
|
||||
{
|
||||
//Copy frame into tx buffer
|
||||
@ -106,5 +196,9 @@ void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_f
|
||||
} else {
|
||||
twai_ll_set_cmd_tx(hal_ctx->dev);
|
||||
}
|
||||
TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
}
|
||||
TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
//Save transmitted frame in case we need to retry
|
||||
memcpy(&hal_ctx->tx_frame_save, tx_frame, sizeof(twai_hal_frame_t));
|
||||
#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user