Skip to content

Commit 1bd971c

Browse files
author
harbaum
committed
Add library support for TLE94108
1 parent e02859b commit 1bd971c

2 files changed

Lines changed: 244 additions & 48 deletions

File tree

ftduino/libraries/Ftduino/Ftduino.cpp

Lines changed: 233 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
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

239293
void 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+
243351
void 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

288445
static const uint8_t mc33879_highside_map[4] = {
289446
MC33879_PORT(2), MC33879_PORT(6), MC33879_PORT(4), MC33879_PORT(5) };
290447

291448
static 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
295453
void 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

716905
Ftduino::Ftduino() { }
717-

ftduino/libraries/Ftduino/Ftduino.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
#include "Arduino.h"
1111

1212
#ifndef IN_FTDUINO_LIB
13-
#if !defined(OUTPUT_DRIVER_MC33879A) && !defined(OUTPUT_DRIVER_AUTO)
14-
#error "Only the MC33879A or AUTO output drivers are currently supported!"
13+
#if !defined(OUTPUT_DRIVER_TLE94108EL) && !defined(OUTPUT_DRIVER_MC33879A) && !defined(OUTPUT_DRIVER_AUTO)
14+
#error "Only the MC33879A, TLE94108EL or AUTO output drivers are currently supported!"
1515
#endif
1616

1717
// make sure WebUSB is being used with correct settings
@@ -108,8 +108,16 @@ class Ftduino {
108108

109109
// ---------- outputs ----------------
110110
void output_init();
111-
111+
#if defined(OUTPUT_DRIVER_AUTO)
112+
static const uint8_t CHIP_UNKNOWN = 0, CHIP_MC33879 = 1, CHIP_TLE94108 = 2, CHIP_DRV8908 = 3;
113+
uint8_t spi_probe();
114+
uint8_t driver_chip;
115+
void spi_interrupt_exec_mc33879();
116+
void spi_interrupt_exec_tle94108();
117+
void (Ftduino::*spi_interrupt_exec)();
118+
#else
112119
void spi_interrupt_exec();
120+
#endif
113121
CLASS_IRQ(spi_interrupt, SPI_STC_vect);
114122

115123
uint8_t spi_state = 0; // byte/word counter for spi transmission

0 commit comments

Comments
 (0)