OVMS3/OVMS.V3/components/esp32can/src/esp32can.cpp

690 lines
21 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 14th March 2017
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
;
; Portions of this are based on the work of Thomas Barth, and licensed
; under MIT license.
; Copyright (c) 2017, Thomas Barth, barth-dev.de
; https://github.com/ThomasBarth/ESP32-CAN-Driver
*/
#include "ovms_log.h"
static const char *TAG = "esp32can";
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include <string.h>
#include "esp32can.h"
#include "esp32can_regdef.h"
#include "ovms_peripherals.h"
#include "ovms_module.h"
esp32can* MyESP32can = NULL;
static portMUX_TYPE esp32can_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define ESP32CAN_ENTER_CRITICAL() portENTER_CRITICAL(&esp32can_spinlock)
#define ESP32CAN_EXIT_CRITICAL() portEXIT_CRITICAL(&esp32can_spinlock)
#define ESP32CAN_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&esp32can_spinlock)
#define ESP32CAN_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&esp32can_spinlock)
static inline uint32_t ESP32CAN_rxframe(esp32can *me, BaseType_t* task_woken)
{
static CAN_queue_msg_t msg;
uint32_t error_irqs = 0;
// The ESP32 CAN controller works different from the SJA1000 here.
//
// https://github.com/espressif/esp-idf/issues/4276:
//
// "When bytes are received, they are written to the FIFO directly,
// and an overflow is not detected until the 64th byte is written.
// The bytes of the overflowing message will remain in the FIFO.
// The RMC should count all messages received (up to 64 messages)
// regardless of whether they were overflowing or not."
//
// "Basically, what should happen is that whenever you release the
// receiver buffer, and the buffer window shifts to an overflowed
// message, the Data overrun interrupt will be set. If that is the
// case, the message contents should be ignored, the clear data
// overrun command set, and the receiver buffer released again.
// Continue this process until RMC reaches zero, or until the
// buffer window rotates to a valid message."
//
// Results from single step tests:
//
// - The interrupt flags are set with a delay after RRB / CDO
// → the RX loop must not use them (but can rely on the status flags)
//
// - At DOS, RMC tells us how many frames need to be discarded
// → on DOS, issuing CDO, then RMC times RRB resyncs the RX to the next valid frame
//
// - DOS can become set on the last RRB (i.e. without any more message
// in the FIFO, RMC=0 and no RI/RBS)
// → the RX loop must check & handle DOS independent of the other indicators
//
// - After an overflow of the receive message counter RMC (at 64), the controller
// cannot recover and continues to deliver wrong & corrupted frames, even if
// clearing the FIFO completely until RMC=0
// → if RMC reaches 64, the controller must be reset
while (MODULE_ESP32CAN->SR.B.RBS | MODULE_ESP32CAN->SR.B.DOS)
{
if (MODULE_ESP32CAN->RMC.B.RMC == 64)
{
// RMC overflow => reset controller:
MODULE_ESP32CAN->MOD.B.RM = 1;
MODULE_ESP32CAN->MOD.B.RM = 0;
error_irqs |= __CAN_IRQ_DATA_OVERRUN;
me->m_status.error_resets++;
}
else if (MODULE_ESP32CAN->SR.B.DOS)
{
// FIFO overflow => clear overflow & discard <RMC> messages to resync:
error_irqs |= __CAN_IRQ_DATA_OVERRUN;
MODULE_ESP32CAN->CMR.B.CDO = 1;
int8_t discard = MODULE_ESP32CAN->RMC.B.RMC;
while (discard--)
{
MODULE_ESP32CAN->CMR.B.RRB = 1;
me->m_status.rxbuf_overflow++;
}
}
else
{
// Valid frame in receive buffer: record the origin
memset(&msg,0,sizeof(msg));
msg.type = CAN_frame;
msg.body.frame.origin = me;
// get FIR
msg.body.frame.FIR.U = MODULE_ESP32CAN->MBX_CTRL.FCTRL.FIR.U;
// Detect invalid frames
if (msg.body.frame.FIR.B.DLC > sizeof(msg.body.frame.data.u8))
{
error_irqs |= __CAN_IRQ_INVALID_RX;
me->m_status.invalid_rx++;
// Request next frame:
MODULE_ESP32CAN->CMR.B.RRB = 1;
continue;
}
// check if this is a standard or extended CAN frame
if (msg.body.frame.FIR.B.FF == CAN_frame_std)
{
// Standard frame: Get Message ID
msg.body.frame.MsgID = ESP32CAN_GET_STD_ID;
// …deep copy data bytes
for (int k=0 ; k<msg.body.frame.FIR.B.DLC ; k++)
msg.body.frame.data.u8[k] = MODULE_ESP32CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[k];
}
else
{
// Extended frame: Get Message ID
msg.body.frame.MsgID = ESP32CAN_GET_EXT_ID;
// …deep copy data bytes
for (int k=0 ; k<msg.body.frame.FIR.B.DLC ; k++)
msg.body.frame.data.u8[k] = MODULE_ESP32CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[k];
}
// Request next frame:
MODULE_ESP32CAN->CMR.B.RRB = 1;
// Send frame to CAN framework:
xQueueSendFromISR(MyCan.m_rxqueue, &msg, task_woken);
}
} // while (MODULE_ESP32CAN->SR.B.RBS | MODULE_ESP32CAN->SR.B.DOS)
return error_irqs;
}
static IRAM_ATTR void ESP32CAN_isr(void *pvParameters)
{
esp32can *me = (esp32can*)pvParameters;
BaseType_t task_woken = pdFALSE;
uint32_t interrupt;
uint32_t error_irqs = 0;
ESP32CAN_ENTER_CRITICAL_ISR();
// Read interrupt status and clear flags
while ((interrupt = MODULE_ESP32CAN->IR.U & 0xff) != 0)
{
me->m_status.interrupts++;
// Errata workaround: TWAI_ERRATA_FIX_BUS_OFF_REC
//
// Add SW workaround for REC change during bus-off
//
// 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 Enabling this option will add a
// workaround in the driver to forcibly reset REC to zero on
// reaching bus-off.
// "Force REC to 0 by re-triggering bus-off (by setting TEC to 0 then 255)"
if ((interrupt & __CAN_IRQ_ERR_WARNING) != 0)
{
uint32_t status = MODULE_ESP32CAN->SR.U;
if ((status & __CAN_STS_BUS_OFF) != 0 && (status & __CAN_STS_ERR_WARNING) != 0)
{
// Freeze TEC/REC by entering listen only mode
MODULE_ESP32CAN->MOD.B.LOM = 1;
// Re-trigger bus-off
MODULE_ESP32CAN->TXERR.B.TXERR = 0;
MODULE_ESP32CAN->TXERR.B.TXERR = 0xff;
// Exit reset mode
MODULE_ESP32CAN->MOD.B.RM = 0;
// Clear the re-triggered bus-off interrupt, collect any new bits
interrupt |= MODULE_ESP32CAN->IR.U & 0xff;
}
if ((status & __CAN_STS_BUS_OFF) == 0 &&
(status & __CAN_STS_ERR_WARNING) != 0)
{
// Entering bus off halts in progress tx
MyESP32can->m_state &= ~CAN_M_STATE_TX_BUF_OCCUPIED;
}
}
// Handle RX frame(s) available & FIFO overflow interrupts:
if ((interrupt & (__CAN_IRQ_RX|__CAN_IRQ_DATA_OVERRUN)) != 0)
{
interrupt |= ESP32CAN_rxframe(me, &task_woken);
}
//
// Errata workaround: TWAI_ERRATA_FIX_TX_INTR_LOST
//
// Add SW workaround for TX interrupt lost
//
// 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.
// Handle TX interrupt:
if ((interrupt & __CAN_IRQ_TX) != 0 ||
((MyESP32can->m_state & CAN_M_STATE_TX_BUF_OCCUPIED) != 0 &&
(MODULE_ESP32CAN->SR.U & __CAN_STS_TXDONE) != 0))
{
MyESP32can->m_state &= ~CAN_M_STATE_TX_BUF_OCCUPIED;
CAN_queue_msg_t msg;
// The TX interrupt occurs when the TX buffer becomes available, which may be due
// to transmission success or abortion. A real SJA1000 would tell the actual result
// by the SR.3 TCS bit, but the ESP32CAN also sets TCS on aborts. So there is no
// way to tell if the frame was really aborted, we can only rely on our own abort
// request status:
if (me->m_tx_abort)
{
// Clear abort command:
MODULE_ESP32CAN->CMR.B.AT = 0;
me->m_tx_abort = false;
msg.type = CAN_txfailedcallback;
}
else
{
msg.type = CAN_txcallback;
}
msg.body.frame = me->m_tx_frame;
msg.body.bus = me;
xQueueSendFromISR(MyCan.m_rxqueue, &msg, &task_woken);
}
// Collect error interrupts:
error_irqs |= interrupt &
(__CAN_IRQ_ERR_WARNING // IR.2 Error Interrupt (warning state change)
|__CAN_IRQ_DATA_OVERRUN // IR.3 Data Overrun Interrupt
|__CAN_IRQ_ERR_PASSIVE // IR.5 Error Passive Interrupt (passive state change)
|__CAN_IRQ_BUS_ERR // IR.7 Bus Error Interrupt
|__CAN_IRQ_INVALID_RX // Invalid RX Frame (synthetic)
);
// Handle wakeup interrupt:
if ((interrupt & __CAN_IRQ_WAKEUP) != 0)
{
// Todo
}
} // while interrupt
// Get error counters:
uint32_t rxerr = MODULE_ESP32CAN->RXERR.U;
uint32_t txerr = MODULE_ESP32CAN->TXERR.U;
uint32_t status = MODULE_ESP32CAN->SR.U;
if (status & __CAN_STS_BUS_OFF)
{
rxerr |= 0x100;
txerr |= 0x100;
}
// Handle error interrupts:
if (error_irqs)
{
uint32_t ecc = MODULE_ESP32CAN->ECC.U;
uint32_t error_flags = error_irqs << 16 | (status & 0b11001110) << 8 | (ecc & 0xff);
// Check for TX failure:
// We consider the TX to have failed if a bus error is detected during the
// transmission attempt and the controller gave up on retrying (= entered
// passive mode, txerr >= 128).
if ((status & (__CAN_STS_TXDONE|__CAN_STS_TXFREE)) == 0 &&
(error_irqs & __CAN_IRQ_BUS_ERR) != 0 &&
(ecc & __CAN_ECC_DIR) == 0 &&
txerr >= 128)
{
// Set abort command:
me->m_tx_abort = true;
MODULE_ESP32CAN->CMR.B.AT = 1;
// Note: another TX IRQ will occur from the abort, see above
}
// Request error log?
if (me->m_status.error_flags != error_flags ||
me->m_status.errors_rx != rxerr ||
me->m_status.errors_tx != txerr)
{
me->m_status.error_flags = error_flags;
me->m_status.errors_rx = rxerr;
me->m_status.errors_tx = txerr;
// …only necessary if no abort has been issued:
if (!me->m_tx_abort)
{
CAN_queue_msg_t msg;
if (ecc != 0 || (status & (__CAN_STS_DATA_OVERRUN|__CAN_STS_BUS_OFF|__CAN_IRQ_INVALID_RX)) != 0)
msg.type = CAN_logerror;
else
msg.type = CAN_logstatus;
msg.body.bus = me;
xQueueSendFromISR(MyCan.m_rxqueue, &msg, &task_woken);
}
}
}
else
{
// Update status:
if (rxerr == 0 && txerr == 0)
me->m_status.error_flags = 0;
me->m_status.errors_rx = rxerr;
me->m_status.errors_tx = txerr;
}
ESP32CAN_EXIT_CRITICAL_ISR();
// Yield to minimize latency if we have woken up a higher priority task:
if (task_woken == pdTRUE)
{
portYIELD_FROM_ISR();
}
}
static void ESP32CAN_init(void *pvParameters)
{
esp32can *me = (esp32can*) pvParameters;
// Install CAN ISR:
esp_intr_alloc(ETS_CAN_INTR_SOURCE, ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL3, ESP32CAN_isr, me, NULL);
vTaskDelete(NULL);
}
esp32can::esp32can(const char* name, int txpin, int rxpin)
: canbus(name)
{
m_txpin = (gpio_num_t)txpin;
m_rxpin = (gpio_num_t)rxpin;
MyESP32can = this;
// Due to startup order, we can't talk to MAX7317 during
// initialisation. So, we'll just enter reset mode for the
// on-chip controller, and let housekeeping power us down
// after startup.
m_powermode = Off;
m_tx_abort = false;
MODULE_ESP32CAN->MOD.B.RM = 1;
// Launch ISR allocator task on core 0:
TaskHandle_t task = NULL;
xTaskCreatePinnedToCore(ESP32CAN_init, "esp32can init", 2048, (void*)this, 23, &task, CORE(0));
AddTaskToMap(task);
}
esp32can::~esp32can()
{
MyESP32can = NULL;
}
esp_err_t esp32can::InitController()
{
bool brp_div = 0;
double __tq; // Time quantum
// Set to PELICAN mode
MODULE_ESP32CAN->CDR.B.CAN_M=0x1;
// Synchronization jump width is the same for all baud rates
MODULE_ESP32CAN->BTR0.B.SJW=0x1;
// TSEG2 is the same for all baud rates
MODULE_ESP32CAN->BTR1.B.TSEG2=0x1;
// select time quantum and set TSEG1
switch (MyESP32can->m_speed)
{
case CAN_SPEED_1000KBPS:
MODULE_ESP32CAN->BTR1.B.TSEG1=0x4;
__tq = 0.125;
break;
default:
MODULE_ESP32CAN->BTR1.B.TSEG1=0xc;
__tq = ((float)1000/MyESP32can->m_speed) / 16;
}
// Set baud rate prescaler
int brp = round((((APB_CLK_FREQ * __tq) / 2) - 1)/1000000)-1;
esp_chip_info_t chip;
esp_chip_info(&chip);
if (brp > BRP_MAX)
{
/* ESP32 revision 2 and higher have a divide by 2 bit */
if (chip.revision < 2)
return ESP_FAIL;
brp = brp / 2;
brp_div = 1;
if (brp > BRP_MAX)
return ESP_FAIL;
}
MODULE_ESP32CAN->BTR0.B.BRP = (uint8_t)brp;
/* Set sampling
* 1 -> triple; the bus is sampled three times; recommended for low/medium speed buses (class A and B) where filtering spikes on the bus line is beneficial
* 0 -> single; the bus is sampled once; recommended for high speed buses (SAE class C)*/
MODULE_ESP32CAN->BTR1.B.SAM=0x1;
// Enable all interrupts except arbitration loss (can be ignored):
uint32_t ier = 0xff & ~__CAN_IRQ_ARB_LOST;
// Turn off BRP_DIV if we're V2 or higher (and don't want it set)
if (chip.revision >= 2 && !brp_div)
ier &= ~__CAN_IER_BRP_DIV;
MODULE_ESP32CAN->IER.U = ier;
// No acceptance filtering, as we want to fetch all messages
MODULE_ESP32CAN->MBX_CTRL.ACC.CODE[0] = 0;
MODULE_ESP32CAN->MBX_CTRL.ACC.CODE[1] = 0;
MODULE_ESP32CAN->MBX_CTRL.ACC.CODE[2] = 0;
MODULE_ESP32CAN->MBX_CTRL.ACC.CODE[3] = 0;
MODULE_ESP32CAN->MBX_CTRL.ACC.MASK[0] = 0xff;
MODULE_ESP32CAN->MBX_CTRL.ACC.MASK[1] = 0xff;
MODULE_ESP32CAN->MBX_CTRL.ACC.MASK[2] = 0xff;
MODULE_ESP32CAN->MBX_CTRL.ACC.MASK[3] = 0xff;
// Set to normal mode
MODULE_ESP32CAN->OCR.B.OCMODE=__CAN_OC_NOM;
// Active/Listen Mode
if (m_mode == CAN_MODE_LISTEN)
MODULE_ESP32CAN->MOD.B.LOM = 1;
else
MODULE_ESP32CAN->MOD.B.LOM = 0;
// Clear error counters
MODULE_ESP32CAN->TXERR.U = 0;
MODULE_ESP32CAN->RXERR.U = 0;
(void)MODULE_ESP32CAN->ECC;
// Clear interrupt flags
(void)MODULE_ESP32CAN->IR.U;
m_tx_abort = false;
return ESP_OK;
}
esp_err_t esp32can::Start(CAN_mode_t mode, CAN_speed_t speed)
{
switch (speed)
{
case CAN_SPEED_33KBPS:
case CAN_SPEED_50KBPS:
case CAN_SPEED_83KBPS:
esp_chip_info_t chip;
esp_chip_info(&chip);
if (chip.revision < 2)
{
ESP_LOGW(TAG, "%d only supported with ESP32 V2 and higher", speed);
return ESP_FAIL;
}
default:
break;
}
canbus::Start(mode, speed);
m_mode = mode;
m_speed = speed;
#ifdef CONFIG_OVMS_COMP_MAX7317
// Power up the matching SN65 transciever
MyPeripherals->m_max7317->Output(MAX7317_CAN1_EN, 0);
#endif // #ifdef CONFIG_OVMS_COMP_MAX7317
// Enable module
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);
// Configure TX pin
gpio_set_level(MyESP32can->m_txpin, 1);
gpio_set_direction(MyESP32can->m_txpin,GPIO_MODE_OUTPUT);
gpio_matrix_out(MyESP32can->m_txpin,CAN_TX_IDX,0,0);
gpio_pad_select_gpio(MyESP32can->m_txpin);
// Configure RX pin
gpio_set_direction(MyESP32can->m_rxpin,GPIO_MODE_INPUT);
gpio_matrix_in(MyESP32can->m_rxpin,CAN_RX_IDX,0);
gpio_pad_select_gpio(MyESP32can->m_rxpin);
ESP32CAN_ENTER_CRITICAL();
esp_err_t err = InitController();
// clear statistics:
ClearStatus();
// Showtime. Release Reset Mode.
MODULE_ESP32CAN->MOD.B.RM = 0;
ESP32CAN_EXIT_CRITICAL();
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to set speed to %d", speed);
return err;
}
// And record that we are powered on
pcp::SetPowerMode(On);
return ESP_OK;
}
esp_err_t esp32can::Stop()
{
canbus::Stop();
ESP32CAN_ENTER_CRITICAL();
// Enter reset mode
MODULE_ESP32CAN->MOD.B.RM = 1;
ESP32CAN_EXIT_CRITICAL();
#ifdef CONFIG_OVMS_COMP_MAX7317
// Power down the matching SN65 transciever
MyPeripherals->m_max7317->Output(MAX7317_CAN1_EN, 1);
#endif // #ifdef CONFIG_OVMS_COMP_MAX7317
// And record that we are powered down
pcp::SetPowerMode(Off);
return ESP_OK;
}
/**
* WriteFrame: deliver a frame to the hardware for transmission (driver internal)
*/
esp_err_t esp32can::WriteFrame(const CAN_frame_t* p_frame)
{
ESP32CAN_ENTER_CRITICAL();
uint8_t __byte_i; // Byte iterator
// Clear abort command:
MODULE_ESP32CAN->CMR.B.AT = 0;
// check if TX buffer is available:
if (MODULE_ESP32CAN->SR.B.TBS == 0)
{
ESP32CAN_EXIT_CRITICAL();
return ESP_FAIL;
}
// copy frame information record
MODULE_ESP32CAN->MBX_CTRL.FCTRL.FIR.U=p_frame->FIR.U;
if (p_frame->FIR.B.FF==CAN_frame_std)
{ // Standard frame
// Write message ID
ESP32CAN_SET_STD_ID(p_frame->MsgID);
// Copy the frame data to the hardware
for (__byte_i=0 ; __byte_i<p_frame->FIR.B.DLC ; __byte_i++)
MODULE_ESP32CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i]=p_frame->data.u8[__byte_i];
}
else
{ // Extended frame
// Write message ID
ESP32CAN_SET_EXT_ID(p_frame->MsgID);
// Copy the frame data to the hardware
for (__byte_i=0 ; __byte_i<p_frame->FIR.B.DLC ; __byte_i++)
MODULE_ESP32CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i]=p_frame->data.u8[__byte_i];
}
// Transmit frame
MODULE_ESP32CAN->CMR.B.TR=1;
MyESP32can->m_state |= CAN_M_STATE_TX_BUF_OCCUPIED;
ESP32CAN_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Write: transmit or queue a frame for transmission (API)
*/
esp_err_t esp32can::Write(const CAN_frame_t* p_frame, TickType_t maxqueuewait /*=0*/)
{
OvmsMutexLock lock(&m_write_mutex);
if (m_mode != CAN_MODE_ACTIVE)
{
ESP_LOGW(TAG,"Cannot write %s when not in ACTIVE mode",m_name);
return ESP_FAIL;
}
// if there are frames waiting in the TX queue, add the new one there as well:
if (uxQueueMessagesWaiting(m_txqueue))
{
return QueueWrite(p_frame, maxqueuewait);
}
// try to deliver the frame to the hardware:
if (WriteFrame(p_frame) != ESP_OK)
{
return QueueWrite(p_frame, maxqueuewait);
}
// stats & logging:
canbus::Write(p_frame, maxqueuewait);
return ESP_OK;
}
void esp32can::TxCallback(CAN_frame_t* p_frame, bool success)
{
// Application callbacks & logging:
canbus::TxCallback(p_frame, success);
// TX buffer has become available; send next queued frame (if any):
{
OvmsMutexLock lock(&m_write_mutex);
CAN_frame_t frame;
while (xQueueReceive(m_txqueue, (void*)&frame, 0) == pdTRUE)
{
if (WriteFrame(&frame) == ESP_FAIL)
{
// This should not happen:
ESP_LOGE(TAG, "TxCallback: fatal error: TX buffer not available");
CAN_queue_msg_t msg;
msg.type = CAN_txfailedcallback;
msg.body.frame = frame;
msg.body.bus = this;
xQueueSend(MyCan.m_rxqueue, &msg, 0);
}
else
{
canbus::Write(&frame, 0);
break;
}
}
}
}
void esp32can::SetPowerMode(PowerMode powermode)
{
pcp::SetPowerMode(powermode);
switch (powermode)
{
case On:
if (m_mode != CAN_MODE_OFF) Start(m_mode,m_speed);
break;
case Sleep:
case DeepSleep:
case Off:
Stop();
break;
default:
break;
};
}