Controlling things with the Teensy without the CPU's involvement?

Status
Not open for further replies.

Kuba0040

Well-known member
Hello!
I want to connect my Teensy 4.0 board up to some older chips. I want the teensy to be able to communicate with them. Now these chips, being you know… old. Want their digital signals to be held high or low for let’s say… a minimum of 250ns. That would constitute a clock of 4MHz.

Now, the digitalWriteFast function is waaaay to fast for these chips, now I could just add nops in between the digitalWriteFast commands, but that’s just wasting CPU time, and I just have a feeling that on ARM, there must be a better way to do this than just brainless bit-banging with the CPU.

I’ve seen in the past the maker Bitluni use on the ESP32 some mysterious I2S pipeline with FIFO buffers to drive a VGA display without the CPU’s involvement! Is there a way to do something similar where I could setup some hardware in the Teensy to communicate with this old chip, and it would keep the signals at correct clocks and bit bang the data out by itself? And the teensy’s CPU, would when it wants to communicate with the chip, just send to that special hardware module a message like: “Hey! Send out this binary string:”. And the hardware would do the bit banging itself.

So, basically, I am asking does a module like this exist in the Teensy 4.0? If so what’s its name so I can learn how to use it.

If it helps, the chip I am trying to communicate with (YM3014) only has 3 wires, it works like a shift register so pull LOAD low, then send bits one after another pulsing the CLOCK line high every time you do so, then halfway in pull LOAD high, and then once you have sent all the bits just pull LOAD low. I would like to just be able to give the hardware module in the teensy the bits to send, and it would do all the communication work all by itself, so the CPU can do other things.
 
You could possibly try to have the SPI module do that. Use the clock of the SPI as the clock for the chip, the MOSI as the SD and you could even choose a CS of the SPI module to connect to the LOAD. If you use pin 10 as your CS/LOAD, you can get inspiration from my post here https://forum.pjrc.com/threads/68521-Reading-ADS868-values-with-hardware-CS-pin-and-DMA.

Your SPI setup function could look like this:

Code:
void adc_setup() {
  SPI.setCS(10); /* Connect pin 10 to LOAD pin on YM3014 */
  LPSPI4_TCR |= LPSPI_TCR_FRAMESZ(15); /* Every transfer will automatically shift out 15 + 1 bits */
  LPSPI4_CFGR1 |= LPSPI_CFGR1_PCSPOL(1); /* Inverts the polarity of the CS pin, so that the edge falls at the end of the frame */
  SPI.begin(); /* Starts the SPI moule with 4MHz clock by default. Isn't that convenient? ;) */
}

After this, everything you write to LPSPI4_TDR will be automatically shifted out. Example:

Code:
void loop() {
  LPSPI4_TDR = 0x1234; /* Transmit 0x1234 */
  delay(100); /* Or do something else */
}

You can write to LPSPI4_TDR as fast as you want, which will queue up the data to be sent. Keep in mind that the queue has 16 spots available. After the queue is full, you can't write anymore. You'll have to wait for the module to shift out the bytes in the queue to add more data to send.

Also keep in mind that the module will "receive" data (which will appear as zeros since you won't have anything connected to the MISO pin) at the same time as it is sending. If the receive queue fills up, this will also prevent you from sending further. To fix that you could flush the receive queue before trying to send anything. Example:

Code:
void loop() {
  LPSPI4_CR |= LPSPI_CR_RRF; /* Reset receive queue */
  LPSPI4_TDR = 0x1234; /* Transmit 0x1234 */
  delay(100); /* Or do something else */
}
 
Hello,
I've gotten the SPI to work at the correct clock and all other critical parameters, however I have ran into a slight problem. While I was figuring out how to communicate with the YM3014 using CPU-driven bit-banging I noticed something. This chip absolutely requires one more clock pulse after LOAD goes low (end of frame) to convert the signal. By looking into the datasheet, I've figured out that this is due to the LOAD signal being delayed internally within the chip and because of that it needs that just one more clock pulse. Is it possible to send that one more clock pulse after LOAD goes low or is that something that SPI simply cannot do?

Here is what I mean, this is what we currently have. The yellow signal is LOAD, the green one is CLOCK. Notice how when LOAD goes low, the clock also stops sending any pulses.
without.png
And here is what we need, notice that one extra clock pulse after LOAD has already gone low.
with_ext_clock.png

If generating the extra clock pulse is not possible then maybe, could I attach an external clock to both the teensy and the YM3014? This way the clock would always be running and the YM3014 would be happy and the teensy could just synchronize it's LOAD and MOSI signals with this external clock. I'm doubtful you can do that as SPI has a master driven clock but hey, worth asking right?

Thank You for the help.
 
Another approach would be to use the CPU, but use an interrupt routine to perform the bit-bang. One of the spare timers could generate an interrupt every 250ns to output the next bit in your serial data stream, and handle the needed final clock pulse too. For something like you're describing the overhead should be pretty low.

For some purposes I've done things like outputting a tone on one pin and connecting it to another pin, set to be an input. Then attach an interrupt to the input pin. No deep dive to figure out timer registers needed. Not elegant but perhaps good enough to do some verification that the rest of your code works OK.

Nice 'scope by the way. I just got myself a Hantek DSO.
 
If you really want to go all in, you could look into FlexIO. There should already be some libraries out there, although I personally don't have any experience with those. It should allow you to program any arbitrary IO interface.
 
Status
Not open for further replies.
Back
Top