forked from espressif/esp-idf
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:
@@ -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"
|
||||
|
@@ -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
|
||||
|
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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ extern "C" {
|
||||
|
||||
#define TWAI_BRP_MIN 2
|
||||
#define TWAI_BRP_MAX 32768
|
||||
#define TWAI_SUPPORTS_RX_STATUS 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user