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

Thread: Implementing XY2-100 serial protocol on Teensy 4.1

  1. #1
    Junior Member
    Join Date
    Sep 2020
    Posts
    9

    Implementing XY2-100 serial protocol on Teensy 4.1

    Hi,

    I'd like to use a Teensy 4.1 to implement the XY2-100 laser scanning control protocol. The protocol is a fairly simple serial protocol with the following signals:

    CLOCK: ~10 MHz, 50% duty cycle
    SYNC: Pulse every 20 ticks of CLOCK.
    X DATA, Y DATA: Transmits a 16-bit data frame in sync with CLOCK, and triggered by edges of SYNC.

    See http://www.alaser.com.tw/db/upload/w...2541519318.pdf for a timing diagram, etc.

    An existing project to do this exists for the 3.2 (https://github.com/Tuet/XY2_100), but the code uses timers that do not exist on the 4.1 -- but at least that means it's feasible.

    I've got a couple questions:

    1. How do I properly generate a ~10 MHz square wave? Until now I've been doing an analogWrite at a 50% duty PWM duty cycle and setting the frequency via analogWriteFrequency, but this seems hacky. Surely there must be a better way with a hardware timer or similar? (If a hardware timer is the best option, is there a good source of examples/documentation apart from the SoC datasheet?)

    2. How do I trigger actions in sync with the CLOCK or SYNC signals? Looping the CLOCK signal back to another input pin and setting a pin change interrupt seems like an option, but is there a better way?

    3. The existing protocol implementation for the Teensy 3.2 uses the DMAChannel library for signal generation. I don't mind reading the source code for that, but is there supplemental documentation/examples somewhere?

    - Franklin

  2. #2
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,747
    I would use one of the timer libraries to see the instructions that are different for T3.X and T4.X

  3. #3
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    I've made some progress on this, but I'm hitting a wall with DMA.

    I've gotten a initial prototype working via the PIT timer (using the IntervalTimer library) that triggers an ISR running at twice the clock rate. This ISR acts as a state machine and handles all the signal generation logic (i.e. toggling CLOCK and setting data bits), but unfortunately it seems that IntervalTimer won't go faster than a 0.75 uS period, so I'll have to use DMA.

    I've been reading through the processor manual about the eDMA module, and I've gathered the following:

    1. eDMA basically acts as an asynchronous memcpy that copies a couple bytes from one memory address to another (i.e. an input buffer to the GPIO data output register) whenever it's triggered.
    2. Triggering can be set to one of 128 sources by changing the bottom 7 bits of the CHCFG register.
    3. These sources include the Quad Timer module, which looks interesting -- can I configure that to generate a periodic trigger at the data rate I need?

    Is my understanding correct? If so, is there existing code that either wraps or uses the Quad Timers (and are they the right thing to use for what I want)?

  4. #4
    Junior Member
    Join Date
    Sep 2020
    Posts
    9

    Medium-speed clocked serial data output on T4.1

    I need to implement a custom serial protocol on T4.1 (https://forum.pjrc.com/threads/62819...-on-Teensy-4-1) that needs to output serial data synchronously with at least a 3.2 MHz clock (preferably higher).

    Data transmission occurs in "frames" which are denoted with a high pulse of the "sync" line, followed by 20 bits of serial data, written on the rising edge of the clock.

    I've gotten a bit-banging implementation working below that uses the PIT to trigger an ISR at 1.33 MHz, which then handles the changing the pin states on both the high and low clock edges. This gives an effective 667 kHz clock rate.

    Code:
    IntervalTimer t;
    
    void setup() {
        pinMode(3, OUTPUT);
        pinMode(5, OUTPUT);
        pinMode(7, OUTPUT);
        pinMode(9, OUTPUT);
        t.begin(callback, .75);
    }
    
    static volatile int clk = LOW;
    static volatile int sync = LOW;
    static volatile int ctr = 0;
    static volatile int transferring = 0;
    
    #define TOGGLE(state) (((state) == HIGH) ? LOW : HIGH)
    #define EXTRACT_BIT(data, idx) (((data) >> (idx)) & 1)
    
    // get bit corresponding to clock according to XY2-100 protocol
    static int xy2_bit(int ctr, uint16_t data) {
        int bit_to_write;
        switch(ctr) {
        case 0:
        case 1:
            bit_to_write = 0;
            break;
        case 2:
            bit_to_write = 1;
            break;
        default:
            bit_to_write = EXTRACT_BIT(data, 18 - ctr);
            break;
        }
        return bit_to_write;
    }
    
    static volatile uint16_t next_xdata, next_ydata;
    static volatile int x_bit = 0, y_bit = 0;
    void callback() {
        static volatile uint16_t x_data = 0x8000, y_data = 0x8000;
        clk = TOGGLE(clk);
        if(clk == HIGH) {
            if(ctr == 0 || ctr == 1) {
                if(ctr == 0) {
                    // begin transfer
                    transferring = 1;
                    x_data = next_xdata;
                    y_data = next_ydata;
                }
                sync = TOGGLE(sync);
            }
    
            if(ctr == 19)
                transferring = 0;
    
            if(transferring) {
                x_bit = xy2_bit(ctr, x_data);
                y_bit = xy2_bit(ctr, y_data);
            } else {
                x_bit = 0;
                y_bit = 0;
            }
        }
        if(clk == HIGH)
            ctr = (ctr + 1) % 20;
        digitalWriteFast(5, sync);
        digitalWriteFast(7, x_bit);
        digitalWriteFast(9, y_bit);
        digitalWriteFast(3, clk);
    }
    
    void loop() {
        while(1) {
            next_xdata = next_ydata = (sin(micros() / 1e6 * 2 * M_PI * 440) + 1) / 2 * 0xffff;
        }
    }
    I've tested this implementation and it works fine, but it appears that IntervalTimer can't go faster than a 0.75 us period by default. I need about a 5x improvement in speed to reach the 3.2 MHz target clock rate -- how could I achieve this? I've read that overclocking the PIT is possible, but I have a feeling that reducing CPU involvement through DMA or similar is the best way to go.

    Thanks!

  5. #5
    Administrator Paul's Avatar
    Join Date
    Oct 2012
    Posts
    379
    I have merged these 2 threads. Please do not create duplicate threads. Most of us use the "What's New" view, so you'll generally get better help if you just post again on the same thread where all the prior info exists.

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    I ran your program here on a Teensy 4.1. These are the waveforms I see. Is this correct?

    Click image for larger version. 

Name:	file.png 
Views:	26 
Size:	50.8 KB 
ID:	21809

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    Assuming this is the waveform you really want, and you really do intend for the clock to be continuous and the data to immediately repeat, this looks like a job for the SAI (digital audio) port.

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    Comparing these waveforms to the SS-III XY2-100-E documentation, I see the sync signal polarity is high for 19 of the 20 clocks. But the waveforms on msg #6 have it high only 1 of the 20 clocks.

    Click image for larger version. 

Name:	sc.png 
Views:	11 
Size:	68.3 KB 
ID:	21812
    Last edited by PaulStoffregen; 09-22-2020 at 03:14 PM.

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    I did some fiddling. Here's a quick test using TDM output code copied from the audio library.

    This probably isn't perfect, but hopefully it will give you a good start. It generates 10 MBit/sec output and calls isr() every 20 clocks as new data is needed. The 2 data streams transmit on pins 7 and 32. Sync is on pin 20 and bit clock is pin 21.

    You'll probably want to edit some of the I2S1_TCR and I2S1_RCR register settings if the waveforms aren't quite right. Documentation is in the IMXRT1060 reference manual (rev 2) in the SAI chapter. Turn to page 1981 for the register defs.

    Code:
    void setup() {
      pinMode(3, OUTPUT);
      config_sai1();
      attachInterruptVector(IRQ_SAI1, isr);
      NVIC_ENABLE_IRQ(IRQ_SAI1);
      I2S1_TCSR |= 1<<8;  // start generating TX FIFO interrupts
    }
    
    volatile uint16_t nextval = 0x0FF0;
    
    void isr() {
      digitalWriteFast(3, HIGH);
      delayNanoseconds(25);
      digitalWriteFast(3, LOW);
      
      uint32_t bits20 = 0x20000 | (nextval << 1) | 0x00;
      I2S1_TDR0 = bits20;
      I2S1_TDR1 = bits20;
    }
    
    void loop() {
      static elapsedMillis msec;
      if (msec > 100) {
        nextval++;
        msec = 0;
      }
    }
    
    
    
    FLASHMEM
    void config_sai1()
    {
      CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
      double fs = 39062.5;
      int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
      int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
    
      double C = ((double)fs * 256 * n1 * n2) / 24000000;
      int c0 = C;
      int c2 = 10000;
      int c1 = C * c2 - (c0 * c2);
    
      audioClock(c0, c1, c2);
      // clear SAI1_CLK register locations
      CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
                   | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
    
      n1 = n1 / 2; //Double Speed for TDM
    
      CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
                   | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
                   | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
    
      IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
                        | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));  //Select MCLK
    
    
      // configure transmitter
      int rsync = 0;
      int tsync = 1;
    
      I2S1_TMR = 0;
      I2S1_TCR1 = I2S_TCR1_RFW(4);
      I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
                  | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
      I2S1_TCR3 = I2S_TCR3_TCE_2CH;
      I2S1_TCR4 = I2S_TCR4_FRSZ(0) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
                  /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSD | I2S_TCR4_FSP;
      I2S1_TCR5 = I2S_TCR5_WNW(19) | I2S_TCR5_W0W(19) | I2S_TCR5_FBT(19);
    
      I2S1_RMR = 0;
      I2S1_RCR1 = I2S_RCR1_RFW(4);
      I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
                  | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
      I2S1_RCR3 = I2S_RCR3_RCE_2CH;
      I2S1_RCR4 = I2S_RCR4_FRSZ(0) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
                  /*| I2S_RCR4_FSE*/ | I2S_RCR4_FSD | I2S_RCR4_FSP;
      I2S1_RCR5 = I2S_RCR5_WNW(19) | I2S_RCR5_W0W(19) | I2S_RCR5_FBT(19);
    
      //CORE_PIN23_CONFIG = 3;  // MCLK
      CORE_PIN21_CONFIG = 3;  // RX_BCLK
      CORE_PIN20_CONFIG = 3;  // RX_SYNC
      CORE_PIN7_CONFIG  = 3;  // TX_DATA0
      CORE_PIN32_CONFIG = 3;  // TX_DATA1
      I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
      I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE /* | I2S_TCSR_FRDE <-- not using DMA */;
    
    }
    
    
    /*
      (c) Frank B
    */
    
    FLASHMEM
    void audioClock(int nfact, int32_t nmult, uint32_t ndiv) // sets PLL4
    {
      //if ((CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
    
      CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
                             | CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
                             | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
    
      CCM_ANALOG_PLL_AUDIO_NUM   = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
      CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
    
      CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
      while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
    
      const int div_post_pll = 1; // other values: 2,4
      CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
      if (div_post_pll > 1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
      if (div_post_pll > 3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
    
      CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
    }

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    Here's the waveforms that code generates. Hopefully close to what you wanted?

    Click image for larger version. 

Name:	file.png 
Views:	16 
Size:	48.9 KB 
ID:	21813
    Last edited by PaulStoffregen; 09-22-2020 at 03:14 PM.

  11. #11
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    The falling edge of the SYNC line is arbitrary -- it just has to be low before the next frame starts. It looks like your attachment links are invalid, however, so I can't see the waveforms you've produced. Can you re-upload?

    Thanks!

  12. #12
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    I've edited those posts to re-upload the images.

  13. #13
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    Just one minor thing -- it looks like your version produces data waveforms that update on falling edge of the clock, not the rising edge as mine does (and the protocol requires) How would I modify things to produce this phase shift?

  14. #14
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,637
    Maybe the BCP bit documented on page 1989 can help?

    When/if this all works, hopefully you can share the final known-good code and some more info about the project? It might really help anyone else who later finds this thread and wants to communicate with this device.

  15. #15
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    I'm planning to build this into an open-source library with a modular interface which I'll post here once it's finished. I notice that your audioClock() function is copyrighted by a certain Frank B. -- how does this affect potential licensing?

    Thanks for all the help!

  16. #16
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    One more thing -- there's a related protocol called FB4 to control laser scanners that's largely similar to XY2-100, but with the following changes:

    1) The sync polarity is reversed, so it should go low at the start of each frame, and high before the end.
    2) The two 16-bit data channels are serialized into one 32-bit word, transmitted across one data line.
    3) The second data line is repurposed for device-to-host communication, sampled synchronously with CLOCK and SYNC just as the host-to-device signal.

    The first two differences should hopefully be trivial to implement, but how about the third? The Teensy would need to sample one of the GPIOs on the falling edge of the clock signal and trigger an ISR to notify application code of the reception of a 32-bit input word from the device.

    EDIT: FB4 timing diagrams can be found on page 14 of https://fwei.tk/MachDSP_Help.pdf.

  17. #17
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    OK, for future reference here's a bare-bones working implementation of XY2-100 over SAI.

    Code:
    void setup() {
        pinMode(3, OUTPUT);
        config_sai1();
        attachInterruptVector(IRQ_SAI1, isr);
        NVIC_ENABLE_IRQ(IRQ_SAI1);
        I2S1_TCSR |= 1<<8;  // start generating TX FIFO interrupts
    }
    
    volatile uint16_t nextval = 0x0FF0;
    
    void isr() {
        digitalWriteFast(3, HIGH);
        delayNanoseconds(25);
        digitalWriteFast(3, LOW);
    
        uint32_t bits20 = 0x10000 | (nextval << 0) | 0x00;
        I2S1_TDR0 = bits20;
        I2S1_TDR1 = bits20;
    }
    
    void loop() {
        nextval = (sin(micros() / 1e6 * 2 * M_PI * 440) + 1) / 2 * 0xffff;
    }
    
    
    
    FLASHMEM
    void config_sai1()
    {
        CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
        double fs = 4000;
        int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
        int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
    
        double C = ((double)fs * 256 * n1 * n2) / 24000000;
        int c0 = C;
        int c2 = 10000;
        int c1 = C * c2 - (c0 * c2);
    
        audioClock(c0, c1, c2);
        // clear SAI1_CLK register locations
        CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
            | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
    
        n1 = n1 / 2; //Double Speed for TDM
    
        CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
            | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
            | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
    
        IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
            | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));  //Select MCLK
    
    
        // configure transmitter
        int rsync = 0;
        int tsync = 1;
    
        I2S1_TMR = 0;
        I2S1_TCR1 = I2S_TCR1_RFW(4);
        I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | /*I2S_TCR2_BCP |*/ I2S_TCR2_MSEL(1)
            | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
        I2S1_TCR3 = I2S_TCR3_TCE_2CH;
        I2S1_TCR4 = I2S_TCR4_FRSZ(0) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
            /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSD | I2S_TCR4_FSP;
        I2S1_TCR5 = I2S_TCR5_WNW(19) | I2S_TCR5_W0W(19) | I2S_TCR5_FBT(19);
    
        I2S1_RMR = 0;
        I2S1_RCR1 = I2S_RCR1_RFW(4);
        I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | /*I2S_TCR2_BCP |*/ I2S_RCR2_MSEL(1)
            | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
        I2S1_RCR3 = I2S_RCR3_RCE_2CH;
        I2S1_RCR4 = I2S_RCR4_FRSZ(0) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
            /*| I2S_RCR4_FSE*/ | I2S_RCR4_FSD | I2S_RCR4_FSP;
        I2S1_RCR5 = I2S_RCR5_WNW(19) | I2S_RCR5_W0W(19) | I2S_RCR5_FBT(19);
    
        //CORE_PIN23_CONFIG = 3;  // MCLK
        CORE_PIN21_CONFIG = 3;  // RX_BCLK
        CORE_PIN20_CONFIG = 3;  // RX_SYNC
        CORE_PIN7_CONFIG  = 3;  // TX_DATA0
        CORE_PIN32_CONFIG = 3;  // TX_DATA1
        I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
        I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE /* | I2S_TCSR_FRDE <-- not using DMA */;
    
    }
    
    
    /*
      (c) Frank B
    */
    
    FLASHMEM
    void audioClock(int nfact, int32_t nmult, uint32_t ndiv) // sets PLL4
    {
        //if ((CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
    
        CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
            | CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
            | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
    
        CCM_ANALOG_PLL_AUDIO_NUM   = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
        CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
    
        CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
        while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
    
        const int div_post_pll = 1; // other values: 2,4
        CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
        if (div_post_pll > 1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
        if (div_post_pll > 3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
    
        CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
    }
    I only had to make a couple minor tweaks to implement the protocol correctly -- namely changing the clock polarity and the transmitted bit pattern.

  18. #18
    Junior Member
    Join Date
    Sep 2020
    Posts
    2
    It almost seems like a moot point with a fancy SAI implementation, but I have found why the PIT timer cannot be shorter than a 0.75 uS period. In the PIT library, there is a minimum 17 clock cycle value for the timer presumably to not lock up your CPU with interrupt calls for Teensy 3.x hardware. However, with the Teensy 4.x, the clock source is currently set to a 24Mhz clock instead of the CPU clock so the minimum cycle check is not needed.

    Code:
    class IntervalTimer {
    ...
    bool begin(void (*funct)(), float microseconds) {
    	if (microseconds <= 0 || microseconds > MAX_PERIOD) return false;
    	uint32_t cycles = (float)(24000000 / 1000000) * microseconds - 0.5f;
    	if (cycles < 17) return false; // <-- HERE: Limits the interrupt interval to 1.5Mhz max
    	return beginCycles(funct, cycles);
    }

  19. #19
    Junior Member
    Join Date
    Sep 2020
    Posts
    9
    Can you clarify why the minimum cycle check isn't needed for the PIT if the clock source is external? The CPU would still be overwhelmed by interrupts no matter what the clock source is, no?

  20. #20
    Junior Member
    Join Date
    Sep 2020
    Posts
    2
    At a 24 Mhz interrupt rate, there would be barely enough cycles for just the interrupts to complete without additional code, so likely the more practical answer would be to just reduce the count. As for how the numbers fall out, the interrupt latency is >=12 clock cycles(of the 600MHz CPU clock) just to get to the start of the handler. Then there is a similar number of cycles to resume the program where it left off. Now at least 24 cycles of the 25 cycles(600Mhz/24Mhz) are accounted for so that's not very helpful as far as a program is concerned...

    At speeds under 10Mhz, however, there should be enough cycles available for simple operations. It still will tax the system a bit to have so many interrupts, but it would be well within the realm of possibility.

    If you really needed parallelized high speed pin control, you can tie together a PIT to a DMA in the DMAMUX registry. The DMA will do a single transfer every time the PIT triggers. With this configuration, the DMA can then periodically transfer values into the port registers. You would no longer need an interrupt callback, just a new buffer to swap in at the end of the DMA transfer.

Posting Permissions

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