Go Down

Topic: Running TC with an external clock and externally triggered capture (Zero/M0) (Read 521 times) previous topic - next topic

iceleff

TL;DR: If you can point me to a working SAMD21 example of running a 32 bit TC with both external clock (GCLKIO) and externally trigged count captures (via EIC/EVSYS), it will be much appreciated. I seem to only be able to get one of those working at a time.


Hardware: My board is an Arduino M0 with the SAMD21G18 processor. Datasheet (~ 17MB). External frequency (1k - 2MHz) generator on PA21 (M0 pin 7) , and 1 pps GPS signal on PA17 (M0 pin 13). This should apply to Arduino Zero as well.

The objective: I'm trying to do capture events on TC4/TC5 in 32 bit mode with an external clock on PA21. The captures should be triggered from PA17. In principle I don't care for the choice of pins and I have tried moving it around.

The paradox: Running TC4/5 on PA21 (arduino 7) via GCLKIO5 works just fine but I cannot make it capture via EIC-events on PA17 (arduino 13). However, running the TC on the main clock (either via GCLK5, or directly on GCLK0) will happily capture my 1 PPS pulses. This is the only change made.

Please see complete (non-)working example below; changing the boolean in the very top switches between the clocks and will result in the behaviour described above.

I'm assuming it's me forgetting to configure something obvious somewhere but I'm starting to run out of suggestions. Interestingly, changing the TC clock source to the 32 kHz from the crystal (available at GCLK1) will also not have working count captures. This makes me suspect some synchronisation issue between the clock domains.

Any suggestions? Thoughts appreciated.

Thank you.

Johan

PS: Sorry for the longish, messy code but it's the shortest complete and working example I could think of to get the exact point across.

Code: [Select]

void setup()
{
    // change here to use main clock or input pin
    bool use_clk_pin = false ;

    Serial1.begin(115200);
    while (!Serial1);
    Serial1.print("\r\nBegin config\r\n");

    pinMode(11, OUTPUT) ;
    pinMode(12, OUTPUT) ;

    // setup main clocks first
    REG_PM_APBAMASK |= PM_APBAMASK_GCLK ;
    REG_PM_APBBMASK |= PM_APBBMASK_PORT ;
    REG_PM_APBCMASK |= PM_APBCMASK_EVSYS | PM_APBCMASK_TC4 | PM_APBCMASK_TC5 ;

    //
    //    Setup gate input on PA17 (M0 pin 13)
    //  

    // send generic GCLK0 to the EIC peripheral
    REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_EIC ;
    while(GCLK->STATUS.bit.SYNCBUSY) ;

    REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_EVSYS_0 ;
    while(GCLK->STATUS.bit.SYNCBUSY) ;

    // Send pin PA17 to EIC (line 1)
    PORT->Group[PORTA].PMUX[17 >> 1].reg |= PORT_PMUX_PMUXO_A ;
    PORT->Group[PORTA].PINCFG[17].reg |= PORT_PINCFG_PMUXEN ;

    // enable ext1 int rising edge to event system
    REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO1 ;
    REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE1_RISE ;
    REG_EIC_CTRL |= EIC_CTRL_ENABLE;
    while (EIC->STATUS.bit.SYNCBUSY);

    // direct ext3 event to TC4
    REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) | EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU) ;
    REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT | EVSYS_CHANNEL_PATH_ASYNCHRONOUS
                        | EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_1) | EVSYS_CHANNEL_CHANNEL(0) ;

    //
    //    Setup timer clock input on PA21 (pin 7 on M0)
    //

    // Source GCLK from PA21 (gclkio 5)
    PORT->Group[PORTA].PMUX[21 / 2].reg = PORT_PMUX_PMUXO_H ;
    PORT->Group[PORTA].PINCFG[21].reg = PORT_PINCFG_PMUXEN ;

    // tick count: gclk_io5 => tc4-5
    REG_GCLK_GENDIV = GCLK_GENDIV_ID(5) ; // GCLK5: no div

    // clock from pin PA21
    //REG_GCLK_GENCTRL = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_SRC_GCLKIN  | GCLK_GENCTRL_ID(5) ;
    //while (GCLK->STATUS.bit.SYNCBUSY);



    if(use_clk_pin) {
        // Feed from GCLK_IO
        REG_GCLK_GENCTRL = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_SRC_GCLKIN  | GCLK_GENCTRL_ID(5) ;
        while (GCLK->STATUS.bit.SYNCBUSY);
    } else {
        // clock from main clock
        REG_GCLK_GENCTRL = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(5) ;
        while (GCLK->STATUS.bit.SYNCBUSY);
    }

    // Feed GCLK5 to TC4/TC5
    REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_TC4_TC5 ;
    while (GCLK->STATUS.bit.SYNCBUSY);
    
    //
    // general timer TC4-TC5 setup for 32 bit
    //

    // Enable event input
    REG_TC4_EVCTRL |= TC_EVCTRL_TCEI ;

    // set capture on channel 0
    REG_TC4_READREQ = TC_READREQ_RREQ | TC_READREQ_ADDR(0x06) ;
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY) ;
    REG_TC4_CTRLC |= TC_CTRLC_CPTEN0 ;
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY) ;

    // enable timer TC4 interrupt for Capture events
    NVIC_SetPriority(TC4_IRQn, 0) ;
    NVIC_EnableIRQ(TC4_IRQn) ;
    REG_TC4_INTENSET = TC_INTENSET_MC0 ;

    // no prescaler, 32 bit mode: run timer!
    REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_MODE_COUNT32 | TC_CTRLA_ENABLE ;
    while (TC4->COUNT16.STATUS.bit.SYNCBUSY) ;

    Serial1.print("config done\r\n") ;
}



void TC4_Handler()
{
    uint32_t count ;
  
    // Check for match counter 0 (MC0) interrupt
    // read automatically clears flag      
    if (TC4->COUNT16.INTFLAG.bit.MC0)
    {
        REG_TC4_READREQ = TC_READREQ_RREQ | TC_READREQ_ADDR(0x18);
        while (TC4->COUNT32.STATUS.bit.SYNCBUSY);
        count = REG_TC4_COUNT32_CC0;

        // print out for debug
        Serial1.print("Count capture: ") ;
        Serial1.print(count) ;
        Serial1.print("\r\n") ;
    }
  
    static bool toggle = false ;
    toggle = !toggle ;
    digitalWrite(11, toggle) ;
}


void loop()
{
  delay(1000) ;
  
  REG_TC4_READREQ = TC_READREQ_RREQ | TC_READREQ_ADDR(0x10) ; // COUNT offset 0x10
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY) ;
  volatile uint32_t count = REG_TC4_COUNT32_COUNT ;
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY) ;

  Serial1.print("Current count: ") ;
  Serial1.print(count) ;
  Serial1.print("\r\n") ;

  static bool toggle = false ;
  toggle = !toggle ;
  digitalWrite(12, toggle) ;
}



EDIT: I should add output data. The GCLK0 case:

Code: [Select]

Begin config
config done
Count capture: 12462258
Current count: 47993356
Count capture: 60431073
Current count: 95993356
Count capture: 108403724
Current count: 143993350
Count capture: 156378173
Current count: 191993356
Count capture: 204352597
Current count: 239993353
Count capture: 252328812
Current count: 287993358
Count capture: 300305004
Current count: 335993350
Count capture: 348278533
Current count: 383993359
Count capture: 396254598
Current count: 431993356
Count capture: 444231927
Current count: 479993350
Count capture: 492208871
Current count: 527993359


Also, I forgot to mention, every once in a blue moon, an odd capture will actually happen in the non-working scenario. But we are talking every 10 min or so. The ~2 MHz GCLKIO[5] case:

Code: [Select]

Begin config
config done
Current count: 2015338
Current count: 4030983
Current count: 6046728
Current count: 8062300
Current count: 10077785
Current count: 12093356
Current count: 14108933
Current count: 16124468
Count capture: 17447337
Current count: 18139945
Current count: 20155267
Current count: 22170451
Current count: 24185506
Current count: 26200437
Current count: 28215295
Current count: 30230214
Current count: 32245066
Current count: 34259869
Current count: 36274630
Current count: 38289377
Current count: 40303980
Current count: 42318587
Current count: 44333123
Current count: 46347657
Current count: 48362252
Current count: 50376876
Current count: 52391547
Current count: 54406037


Again, any thoughts will be greatly appreciated :)


iceleff

#1
Dec 06, 2017, 09:30 am Last Edit: Dec 06, 2017, 12:15 pm by iceleff Reason: Spelling
Problem solved. The event source and receiver need to be in synchronisation.

So, the EIC and TC clock must be the same (asynchronous/synchronous) or the event channel needs to be resynchronised. I have not tested it but I will assume that one can get away with slightly different frequencies in the asynchronous case.

Solution: Change the event system setup to resynchronised rising edge detect.

Code: [Select]

    REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_RISING_EDGE | EVSYS_CHANNEL_PATH_RESYNCHRONIZED
                        | EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_1) | EVSYS_CHANNEL_CHANNEL(0) ;


I also tried clocking the EIC unit directly from the GCLK input pin (GCLK5) which also works (but has it working much below the 48 MHz core frequency.)

Relevant section from the datasheet (page 410):

Quote
The resynchronized path should be used when the event generator and the event channel do not share the same generic
clock generator. When the resynchronized path is used, resynchronization of the event from the event generator is done
in the channel.
EDIT: I tried playing around a little and it seems the asynchronous path can be trusted when the EIC frequency is around half the TC frequency. Above that there will be an occasional glitch and when the frequencies are almost the same (but not in sync!), the event/capture will be missed regularly.

Go Up