Merge branch 'bugfix/twai_esp32_errata_workarounds_backport_v4.2' into 'release/v4.2'

TWAI: Add FIFO overrun handling and ESP32 hardware errata workarounds + Refactor TWAI HAL to store state information  (backport v4.2)

See merge request espressif/esp-idf!17106
This commit is contained in:
Jiang Jiang Jian
2022-05-19 10:55:59 +08:00
15 changed files with 742 additions and 431 deletions

View File

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

View File

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

View File

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

View File

@@ -40,9 +40,9 @@ extern "C" {
* configured. The other members of the general configuration structure are
* assigned default values.
*/
#define TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \
.clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, \
.tx_queue_len = 5, .rx_queue_len = 5, \
#define TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \
.clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, \
.tx_queue_len = 5, .rx_queue_len = 5, \
.alerts_enabled = TWAI_ALERT_NONE, .clkout_divider = 0, \
.intr_flags = ESP_INTR_FLAG_LEVEL1}
@@ -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;
@@ -168,9 +172,9 @@ esp_err_t twai_driver_uninstall(void);
*
* This function starts the TWAI driver, putting the TWAI driver into the running
* state. This allows the TWAI driver to participate in TWAI bus activities such
* as transmitting/receiving messages. The RX queue is reset in this function,
* clearing any unread messages. This function can only be called when the TWAI
* driver is in the stopped state.
* as transmitting/receiving messages. The TX and RX queue are reset in this function,
* clearing any messages that are unread or pending transmission. This function
* can only be called when the TWAI driver is in the stopped state.
*
* @return
* - ESP_OK: TWAI driver is now running

View 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)

View File

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

View File

@@ -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"
@@ -57,14 +57,6 @@
#define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4])
//Control flags
#define CTRL_FLAG_STOPPED 0x001 //TWAI peripheral in stopped state
#define CTRL_FLAG_RECOVERING 0x002 //Bus is undergoing bus recovery
#define CTRL_FLAG_ERR_WARN 0x004 //TEC or REC is >= error warning limit
#define CTRL_FLAG_ERR_PASSIVE 0x008 //TEC or REC is >= 128
#define CTRL_FLAG_BUS_OFF 0x010 //Bus-off due to TEC >= 256
#define CTRL_FLAG_TX_BUFF_OCCUPIED 0x020 //Transmit buffer is occupied
#define ALERT_LOG_LEVEL_WARNING TWAI_ALERT_ARB_LOST //Alerts above and including this level use ESP_LOGW
#define ALERT_LOG_LEVEL_ERROR TWAI_ALERT_TX_FAILED //Alerts above and including this level use ESP_LOGE
@@ -73,9 +65,10 @@
//Control structure for TWAI driver
typedef struct {
//Control and status members
uint32_t control_flags;
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;
@@ -129,102 +122,55 @@ TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req
ESP_EARLY_LOGI(TWAI_TAG, "Alert %d", alert_code);
}
}
#endif
#endif //CONFIG_TWAI_ISR_IN_IRAM
}
}
static inline void twai_handle_bus_off(int *alert_req)
{
//Bus-Off condition. TEC should set and held at 127, REC should be 0, reset mode entered
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_BUS_OFF);
/* Note: REC is still allowed to increase during bus-off. REC > err_warn
can prevent "bus recovery complete" interrupt from occurring. Set to
listen only mode to freeze REC. */
twai_hal_handle_bus_off(&twai_context);
twai_alert_handler(TWAI_ALERT_BUS_OFF, alert_req);
}
static inline void twai_handle_recovery_complete(int *alert_req)
{
//Bus recovery complete.
bool recov_cplt = twai_hal_handle_bus_recov_cplt(&twai_context);
assert(recov_cplt);
//Reset and set flags to the equivalent of the stopped state
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN |
CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF |
CTRL_FLAG_TX_BUFF_OCCUPIED);
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED);
twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, alert_req);
}
static inline void twai_handle_recovery_in_progress(int * alert_req)
{
//Bus-recovery in progress. TEC has dropped below error warning limit
twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, alert_req);
}
static inline void twai_handle_above_ewl(int *alert_req)
{
//TEC or REC surpassed error warning limit
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_WARN);
twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, alert_req);
}
static inline void twai_handle_below_ewl(int *alert_req)
{
//TEC and REC are both below error warning
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_WARN);
twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, alert_req);
}
static inline void twai_handle_error_passive(int *alert_req)
{
//Entered error passive
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
twai_alert_handler(TWAI_ALERT_ERR_PASS, alert_req);
}
static inline void twai_handle_error_active(int *alert_req)
{
//Returned to error active
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, alert_req);
}
static inline void twai_handle_bus_error(int *alert_req)
{
// ECC register is read to re-arm bus error interrupt. ECC is not used
twai_hal_handle_bus_error(&twai_context);
p_twai_obj->bus_error_count++;
twai_alert_handler(TWAI_ALERT_BUS_ERROR, alert_req);
}
static inline void twai_handle_arb_lost(int *alert_req)
{
//ALC register is read to re-arm arb lost interrupt. ALC is not used
twai_hal_handle_arb_lost(&twai_context);
p_twai_obj->arb_lost_count++;
twai_alert_handler(TWAI_ALERT_ARB_LOST, 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);
}
}
//Todo: Add Software Filters
//Todo: Check for data overrun of RX FIFO, then trigger alert
#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)
@@ -252,7 +198,6 @@ static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *aler
}
} else {
//No more frames to transmit
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
twai_alert_handler(TWAI_ALERT_TX_IDLE, alert_req);
}
}
@@ -261,49 +206,69 @@ 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, p_twai_obj->control_flags & CTRL_FLAG_RECOVERING);
events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt
if (event & TWAI_HAL_EVENT_BUS_OFF) {
twai_handle_bus_off(&alert_req);
#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);
}
if (event & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
twai_handle_recovery_complete(&alert_req);
}
if (event & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
twai_handle_recovery_in_progress(&alert_req);
}
if (event & TWAI_HAL_EVENT_ABOVE_EWL) {
twai_handle_above_ewl(&alert_req);
}
if (event & TWAI_HAL_EVENT_BELOW_EWL) {
twai_handle_below_ewl(&alert_req);
}
if (event & TWAI_HAL_EVENT_ERROR_PASSIVE) {
twai_handle_error_passive(&alert_req);
}
if (event & TWAI_HAL_EVENT_ERROR_ACTIVE) {
twai_handle_error_active(&alert_req);
}
if (event & TWAI_HAL_EVENT_BUS_ERR) {
twai_handle_bus_error(&alert_req);
}
if (event & TWAI_HAL_EVENT_ARB_LOST) {
twai_handle_arb_lost(&alert_req);
}
if (event & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
#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 related handlers should be called last, so that other commands
//do not overwrite the TX command related bits in the command register.
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 (events & TWAI_HAL_EVENT_BUS_OFF) {
p_twai_obj->state = TWAI_STATE_BUS_OFF;
twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req);
}
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 (events & TWAI_HAL_EVENT_BUS_ERR) {
p_twai_obj->bus_error_count++;
twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req);
}
if (events & TWAI_HAL_EVENT_ARB_LOST) {
p_twai_obj->arb_lost_count++;
twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req);
}
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 (events & TWAI_HAL_EVENT_ERROR_PASSIVE) {
//Entered error passive
twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req);
}
if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) {
//Returned to error active
twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req);
}
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 (events & TWAI_HAL_EVENT_BELOW_EWL) {
//TEC and REC are both below error warning
twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req);
}
TWAI_EXIT_CRITICAL_ISR();
if (p_twai_obj->alert_semphr != NULL && alert_req) {
@@ -335,6 +300,7 @@ static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout,
gpio_set_pull_mode(clkout, GPIO_FLOATING);
gpio_matrix_out(clkout, TWAI_CLKOUT_IDX, false, false);
gpio_pad_select_gpio(clkout);
}
//Configure bus status pin (Optional)
@@ -433,6 +399,8 @@ cleanup:
return NULL;
}
/* ---------------------------- Public Functions ---------------------------- */
esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config)
@@ -464,7 +432,7 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
TWAI_CHECK(p_twai_obj_dummy != NULL, ESP_ERR_NO_MEM);
//Initialize flags and variables. All other members are already set to zero by twai_alloc_driver_obj()
p_twai_obj_dummy->control_flags = CTRL_FLAG_STOPPED;
p_twai_obj_dummy->state = TWAI_STATE_STOPPED;
p_twai_obj_dummy->mode = g_config->mode;
p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled;
@@ -483,7 +451,6 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
bool init = twai_hal_init(&twai_context);
assert(init);
twai_hal_configure(&twai_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider);
//Todo: Allow interrupt to be registered to specified CPU
TWAI_EXIT_CRITICAL();
//Allocate GPIO and Interrupts
@@ -496,7 +463,6 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards
err:
//Free can driver object
twai_free_driver_obj(p_twai_obj_dummy);
return ret;
}
@@ -508,8 +474,7 @@ esp_err_t twai_driver_uninstall(void)
TWAI_ENTER_CRITICAL();
//Check state
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
//Todo: Add check to see if in reset mode. //Enter reset mode to stop any TWAI bus activity
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE);
//Clear registers by reading
twai_hal_deinit(&twai_context);
periph_module_disable(PERIPH_TWAI_MODULE); //Disable TWAI peripheral
@@ -533,18 +498,18 @@ esp_err_t twai_start(void)
//Check state
TWAI_ENTER_CRITICAL();
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & CTRL_FLAG_STOPPED, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED, ESP_ERR_INVALID_STATE);
//Reset RX queue, and RX message count
//Reset RX queue, RX message count, amd TX queue
xQueueReset(p_twai_obj->rx_queue);
if (p_twai_obj->tx_queue != NULL) {
xQueueReset(p_twai_obj->tx_queue);
}
p_twai_obj->rx_msg_count = 0;
//Todo: Add assert to see if in reset mode. //Should already be in bus-off mode, set again to make sure
p_twai_obj->tx_msg_count = 0;
twai_hal_start(&twai_context, p_twai_obj->mode);
//Currently in listen only mode, need to set to mode specified by configuration
bool started = twai_hal_start(&twai_context, p_twai_obj->mode);
assert(started);
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED);
p_twai_obj->state = TWAI_STATE_RUNNING;
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
@@ -554,19 +519,16 @@ esp_err_t twai_stop(void)
//Check state
TWAI_ENTER_CRITICAL();
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE);
bool stopped = twai_hal_stop(&twai_context);
assert(stopped);
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED);
twai_hal_stop(&twai_context);
//Reset TX Queue and message count
if (p_twai_obj->tx_queue != NULL) {
xQueueReset(p_twai_obj->tx_queue);
}
p_twai_obj->tx_msg_count = 0;
p_twai_obj->state = TWAI_STATE_STOPPED;
TWAI_EXIT_CRITICAL();
@@ -583,18 +545,17 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
TWAI_ENTER_CRITICAL();
//Check State
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->mode == TWAI_MODE_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED);
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE);
//Format frame
esp_err_t ret = ESP_FAIL;
twai_hal_frame_t tx_frame;
twai_hal_format_frame(message, &tx_frame);
//Check if frame can be sent immediately
if ((p_twai_obj->tx_msg_count == 0) && !(p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) {
if (p_twai_obj->tx_msg_count == 0) {
//No other frames waiting to transmit. Bypass queue and transmit immediately
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
p_twai_obj->tx_msg_count++;
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
ret = ESP_OK;
}
TWAI_EXIT_CRITICAL();
@@ -606,19 +567,19 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
} else if (xQueueSend(p_twai_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) {
//Copied to TX Queue
TWAI_ENTER_CRITICAL();
if (p_twai_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
int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
ret = ESP_ERR_INVALID_STATE;
} else if ((p_twai_obj->tx_msg_count == 0) && !(p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) {
//TX buffer was freed during copy, manually trigger transmission
int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
p_twai_obj->tx_msg_count++;
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
ret = ESP_OK;
if ((!twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED)) && uxQueueMessagesWaiting(p_twai_obj->tx_queue) > 0) {
//If the TX buffer is free but the TX queue is not empty. Check if we need to manually start a transmission
if (twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_BUS_OFF) || !twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_RUNNING)) {
//TX buffer became free due to bus-off or is no longer running. No need to start a transmission
ret = ESP_ERR_INVALID_STATE;
} else {
//Manually start a transmission
int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
p_twai_obj->tx_msg_count++;
ret = ESP_OK;
}
} else {
//Frame was copied to queue, waiting to be transmitted
p_twai_obj->tx_msg_count++;
@@ -694,20 +655,17 @@ esp_err_t twai_initiate_recovery(void)
TWAI_ENTER_CRITICAL();
//Check state
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & CTRL_FLAG_BUS_OFF, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & CTRL_FLAG_RECOVERING), ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE);
//Reset TX Queue/Counters
if (p_twai_obj->tx_queue != NULL) {
xQueueReset(p_twai_obj->tx_queue);
}
p_twai_obj->tx_msg_count = 0;
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_RECOVERING);
//Trigger start of recovery process
bool started = twai_hal_start_bus_recovery(&twai_context);
assert(started);
twai_hal_start_bus_recovery(&twai_context);
p_twai_obj->state = TWAI_STATE_RECOVERING;
TWAI_EXIT_CRITICAL();
return ESP_OK;
@@ -726,17 +684,10 @@ 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;
if (p_twai_obj->control_flags & CTRL_FLAG_RECOVERING) {
status_info->state = TWAI_STATE_RECOVERING;
} else if (p_twai_obj->control_flags & CTRL_FLAG_BUS_OFF) {
status_info->state = TWAI_STATE_BUS_OFF;
} else if (p_twai_obj->control_flags & CTRL_FLAG_STOPPED) {
status_info->state = TWAI_STATE_STOPPED;
} else {
status_info->state = TWAI_STATE_RUNNING;
}
status_info->state = p_twai_obj->state;
TWAI_EXIT_CRITICAL();
return ESP_OK;
@@ -750,7 +701,7 @@ esp_err_t twai_clear_transmit_queue(void)
TWAI_ENTER_CRITICAL();
//If a message is currently undergoing transmission, the tx interrupt handler will decrement tx_msg_count
p_twai_obj->tx_msg_count = (p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
p_twai_obj->tx_msg_count = twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
xQueueReset(p_twai_obj->tx_queue);
TWAI_EXIT_CRITICAL();

View File

@@ -26,30 +26,54 @@ extern "C" {
#include <stddef.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "hal/twai_types.h"
#include "hal/twai_ll.h"
/* ------------------------- Defines and Typedefs --------------------------- */
//Error active interrupt related
#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)
#define TWAI_HAL_EVENT_ABOVE_EWL (1 << 3)
#define TWAI_HAL_EVENT_BELOW_EWL (1 << 4)
#define TWAI_HAL_EVENT_ERROR_PASSIVE (1 << 5)
#define TWAI_HAL_EVENT_ERROR_ACTIVE (1 << 6)
#define TWAI_HAL_EVENT_BUS_ERR (1 << 7)
#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)
#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)
#define TWAI_HAL_STATE_FLAG_RECOVERING (1 << 1) //Bus is undergoing bus recovery
#define TWAI_HAL_STATE_FLAG_ERR_WARN (1 << 2) //TEC or REC is >= error warning limit
#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
//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)
#define TWAI_HAL_EVENT_ABOVE_EWL (1 << 3)
#define TWAI_HAL_EVENT_BELOW_EWL (1 << 4)
#define TWAI_HAL_EVENT_ERROR_PASSIVE (1 << 5)
#define TWAI_HAL_EVENT_ERROR_ACTIVE (1 << 6)
#define TWAI_HAL_EVENT_BUS_ERR (1 << 7)
#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 ----------------------------- */
/**
@@ -93,9 +117,8 @@ void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t
*
* @param hal_ctx Context of the HAL layer
* @param mode Operating mode
* @return True if successfully started, false otherwise.
*/
bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode);
void twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode);
/**
* @brief Stop the TWAI peripheral
@@ -104,19 +127,18 @@ bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode);
* setting the operating mode to Listen Only so that REC is frozen.
*
* @param hal_ctx Context of the HAL layer
* @return True if successfully stopped, false otherwise.
*/
bool twai_hal_stop(twai_hal_context_t *hal_ctx);
void twai_hal_stop(twai_hal_context_t *hal_ctx);
/**
* @brief Start bus recovery
*
* @param hal_ctx Context of the HAL layer
* @return True if successfully started bus recovery, false otherwise.
*/
static inline bool twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx)
static inline void twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx)
{
return twai_ll_exit_reset_mode(hal_ctx->dev);
TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING);
twai_ll_exit_reset_mode(hal_ctx->dev);
}
/**
@@ -163,73 +185,43 @@ static inline bool twai_hal_check_last_tx_successful(twai_hal_context_t *hal_ctx
return twai_ll_is_last_tx_successful((hal_ctx)->dev);
}
/**
* @brief Check if certain HAL state flags are set
*
* The HAL will maintain a record of the controller's state via a set of flags.
* These flags are automatically maintained (i.e., set and reset) inside various
* HAL function calls. This function checks if certain flags are currently set.
*
* @param hal_ctx Context of the HAL layer
* @param check_flags Bit mask of flags to check
* @return True if one or more of the flags in check_flags are set
*/
static inline bool twai_hal_check_state_flags(twai_hal_context_t *hal_ctx, uint32_t check_flags)
{
return hal_ctx->state_flags & check_flags;
}
/* ----------------------------- 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 called on every TWAI interrupt. It will read (and
* thereby clear) the interrupt register, then determine what events have
* occurred to trigger the 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 freeze TEC/REC. Errata 1 Fix
* - Recovery complete: Enter reset mode
* - 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
* @param bus_recovering Whether the TWAI peripheral was previous undergoing bus recovery
* @return Bit mask of events that have occurred
*/
uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx, bool bus_recovering);
/**
* @brief Handle bus recovery complete
*
* This function should be called on an bus recovery complete event. It simply
* enters reset mode to stop bus activity.
*
* @param hal_ctx Context of the HAL layer
* @return True if successfully handled bus recovery completion, false otherwise.
*/
static inline bool twai_hal_handle_bus_recov_cplt(twai_hal_context_t *hal_ctx)
{
return twai_ll_enter_reset_mode((hal_ctx)->dev);
}
/**
* @brief Handle arbitration lost
*
* This function should be called on an arbitration lost event. It simply clears
* the clears the ALC register.
*
* @param hal_ctx Context of the HAL layer
*/
static inline void twai_hal_handle_arb_lost(twai_hal_context_t *hal_ctx)
{
twai_ll_clear_arb_lost_cap((hal_ctx)->dev);
}
/**
* @brief Handle bus error
*
* This function should be called on an bus error event. It simply clears
* the clears the ECC register.
*
* @param hal_ctx Context of the HAL layer
*/
static inline void twai_hal_handle_bus_error(twai_hal_context_t *hal_ctx)
{
twai_ll_clear_err_code_cap((hal_ctx)->dev);
}
/**
* @brief Handle BUS OFF
*
* This function should be called on a BUS OFF event. It simply changes the
* mode to LOM to freeze REC
*
* @param hal_ctx Context of the HAL layer
*/
static inline void twai_hal_handle_bus_off(twai_hal_context_t *hal_ctx)
{
twai_ll_set_mode((hal_ctx)->dev, TWAI_MODE_LISTEN_ONLY);
}
uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx);
/* ------------------------------- TX and RX -------------------------------- */
@@ -281,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
}

View File

@@ -20,6 +20,7 @@ extern "C" {
#define TWAI_BRP_MIN 2
#define TWAI_BRP_MAX 32768
#define TWAI_SUPPORTS_RX_STATUS 1
#ifdef __cplusplus
}

View File

@@ -66,19 +66,19 @@ static inline void can_hal_configure(can_hal_context_t *hal_ctx, const can_timin
/* -------------------------------- Actions --------------------------------- */
static inline bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode)
static inline void can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode)
{
return twai_hal_start(hal_ctx, mode);
twai_hal_start(hal_ctx, mode);
}
static inline bool can_hal_stop(can_hal_context_t *hal_ctx)
static inline void can_hal_stop(can_hal_context_t *hal_ctx)
{
return twai_hal_stop(hal_ctx);
twai_hal_stop(hal_ctx);
}
static inline bool can_hal_start_bus_recovery(can_hal_context_t *hal_ctx)
static inline void can_hal_start_bus_recovery(can_hal_context_t *hal_ctx)
{
return twai_hal_start_bus_recovery(hal_ctx);
twai_hal_start_bus_recovery(hal_ctx);
}
static inline uint32_t can_hal_get_tec(can_hal_context_t *hal_ctx)
@@ -101,37 +101,22 @@ static inline bool can_hal_check_last_tx_successful(can_hal_context_t *hal_ctx)
return twai_hal_check_last_tx_successful(hal_ctx);
}
static inline bool can_hal_check_state_flags(can_hal_context_t *hal_ctx, uint32_t check_flags)
{
return twai_hal_check_state_flags(hal_ctx, check_flags);
}
/* ----------------------------- Event Handling ----------------------------- */
static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering) {
return twai_hal_decode_interrupt_events(hal_ctx, bus_recovering);
}
static inline bool can_hal_handle_bus_recov_cplt(can_hal_context_t *hal_ctx)
{
return twai_hal_handle_bus_recov_cplt(hal_ctx);
}
static inline void can_hal_handle_arb_lost(can_hal_context_t *hal_ctx)
{
twai_hal_handle_arb_lost(hal_ctx);
}
static inline void can_hal_handle_bus_error(can_hal_context_t *hal_ctx)
{
twai_hal_handle_bus_error(hal_ctx);
}
static inline void can_hal_handle_bus_off(can_hal_context_t *hal_ctx)
{
twai_hal_handle_bus_off(hal_ctx);
static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx) {
return twai_hal_decode_interrupt(hal_ctx);
}
/* ------------------------------- TX and RX -------------------------------- */
static inline void can_hal_format_frame(const can_message_t *message, can_hal_frame_t *frame)
{
twai_hal_format_frame(message, frame);
twai_hal_format_frame(message, frame);
}
static inline void can_hal_parse_frame(can_hal_frame_t *frame, can_message_t *message)

View File

@@ -54,14 +54,14 @@ typedef twai_ll_frame_buffer_t can_ll_frame_buffer_t;
/* ---------------------------- Mode Register ------------------------------- */
static inline bool can_ll_enter_reset_mode(can_dev_t *hw)
static inline void can_ll_enter_reset_mode(can_dev_t *hw)
{
return twai_ll_enter_reset_mode(hw);
twai_ll_enter_reset_mode(hw);
}
static inline bool can_ll_exit_reset_mode(can_dev_t *hw)
static inline void can_ll_exit_reset_mode(can_dev_t *hw)
{
return twai_ll_exit_reset_mode(hw);
twai_ll_exit_reset_mode(hw);
}
static inline bool can_ll_is_in_reset_mode(can_dev_t *hw)

View File

@@ -31,27 +31,26 @@ extern "C" {
#include "sdkconfig.h"
#include "hal/twai_types.h"
#include "soc/twai_periph.h"
#include "soc/twai_struct.h"
#include "hal/hal_defs.h"
/* ------------------------- Defines and Typedefs --------------------------- */
#define TWAI_LL_STATUS_RBS (0x1 << 0)
#define TWAI_LL_STATUS_DOS (0x1 << 1)
#define TWAI_LL_STATUS_TBS (0x1 << 2)
#define TWAI_LL_STATUS_TCS (0x1 << 3)
#define TWAI_LL_STATUS_RS (0x1 << 4)
#define TWAI_LL_STATUS_TS (0x1 << 5)
#define TWAI_LL_STATUS_ES (0x1 << 6)
#define TWAI_LL_STATUS_BS (0x1 << 7)
#define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status
#define TWAI_LL_STATUS_DOS (0x1 << 1) //Data Overrun Status
#define TWAI_LL_STATUS_TBS (0x1 << 2) //Transmit Buffer Status
#define TWAI_LL_STATUS_TCS (0x1 << 3) //Transmission Complete Status
#define TWAI_LL_STATUS_RS (0x1 << 4) //Receive Status
#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_INTR_RI (0x1 << 0)
#define TWAI_LL_INTR_TI (0x1 << 1)
#define TWAI_LL_INTR_EI (0x1 << 2)
#define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt
#define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt
#define TWAI_LL_INTR_EI (0x1 << 2) //Error Interrupt
//Data overrun interrupt not supported in SW due to HW peculiarities
#define TWAI_LL_INTR_EPI (0x1 << 5)
#define TWAI_LL_INTR_ALI (0x1 << 6)
#define TWAI_LL_INTR_BEI (0x1 << 7)
#define TWAI_LL_INTR_EPI (0x1 << 5) //Error Passive Interrupt
#define TWAI_LL_INTR_ALI (0x1 << 6) //Arbitration Lost Interrupt
#define TWAI_LL_INTR_BEI (0x1 << 7) //Bus Error Interrupt
/*
* The following frame structure has an NEARLY identical bit field layout to
@@ -87,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 ------------------------------- */
/**
@@ -97,14 +160,12 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b
* in order to write the majority of configuration registers.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was entered successfully
*
* @note Reset mode is automatically entered on BUS OFF condition
*/
static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw)
static inline void twai_ll_enter_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 1;
return hw->mode_reg.rm;
}
/**
@@ -115,14 +176,12 @@ static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw)
* operating mode.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was exit successfully
*
* @note Reset mode must be exit to initiate BUS OFF recovery
*/
static inline bool twai_ll_exit_reset_mode(twai_dev_t *hw)
static inline void twai_ll_exit_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 0;
return !(hw->mode_reg.rm);
}
/**
@@ -137,10 +196,10 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw)
/**
* @brief Set operating mode of TWAI controller
*
*
* @param hw Start address of the TWAI registers
* @param mode Operating mode
*
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode)
@@ -309,8 +368,6 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw)
return hw->status_reg.tcs;
}
//Todo: Add stand alone status bit check functions when necessary
/* -------------------------- Interrupt Register ---------------------------- */
/**
@@ -393,7 +450,6 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t
static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
{
(void)hw->arbitration_lost_captue_reg.val;
//Todo: Decode ALC register
}
/* ----------------------------- ECC Register ------------------------------- */
@@ -408,9 +464,21 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
{
(void)hw->error_code_capture_reg.val;
//Todo: Decode error code capture
}
#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 ------------------------------- */
/**
@@ -556,7 +624,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t
* This function encodes a message into a frame structure. The frame structure
* has an identical layout to the TX buffer, allowing the frame structure to be
* directly copied into TX buffer.
*
*
* @param[in] 11bit or 29bit ID
* @param[in] dlc Data length code
* @param[in] data Pointer to an 8 byte array containing data. NULL if no data
@@ -665,9 +733,9 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw)
/**
* @brief Set CLKOUT Divider and enable/disable
*
*
* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
* 1, or any even number from 2 to 14. Set the divider to 0 to disable CLKOUT.
* 1, or any even number from 2 to 14. Set the divider to 0 to disable CLKOUT.
*
* @param hw Start address of the TWAI registers
* @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT
@@ -703,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

View File

@@ -30,28 +30,27 @@ extern "C" {
#include <stdbool.h>
#include "hal/twai_types.h"
#include "soc/twai_periph.h"
#include "soc/twai_struct.h"
#include "hal/hal_defs.h"
/* ------------------------- Defines and Typedefs --------------------------- */
#define TWAI_LL_STATUS_RBS (0x1 << 0)
#define TWAI_LL_STATUS_DOS (0x1 << 1)
#define TWAI_LL_STATUS_TBS (0x1 << 2)
#define TWAI_LL_STATUS_TCS (0x1 << 3)
#define TWAI_LL_STATUS_RS (0x1 << 4)
#define TWAI_LL_STATUS_TS (0x1 << 5)
#define TWAI_LL_STATUS_ES (0x1 << 6)
#define TWAI_LL_STATUS_BS (0x1 << 7)
//Todo: Add Miss status support
#define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status
#define TWAI_LL_STATUS_DOS (0x1 << 1) //Data Overrun Status
#define TWAI_LL_STATUS_TBS (0x1 << 2) //Transmit Buffer Status
#define TWAI_LL_STATUS_TCS (0x1 << 3) //Transmission Complete Status
#define TWAI_LL_STATUS_RS (0x1 << 4) //Receive Status
#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)
#define TWAI_LL_INTR_TI (0x1 << 1)
#define TWAI_LL_INTR_EI (0x1 << 2)
#define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt
#define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt
#define TWAI_LL_INTR_EI (0x1 << 2) //Error Interrupt
//Data overrun interrupt not supported in SW due to HW peculiarities
#define TWAI_LL_INTR_EPI (0x1 << 5)
#define TWAI_LL_INTR_ALI (0x1 << 6)
#define TWAI_LL_INTR_BEI (0x1 << 7)
#define TWAI_LL_INTR_EPI (0x1 << 5) //Error Passive Interrupt
#define TWAI_LL_INTR_ALI (0x1 << 6) //Arbitration Lost Interrupt
#define TWAI_LL_INTR_BEI (0x1 << 7) //Bus Error Interrupt
/*
* The following frame structure has an NEARLY identical bit field layout to
@@ -97,14 +96,12 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b
* in order to write the majority of configuration registers.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was entered successfully
*
* @note Reset mode is automatically entered on BUS OFF condition
*/
static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw)
static inline void twai_ll_enter_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 1;
return hw->mode_reg.rm;
}
/**
@@ -115,14 +112,12 @@ static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw)
* operating mode.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was exit successfully
*
* @note Reset mode must be exit to initiate BUS OFF recovery
*/
static inline bool twai_ll_exit_reset_mode(twai_dev_t *hw)
static inline void twai_ll_exit_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 0;
return !(hw->mode_reg.rm);
}
/**
@@ -137,10 +132,10 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw)
/**
* @brief Set operating mode of TWAI controller
*
*
* @param hw Start address of the TWAI registers
* @param mode Operating mode
*
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode)
@@ -309,8 +304,6 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw)
return hw->status_reg.tcs;
}
//Todo: Add stand alone status bit check functions when necessary
/* -------------------------- Interrupt Register ---------------------------- */
/**
@@ -378,7 +371,6 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t
static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
{
(void)hw->arbitration_lost_captue_reg.val;
//Todo: Decode ALC register
}
/* ----------------------------- ECC Register ------------------------------- */
@@ -393,7 +385,6 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
{
(void)hw->error_code_capture_reg.val;
//Todo: Decode error code capture
}
/* ----------------------------- EWL Register ------------------------------- */
@@ -541,7 +532,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t
* This function encodes a message into a frame structure. The frame structure
* has an identical layout to the TX buffer, allowing the frame structure to be
* directly copied into TX buffer.
*
*
* @param[in] 11bit or 29bit ID
* @param[in] dlc Data length code
* @param[in] data Pointer to an 8 byte array containing data. NULL if no data
@@ -652,8 +643,8 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw)
* @brief Set CLKOUT Divider and enable/disable
*
* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
* 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT.
*
* 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT.
*
* @param hw Start address of the TWAI registers
* @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT
*/

View File

@@ -12,22 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//Todo: Place the implementation of all common HAL functions here
#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
#define TWAI_HAL_INIT_REC 0
#define TWAI_HAL_INIT_EWL 96
/* ---------------------------- Init and Config ----------------------------- */
bool twai_hal_init(twai_hal_context_t *hal_ctx)
{
//Initialize HAL context
hal_ctx->dev = &TWAI;
hal_ctx->state_flags = 0;
//Initialize TWAI controller, and set default values to registers
if (!twai_ll_enter_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers
twai_ll_enter_reset_mode(hal_ctx->dev);
if (!twai_ll_is_in_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers
return false;
}
#ifdef TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT
@@ -61,21 +65,21 @@ void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
}
bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode)
/* -------------------------------- Actions --------------------------------- */
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
//Todo: Check if this can be removed
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
return twai_ll_exit_reset_mode(hal_ctx->dev); //Return false if failed to exit reset mode
TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING);
twai_ll_exit_reset_mode(hal_ctx->dev);
}
bool twai_hal_stop(twai_hal_context_t *hal_ctx)
void twai_hal_stop(twai_hal_context_t *hal_ctx)
{
if (!twai_ll_enter_reset_mode(hal_ctx->dev)) {
return false;
}
//Todo: Check if this can be removed
twai_ll_enter_reset_mode(hal_ctx->dev);
(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
return true;
}
//Any TX is immediately halted on entering reset mode
TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED | TWAI_HAL_STATE_FLAG_RUNNING);
}

View File

@@ -12,57 +12,174 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "sdkconfig.h"
#include "hal/twai_hal.h"
uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx, bool bus_recovering)
#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 ----------------------------- */
/**
* 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;
//Receive Interrupt set whenever RX FIFO is not empty
if (interrupts & TWAI_LL_INTR_RI) {
events |= TWAI_HAL_EVENT_RX_BUFF_FRAME;
}
//Transmit interrupt set whenever TX buffer becomes free
if (interrupts & TWAI_LL_INTR_TI) {
events |= TWAI_HAL_EVENT_TX_BUFF_FREE;
}
//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
//EWL is exceeded, thus must have entered BUS OFF
//Below EWL. Therefore TEC is counting down in bus recovery
//Todo: Check if BUS Recov can be removed for esp32s2
events |= (status & TWAI_LL_STATUS_ES) ? TWAI_HAL_EVENT_BUS_OFF : TWAI_HAL_EVENT_BUS_RECOV_PROGRESS;
} else {
//Not in BUS OFF
events |= (status & TWAI_LL_STATUS_ES) ? TWAI_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL
((bus_recovering) ? //If previously undergoing bus recovery
TWAI_HAL_EVENT_BUS_RECOV_CPLT :
TWAI_HAL_EVENT_BELOW_EWL);
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_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_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
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_PROGRESS);
}
} else { //Not in BUS OFF
if (status & TWAI_LL_STATUS_ES) { //Just Exceeded EWL
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_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
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
if (interrupts & TWAI_LL_INTR_RI) {
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) {
#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) {
events |= (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) ? TWAI_HAL_EVENT_ERROR_PASSIVE : TWAI_HAL_EVENT_ERROR_ACTIVE;
}
//Arbitration Lost Interrupt triggered on losing arbitration
if (interrupts & TWAI_LL_INTR_ALI) {
events |= TWAI_HAL_EVENT_ARB_LOST;
if (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) {
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_PASSIVE);
TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
} else {
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) {
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_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
@@ -79,4 +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_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)
}