11/*
22 Ftduino.cpp - Library for ftDuino
33
4- (c) 2017 by Till Harbaum <till@harbaum.org>
4+ (c) 2017-2022 by Till Harbaum <till@harbaum.org>
55*/
66
7+ /*
8+ * TODO:
9+ * - /CS high time by emtpy spi transfer for TLE94108
10+ *
11+ */
12+
713#include < Arduino.h>
814
915#include < avr/pgmspace.h>
@@ -221,25 +227,127 @@ uint16_t Ftduino::input_get(uint8_t ch) {
221227// (- both MOSFETs should never be enabled at the same time)
222228// PWM toggles between floating and low or floating and high
223229
224- void Ftduino::spi_interrupt_exec () {
230+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_MC33879A)
231+ #if defined(OUTPUT_DRIVER_AUTO)
232+ void Ftduino::spi_interrupt_exec_mc33879 ()
233+ #else
234+ void Ftduino::spi_interrupt_exec ()
235+ #endif
236+ {
237+ // The spi state machine counter runs through 4*64 states.
238+ // It sends 64 sequences of 32 on/off bits each.
239+
225240 if (!(spi_state & 3 )) {
226- // set /SS high and low again
227- PORTB |= (1 <<0 );
241+ // set /SS high and low again. The mc33879 can live with this being only
242+ // high for a very short moment.
243+ PORTB |= (1 <<0 );
228244 PORTB &= ~(1 <<0 );
229245
230246 // fetch next data word
231247 spi_tx_data.w = spi_tx_in[(spi_state>>2 )&(SPI_PWM_CYCLE_LEN-1 )];
232248 }
233249
250+ // Each sequence sends
251+ // two bytes to each of the two MC33879. The second byte contains
252+ // the information which drivers are to be enabled. The first
253+ // byte contains the configuration for the open load detection
254+ // and is always zero.
255+
256+ // MC33879: <00><OUT0-3><00><OUT4-7>
234257 if (spi_state & 1 ) SPDR = spi_tx_data.b [(spi_state>>1 ) & 1 ];
235258 else SPDR = 0 ;
259+
236260 spi_state++;
237261}
262+ #endif
263+
264+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_TLE94108EL)
265+ #if defined(OUTPUT_DRIVER_AUTO)
266+ void Ftduino::spi_interrupt_exec_tle94108 ()
267+ #else
268+ void Ftduino::spi_interrupt_exec ()
269+ #endif
270+ {
271+ // The spi state machine counter runs through 4*64 states.
272+ // It sends 64 sequences of 32 on/off bits each.
273+
274+ if (!(spi_state & 1 )) {
275+ // set /SS high and low again. The TLE94108 needs this to be high for
276+ // at least 5us (t_CSNH)
277+ PORTB |= (1 <<0 );
278+ _delay_us (4 );
279+ PORTB &= ~(1 <<0 );
280+
281+ // fetch next data word
282+ spi_tx_data.w = spi_tx_in[(spi_state>>2 )&(SPI_PWM_CYCLE_LEN-1 )];
283+ }
284+
285+ // TLE94108: <ADR0><HB_ACT_1_CTRL><ADR1><HB_ACT_2_CTRL>
286+ if (spi_state & 1 ) SPDR = spi_tx_data.b [(spi_state>>1 ) & 1 ];
287+ else SPDR = (spi_state & 2 )?0b11000011 :0b10000011 ;
288+
289+ spi_state++;
290+ }
291+ #endif
238292
239293void Ftduino::spi_interrupt () {
294+ // Use chip specific handler routine
295+ #if defined(OUTPUT_DRIVER_AUTO)
296+ (ftduino.*ftduino.spi_interrupt_exec )();
297+ #else
240298 ftduino.spi_interrupt_exec ();
299+ #endif
241300}
242301
302+ #if defined(OUTPUT_DRIVER_AUTO)
303+ uint8_t Ftduino::spi_probe () {
304+ // try to figure out which chip is installed. This may either be a pair of MC33879
305+ // in ftDuinos up to V1.3 or the TLE94108 or the DRV8908 in newer ones starting with
306+ // V1.4
307+
308+ // sending this 0x00 will trigger a SPI ERR on TLE94108EL, however
309+ // it will not trigger anything on MC33879A
310+
311+ // send a single 00 byte. The TLE94108 will set SPI error as the first bit
312+ // always needs a set first bit. Actually sending only one byte also triggers the
313+ // spi error
314+ delay (1 ); // give control signals a millisecond to settle
315+ PORTB &= ~(1 <<0 ); // drive /SS low
316+ _delay_us (1 ); // TLE94108EL needs this pause, MC33879A doesn't care
317+ SPDR = 0x00 ; while (!(SPSR & (1 <<SPIF))); // wait for SPI transfer to end
318+ _delay_us (1 );
319+ PORTB |= (1 <<0 );
320+
321+ _delay_us (5 );
322+ PORTB &= ~(1 <<0 );
323+ _delay_us (1 );
324+ SPDR = 0x00 ; while (!(SPSR & (1 <<SPIF))); // wait for SPI transfer to end
325+ uint8_t r = SPDR;
326+ _delay_us (1 );
327+ PORTB |= (1 <<0 ); // drive /SS high
328+
329+ // 1. no chip at all connected returns 0xff (floating input, may vary)
330+ // 2. MC33879 returns 0x00
331+ // 3. TLE94018 returns bit 0 set on second read (not bit 7 since SPI is configured MSB first,
332+ // while TLE94108 is LSB first. Without VS (9V) will report 0x05, otherwise 0x01
333+ // 4. DRV8908 returns C0 (C4 with no power) as upper two bits are always one with this
334+ // chip. The DRV8908 is MSB first
335+
336+ // test for MC33879: This simply returns 0x00
337+ if (!r) return CHIP_MC33879;
338+
339+ // test for TLE94108: ignore undervoltage bit 4, all other bits should be
340+ // zero except bit 0 which reports a SPI error as 0x00 is not a valid request
341+ if ((r & ~4 ) == 0x01 ) return CHIP_TLE94108;
342+
343+ // test for drv8908: The two upper bits are always 1, ignore undervoltage
344+ // bit 4, all other bits should be zero
345+ if ((r & ~4 ) == 0xc0 ) return CHIP_DRV8908;
346+
347+ return CHIP_UNKNOWN;
348+ }
349+ #endif
350+
243351void Ftduino::output_init () {
244352 uint8_t i;
245353
@@ -259,8 +367,22 @@ void Ftduino::output_init() {
259367 // enable SPI, enable interrupts, MSB first, Master mode,
260368 // mode 1 (SCK low when idle, data valid on falling edge)
261369 // and clock = FCPU/64 = 250khz
370+
371+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_DRV8908)
372+ // Don't enable interrupt yet to not affect probing. Also
373+ // don't enable it if hard configured for DRV8908 as that
374+ // doesn't use interrupts at all
375+
376+ // DORD: 0=MSB first, 1=LSB first
377+ // CPOL: 0=SCK low when idle, 1=SCK high when idle
378+ // CPHA: 0=sample on first edge, 1=sample on second edge
379+ // SPR1/0: 00=f/4, 01=/16, 10=/64, 11=/128 (250kHz)
380+ SPCR = (0 <<SPIE) | (1 <<SPE) | (0 <<DORD) | (1 <<MSTR) |
381+ (0 <<CPOL) | (1 <<CPHA) | (1 <<SPR1) | (0 <<SPR0);
382+ #else
262383 SPCR = (1 <<SPIE) | (1 <<SPE) | (0 <<DORD) | (1 <<MSTR) |
263384 (0 <<CPOL) | (1 <<CPHA) | (1 <<SPR1) | (0 <<SPR0);
385+ #endif
264386
265387 SPSR &= ~(1 <<SPI2X); // single speed
266388
@@ -269,68 +391,135 @@ void Ftduino::output_init() {
269391 DDRE |= (1 <<6 );
270392 PORTE |= (1 <<6 ); // not in sleep mode
271393
272- // PB.7 is the PWM input of the IN6 of O2. We don't use that feature by
273- // now, so it's just pulled down
394+ // For MC33879 PB.7 is the PWM input of the IN6 of O2. We don't use that
395+ // feature in this driver, so it's just pulled down. This is also ok with
396+ // the TLE94108 which has this pin grounded. For the DRV8908 this is the
397+ // fault output which is an open collector output. Grounding that is also
398+ // not a problem.
274399 DDRB |= (1 <<7 );
275400 PORTB &= ~(1 <<7 ); // no PWM on IN6
401+
402+ #if defined(OUTPUT_DRIVER_AUTO)
403+ // probe for chip type
404+ driver_chip = spi_probe ();
405+
406+ // setup interrupt vector
407+ if (driver_chip == CHIP_MC33879)
408+ spi_interrupt_exec = &Ftduino::spi_interrupt_exec_mc33879;
409+ else if (driver_chip == CHIP_TLE94108)
410+ spi_interrupt_exec = &Ftduino::spi_interrupt_exec_tle94108;
411+
412+ // finally enable SPI interrupt for the chips using soft-pwm
413+ if ((driver_chip == CHIP_MC33879) || (driver_chip == CHIP_TLE94108))
414+ SPCR |= (1 <<SPIE);
415+ #endif
416+
417+ // in case of TLE94108 switch to MSB first
418+ #if defined(OUTPUT_DRIVER_AUTO)
419+ if (driver_chip == CHIP_TLE94108)
420+ #endif
421+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_TLE94108EL)
422+ SPCR |= (1 <<DORD);
423+ #endif
276424
277- // clear SPI PWM table
278- for (i=0 ;i<SPI_PWM_CYCLE_LEN;i++)
279- spi_tx_in[i] = 0 ;
425+ #if defined(OUTPUT_DRIVER_AUTO)
426+ if ((driver_chip == CHIP_MC33879) || (driver_chip == CHIP_TLE94108))
427+ #endif
428+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_MC33879A) || defined(OUTPUT_DRIVER_TLE94108EL)
429+ {
430+ // clear SPI PWM table
431+ for (i=0 ;i<SPI_PWM_CYCLE_LEN;i++)
432+ spi_tx_in[i] = 0 ;
280433
281- // trigger first transfer
282- spi_state = 0 ;
283- SPDR = 0x00 ;
434+ // trigger first transfer. This happens without /CE being asserted, so the
435+ // driver chips won't notice this. But this gets the transfer interrupts going
436+ spi_state = 0 ;
437+ SPDR = 0x00 ;
438+ }
439+ #endif
284440}
285441
442+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_MC33879A)
286443#define MC33879_PORT (a ) (1 <<(a-1 ))
287444
288445static const uint8_t mc33879_highside_map[4 ] = {
289446 MC33879_PORT (2 ), MC33879_PORT (6 ), MC33879_PORT (4 ), MC33879_PORT (5 ) };
290447
291448static const uint8_t mc33879_lowside_map[4 ] = {
292449 MC33879_PORT (8 ), MC33879_PORT (1 ), MC33879_PORT (7 ), MC33879_PORT (3 ) };
450+ #endif
293451
294452// any SPI transfer transfers 16 bits per mc33879 and 32 bits in total
295453void Ftduino::output_set (uint8_t port, uint8_t mode, uint8_t pwm) {
296-
297- // mc33879 MSB = Open Load Detection Current On/OFF
298- // mc33879 LSB = Output ON/OFF
299-
300- // MOSFET connections according to schematic
301- // O1/O5 -> highside S2, lowside D8
302- // O2/O6 -> highside S6, lowside D1
303- // O3/O7 -> highside S4, lowside D7
304- // O4/O8 -> highside S5, lowside D3
305-
306- // Never enable at the same time:
307- // channel 2 and 8, 6 and 1, 4 and 7, 5 and 3
308-
309- // O1-O4 = first 33879 in chain
310- // O5-O8 = second 33879 in chain
311-
454+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_MC33879A) || defined(OUTPUT_DRIVER_TLE94108EL)
312455 uint32_t set_mask = 0 , clr_mask = 0 ;
456+ #endif
457+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_MC33879A)
458+ #if defined(OUTPUT_DRIVER_AUTO)
459+ if (driver_chip == CHIP_MC33879)
460+ #endif
461+ {
462+ // mc33879 MSB = Open Load Detection Current On/OFF
463+ // mc33879 LSB = Output ON/OFF
464+
465+ // MOSFET connections according to schematic
466+ // O1/O5 -> highside S2, lowside D8
467+ // O2/O6 -> highside S6, lowside D1
468+ // O3/O7 -> highside S4, lowside D7
469+ // O4/O8 -> highside S5, lowside D3
470+
471+ // Never enable at the same time:
472+ // channel 2 and 8, 6 and 1, 4 and 7, 5 and 3
473+
474+ // O1-O4 = first 33879 in chain
475+ // O5-O8 = second 33879 in chain
476+
477+ // mode can be 0 (floating), 1 (high) or 2 (low)
478+ if (mode == 1 ) {
479+ set_mask = mc33879_highside_map[port&3 ]; // highside to be set
480+ clr_mask = mc33879_lowside_map[port&3 ]; // lowside to be cleared
481+ } else if (mode == 2 ) {
482+ set_mask = mc33879_lowside_map[port&3 ]; // lowside to be set
483+ clr_mask = mc33879_highside_map[port&3 ]; // highside to be cleared
484+ } else {
485+ set_mask = 0 ; // nothing to be set
486+ clr_mask = mc33879_highside_map[port&3 ] | // highside and lowside to be cleared
487+ mc33879_lowside_map[port&3 ];
488+ }
313489
314- // mode can be 0 (floating), 1 (high) or 2 (low)
315- if (mode == 1 ) {
316- set_mask = mc33879_highside_map[port&3 ]; // highside to be set
317- clr_mask = mc33879_lowside_map[port&3 ]; // lowside to be cleared
318- } else if (mode == 2 ) {
319- set_mask = mc33879_lowside_map[port&3 ]; // lowside to be set
320- clr_mask = mc33879_highside_map[port&3 ]; // highside to be cleared
321- } else {
322- set_mask = 0 ; // nothing to be set
323- clr_mask = mc33879_highside_map[port&3 ] | // highside and lowside to be cleared
324- mc33879_lowside_map[port&3 ];
490+ // ports O1..O4 (0-3) are in MSB, ports O5-O8 (4-7) are in MSB of lower 16 bit
491+ if (!(port & 4 )) {
492+ set_mask <<= 8 ;
493+ clr_mask <<= 8 ;
494+ }
325495 }
496+ #endif
326497
327- // ports O1..O4 (0-3) are in MSB, ports O5-O8 (4-7) are in MSB of lower 16 bit
328- if (!(port & 4 )) {
329- set_mask <<= 8 ;
330- clr_mask <<= 8 ;
498+ #if defined(OUTPUT_DRIVER_AUTO) || defined(OUTPUT_DRIVER_TLE94108EL)
499+ #if defined(OUTPUT_DRIVER_AUTO)
500+ if (driver_chip == CHIP_TLE94108)
501+ #endif
502+ {
503+ // TLE94108
504+ // mode can be 0 (floating), 1 (high) or 2 (low)
505+ // unlike the 33879, the outputs of the 94108 are ordered nicely
506+ if (mode == 1 ) {
507+ set_mask = (2 <<(2 *port)); // set highside
508+ clr_mask = (1 <<(2 *port)); // clear lowside
509+ } else if (mode == 2 ) {
510+ set_mask = (1 <<(2 *port)); // set lowside
511+ clr_mask = (2 <<(2 *port)); // clear highside
512+ } else {
513+ set_mask = 0 ;
514+ clr_mask = (3 <<(2 *port)); // highside and lowside to be cleared
515+ }
331516 }
517+ #endif
518+
519+ // Serial.print("SET/CLR "); Serial.print(set_mask, HEX); Serial.print(" "); Serial.println(clr_mask, HEX);
520+ // Serial.print("STATE "); Serial.println(spi_state, HEX);
332521
333- // set 0..32 entries in the PWN table according the requested PWM
522+ // set entries 0..63 in the PWN table according the requested PWM
334523 for (uint8_t i=0 ;i<SPI_PWM_CYCLE_LEN;i++) {
335524 if (i<pwm) {
336525 spi_tx_in[i] |= set_mask;
@@ -714,4 +903,3 @@ void Ftduino::init() {
714903}
715904
716905Ftduino::Ftduino () { }
717-
0 commit comments