Hi all,
I'm working on a project that's interfacing a Teensy 4.0 with a NRF24L01+ and TLC5948 LED Driver. Because I need to bitbang at some point while communicating with the TLC5948, I'm looking for a way to disable/reenable SPI as quickly as possible, so that I can also communicate with the NRF24. I wrote my own library for the TLC5948, where you can see my current attempts at getting this to work.
Background:
Basically, the TLC5948 takes an input buffer of 257 bits (1 'type' bit that specifies whether the data controls brightness or modifies the device configuration + 32 bytes of data), which makes it really ugly for chaining when using SPI (as far as I can tell I can only send bytes, not bits with the SPI library). To get around this, I first tried to disable SPI, bit bang the first bit, then enable SPI and use an SPI.transfer() to do the majority of the work. For an Arduino, this was as simple as:
Unfortunately, doing something similar with the Teensy 4 did not work:
I ended up just writing:
Circuit Design
Which was terrible in performance compared to my other attempt (a write to two TLC5948s took 500us+ vs 75us for a shiftOut version) and at 600Mhz the TLC5948s were a bit glitchy (sometimes not turning on, sometimes turning on the wrong LEDs, etc.). Researching the glitchiness problem, I was inspired by another post on this forum (sadly I couldn't find it again) where someone noted that shiftOut could potentially be better than SPI due to the raw speed of the fastio pin-mode used by SPI (if I understood correctly) and the signal integrity effects the fast rise time has. I tried it and ended up using a shiftOut-based method that seems a lot more solid in terms of reliability (@600Mhz it's no longer glitching) and was faster (75us vs 500+ from the SPI disabling version). The problem now is that I can't talk with the NRF24 if I'm using the LED drivers since SPI disables bit-banging on those pins and I'd like to avoid repeatedly calling SPI.begin() and SPI.end(), which seems to do a lot more than I need it to (just disable the SPI module so I can write to the pins). Is this even possible? I took a look at the SPI implementation for the Teensy 4 and to be honest I'm not sure what's essential or not. It looks like a lot of mux/pin configuration is required, but something like restoring the SPISettings() (on line 1314 of SPI.cpp) might be superfluous, right? My thought is the entire SPI configuration might not have to be restored if I'm just doing some bit-banging and then starting to use SPI again, but perhaps I'm wrong. I guess the other way to do it would be to re-implement the NRF24 code as a bit-bang or re-implement the TLC5948 library to avoid bit-banging at all, but I was hoping to avoid that. I'd love to hear some wiser peoples' thoughts on this, as I'm relatively new to Teensy and don't want to re-invent the wheel.
Thanks for your time,
Will
I'm working on a project that's interfacing a Teensy 4.0 with a NRF24L01+ and TLC5948 LED Driver. Because I need to bitbang at some point while communicating with the TLC5948, I'm looking for a way to disable/reenable SPI as quickly as possible, so that I can also communicate with the NRF24. I wrote my own library for the TLC5948, where you can see my current attempts at getting this to work.
Background:
Basically, the TLC5948 takes an input buffer of 257 bits (1 'type' bit that specifies whether the data controls brightness or modifies the device configuration + 32 bytes of data), which makes it really ugly for chaining when using SPI (as far as I can tell I can only send bytes, not bits with the SPI library). To get around this, I first tried to disable SPI, bit bang the first bit, then enable SPI and use an SPI.transfer() to do the majority of the work. For an Arduino, this was as simple as:
Code:
inline void bitBangSpi1() {
noInterrupts();
SPCR &= ~_BV(SPE); // disable hw SPI (SPI.begin() stops us from writing to MOSI)
// Bit bang a '1'
PORTB |= 0b00001000; // set PB3/D11/MOSI high
PORTB |= 0b00100000; // set PB5/D13/SCLK high
PORTB &= 0b11011111; // set PB5/D13/SCLK low
SPCR |= _BV(SPE); // restore hw SPI
interrupts();
}
Unfortunately, doing something similar with the Teensy 4 did not work:
Code:
inline void bitBangSpi1() {
LPSPI4.CR &= ~LPSPI_CR_MEN;
digitalWriteFast(SIN,HIGH);
digitalWriteFast(SCLK,HIGH);
digitalWriteFast(SCLK,LOW);
digitalWriteFast(SIN,LOW);
LPSPI4.CR |= ~LPSPI_CR_MEN;
}
I ended up just writing:
Code:
inline void bitBangSpi1() {
SPI.end();
pinMode(SIN,OUTPUT);
pinMode(SCLK,OUTPUT);
digitalWriteFast(SIN,HIGH);
digitalWriteFast(SCLK,HIGH);
digitalWriteFast(SCLK,LOW);
digitalWriteFast(SIN,LOW);
SPI.begin(); // re-enable SPI
}
Which was terrible in performance compared to my other attempt (a write to two TLC5948s took 500us+ vs 75us for a shiftOut version) and at 600Mhz the TLC5948s were a bit glitchy (sometimes not turning on, sometimes turning on the wrong LEDs, etc.). Researching the glitchiness problem, I was inspired by another post on this forum (sadly I couldn't find it again) where someone noted that shiftOut could potentially be better than SPI due to the raw speed of the fastio pin-mode used by SPI (if I understood correctly) and the signal integrity effects the fast rise time has. I tried it and ended up using a shiftOut-based method that seems a lot more solid in terms of reliability (@600Mhz it's no longer glitching) and was faster (75us vs 500+ from the SPI disabling version). The problem now is that I can't talk with the NRF24 if I'm using the LED drivers since SPI disables bit-banging on those pins and I'd like to avoid repeatedly calling SPI.begin() and SPI.end(), which seems to do a lot more than I need it to (just disable the SPI module so I can write to the pins). Is this even possible? I took a look at the SPI implementation for the Teensy 4 and to be honest I'm not sure what's essential or not. It looks like a lot of mux/pin configuration is required, but something like restoring the SPISettings() (on line 1314 of SPI.cpp) might be superfluous, right? My thought is the entire SPI configuration might not have to be restored if I'm just doing some bit-banging and then starting to use SPI again, but perhaps I'm wrong. I guess the other way to do it would be to re-implement the NRF24 code as a bit-bang or re-implement the TLC5948 library to avoid bit-banging at all, but I was hoping to avoid that. I'd love to hear some wiser peoples' thoughts on this, as I'm relatively new to Teensy and don't want to re-invent the wheel.
Thanks for your time,
Will