SAMD21 External Clock Source or External Clock for Timer-Counter - Page 1
Forum section: electronics / general

author: fourfathom
author_rank: Super Contributor
timestamp: June 12, 2023, 11:48:31 pm
content: I need help setting up the processor clock on a board with a SAMD21 processor.I have designed a board for a radio communications project and need reasonable accuracy for the Timer-Counter (on the order of 100ppm). The processor circuit is a virtual clone of the Adafruit ItsyBitsy-M0, and is clocked at 48MHz by the internal DFLL in USB clock-recovery mode. With the USB connected the clock wanders a bit, but is acceptable. When there is no USB the free-running frequency is quite imprecise. I am using the Adafruit boot-code, with a very small bit of modification.For radio frequency-setting I have a 2 ppm 10 MHz oscillator and if available I use a GPSDO. These are not currently connected to the processor, but jumper wires are easy.To get the accuracy I need, I figure I can set up the SAMD to use the 10 MHz clock as a clock reference (divide by 10 and then multiply by 48, or similar). I've looked at the ItsyBitsy boot code and studied examples, but I'm no expert and it shows -- I've not been able to get this to work. As a test I plan to attach a 32KHz resonator to the controller and see if I can make any of the many examples out there work, but I would rather use the existing 10 MHz reference.Alternately, I can accept the "48 MHz" clock variability, and instead clock the TC from my 10 external MHz clock. I see that there is a two-stage synchronizer available at the input pins.I was hoping to not have to become an expert in this, and would love a little bit of hand-holding. If it comes down to it, I would pay a reasonable price to have someone help me out -- I've got other tasks that I really need to be working on. I do need to retain USB functionality. I am currently using the Arduino IDE and various libraries from the usual sources, sometimes modified.Suggestions greatly appreciated!

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2023, 12:12:32 am
content: Configure XIN to be a clock input, then use it for DFLL:Code:static void sys_init(void){NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_RWS(2);SYSCTRL->XOSC.reg = SYSCTRL_XOSC_ENABLE | SYSCTRL_XOSC_RUNSTDBY;while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSCRDY));GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(305) |GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC) |GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SYSCTRL_GCLK_ID_DFLL48) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(1);SYSCTRL->INTFLAG.reg = SYSCTRL_INTFLAG_BOD33RDY | SYSCTRL_INTFLAG_BOD33DET | SYSCTRL_INTFLAG_DFLLRDY;SYSCTRL->DFLLCTRL.reg = 0; // See Errata 9905while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY));SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(1) | SYSCTRL_DFLLMUL_FSTEP(1) |SYSCTRL_DFLLMUL_MUL(1464 - 1);SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE | SYSCTRL_DFLLCTRL_MODE;while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY));GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) | GCLK_GENCTRL_SRC(GCLK_SOURCE_DFLL48M) |GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_OE | GCLK_GENCTRL_IDC;while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);}10 MHz gets divided to ~32.7 kHz, then multiplied back to 48 MHz.With DPLL you can divide down to 1 MHz, and then multiply to 48 MHz. But the end result would be the same.There are many ways to get the same result, it all depends on what else you have going on in the system.The code was combined from multiple chunks, so there may be minor mistakes, but overall it shows the idea.

author: fourfathom
author_rank: Super Contributor
timestamp: June 13, 2023, 12:28:07 am
content: Thanks, Alex -- I was hoping you would reply! I had tried using XIN, but likely hadn't correctly set up all the other stuff. I'm about to hit the road for a couple of days and just packed up my prototype hardware, but will try this out before the week ends.-Paul

author: fourfathom
author_rank: Super Contributor
timestamp: June 13, 2023, 01:56:37 am
content: I unpacked enough hardware to try this out -- and still have problems.First, early on in your code there is the line:GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(305) |with no terminating ';' Looking at the .h file for this it looks like the rest of that register is "reserved", so should that trailing '|' be a semicolon? That's what I changed it to.But my processor still hangs when trying to change the clock.So a stupid question: Can I run your sys_init inside the Arduino "setup()" function? That's what I've been doing. But perhaps there is some resource conflict going on -- I haven't looked into it. There's a bunch of stuff in the Adafruit bootcode, including an embedded Python interpreter. I would prefer a stripped-down version, but until now haven't run into any issues that required it. But I don't know why this is hanging. I suppose I should look into the Microchip Studio or similar IDE, but I hadn't planned on needing to dig that deep.

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2023, 02:08:02 am
content: Quote from: fourfathom on June 13, 2023, 01:56:37 amYes, just a copy-paste error.Quote from: fourfathom on June 13, 2023, 01:56:37 amLikely not. You need to figure out what clock settings Arduino does and undo them first. It might already use GCLK1 for something and DFLL itself.And more importantly, it is not clear what will break when you change those settings. Some peripherals may be expecting a real clock on GCLK1, not 34 kHz. And those peripherals would stop working correctly and firmware may hang.

author: fourfathom
author_rank: Super Contributor
timestamp: June 13, 2023, 03:10:00 am
content: Before I start looking under the hood of the boot-code and the other Arduino stuff that happens before my program can run, is there a good way to run one of the TC timers from my 10 MHz clock coming on on an external pin? I currently have TC5 incrementing on GCLK0 (48 MHz) and am generating a timer interrupt every (2 x 50000) ticks (480 Hz, which is 256 x the symbol rate of the FSK mode I am running). if I can clock this at 10 MHz then I should be able to find some divisors and ratios that I can work with.I've seen some examples there the timer increments on an interrupt, but I sure don't want to run an interrupt at 10 MHz. So can I clock the TC counter directly this way?

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2023, 03:19:11 am
content: You would still have to figure out how it is done in the Arduino environment.In general the minimum code for that is something like this:Code:// Enable XOSC (bypass mode)SYSCTRL->XOSC.reg = SYSCTRL_XOSC_ENABLE | SYSCTRL_XOSC_RUNSTDBY;while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSCRDY));// Connect XOSC to GCLK NNNGCLK->GENCTRL.reg = GCLK_GENCTRL_ID(NNN) | GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC) |GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_IDC;while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);Then use GCLK NNN as a source for the TC. You need to find NNN index that is not used already by the Arduino.

author: fourfathom
author_rank: Super Contributor
timestamp: June 13, 2023, 03:41:38 am
content: Thanks, I really appreciate the help. I'm a hardware engineer who knows just enough software to usually do what I need. Funny though, in one of my first serious engineering jobs I ended up writing the boot-code for the 68HC11 processors that ran our line-cards. But things were much simpler way back then...

author: fourfathom
author_rank: Super Contributor
timestamp: June 14, 2023, 11:37:01 pm
content: Hey, that worked! I picked GCLK7 more or less at random, and the TC is now running from my 10 MHz clock. This didn't seem to break anything but I will try to do an inventory of the GCLK assignments.Thanks!