Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: Precise Serial Data Output Clock Speed?

  1. #1
    Junior Member
    Join Date
    Apr 2019
    Posts
    9

    Precise Serial Data Output Clock Speed?

    For a project I'm working on I'll need to output data at almost exactly 4.3MHz and 8.6MHz from one digital pin. Right now I'm using DMA transfers to the SPI peripheral, but I've noticed that no matter what SPISettings I pass in for SPI.beginTransaction, my oscilloscope measures that the SCK pin only ever outputs multiples of 1MHz. This might cut it for the 4.3Mb/s speed, but definitely not for 8.6Mb/s.

    I should note that although I'm using the SPI peripheral right now, I'm only actually using the MOSI pin and leaving SCK unconnected since the target device has its own clock recovery mechanism.

    (Okay, that's not totally honest. I noticed that MOSI goes high between SCK pulses after each byte for some reason, so in reality I'm using a CMOS AND gate to filter that out and I'm actually trying to use SCK&MOSI as my output. that's not totally relevant though, I think.)

    I'm using the Teensy 3.6 right now (for the clock speed and convenient SD card access) but I wouldn't object to using a different micro controller as long as I can still use DMA for the data output. I figured I could probably try replacing the crystal with something slightly off in order to make the difference, but I feel like that's abusing the hardware. What alternatives are there?

    I doubt the source code would help much, but here's what I've got right now:
    Code:
    #include <Arduino.h>
    #include "DMAChannel.h"
    #include <spi.h>
    #include <SdFs.h>
    
    //SD CARD CARGO CODE=========================================================
    #define SD_FAT_TYPE 3
    
    const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
    #define SD_CONFIG SdioConfig(FIFO_SDIO)
    SdFs sd;
    FsFile file;
    //===========================================================================
    
    DMAChannel dmachannel;
    uint32_t data[256];
    
    void dmaFinishedTest() {  
      digitalWrite(33,HIGH);
    }
    
    void dmaSpiTest() {
      for (uint16_t i = 0; i < 256; i++) {
        data[i] = 0b10101010;
      }
    
      // Setup the SPI clocks and pin configurations
      SPI.begin();
      SPI.beginTransaction(SPISettings(4300000, MSBFIRST, SPI_MODE0)); //this gives me a real output speed of 5Mb/s for some reason
    
      // Setup SPI for DMA transfer
      SPI0_SR = 0xFF0F0000;
      SPI0_RSER = 0x00;
      SPI0_RSER = SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS; // Make sure SPI triggers a DMA transfer after each transmit
      
      dmachannel.sourceBuffer(data, 256); // The data for which we wish to transmit and its length
      dmachannel.destination((volatile uint8_t&)SPI0_PUSHR); // Move data into the SPI FIFO register
      dmachannel.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI0_TX); // Only transfer data once the previous byte has been transmited (This is to ensure all bytes are sent)
      dmachannel.disableOnCompletion(); // Stop after transmitting all 256 bytes
      // dmachannel.interruptAtCompletion();
      dmachannel.attachInterrupt(dmaFinishedTest);
      
      pinMode(33,OUTPUT);
      dmachannel.enable(); // Begin transmit
    }
    
    /** Wait for and consume a keypress over USB **/
    void waitForKeyPress()
    {
      Serial.println("\nPress a key to continue\n");
      while(!Serial.available());
      while(Serial.available())
      {
        Serial.read();
      }
    }
    
    void sdCardTest() {
      if (!sd.begin(SD_CONFIG)) {
        Serial.println(F("sd card?"));
        while(true);
      }
    
      if (!file.open("test", FILE_READ)) {
        Serial.println(F("file?"));
        while(true);
      }
      
      #define BUF_SIZE 512
      uint8_t buf[BUF_SIZE/8];
    
      Serial.println(F("Reading..."));
    
      for (int i = 0; i < 5; i++) {
        file.read(buf,8);
      }
    
      uint64_t filesize = file.size();
      for (uint64_t i = 0; i < filesize; i++) {
        Serial.print("value="); Serial.println(buf[i],HEX);
      }
    }
    
    void setup() {
      Serial.begin(9600);
      waitForKeyPress();
    
      sdCardTest();
    
      dmaSpiTest();
    }
    
    void loop() {
    }

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,893
    The divisors for SPI do not allow exact speeds.
    Maybe you can try slave mode and use tone() (EDIT: Better use analogWriteFrequency() ) to generate a clk on an other pin and just connect the pins. However, i've not tested if tone() is exact enough.

    Theyn, you can always use an external oscillator (with SPI-slave mode).
    Last edited by Frank B; 03-28-2020 at 11:22 AM.

  3. #3
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,893
    On the T4 you could adjust the PLL frequency.

  4. #4
    Junior Member
    Join Date
    Apr 2019
    Posts
    9
    Thank you for the suggestions. It looks like the fastest that tone() will get me is close to 40kHz, so that's right out. analogWriteFrequency() has got me to 8.62MHz which is close enough for rock and roll (and good thing too, because it's strangely difficult to find crystals at multiples of 4.3MHz).
    Then the new problem; it looks like it's not a simple task to get the Teensy functioning in SPI Slave mode. I found the library on github at /tonton81/TSPISlave, but it has virtually no documentation (even in the example there is no comments). I'm kind of nervous because I need to have the Teensy working on other things while this SPI data is being clocked out. The end goal here is to use DMA and double-buffering to decompress and stream data out of an SD card at this 8.6Mb/s speed. DMAChannel is another thing I have a hard time finding documentation for...
    I'll see if I can use the slave mode for now.

  5. #5
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,893
    SO that questions was for SD reading?
    https://forum.pjrc.com/threads/60251...350#post234350

  6. #6
    Member
    Join Date
    Oct 2019
    Location
    Germany Maintal
    Posts
    21
    I use the function shiftout() for writing to a Led display. This is much slower, but i think shiftout is using a timer for the clock. So perhaps you can change the timer setting......

  7. #7
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,893
    No, shiftout just shifts out as fast as it can with code.
    On Teensy 4, there is a builtin "brake" to make it not too fast for slower chips.

  8. #8
    Junior Member
    Join Date
    Apr 2019
    Posts
    9
    Quote Originally Posted by Frank B View Post
    SO that questions was for SD reading?
    https://forum.pjrc.com/threads/60251...350#post234350
    I'm not exactly sure what you mean. To be clear, this thread and the thread you linked are both not about SD card reading. After you showed me the trick to use analogWriteFrequency() and get the SPI interface outputting data in slave mode I wanted to look further into the interactions between DMA and SPI output, which was sort of difficult but I've made my way through it now. Reading from the SD card is an issue that I'm just now tackling, and I'm beginning to work with Greiman's SD library at your suggestion.
    EDIT: I think I see where I was unclear. The system I'm developing would be reading data from an SD card, then decompressing it very quickly, then placing this data into one of two buffers. During this process, DMA will be streaming the contents of the other buffer out of the SPI interface at that 8.6Mb/s frequency. I'm using DmaChannel.h's replaceSettingsOnCompletion() and an interrupt at completion to switch buffers and keep the ping-pong dma constant. DMA is not involved in reading from the SD card (I hadn't even considered that) it's just for getting the data out of the serial interface.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •