Beginner SAMD51 questions

Dear all,

I am currently using the ItsyBitsy M4 + Arduino IDE for a project.
For this project I have to send a 48kHz signal.

The M4 has a 1MSPS 12 bit true DAC which is perfect. When using analogWrite() in a loop, the update frequency is not high enough for my application (as expected).

I am currently using the following code I found online to generate a sawtooth and have some questions:

int i;

void setup() {
GCLK->GENCTRL[1].reg =
GCLK_GENCTRL_DIV(1) |
GCLK_GENCTRL_GENEN |
GCLK_GENCTRL_SRC_XOSC0;
GCLK->PCHCTRL[42].reg =
GCLK_PCHCTRL_CHEN |
GCLK_PCHCTRL_GEN_GCLK1;

MCLK->APBDMASK.bit.DAC_ = 1;
DAC->CTRLA.bit.SWRST = 1;
while(DAC->CTRLA.bit.SWRST);
DAC->DACCTRL[1].reg = DAC_DACCTRL_REFRESH(2) |
DAC_DACCTRL_CCTRL_CC12M |
DAC_DACCTRL_ENABLE |
DAC_DACCTRL_LEFTADJ;
DAC->CTRLB.reg = DAC_CTRLB_REFSEL_INTREF;
DAC->DBGCTRL.bit.DBGRUN = 1;
DAC->CTRLA.reg = DAC_CTRLA_ENABLE;
while(DAC->SYNCBUSY.bit.ENABLE);
while(!DAC->STATUS.bit.READY1);
PORT->Group[0].PMUX[2].bit.PMUXO = 1;
PORT->Group[0].PINCFG[5].reg = PORT_PINCFG_PMUXEN;

int i = 0;
}

void loop() {
DAC->DATA[1].reg = i; while(!DAC->STATUS.bit.EOC1);
i++;
}

What do these commands like "GCLK->GENCTRL[1].reg = GCLK_GENCTRL_DIV(1)" do ?
I know they are manipulating the registers inside of the SAMD51 to configure the clock,
and have some experience with port manipulation on the UNO.
But where can I find a list of these commands ? I cant find code such as "GCLK_GENCTRL_DIV" on the adafruit Github, or the µController datasheet.

On top of that, the current sawtooth wave is not really smooth:
Image

Is this due to the current code/user error? or is there always this kind of nonlinearity in the DAC, and compensate it in software ?

Thanks in advance!

I am currently using the ItsyBitsy M4
What do these commands like “GCLK->GENCTRL[1].reg = GCLK_GENCTRL_DIV(1)” do ?
where can I find a list of these commands ?

The SAMD51 has 8 Clock Sources, 12 Clock Generators, and 47 peripherals that need a clock. (Feels like “Alice’s ARM”!)
You can set up each clock generator to be some modification (prescaling, mostly) of some clock source, and then you can set each of the peripherals to use one of the clock generators (perhaps with more prescaling.) (Did I mention that some of the clock sources need a clock generator as an input? So the 48MHz main clock comes from the DPLL or DFLL clock source, which uses a GLCK as an input, that that GCLK uses one of the oscillators as ITS input. (“Circles and Arrows”))
It’s all described in the datasheet (Section 13-15, 28, and 29.) Not very clearly :frowning: But eventually it sort-of makes sense.

I cant find code such as “GCLK_GENCTRL_DIV” on the adafruit Github, or the µController datasheet.

That’s a macro for setting particular values of the GCLK->PCHCTRL register fields. You can find it in, um:
…/packages/arduino/tools/CMSIS-Atmel/1.2.0/CMSIS/Device/ATMEL/samd51/include/component/gclk.h

#define GCLK_GENCTRL_DIV(value)     (GCLK_GENCTRL_DIV_Msk & ((value) << GCLK_GENCTRL_DIV_Pos))
void setup() {

/*
   * Set up GCLK1 to be the undivided external Oscillator
   /
  GCLK->GENCTRL[1].reg = GCLK_GENCTRL_DIV(1) |
        GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC0;
  /

   * Set the DAC to use GCLK1
   /
  GCLK->PCHCTRL[42].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK1;
       
  /
Enable the clock switching to DAC (?) */
  MCLK->APBDMASK.bit.DAC_ = 1;

/*
   * Configure the DAC
   */
  DAC->CTRLA.bit.SWRST = 1;
  while(DAC->CTRLA.bit.SWRST);
  DAC->DACCTRL[1].reg = DAC_DACCTRL_REFRESH(2) |
        DAC_DACCTRL_CCTRL_CC12M |
        DAC_DACCTRL_ENABLE |
        DAC_DACCTRL_LEFTADJ;
  DAC->CTRLB.reg = DAC_CTRLB_REFSEL_INTREF;
  DAC->DBGCTRL.bit.DBGRUN = 1;  // start it up!

There are some problems with that code:

  1. Redefining GCLK1? I’d expect that to be in use by “other code”, probably at a different frequency. (Looks like the Adafruit code uses it as a 48MHz clock.) I would certainly look through code to try to find a GCLK that wasn’t used. (There are 12!!!)
  2. the ItsyBitsy doesn’t have an external Crystal. I’m not sure what happens when you try to use the XOSC clock source and there is no crystal; there are some fancy “backup clock” features in the SAMD51, but I don’t know exactly when they come into play.
  3. “42”? Surely we should use the GCLK_DAC symbol mentioned in the datasheet, right? (Nope - doesn’t seem to exist. WTF, Atmel? Oh, it’s DAC_GCLK_ID…)
  4. Instead of using a new GCLK, you might look for an existing clock close to what you need. It looks like the Adafruit code (cores/arduino/startup.c) has the main clock (120MHz: GCLK0), a 100MHz clock (GCLK2), 48MHz (GCLK1), and 12MHz (GCLK4). Not defined externally to startup.c, though (sigh.) Since the DAC has a max input clock of 12MHz, GCLK4 should be what you want:
  /*
   * Set the DAC to use 12MHz clock (adafruit boards)
   */
  GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK4;
  1. Actually, Adafruit’s init() code already sets that up, so the DAC clock is already set as high as it can go. Any speed limitations are in the analogWrite() function. (huh. Which rather inexplicably contains a 10ms delay! I’ve created https://github.com/adafruit/ArduinoCore-samd/issues/94)

For this project I have to send a 48kHz signal.
The M4 has a 1MSPS 12 bit true DAC which is perfect.

A 1MHz DAC, if you use ALL 4096 steps, won’t get you more than about 240Hz…

I don’t have any ideas about the weird steps in your scope picture…

I have noticed some peculiarities with the SAMD51 DACs on an Adafruit Feather M4. Discussion and pictures on Adafruit Forums: SAMD51 M4 DAC has problems getting it up