21#include <rusefi/endian.h>
26#define DRIVER_NAME "IOBox"
34#define MSIOBOX_OUTPUTS 8
38#define MSIOBOX_INPUTS 8
40#define MSIOBOX_SIGNALS (MSIOBOX_OUTPUTS + MSIOBOX_INPUTS)
44#define MSIOBOX_OUT_COUNT 7
46#define MSIOBOX_ADC_IN_COUNT 7
48#define MSIOBOX_TACH_IN_COUNT 4
51#define MSIOBOX_PING_TIMEOUT 100
52#define MSIOBOX_RESTART_TIMEOUT 1000
61#define CAN_IOBOX_BASE1 0x200
62#define CAN_IOBOX_BASE2 0x220
63#define CAN_IOBOX_BASE3 0x240
66#define CAN_IOBOX_PING 0x00
67#define CAN_IOBOX_CONFIG 0x01
68#define CAN_IOBOX_SET_PWM(n) (0x02 + ((n) & 0x03))
69#define CAN_IOBOX_LAST_IN 0x05
72#define CAN_IOBOX_WHOAMI 0x08
73#define CAN_IOBOX_ADC14 0x09
74#define CAN_IOBOX_ADC57 0x0A
75#define CAN_IOBOX_TACH1 0x0B
77#define CAN_IOBOX_TACH4 0x0E
106static_assert(
sizeof(iobox_pwm) == 8);
109struct iobox_pwm_last {
114static_assert(
sizeof(iobox_pwm_last) == 5);
124static_assert(
sizeof(iobox_whoami) == 8);
131static_assert(
sizeof(iobox_adc14) == 8);
140static_assert(
sizeof(iobox_adc57) == 8);
149static_assert(
sizeof(iobox_tach) == 8);
167 int setPadPWM(
size_t pin,
float frequency,
float duty)
override;
172 MsIoBox(uint32_t bus, uint32_t base, uint16_t
period);
175 efiPrintf(
"IO state: %d", (
int)
state);
176 efiPrintf(
"pwmBaseFreq: %d", (
int)pwmBaseFreq);
183 int config(uint32_t bus, uint32_t base, uint16_t period);
203 void CalcOnOffPeriod(
int ch, pwm_settings &pwm);
211 } OutPwm[MSIOBOX_OUT_COUNT];
213 uint16_t AdcValue[MSIOBOX_ADC_IN_COUNT];
223 } Tach[MSIOBOX_TACH_IN_COUNT];
230 uint32_t pwmBaseFreq = 1000 * 1000 * 100 / 5000;
232 uint32_t tachinBaseFreq = 1000 * 1000 * 100 / 66;
237 bool needUpdateConfig;
245 :
CanListener(0), m_bus(0), m_base(0), m_period(20) {
248MsIoBox::MsIoBox(uint32_t bus, uint32_t base, uint16_t
period)
261int MsIoBox::config(uint32_t bus, uint32_t base, uint16_t
period)
278bool MsIoBox::acceptFrame(
const size_t busIndex,
const CANRxFrame& frame)
const {
283 if (CAN_ISX(frame)) {
287 uint32_t
id = CAN_ID(frame);
291 if ((
id >= m_base + 8) && (
id <= m_base + 14)) {
306int MsIoBox::setup() {
309 cfg->pwm_mask = OutMode;
310 cfg->tachin_mask = InMode;
311 cfg->adc_broadcast_interval = m_period;
312 cfg->tach_broadcast_interval = m_period;
317void MsIoBox::CalcOnOffPeriod(
int channel, pwm_settings &pwm)
319 if ((OutMode & BIT(
channel)) == 0) {
320 pwm.on = pwm.off = 0;
327 pwm.off =
period - pwm.on;
331int MsIoBox::update() {
335 for (
size_t i = 0; i < 3; i++) {
337 if ((OutMode & (BIT(i) | BIT(i + 1))) == 0)
341 for (
size_t j = 0; j < 2; j++) {
342 CalcOnOffPeriod(i + j, pwm->ch[j]);
350 CalcOnOffPeriod(MSIOBOX_OUT_COUNT - 1, pwm->ch[0]);
352 pwm->out_state = OutVal;
358void MsIoBox::decodeFrame(
const CANRxFrame& frame, efitick_t) {
359 uint32_t
id = CAN_ID(frame);
360 uint32_t
offset =
id - m_base;
364 if (
offset == CAN_IOBOX_ADC14) {
365 auto data =
reinterpret_cast<const iobox_adc14*
>(&frame.
data8[0]);
367 for (
size_t i = 0; i < 4; i++) {
368 AdcValue[i] = data->adc[i];
370 }
else if (
offset == CAN_IOBOX_ADC57) {
371 auto data =
reinterpret_cast<const iobox_adc57*
>(&frame.
data8[0]);
373 InVal = data->inputs;
374 for (
size_t i = 0; i < 3; i++) {
375 AdcValue[i + 4] = data->adc[i];
377 }
else if ((
offset >= CAN_IOBOX_TACH1) && (
offset <= CAN_IOBOX_TACH4)) {
378 size_t i =
offset - CAN_IOBOX_TACH1;
379 auto data =
reinterpret_cast<const iobox_tach*
>(&frame.
data8[0]);
383 Tach[i].period = data->period;
384 Tach[i].teeths = data->n_teeth;
385 Tach[i].totalTooth = data->total_tooth;
390 if (
offset == CAN_IOBOX_WHOAMI) {
391 auto data =
reinterpret_cast<const iobox_whoami*
>(&frame.
data8[0]);
395 pwmBaseFreq = 1000 * 1000 * 100 / data->pwm_period;
396 tachinBaseFreq = 1000 * 1000 * 100 / data->tachin_period;
420int MsIoBox::writePad(
unsigned int pin,
int value) {
421 if (
pin >= MSIOBOX_OUTPUTS)
424 uint8_t OutModeNew = OutMode & (~BIT(
pin));
425 uint8_t OutValNew = OutVal;
427 OutValNew |= BIT(
pin);
429 OutValNew &= ~BIT(
pin);
432 if (OutValNew != OutVal) {
436 if (OutModeNew != OutMode) {
437 OutMode = OutModeNew;
438 needUpdateConfig =
true;
444int MsIoBox::readPad(
size_t pin) {
445 if ((
pin < MSIOBOX_OUTPUTS) || (
pin >= MSIOBOX_SIGNALS))
448 pin -= MSIOBOX_OUTPUTS;
450 if (InMode & BIT(
pin)) {
455 return !!(InVal & BIT(
pin));
460 if (
pin >= MSIOBOX_OUT_COUNT)
471 if ((OutMode & BIT(
pin)) == 0) {
473 needUpdateConfig =
true;
484 if (
pin >= MSIOBOX_SIGNALS)
491 return PIN_DRIVER_OFF;
494void MsIoBox::checkState(
void)
508 if (stateTimer.hasElapsedMs(MSIOBOX_PING_TIMEOUT)) {
514 if (stateTimer.hasElapsedMs(m_period * 3)) {
518 if (needUpdateConfig) {
520 needUpdateConfig =
false;
530 if (stateTimer.hasElapsedMs(MSIOBOX_RESTART_TIMEOUT)) {
553 for (
size_t i = 0; i < BOARD_CAN_GPIO_COUNT; i++) {
569 for (
size_t i = 0; i < BOARD_CAN_GPIO_COUNT; i++) {
void registerCanListener(CanListener &listener)
uint8_t tach_broadcast_interval
typedef __attribute__
Ignition Mode.
uint8_t adc_broadcast_interval
static MsIoBox instance[BOARD_CAN_GPIO_COUNT]
void initCanGpioMsiobox()
virtual void decodeFrame(const CANRxFrame &frame, efitick_t nowNt)=0
virtual CanListener * request()
virtual bool acceptFrame(const size_t busIndex, const CANRxFrame &frame) const
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
static constexpr persistent_config_s * config
static constexpr engine_configuration_s * engineConfiguration
UNUSED(samplingTimeSeconds)
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1871, 1.0, -1.0, -1.0, "")
uint8_t data8[8]
Frame data.
virtual int setPadPWM(size_t, float, float)
virtual brain_pin_diag_e getDiag(size_t)
virtual int writePad(size_t, int)
virtual int readPad(size_t)
MsIoBox_config_s msIoBox0