Skip to content

Commit 976d005

Browse files
committed
Implemented MS IoBox protocol
1 parent abb1902 commit 976d005

7 files changed

Lines changed: 263 additions & 48 deletions

File tree

firmware/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ CPPSRC = $(ALLCPPSRC) \
157157
can/can_link.cpp \
158158
can/can_motec.cpp \
159159
can/can_rusefi.cpp \
160+
can/can_msiobox.cpp \
160161
status.cpp \
161162
lambda_conversion.cpp \
162163
pwm.cpp \

firmware/boards/port.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ enum class AuxOutputMode : uint8_t {
6262
Lambda1 = 3,
6363
Egt0 = 4,
6464
Egt1 = 5,
65+
IOExpander = 6,
6566
};
6667

6768
// These values are kept in sync just for convenience
@@ -92,6 +93,7 @@ enum class CanIoProtocol : uint8_t {
9293
Haltech = 3,
9394
Motec = 6,
9495
Emtron = 7,
96+
MsIoBox = 9,
9597
};
9698

9799
class Configuration {
@@ -206,7 +208,7 @@ class Configuration {
206208
uint8_t Offset;
207209
uint16_t IOInputsEnabled; // Bitmask of which inputs should be reported in CAN messages
208210
uint16_t IOOutputsEnabled; // Bitmask of which outputs should be controlled via CAN messages
209-
uint8_t Reserved1[2];
211+
uint8_t Reserved1[10];
210212
} ioExpanderConfig;
211213

212214
} __attribute__((packed));

firmware/can.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "can/can_link.h"
1414
#include "can/can_emtron.h"
1515
#include "can/can_motec.h"
16+
#include "can/can_msiobox.h"
1617

1718
#include "port.h"
1819

@@ -50,6 +51,7 @@ static BaseProtocolHandler* const TxHandlers[] = {
5051
#if (IO_EXPANDER_ENABLED > 0)
5152
&haltechIoTxHandler,
5253
&emtronIoTxHandler,
54+
&msIoBoxTxHandler,
5355
#endif
5456

5557
#if (MOTEC_E888_ENABLED > 0)
@@ -69,6 +71,7 @@ __attribute__((weak)) void ProcessCanMessage(const CANRxFrame* frame)
6971
ProcessRusefiCanMessage(frame, configuration, &canStatusData);
7072
ProcessLinkCanMessage(frame, configuration, &canStatusData);
7173
ProcessHaltechIO12Message(frame, configuration);
74+
ProcessMsIoBoxCanMessage(frame, configuration);
7275
}
7376

7477
static THD_WORKING_AREA(waCanTxThread, 512);

firmware/can/base_protocol_handler.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ class BaseProtocolHandler {
4343
send_message(configuration);
4444
}
4545

46+
uint16_t m_intervalMs;
47+
4648
private:
47-
const uint16_t m_intervalMs;
4849
uint32_t m_elapsedSinceDispatchMs = 0;
4950
};
5051

firmware/can/can_motec.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,22 @@ static_assert(sizeof(E888Data1) == 8);
158158

159159
void SendMotec888Format(Configuration* configuration)
160160
{
161-
auto id = MOTEC_E888_BASE_ID + configuration->egt[0].ExtraCanIdOffset;
161+
auto id = MOTEC_E888_BASE_ID;
162+
auto offset = 0;
163+
164+
#if (EGT_CHANNELS > 0)
165+
if (configuration->egt[0].ExtraCanProtocol == CanEgtProtocol::Motec) {
166+
offset = configuration->egt[0].ExtraCanIdOffset;
167+
}
168+
#endif
169+
170+
#if (IO_EXPANDER_ENABLED > 0)
171+
if (configuration->ioExpanderConfig.Protocol == CanIoProtocol::Motec) {
172+
offset += configuration->ioExpanderConfig.Offset;
173+
}
174+
#endif
175+
176+
id += offset;
162177

163178
CanTxTyped<motec::E888Data1> frame(id, true);
164179

firmware/can/can_msiobox.cpp

Lines changed: 235 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,71 +13,263 @@
1313
#include "sampling.h"
1414
#include "pump_dac.h"
1515
#include "max3185x.h"
16+
#include "auxout.h"
17+
#include "pwmout.h"
18+
19+
void SendMsIoBoxFormat(Configuration* configuration);
20+
21+
IoHandler msIoBoxTxHandler(CanIoProtocol::MsIoBox, 20, SendMsIoBoxFormat);
1622

1723
// MS IoBox protocol
1824

19-
#define MSIOBOX_TX_PERIOD_MS 10
20-
#define MSIOBOX_BASE_ID 0x200
21-
#define MSIOBOX_OFFSSET 0x20
25+
#define MS_IOBOX_BASE_ID 0x200
26+
#define MS_IOBOX_OFFSSET 0x20
27+
#define MS_IOBOX_DEVICE_ID(n) (MS_IOBOX_BASE_ID + (MS_IOBOX_OFFSSET)*(n))
28+
29+
#define MS_IOBOX_PING 0x00
30+
#define MS_IOBOX_CONFIG 0x01
31+
#define MS_IOBOX_SET_PWM(n) (0x02 + ((n) & 0x03))
32+
#define MS_IOBOX_LAST_IN 0x05
33+
34+
/* Packets from device to MS3 */
35+
#define MS_IOBOX_WHOAMI 0x08
36+
#define MS_IOBOX_ADC14 0x09
37+
#define MS_IOBOX_ADC57 0x0A
2238

2339
namespace msiobox
2440
{
2541

26-
// RX SYN: ID = BASE_ID + 0
27-
// TX ACK: ID = BASE_ID + 8
42+
struct pwm_settings {
43+
beuint16_t on;
44+
beuint16_t off;
45+
} __attribute__ ((packed));
2846

29-
// Settings: ID = BASE_ID + 1
30-
struct IoBoxSettings
31-
{
32-
uint8_t OutputMode; // 0 = On/Off, 1 = PWM
33-
uint8_t : 8; // Reserved
34-
uint8_t TachConfig; // Bitfield
35-
uint8_t : 8; // Reserved
36-
uint8_t AdcBroadcastInterval; // ms
37-
uint8_t TachBroadcastInterval; // ms
38-
uint8_t : 16; // Reserved
47+
/* Base + 0x00 */
48+
/* "Are you there?" packet with zero payload */
49+
50+
/* Base + 0x01 */
51+
struct cfg {
52+
uint8_t pwm_mask; // 0 - On/Off, 1 - PWM
53+
uint8_t pad0;
54+
uint8_t tachin_mask;
55+
uint8_t pad1;
56+
uint8_t adc_broadcast_interval; // mS
57+
uint8_t tach_broadcast_interval; // mS
58+
uint8_t pad2[2];
3959
} __attribute__((packed));
4060

41-
static_assert(sizeof(IoBoxSettings) == 8);
61+
/* Base + 0x02, 0x03, 0x04 */
62+
struct pwm {
63+
pwm_settings ch[2];
64+
} __attribute__ ((packed));
4265

43-
// Output:
44-
// ID = BASE_ID + 2 : Channel 1, 2
45-
// ID = BASE_ID + 3 : Channel 3, 4
46-
// ID = BASE_ID + 4 : Channel 5, 6
47-
struct IoBoxOutputSettings
48-
{
49-
struct {
50-
uint16_t OnPeriod;
51-
uint16_t OffPeriod;
52-
} __attribute__((packed)) Channels[2];
66+
static_assert(sizeof(pwm) == 8);
67+
68+
/* Base + 0x05 */
69+
struct pwm_last {
70+
pwm_settings ch[1];
71+
uint8_t out_state;
72+
} __attribute__ ((packed));
73+
74+
static_assert(sizeof(pwm_last) == 5);
75+
76+
/* Base + 0x08 */
77+
struct whoami {
78+
uint8_t version;
79+
uint8_t pad[3];
80+
beuint16_t pwm_period; // PWM clock periods in 0.01 uS
81+
beuint16_t tachin_period; // Tach-in clock periods in 0.01 uS
5382
} __attribute__((packed));
5483

55-
static_assert(sizeof(IoBoxOutputSettings) == 8);
84+
static_assert(sizeof(whoami) == 8);
5685

57-
// Output:
58-
// ID = BASE_ID + 5 : Channel 7, Output enables
59-
struct IoBoxOutputState
60-
{
61-
struct {
62-
uint16_t OnPeriod;
63-
uint16_t OffPeriod;
64-
} __attribute__((packed)) Channels[1];
65-
uint8_t OutputEnable; // Bitfield
66-
uint8_t : 24; // Reserved
86+
/* Base + 0x09 */
87+
struct adc14 {
88+
beuint16_t adc[4];
6789
} __attribute__((packed));
6890

69-
static_assert(sizeof(IoBoxOutputState) == 8);
91+
static_assert(sizeof(adc14) == 8);
92+
93+
/* Base + 0x0A */
94+
struct adc57 {
95+
uint8_t inputs;
96+
uint8_t pad;
97+
beuint16_t adc[3];
98+
} __attribute__((packed));
99+
100+
static_assert(sizeof(adc57) == 8);
70101

71102
} //namespace msiobox
72103

104+
static bool configured = false;
105+
static uint8_t pwm_mask = 0x00;
106+
static uint8_t tachin_mask = 0x00;
107+
static uint8_t tach_broadcast_interval = 20;
108+
109+
/*
110+
* TODO: validate
111+
* Scale value to 0 .. 1023
112+
* MegaSquirt IOBox has 0..5V ADC input range
113+
*/
114+
static uint16_t CanIoBoxGetAdc(size_t index)
115+
{
116+
/* Total 7 ADC inputs, mapping:
117+
* 1 - AUX left
118+
* 2 - AUX right
119+
* 3 - AUX out left voltage
120+
* 4 - AUX out right voltage
121+
* 5 - WBO supply voltage
122+
* 6 - Left sensor heater supply
123+
* 7 - Right sensor heater supply */
124+
switch (index) {
125+
case 1: // AUX Left
126+
case 2: // AUX Right
127+
{
128+
#if (AUX_INPUT_CHANNELS > 0)
129+
if (index > AUX_INPUT_CHANNELS) {
130+
return 0;
131+
}
132+
float voltage = GetAuxInputVoltage(index - 1);
133+
if (voltage < 0) voltage = 0;
134+
if (voltage > 5) voltage = 5;
135+
return (uint16_t)(voltage / 5.0 * 1024.0);
136+
#else
137+
return 0;
138+
#endif
139+
}
140+
case 3: // AUX out left voltage
141+
case 4: // AUX out right voltage
142+
return 0;
143+
case 5: // WBO supply voltage
144+
return 0;
145+
case 6:
146+
case 7:
147+
{
148+
const auto& sampler = GetSampler(index - 6);
149+
/* TODO: clamp */
150+
return sampler.GetInternalHeaterVoltage() / 25.5 * 1024;
151+
}
152+
default:
153+
return 0.0;
154+
}
155+
}
156+
73157
void SendMsIoBoxFormat(Configuration* configuration)
74158
{
75-
(void)configuration;
159+
// if (!configuration->ioExpanderConfig.enable_tx)
160+
// return;
161+
162+
if (!configured)
163+
return;
164+
165+
const uint32_t base = MS_IOBOX_DEVICE_ID(configuration->ioExpanderConfig.Offset);
166+
167+
if(1) // Create a scope to ensure frame is sent early
168+
{
169+
CanTxTyped<msiobox::adc14> frame1(base + MS_IOBOX_ADC14, false);
170+
171+
for (size_t i = 0; i < 4; i++) {
172+
frame1->adc[i] = CanIoBoxGetAdc(i);
173+
}
174+
}
175+
176+
if (1) // Create a scope to ensure frame is sent early
177+
{
178+
CanTxTyped<msiobox::adc57> frame2(base + MS_IOBOX_ADC57, false);
179+
180+
for (size_t i = 0; i < 3; i++) {
181+
frame2.get().adc[i] = CanIoBoxGetAdc(4 + i);
182+
}
183+
}
76184
}
77185

78-
void HandleMsIoBoxCanMessage(const CANRxFrame* msg, Configuration* configuration, struct CanStatusData* statusData)
186+
void ProcessMsIoBoxCanMessage(const CANRxFrame* fr, Configuration* cfg)
79187
{
80-
(void)msg;
81-
(void)configuration;
82-
(void)statusData;
188+
if (!(cfg->ioExpanderConfig.Protocol == CanIoProtocol::MsIoBox)) {
189+
return;
190+
}
191+
// For now only support standard IDs
192+
if (fr->IDE != CAN_IDE_STD) {
193+
return;
194+
}
195+
196+
const uint32_t base = MS_IOBOX_DEVICE_ID(cfg->ioExpanderConfig.Offset);
197+
uint32_t frame_id = fr->SID;
198+
199+
if ((frame_id < base) || (frame_id > base + MS_IOBOX_LAST_IN))
200+
return;
201+
202+
if ((frame_id == base + MS_IOBOX_PING) && (fr->DLC == 0))
203+
{
204+
CanTxTyped<msiobox::whoami> frame(base + MS_IOBOX_WHOAMI, false);
205+
206+
frame->version = 1;
207+
208+
// PWM clock periods in 0.01 uS, equal to clock freq / 100 * 1000
209+
frame->pwm_period = 5000;
210+
// Tach-in clock periods in 0.01 uS, equal to clock freq / 100 * 1000
211+
frame->tachin_period = 66;
212+
213+
return;
214+
}
215+
216+
if ((frame_id == base + MS_IOBOX_CONFIG) && (fr->DLC == sizeof(msiobox::cfg)))
217+
{
218+
const msiobox::cfg *iobox_config = reinterpret_cast<const msiobox::cfg *>(fr->data8);
219+
220+
//can0 201 [8] 00 00 00 00 14 14 00 00
221+
pwm_mask = iobox_config->pwm_mask;
222+
tachin_mask = iobox_config->tachin_mask;
223+
tach_broadcast_interval = iobox_config->tach_broadcast_interval;
224+
225+
msIoBoxTxHandler.m_intervalMs = iobox_config->adc_broadcast_interval;
226+
227+
configured = true;
228+
229+
return;
230+
}
231+
232+
if ((frame_id >= base + MS_IOBOX_SET_PWM(0)) &&
233+
(frame_id <= base + MS_IOBOX_SET_PWM(2)) &&
234+
(fr->DLC == sizeof(msiobox::pwm)))
235+
{
236+
const msiobox::pwm *pwm = reinterpret_cast<const msiobox::pwm *>(fr->data8);
237+
238+
/* Two first channels are mapped to analog outputs */
239+
if (frame_id == base + MS_IOBOX_SET_PWM(0)) {
240+
for (size_t n = 0; n < AFR_CHANNELS; n++) {
241+
// if allowed to control DAC output over CAN
242+
if (cfg->auxOutputSource[n] != AuxOutputMode::IOExpander) {
243+
continue;
244+
}
245+
246+
uint32_t period = pwm->ch[n].off + pwm->ch[n].on;
247+
float duty = (period == 0) ? 0 : (float)pwm->ch[n].on / period;
248+
SetAuxDac(n, 5.0 * duty);
249+
}
250+
}
251+
#if (PWM_OUTPUT_CHANNELS > 0)
252+
/* Second set of PWM channels */
253+
else if (frame_id == base + MS_IOBOX_SET_PWM(1)) {
254+
for (size_t n = 0; n < PWM_OUTPUT_CHANNELS; n++) {
255+
uint32_t period = pwm->ch[n].off + pwm->ch[n].on;
256+
float duty = (period == 0) ? 0 : (float)pwm->ch[n].on / period;
257+
SetAuxPwmDuty(n, duty);
258+
}
259+
}
260+
#endif
261+
262+
/* TODO: PWM periods */
263+
return;
264+
}
265+
266+
if ((frame_id == base + MS_IOBOX_SET_PWM(3)) && (fr->DLC == sizeof(msiobox::pwm_last)))
267+
{
268+
const msiobox::pwm_last *pwm = reinterpret_cast<const msiobox::pwm_last *>(fr->data8);
269+
270+
/* TODO: PWM periods and outputs */
271+
(void)pwm;
272+
273+
return;
274+
}
83275
}

0 commit comments

Comments
 (0)