implement CAN driver for simulator (#4312)

* stub simulator CAN driver

* comments

* CAN sensor

* maybe implement?

* s

* fix

* tx and rx actually work!?!?!

* check_can_isr

* modify chibios

* Sim has no CAN on Windows (for now?)

* safer init/deinit, enable assertions

* smarter handling of missing CAN device

* better guards

* guards for windows
This commit is contained in:
Matthew Kennedy 2022-07-03 05:25:24 -07:00 committed by GitHub
parent ad424f77b3
commit 2c80b8cf19
20 changed files with 566 additions and 21 deletions

@ -1 +1 @@
Subproject commit eed346d57f80efd78bd4130c7088066a1169d495
Subproject commit 4a81e00519e4b592176d974c37abc6249ab5294b

View File

@ -187,10 +187,6 @@ void setFrankensoConfiguration() {
// todo: 8.2 or 10k?
engineConfiguration->vbattDividerCoeff = ((float) (10 + 33)) / 10 * 2;
#if EFI_CAN_SUPPORT
enableFrankensoCan();
#endif /* EFI_CAN_SUPPORT */
}
/**

View File

@ -125,9 +125,5 @@ void setHonda600() {
// todo: 8.2 or 10k?
engineConfiguration->vbattDividerCoeff = ((float) (10 + 33)) / 10 * 2;
#if EFI_CAN_SUPPORT
enableFrankensoCan();
#endif /* EFI_CAN_SUPPORT */
}

View File

@ -12,7 +12,7 @@
#include "can_listener.h"
#include "can_msg_tx.h"
#if EFI_PROD_CODE
#if EFI_PROD_CODE | EFI_SIMULATOR
#define can_msg_t msg_t
#define can_sysinterval_t sysinterval_t
#define CAN_MSG_OK MSG_OK

View File

@ -7,7 +7,7 @@
#pragma once
#if !EFI_PROD_CODE || !EFI_CAN_SUPPORT
#if EFI_UNIT_TEST || !EFI_CAN_SUPPORT
#include "can_mocks.h"
#endif // EFI_PROD_CODE

View File

@ -56,9 +56,11 @@ void CanWrite::PeriodicTask(efitime_t nowNt) {
updateDash(cycle);
#endif /* DISABLE_CAN_UPDATE_DASH */
#if EFI_WIDEBAND_FIRMWARE_UPDATE
if (engineConfiguration->enableAemXSeries && cycle.isInterval(CI::_50ms)) {
sendWidebandInfo();
}
#endif
cycleCount++;
}

View File

@ -98,7 +98,10 @@ static void populateFrame(Sensors1& msg) {
msg.aux1 = Sensor::getOrZero(SensorType::AuxTemp1) + PACK_ADD_TEMPERATURE;
msg.aux2 = Sensor::getOrZero(SensorType::AuxTemp2) + PACK_ADD_TEMPERATURE;
#if HAL_USE_ADC
msg.mcuTemp = getMCUInternalTemperature();
#endif
msg.fuelLevel = Sensor::getOrZero(SensorType::FuelLevel);
}

View File

@ -24,6 +24,8 @@
static bool isCanEnabled = false;
#if EFI_PROD_CODE
// Values below calculated with http://www.bittiming.can-wiki.info/
// Pick ST micro bxCAN
// Clock rate of 42mhz for f4, 54mhz for f7, 80mhz for h7
@ -120,6 +122,15 @@ static const CANConfig canConfig1000 = {
};
#endif
#else // not EFI_PROD_CODE
// Nothing to actually set for the simulator's CAN config.
// It's impossible to set CAN bitrate from userspace, so we can't set it.
static const CANConfig canConfig100;
static const CANConfig canConfig250;
static const CANConfig canConfig500;
static const CANConfig canConfig1000;
#endif
class CanRead final : protected ThreadController<UTILITY_THREAD_STACK_SIZE> {
public:
CanRead(size_t index)
@ -238,11 +249,13 @@ void startCanPins() {
return;
}
#if EFI_PROD_CODE
efiSetPadModeIfConfigurationChanged("CAN TX", canTxPin, PAL_MODE_ALTERNATE(EFI_CAN_TX_AF));
efiSetPadModeIfConfigurationChanged("CAN RX", canRxPin, PAL_MODE_ALTERNATE(EFI_CAN_RX_AF));
efiSetPadModeIfConfigurationChanged("CAN2 TX", can2TxPin, PAL_MODE_ALTERNATE(EFI_CAN_TX_AF));
efiSetPadModeIfConfigurationChanged("CAN2 RX", can2RxPin, PAL_MODE_ALTERNATE(EFI_CAN_RX_AF));
#endif // EFI_PROD_CODE
}
static const CANConfig * findConfig(can_baudrate_e rate) {
@ -262,7 +275,7 @@ static const CANConfig * findConfig(can_baudrate_e rate) {
}
}
void initCan(void) {
void initCan() {
addConsoleAction("caninfo", canInfo);
isCanEnabled = false;

View File

@ -9,7 +9,7 @@
#include "efifeatures.h"
void initCan(void);
void initCan();
void setCanType(int type);
void setCanVss(int type);

View File

@ -7,7 +7,7 @@
#include "pch.h"
#if EFI_CAN_SUPPORT
#if EFI_PROD_CODE && EFI_CAN_SUPPORT
#include "can_sensor.h"
#include "can.h"
@ -50,4 +50,4 @@ void initCanSensors() {
registerCanSensor(obdTpsSensor);
}
}
#endif // EFI_CAN_SUPPORT
#endif // EFI_PROD_CODE && EFI_CAN_SUPPORT

View File

@ -46,7 +46,7 @@ static void deInitOldAnalogInputs() {
}
void initNewSensors() {
#if EFI_CAN_SUPPORT
#if EFI_PROD_CODE && EFI_CAN_SUPPORT
initCanSensors();
#endif

View File

@ -46,8 +46,9 @@ ifeq ($(USE_OPT),)
# USE_OPT += -O0 -g
ifeq ($(OS),Windows_NT)
USE_OPT += -DEFI_SIM_IS_WINDOWS=1
else
USE_OPT += -m32
USE_OPT += -m32 -DEFI_SIM_IS_WINDOWS=0
endif
endif
@ -157,6 +158,7 @@ include $(PROJECT_DIR)/common.mk
# setting.
CSRC = $(ALLCSRC) \
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CPPSRC = $(ALLCPPSRC) \
@ -167,6 +169,7 @@ CPPSRC = $(ALLCPPSRC) \
$(CONSOLE_SRC_CPP) \
$(DEV_SIMULATOR_SRC_CPP) \
simulator/rusEfiFunctionalTest.cpp \
simulator/can/hal_can_lld.cpp \
simulator/framework.cpp \
simulator/boards.cpp \
$(TEST_SRC_CPP) \
@ -183,6 +186,7 @@ INCDIR = . \
$(PROJECT_DIR)/hw_layer/drivers/can \
${CHIBIOS}/os/various \
$(CHIBIOS)/os/hal/lib/streams \
simulator/can \
simulator
# List ASM source files here

View File

@ -533,7 +533,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_CHECKS)
#define CH_DBG_ENABLE_CHECKS FALSE
#define CH_DBG_ENABLE_CHECKS TRUE
#endif
/**
@ -545,7 +545,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_ASSERTS)
#define CH_DBG_ENABLE_ASSERTS FALSE
#define CH_DBG_ENABLE_ASSERTS TRUE
#endif
/**

View File

@ -58,7 +58,7 @@
* @brief Enables the CAN subsystem.
*/
#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
#define HAL_USE_CAN FALSE
#define HAL_USE_CAN !EFI_SIM_IS_WINDOWS
#endif
/**

View File

@ -0,0 +1,303 @@
/*
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file hal_can_lld.c
* @brief PLATFORM CAN subsystem low level driver source.
*
* @addtogroup CAN
* @{
*/
#include "hal.h"
#if (HAL_USE_CAN == TRUE) || defined(__DOXYGEN__)
#include <linux/can.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <cstring>
#include <queue>
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief CAN1 driver identifier.
*/
CANDriver CAND1;
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level CAN driver initialization.
*
* @notapi
*/
void can_lld_init(void) {
/* Driver initialization.*/
canObjectInit(&CAND1);
CAND1.sock = -1;
}
static std::vector<CANDriver*> instances;
/**
* @brief Configures and activates the CAN peripheral.
*
* @param[in] canp pointer to the @p CANDriver object
*
* @notapi
*/
void can_lld_start(CANDriver *canp) {
(void)canp;
// Check that this device is not already started
osalDbgCheck(canp->sock <= 0);
// Check that a name is set
osalDbgCheck(canp->deviceName != nullptr);
// create socket
int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (sock < 0) {
// TODO: handle
return;
}
sockaddr_can addr;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
{
// Determine index of the CAN device with the requested name
ifreq ifr;
strcpy(ifr.ifr_name, canp->deviceName);
ioctl(sock, SIOCGIFINDEX, &ifr);
addr.can_ifindex = ifr.ifr_ifindex;
}
if (bind(sock, (sockaddr*)&addr, sizeof(addr)) < 0) {
// TODO: handle
return;
}
canp->sock = sock;
// Initialize the rx queue
canp->rx = new std::queue<can_frame>;
// Add this instance so it will have receive listened to by the "interrupt handler"
instances.push_back(canp);
// TODO: can we even set bitrate from userspace?
}
/**
* @brief Deactivates the CAN peripheral.
*
* @param[in] canp pointer to the @p CANDriver object
*
* @notapi
*/
void can_lld_stop(CANDriver *canp) {
(void)canp;
// Remove from the "interrupt handler" list
std::remove(instances.begin(), instances.end(), canp);
// Close the socket.
close(canp->sock);
canp->sock = -1;
// Free the rx queue
delete reinterpret_cast<std::queue<can_frame>*>(canp->rx);
}
/**
* @brief Determines whether a frame can be transmitted.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
*
* @return The queue space availability.
* @retval false no space in the transmit queue.
* @retval true transmit slot available.
*
* @notapi
*/
bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox) {
(void)canp;
(void)mailbox;
// The queue is practically infinitely deep, so it is always safe to call can_lld_transmit.
// Therefore, just return whether or not the CAN interface actually got initialized
return canp->sock >= 0;
}
/**
* @brief Inserts a frame into the transmit queue.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] ctfp pointer to the CAN frame to be transmitted
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
*
* @notapi
*/
void can_lld_transmit(CANDriver *canp,
canmbx_t mailbox,
const CANTxFrame *ctfp) {
(void)mailbox;
can_frame frame;
memcpy(frame.data, ctfp->data8, 8);
frame.can_dlc = ctfp->DLC;
frame.can_id = ctfp->IDE ? ctfp->EID : ctfp->SID;
// bit 31 is 1 for extended, 0 for standard
frame.can_id |= ctfp->IDE ? (1 << 31) : 0;
int res = write(canp->sock, &frame, sizeof(frame));
if (res != sizeof(frame)) {
// TODO: handle err
return;
}
}
/**
* @brief Determines whether a frame has been received.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
*
* @return The queue space availability.
* @retval false no space in the transmit queue.
* @retval true transmit slot available.
*
* @notapi
*/
bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox) {
(void)mailbox;
// CAN init failed, claim that the queue is full.
if (canp->sock < 0) {
return false;
}
return !reinterpret_cast<std::queue<can_frame>*>(canp->rx)->empty();
}
bool check_can_isr() {
bool intOccured = false;
for (auto canp : instances) {
can_frame frame;
// nonblocking read so it fails instantly in case no frame is queued
int result = recv(canp->sock, &frame, sizeof(frame), MSG_DONTWAIT);
// no frame received, nothing to do
if (result != sizeof(frame)) {
continue;
}
intOccured = true;
CH_IRQ_PROLOGUE();
reinterpret_cast<std::queue<can_frame>*>(canp->rx)->push(frame);
_can_rx_full_isr(canp, 0);
CH_IRQ_EPILOGUE();
}
return intOccured;
}
/**
* @brief Receives a frame from the input queue.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
* @param[out] crfp pointer to the buffer where the CAN frame is copied
*
* @notapi
*/
void can_lld_receive(CANDriver *canp,
canmbx_t mailbox,
CANRxFrame *crfp) {
(void)mailbox;
auto queue = reinterpret_cast<std::queue<can_frame>*>(canp->rx);
can_frame frame = queue->front();
queue->pop();
crfp->DLC = frame.can_dlc;
memcpy(crfp->data8, frame.data, crfp->DLC);
// If <8 byte packet, pad with zeroes to avoid spilling stack state or garbage data from the returned frame
if (crfp->DLC < 8) {
memset(crfp->data8 + crfp->DLC, 0, 8 - crfp->DLC);
}
// SID bits overlap with EID, no reason to copy both, but mask off err/rtr/etc bits
crfp->EID = CAN_ERR_MASK & frame.can_id;
crfp->IDE = (frame.can_id & CAN_EFF_FLAG) != 0;
}
/**
* @brief Tries to abort an ongoing transmission.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] mailbox mailbox number
*
* @notapi
*/
void can_lld_abort(CANDriver *canp,
canmbx_t mailbox) {
(void)canp;
(void)mailbox;
}
#endif /* HAL_USE_CAN == TRUE */
/** @} */

View File

@ -0,0 +1,194 @@
#ifndef HAL_CAN_LLD_H
#define HAL_CAN_LLD_H
#if HAL_USE_CAN || defined(__DOXYGEN__)
/**
* @brief This switch defines whether the driver implementation supports
* a low power switch mode with automatic an wakeup feature.
*/
#define CAN_SUPPORTS_SLEEP FALSE
/**
* @brief This implementation supports three transmit mailboxes.
*/
#define CAN_TX_MAILBOXES 1
/**
* @brief This implementation supports two receive mailboxes.
*/
#define CAN_RX_MAILBOXES 1
#define CAN_BTR_BRP(n) (n) /**< @brief BRP field macro.*/
#define CAN_BTR_TS1(n) ((n) << 16) /**< @brief TS1 field macro.*/
#define CAN_BTR_TS2(n) ((n) << 20) /**< @brief TS2 field macro.*/
#define CAN_BTR_SJW(n) ((n) << 24) /**< @brief SJW field macro.*/
#define CAN_IDE_STD 0 /**< @brief Standard id. */
#define CAN_IDE_EXT 1 /**< @brief Extended id. */
#define CAN_RTR_DATA 0 /**< @brief Data frame. */
#define CAN_RTR_REMOTE 1 /**< @brief Remote frame. */
typedef uint32_t canmbx_t;
typedef struct CANDriver CANDriver;
typedef struct { } CANConfig;
struct CANDriver {
/**
* @brief Driver state.
*/
canstate_t state;
/**
* @brief Current configuration data.
*/
const CANConfig *config;
/**
* @brief Transmission threads queue.
*/
threads_queue_t txqueue;
/**
* @brief Receive threads queue.
*/
threads_queue_t rxqueue;
#if (CAN_ENFORCE_USE_CALLBACKS == FALSE) || defined(__DOXYGEN__)
/**
* @brief One or more frames become available.
* @note After broadcasting this event it will not be broadcasted again
* until the received frames queue has been completely emptied. It
* is <b>not</b> broadcasted for each received frame. It is
* responsibility of the application to empty the queue by
* repeatedly invoking @p canReceive() when listening to this event.
* This behavior minimizes the interrupt served by the system
* because CAN traffic.
* @note The flags associated to the listeners will indicate which
* receive mailboxes become non-empty.
*/
event_source_t rxfull_event;
/**
* @brief One or more transmission mailbox become available.
* @note The flags associated to the listeners will indicate which
* transmit mailboxes become empty.
* @note The upper 16 bits are transmission error flags associated
* to the transmit mailboxes.
*/
event_source_t txempty_event;
/**
* @brief A CAN bus error happened.
* @note The flags associated to the listeners will indicate that
* receive error(s) have occurred.
* @note In this implementation the upper 16 bits are filled with the
* unprocessed content of the ESR register.
*/
event_source_t error_event;
#else /* CAN_ENFORCE_USE_CALLBACKS == TRUE */
/**
* @brief One or more frames become available.
* @note After calling this function it will not be called again
* until the received frames queue has been completely emptied. It
* is <b>not</b> called for each received frame. It is
* responsibility of the application to empty the queue by
* repeatedly invoking @p chTryReceiveI().
* This behavior minimizes the interrupt served by the system
* because CAN traffic.
*/
can_callback_t rxfull_cb;
/**
* @brief One or more transmission mailbox become available.
* @note The flags associated to the callback will indicate which
* transmit mailboxes become empty.
*/
can_callback_t txempty_cb;
/**
* @brief A CAN bus error happened.
*/
can_callback_t error_cb;
#endif
const char* deviceName;
int sock;
// This contains a std::queue of received frames
void* rx;
};
typedef struct {
struct {
uint8_t DLC:4; /**< @brief Data length. */
uint8_t RTR:1; /**< @brief Frame type. */
uint8_t IDE:1; /**< @brief Identifier type. */
};
union {
struct {
uint32_t SID:11; /**< @brief Standard identifier.*/
};
struct {
uint32_t EID:29; /**< @brief Extended identifier.*/
};
};
union {
uint8_t data8[8]; /**< @brief Frame data. */
uint16_t data16[4]; /**< @brief Frame data. */
uint32_t data32[2]; /**< @brief Frame data. */
uint64_t data64[1]; /**< @brief Frame data. */
};
} CANTxFrame;
typedef struct {
struct {
uint8_t FMI; /**< @brief Filter id. */
uint16_t TIME; /**< @brief Time stamp. */
};
struct {
uint8_t DLC:4; /**< @brief Data length. */
uint8_t RTR:1; /**< @brief Frame type. */
uint8_t IDE:1; /**< @brief Identifier type. */
};
union {
struct {
uint32_t SID:11; /**< @brief Standard identifier.*/
};
struct {
uint32_t EID:29; /**< @brief Extended identifier.*/
};
};
union {
uint8_t data8[8]; /**< @brief Frame data. */
uint16_t data16[4]; /**< @brief Frame data. */
uint32_t data32[2]; /**< @brief Frame data. */
uint64_t data64[1]; /**< @brief Frame data. */
};
} CANRxFrame;
extern CANDriver CAND1;
#ifdef __cplusplus
extern "C" {
#endif
void can_lld_init(void);
void can_lld_start(CANDriver *canp);
void can_lld_stop(CANDriver *canp);
bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox);
void can_lld_transmit(CANDriver *canp,
canmbx_t mailbox,
const CANTxFrame *crfp);
bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox);
void can_lld_receive(CANDriver *canp,
canmbx_t mailbox,
CANRxFrame *ctfp);
void can_lld_abort(CANDriver *canp,
canmbx_t mailbox);
// Called from _sim_check_for_interrupts
bool check_can_isr(void);
#ifdef __cplusplus
}
#endif
#endif // HAL_USE_CAN
#endif // HAL_CAN_LLD_H

View File

@ -96,7 +96,11 @@
#define EFI_MAIN_RELAY_CONTROL FALSE
#define EFI_HIP_9011 TRUE
#define EFI_CJ125 TRUE
#define EFI_CAN_SUPPORT FALSE
// Simulator supports real CAN, but not on Windows (yet?)
#define EFI_CAN_SUPPORT !EFI_SIM_IS_WINDOWS
#define EFI_WIDEBAND_FIRMWARE_UPDATE TRUE
#define EFI_MAX_31855 FALSE
#define EFI_ELECTRONIC_THROTTLE_BODY TRUE

View File

@ -22,6 +22,7 @@
#include "chprintf.h"
#include "cli_registry.h"
#include "eficonsole.h"
#include <ch.hpp>
#endif /* __cplusplus */
#define hasOsPanicError() (FALSE)

View File

@ -0,0 +1,7 @@
#pragma once
#if HAL_USE_CAN
CANDriver* detectCanDevice(brain_pin_e pinRx, brain_pin_e pinTx);
bool isValidCanTxPin(brain_pin_e) { return true; }
bool isValidCanRxPin(brain_pin_e) { return true; }
#endif // HAL_USE_CAN

View File

@ -24,6 +24,7 @@
#include "memstreams.h"
#include <chprintf.h>
#include "rusefi_lua.h"
#include "can_hw.h"
#define DEFAULT_SIM_RPM 1200
#define DEFAULT_SNIFFER_THR 2500
@ -115,6 +116,15 @@ void rusEfiFunctionalTest(void) {
startSerialChannels();
engineConfiguration->enableVerboseCanTx = true;
#if HAL_USE_CAN
// Set CAN device name
CAND1.deviceName = "can0";
initCan();
#endif // HAL_USE_CAN
startLua();
extern bool main_loop_started;
@ -150,3 +160,15 @@ void logMsg(const char *format, ...) {
//
// fclose(fp);
}
#if HAL_USE_CAN
static bool didInitCan = false;
CANDriver* detectCanDevice(brain_pin_e pinRx, brain_pin_e pinTx) {
if (didInitCan) {
return nullptr;
}
didInitCan = true;
return &CAND1;
}
#endif // HAL_USE_CAN