rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
mc33972.cpp
Go to the documentation of this file.
1/*
2 * mc33972.cpp
3 * looks like chip is not very available these days TODO https://github.com/rusefi/rusefi/issues/5733
4 *
5 * Multiple Switch Detection Interface with Suppressed Wake-up
6 *
7 * TODO: add analog muxing part of the driver
8 *
9 * The 33972 Multiple Switch Detection Interface with suppressed
10 * wake-up is designed to detect the closing and opening of up to 22
11 * switch contacts: 14 switch to ground detection and 8 switch to
12 * ground or battery detection,
13 *
14 * SPI protocol 3.3/5.0V
15 *
16 * @date Apr 07, 2019
17 * @author Andrey Gusakov <dron0gus@gmail.com>, (c) 2019
18 */
19
20#include "global.h"
21#include "gpio/gpio_ext.h"
22#include "gpio/mc33972.h"
23
24#if (BOARD_MC33972_COUNT > 0)
25
26/*
27 * TODO list:
28 * - add irq support with fallback to polling mode (now polling mode only)
29 */
30
31/*==========================================================================*/
32/* Driver local definitions. */
33/*==========================================================================*/
34
35#define DRIVER_NAME "mc33972"
36
43
44#define SP_BANK 0
45#define SG_BANK 1
46
47/* all commands and reply data are 24 bit */
48#define CMD(cmd, data) (((cmd) << 16) | ((data) << 0))
49
50#define CMD_STATUS CMD(0x00, 0x00)
51#define CMD_SETTINGS(mask) CMD(0x01, (mask))
52#define CMD_WAKEUPEN(i, mask) CMD(0x02 + (i), (mask))
53#define CMD_METALLIC(i, mask) CMD(0x04 + (i), (mask))
54#define CMD_ANALOG(curr, ch) CMD(0x06, (((curr) & 0x3) << 5) | (((ch) & 0x1f) << 0))
55#define CMD_WETTING_TMR(i, mask) CMD(0x07 + (i), (mask))
56#define CMD_TRI_STATE(i, mask) CMD(0x09 + (i), (mask))
57#define CMD_CALIB CMD(0x0b, 0x00)
58#define CMD_SLEEP(int_t, scan_t) CMD(0x0c, (((int_t) & 0x7) << 3) | (((scan_t) & 0x7) << 0))
59#define CMD_RST CMD(0x7f, 0x00)
60
61/* all switch to battery or ground pins mask */
62#define SP_PINS_MASK 0x00ff
63#define SP_PINS_EXTRACT(pins) (((pins) >> 14) & SP_PINS_MASK)
64/* all switch-to-ground inputs mask */
65#define SG_PINS_MASK 0x3fff
66#define SG_PINS_EXTRACT(pins) (((pins) >> 0) & SG_PINS_MASK)
67
68/* reply is allways same 24 status bits */
69#define FLAG_THERM (1 << 23)
70#define FLAG_INT (1 << 22)
71/* from LSB to MSB: SG0..SG13, SP0..SP7 */
72#define PIN_MASK(pin) (BIT(pin))
73
74/*==========================================================================*/
75/* Driver exported variables. */
76/*==========================================================================*/
77
78/*==========================================================================*/
79/* Driver local variables and types. */
80/*==========================================================================*/
81
82/* Driver */
83struct Mc33972 : public GpioChip {
84 int init() override;
85 int deinit() override;
86
87 // These functions need not be implemented if not supported by the particular chip.
88 /* pin argument is pin number within gpio chip, not a global number */
89 int setPadMode(size_t pin, iomode_t mode) override;
90 int readPad(size_t pin) override;
91 brain_pin_diag_e getDiag(size_t pin) override;
92
93 // internal functions
94
95 int chip_init();
96 int update_pullups();
97 int update_status();
98
99 void wake_driver();
100
101 int spi_w(uint32_t tx);
102
103 int comm_test();
104
105
106
107 const struct mc33972_config *cfg;
108
109 /* thread stuff */
110 thread_t *thread;
111 THD_WORKING_AREA(thread_wa, 256);
112 semaphore_t wake;
113
114 /* last input state from chip */
115 uint32_t i_state;
116 /* currently selected analog input */
117 uint8_t analog_ch;
118 /* enabled inputs */
119 uint32_t en_pins;
120
121 mc33972_drv_state drv_state;
122};
123
124static Mc33972 chips[BOARD_MC33972_COUNT];
125
126/*==========================================================================*/
127/* Driver local functions. */
128/*==========================================================================*/
129
130/**
131 * @brief MC33972 send cmd routine.
132 * @details Sends 24 bits of data. CS asserted before and released
133 * after transaction. Chip allways reply with input state + 2 bits
134 * of diagnostic. This routine save it to chip->i_state
135 */
136
137int Mc33972::spi_w(uint32_t tx)
138{
139 int i;
140 uint8_t rxb[3];
141 uint8_t txb[3];
142 SPIDriver *spi = cfg->spi_bus;
143
144 txb[0] = (tx >> 16) & 0xff;
145 txb[1] = (tx >> 8) & 0xff;
146 txb[2] = (tx >> 0) & 0xff;
147 /* Acquire ownership of the bus. */
148 spiAcquireBus(spi);
149 /* Setup transfer parameters. */
150 spiStart(spi, &cfg->spi_config);
151 /* Slave Select assertion. */
152 spiSelect(spi);
153 /* Atomic transfer operations. */
154 for (i = 0; i < 3; i++)
155 rxb[i] = spiPolledExchange(spi, txb[i]);
156 /* Slave Select de-assertion. */
157 spiUnselect(spi);
158 /* Ownership release. */
159 spiReleaseBus(spi);
160
161 /* save received data */
162 i_state = (rxb[0] << 16) | (rxb[1] << 8) | (rxb[2] << 0);
163
164 /* no errors for now */
165 return 0;
166}
167
168/**
169 * @brief MC33972 update status
170 * @details Chip reply with input data and two bits of diag
171 */
172
173int Mc33972::update_status()
174{
175 return spi_w(CMD_STATUS);
176}
177
178int Mc33972::update_pullups()
179{
180 int ret;
181
182 /* enable tri-state for all unused pins */
183 ret = spi_w(CMD_TRI_STATE(SP_BANK, SP_PINS_EXTRACT(~en_pins)));
184 if (ret)
185 return ret;
186 ret = spi_w(CMD_TRI_STATE(SG_BANK, SG_PINS_EXTRACT(~en_pins)));
187
188 return ret;
189}
190
191int Mc33972::comm_test()
192{
193 int ret;
194
195 /* After an input has been selected as the analog,
196 * the corresponding bit in the next SO data stream
197 * will be logic [0] */
198
199 /* go throught all inputs, read inputs status and
200 * check that muxed input bit is zero */
201 for (int i = 0; i < MC33972_INPUTS; i++) {
202 /* indexed starting from 1 */
203 ret = spi_w(CMD_ANALOG(0, i + 1));
204 if (ret)
205 return ret;
206 ret = update_status();
207 if (ret)
208 return ret;
209
210 if (i_state & PIN_MASK(i))
211 return -1;
212 }
213
214 return 0;
215}
216
217/**
218 * @brief MC33972 chip init.
219 * @details There is no way to check communication.
220 * Performs reset.
221 */
222
223int Mc33972::chip_init()
224{
225 int ret;
226
227 /* reset first */
228 ret = spi_w(CMD_RST);
229 if (ret)
230 return ret;
231
232 /* is it enought? */
233 chThdSleepMilliseconds(3);
234
235 /*
236 * Default settings from Power-ON Reset via V PWR or the
237 * Reset Command are as follows:
238 * * Programmable switch - set to switch to battery
239 * * All inputs set as wake-up
240 * * Wetting current on (16 mA)
241 * * Wetting current timer on (20 ms)
242 * * All inputs tri-state
243 * * Analog select 00000 (no input channel selected)
244 */
245
246 /* check communication */
247 ret = comm_test();
248 if (ret)
249 return ret;
250
251 /* disable tri-state for used pins only */
252 ret = update_pullups();
253 if (ret)
254 return ret;
255
256 /* Set wetting current to 2 mA */
257 ret = spi_w(CMD_METALLIC(SP_BANK, 0));
258 if (ret)
259 return ret;
260 ret = spi_w(CMD_METALLIC(SG_BANK, 0));
261 if (ret)
262 return ret;
263
264 return 0;
265}
266
267/**
268 * @brief MC33972 chip driver wakeup.
269 * @details Wake up driver. Will cause input and diagnostic
270 * update
271 */
272void Mc33972::wake_driver()
273{
274 /* Entering a reentrant critical zone */
275 chibios_rt::CriticalSectionLocker csl;
276
277 chSemSignalI(&wake);
278 if (!port_is_isr_context()) {
279 /**
280 * chSemSignalI above requires rescheduling
281 * interrupt handlers have implicit rescheduling
282 */
283 chSchRescheduleS();
284 }
285}
286
287/*==========================================================================*/
288/* Driver thread. */
289/*==========================================================================*/
290
291static THD_FUNCTION(mc33972_driver_thread, p)
292{
293 int ret;
294 Mc33972 *chip = reinterpret_cast<Mc33972*>(p);
295
296 chRegSetThreadName(DRIVER_NAME);
297
298 /* repeat init until success */
299 do {
300 ret = chip->chip_init();
301 if (ret) {
302 chThdSleepMilliseconds(1000);
303 continue;
304 }
305 /* else */
306 chip->drv_state = MC33972_READY;
307 } while (chip->drv_state != MC33972_READY);
308
309 while(1) {
310 msg_t msg = chSemWaitTimeout(&chip->wake, TIME_MS2I(MC33972_POLL_INTERVAL_MS));
311
312 if ((chip->cfg == NULL) ||
313 (chip->drv_state == MC33972_DISABLED) ||
314 (chip->drv_state == MC33972_FAILED))
315 continue;
316
317 if (msg == MSG_TIMEOUT) {
318 /* only input state update */
319 ret = chip->update_status();
320 } else {
321 /* someone waked thread and asks us to update pin config */
322 /* inputs state is also read */
323 ret = chip->update_pullups();
324 }
325
326 if (ret) {
327 /* set state to MC33972_FAILED? */
328 }
329 }
330}
331
332/*==========================================================================*/
333/* Driver interrupt handlers. */
334/*==========================================================================*/
335
336/* TODO: add IRQ support */
337
338/*==========================================================================*/
339/* Driver exported functions. */
340/*==========================================================================*/
341
342int Mc33972::setPadMode(size_t pin, iomode_t mode) {
343 if (pin >= MC33972_INPUTS)
344 return -1;
345
346 /* currently driver doesn't know how to hanlde different modes */
347 (void)mode;
348
349 /* NOTE: currently this driver supports only input mode...
350 * while chip can output by manipulating with pull-up and
351 * pull-down modes....
352 * if this function is called for pin, that means someone
353 * wants to read this pin and we can enable pull-up
354 * Also pull down is not supported yet */
355 en_pins |= PIN_MASK(pin);
356
357 /* ask for reinit */
358 wake_driver();
359
360 return 0;
361}
362
363int Mc33972::readPad(size_t pin) {
364 if (pin >= MC33972_INPUTS)
365 return -1;
366
367 /* convert to some common enum? */
368 return !!(i_state & PIN_MASK(pin));
369}
370
371brain_pin_diag_e Mc33972::getDiag(size_t pin) {
372 brain_pin_diag_e diag = PIN_OK;
373
374 if (pin >= MC33972_INPUTS)
375 return PIN_UNKNOWN;
376
377 /* one diag bit for all pins */
378 if (i_state & FLAG_THERM)
379 diag = PIN_DRIVER_OVERTEMP;
380
381 return diag;
382}
383
384int Mc33972::init()
385{
386 /* no pins enabled yet */
387 en_pins = 0x0000;
388
389 /* init semaphore */
390 chSemObjectInit(&wake, 0);
391
392 /* start thread */
393 thread = chThdCreateStatic(thread_wa, sizeof(thread_wa),
394 PRIO_GPIOCHIP, mc33972_driver_thread, this);
395
396 return 0;
397}
398
399int Mc33972::deinit()
400{
401 /* TODO: disable pulls for all pins? */
402
403 /* stop thread */
404 chThdTerminate(thread);
405
406 return 0;
407}
408
409/**
410 * @brief MC33972 driver add.
411 * @details Checks for valid config
412 */
413
414int mc33972_add(brain_pin_e base, unsigned int index, const struct mc33972_config *cfg)
415{
416 /* no config or no such chip */
417 if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_MC33972_COUNT))
418 return -1;
419
420 /* check for valid cs.
421 * TODO: remove this check? CS can be driven by SPI */
422 if (!cfg->spi_config.ssport) {
423 return -1;
424 }
425
426 Mc33972& chip = chips[index];
427
428 /* already initted? */
429 if (chip.cfg != NULL)
430 return -1;
431
432 chip.cfg = cfg;
433 chip.i_state = 0;
434 chip.drv_state = MC33972_WAIT_INIT;
435
436 /* register, return gpio chip base */
437 return gpiochip_register(base, DRIVER_NAME, chip, MC33972_INPUTS);
438}
439
440#else /* BOARD_MC33972_COUNT > 0 */
441
442int mc33972_add(brain_pin_e base, unsigned int index, const struct mc33972_config *cfg)
443{
444 (void)base; (void)index; (void)cfg;
445
446 return -1;
447}
448
449#endif /* BOARD_MC33972_COUNT */
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
Definition core.cpp:186
uint32_t iomode_t
Digital I/O modes.
Definition hal_pal_lld.h:83
mc33972_drv_state
Definition mc33972.cpp:37
@ MC33972_READY
Definition mc33972.cpp:40
@ MC33972_FAILED
Definition mc33972.cpp:41
@ MC33972_WAIT_INIT
Definition mc33972.cpp:39
@ MC33972_DISABLED
Definition mc33972.cpp:38
static THD_FUNCTION(mc33972_driver_thread, p)
Definition mc33972.cpp:291
static Mc33972 chips[BOARD_MC33972_COUNT]
Definition mc33972.cpp:124
int mc33972_add(brain_pin_e base, unsigned int index, const struct mc33972_config *cfg)
MC33972 driver add.
Definition mc33972.cpp:414
brain_pin_diag_e
brain_pin_e pin
Definition stm32_adc.cpp:15
static THD_WORKING_AREA(storageManagerThreadStack, 3 *UTILITY_THREAD_STACK_SIZE)
virtual brain_pin_diag_e getDiag(size_t)
Definition gpio_ext.h:31
virtual int readPad(size_t)
Definition gpio_ext.h:29
virtual int deinit()
Definition gpio_ext.h:32
virtual int init()=0
virtual int setPadMode(size_t, iomode_t)
Definition gpio_ext.h:27
SPIConfig spi_config
Definition mc33972.h:23
SPIDriver * spi_bus
Definition mc33972.h:22