Author Topic: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?  (Read 3643 times)

0 Members and 6 Guests are viewing this topic.

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Hi,

I'm pretty new to the SAMD21, I'm programming it baremetal and have some questions about peripherals and clock domains.
I've been able to set up my samd21 so that the GCLK0 for the CPU is at 48MHz https://blog.thea.codes/understanding-the-sam-d21-clocks/

I had previously the samd21 running at 8MHz and the SERCOM5 configured to use the OSCM8 clock as well, and UART was working fine. Now it just outputs gibberish. Why does this happen? I thought the peripheral was independent to the CPU, and since they now use independent clocks, shouldn't UART keep working?

Thanks

PS: Here's my code for reference
Code: [Select]
void clock_init()
    {
      SYSCTRL->OSC8M.bit.PRESC = 0;                          // no prescaler (is 8 on reset)
      SYSCTRL->OSC8M.reg |= 1 << SYSCTRL_OSC8M_ENABLE_Pos;   // enable source
   
      GCLK->GENDIV.bit.ID = 0x03;                            // select GCLK_GEN[3]
      GCLK->GENDIV.bit.DIV = 0;                              // no prescaler

      GCLK->GENCTRL.bit.ID = 0x03;                           // select GCLK_GEN[3]
      GCLK->GENCTRL.reg |= GCLK_GENCTRL_SRC_OSC8M;           // OSC8M source
      GCLK->GENCTRL.bit.GENEN = 1;                           // enable generator
     

      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_SERCOM5_CORE;      // SERCOM5 multiplexer GCLK_PERIPHERAL[n]
      GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK0;           // select multiplexer source GCLK_GEN[3]
      GCLK->CLKCTRL.bit.CLKEN = 1;                           // enable generic clock (for baud rate generation)

      PM->APBCSEL.bit.APBCDIV = 0;                           // no prescaler
      PM->APBCMASK.bit.SERCOM5_ = 1;                         // enable SERCOM5 synchronous clock
    }

void port_init()
    {
      /* assign alternate function (SERCOM5 PAD2 and PAD3) to pin PB22 and PB23 */
      PORT->Group[1].PINCFG[22].bit.PMUXEN = 1;  // enable alternate function on pin PB22
      PORT->Group[1].PINCFG[23].bit.PMUXEN = 1;  // enable alternate function on pin PB23
      PORT->Group[1].PMUX[11].bit.PMUXO = 0x03;  // pin PB23 is assigned to SERCOM5 PAD3
    }

void USART_init()
    {
      // configure USART 8N1
      SERCOM5->USART.CTRLA.bit.MODE = 0x1;    // USART internal clock
      SERCOM5->USART.CTRLA.bit.CMODE = 0;     // asynchronous mode
      SERCOM5->USART.CTRLA.bit.RXPO = 0x3;    // RX on PAD3
      SERCOM5->USART.CTRLA.bit.TXPO = 0x1;    // TX on PAD2
      SERCOM5->USART.CTRLA.bit.DORD = 1;      // data order: LSB first
      SERCOM5->USART.CTRLB.bit.CHSIZE = 0x0;  // 8-bit character
      SERCOM5->USART.CTRLA.bit.FORM = 0;      // no parity bit
      SERCOM5->USART.CTRLB.bit.SBMODE = 0;    // 1 stop bit
      SERCOM5->USART.BAUD.reg = 50437;        // 115200 baud rate
      SERCOM5->USART.CTRLB.bit.RXEN = 1;      // enable receiver
      SERCOM5->USART.CTRLB.bit.TXEN = 1;      // enable transmitter
      SERCOM5->USART.CTRLA.reg |= 1 << SERCOM_USART_CTRLA_ENABLE_Pos;    // enable USART
    }

« Last Edit: July 16, 2021, 09:05:16 am by reneman »
Always Learning
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: SAMD21: Can UART/SERCOM use the 48Mhz clock?
« Reply #1 on: July 15, 2021, 01:24:01 pm »
I have a samD10, which is similar enough. You have specific requirements for reading/writing some of those gclk registers, including checking for syncbusy, so accessing these registers via anything other than the 'top' struct member (register size) will result in those requirements not being met.

A couple examples from my gclk functions, which do the 32bit writes and do not preserve any bits (these are simple functions, and if other options needed in genctrl then modification or more functions needed)-
Code: [Select]
Gclk.generatorSource( Gclk.GEN3, Gclk.OSC8M );
Gclk.generatorDivide( Gclk.GEN3, 0 );
Gclk.generatorSource( Gclk.MAIN, Gclk.DFLL48M );
Gclk.generatorDivide( Gclk.MAIN, mhz <= 12_MHz ? 2 : mhz <= 24_MHz ? 1 : 0 );
Gclk.generatorUser( Gclk.GEN3, Gclk.SERCOM0 );
//etc.

//source
                auto
generatorSource (GENERATOR g, SOURCE s)
                {
                while( busy() ){}
                reg_.GCLK_GENCTRL = GENENbm bitor (s<<8) bitor g; //32bit write
                }

                auto
generatorDivide (GENERATOR g, u16 d)
                {
                while( busy() ){}
                reg_.GCLK_GENDIV = (d<<8) bitor g; //32bit write
                }

                auto
generatorUser   (GENERATOR g, USER user, bool enable = true)
                {
                while( busy() ){}
                reg_.GCLK_CLKCTRL = (enable ? CLKENbm : 0) bitor (g<<8) bitor user; //16bit write
                }

You also have wait states to configure for various cpu speeds and voltages, so if you skip that and are running fast, then that would also cause problems.
« Last Edit: July 15, 2021, 01:39:42 pm by cv007 »
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11780
  • Country: us
    • Personal site
Re: SAMD21g18: Can UART/SERCOM use the 48Mhz clock?
« Reply #2 on: July 15, 2021, 06:11:38 pm »
Your code selects GCLK0 as the source of the cloc for the SERCOM CORE. And your commend does not match the code:

 GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK0;           // select multiplexer source GCLK_GEN[3]

Also, you don't need to write that much code, you can do all the settings in one write, which would make the code way more readable and efficient:
Code: [Select]
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) | GCLK_GENCTRL_SRC(GCLK_SOURCE_FDPLL) |
    GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);

Code: [Select]
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM0_GCLK_ID_CORE) |
      GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

Code: [Select]
  SERCOM0->USART.CTRLA.reg =
      SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE_USART_INT_CLK |
      SERCOM_USART_CTRLA_RXPO(3/*PAD3*/) | SERCOM_USART_CTRLA_TXPO(1/*PAD2*/);

  SERCOM0->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |
      SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);

  uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;
  SERCOM0->USART.BAUD.reg = (uint16_t)br+1;

  SERCOM0->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
Alex
 

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can UART/SERCOM use the 48Mhz clock?
« Reply #3 on: July 16, 2021, 08:19:39 am »
Your code selects GCLK0 as the source of the cloc for the SERCOM CORE. And your commend does not match the code:

 GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK0;           // select multiplexer source GCLK_GEN[3]

Also, you don't need to write that much code, you can do all the settings in one write, which would make the code way more readable and efficient:
Code: [Select]
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) | GCLK_GENCTRL_SRC(GCLK_SOURCE_FDPLL) |
    GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);

Code: [Select]
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM0_GCLK_ID_CORE) |
      GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

Code: [Select]
  SERCOM0->USART.CTRLA.reg =
      SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE_USART_INT_CLK |
      SERCOM_USART_CTRLA_RXPO(3/*PAD3*/) | SERCOM_USART_CTRLA_TXPO(1/*PAD2*/);

  SERCOM0->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |
      SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);

  uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;
  SERCOM0->USART.BAUD.reg = (uint16_t)br+1;

  SERCOM0->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;

Yes, I think I forgot to update the comment, but the code was right in the sense that I want to use GCLK0 (the main CPU's clock, that is 48MHz) as well for the SERCOM5 peripheral.

I get an error with your code saying that GCLK_SOURCE_FDPLL is not defined so I'm guessing the SAMD21g18a doesn't have that specific clock source. Should I use GCLK_SOURCE_DFLL48M (The 48MHz clock source) instead?

This is my sys_init for reference:
Code: [Select]
    static void sys_init(void){
      // Switch to 8MHz clock (disable prescaler)
      //SYSCTRL->OSC8M.bit.PRESC = 0;

      // COnfiguramos los wait states de la NVM a 1 (para correr entre 2.7v y 3.3v a 48MHz)
      NVMCTRL->CTRLB.bit.RWS = 1;

      SYSCTRL->XOSC32K.reg =
      /* Crystal oscillators can take a long time to startup. This
        waits the maximum amount of time (4 seconds). This can be
        reduced depending on your crystal oscillator. */
      SYSCTRL_XOSC32K_STARTUP(0x7) |
      SYSCTRL_XOSC32K_EN32K |
      SYSCTRL_XOSC32K_XTALEN;

      /* This has to be a separate write as per datasheet section 17.6.3 */
      SYSCTRL->XOSC32K.bit.ENABLE = 1;

      /* Wait for the external crystal to be ready */
      while(!SYSCTRL->PCLKSR.bit.XOSC32KRDY);

      /* Configure GCLK1's divider - in this case, no division - so just divide by one */
      GCLK->GENDIV.reg =
          GCLK_GENDIV_ID(1) |
          GCLK_GENDIV_DIV(1);

      /* Setup GCLK1 using the external 32.768 kHz oscillator */
      GCLK->GENCTRL.reg =
          GCLK_GENCTRL_ID(1) |
          GCLK_GENCTRL_SRC_XOSC32K |
          /* Improve the duty cycle. */
          GCLK_GENCTRL_IDC |
          GCLK_GENCTRL_GENEN;

      /* Wait for the write to complete */
      while(GCLK->STATUS.bit.SYNCBUSY);

      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_DFLL48 | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_CLKEN;

      /* This works around a quirk in the hardware (errata 1.2.1) -
        the DFLLCTRL register must be manually reset to this value before
        configuration. */
      while(!SYSCTRL->PCLKSR.bit.DFLLRDY);
      SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
      while(!SYSCTRL->PCLKSR.bit.DFLLRDY);

      /* Set up the multiplier. This tells the DFLL to multiply the 32.768 kHz
        reference clock to 48 MHz */
      SYSCTRL->DFLLMUL.reg =
          /* This value is output frequency / reference clock frequency,
            so 48 MHz / 32.768 kHz */
          SYSCTRL_DFLLMUL_MUL(1465) |
          /* The coarse and fine step are used by the DFLL to lock
            on to the target frequency. These are set to half
            of the maximum value. Lower values mean less overshoot,
            whereas higher values typically result in some overshoot but
            faster locking. */
          SYSCTRL_DFLLMUL_FSTEP(511) | // max value: 1023
          SYSCTRL_DFLLMUL_CSTEP(31);  // max value: 63

      /* Wait for the write to finish */
      while(!SYSCTRL->PCLKSR.bit.DFLLRDY);

      uint32_t coarse = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) & FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos;

      SYSCTRL->DFLLVAL.bit.COARSE = coarse;

      /* Wait for the write to finish */
      while(!SYSCTRL->PCLKSR.bit.DFLLRDY);

      SYSCTRL->DFLLCTRL.reg |=
      /* Closed loop mode */
      SYSCTRL_DFLLCTRL_MODE |
      /* Wait for the frequency to be locked before outputting the clock */
      SYSCTRL_DFLLCTRL_WAITLOCK |
      /* Enable it */
      SYSCTRL_DFLLCTRL_ENABLE;

      /* Wait for the frequency to lock */
      while (!SYSCTRL->PCLKSR.bit.DFLLLCKC || !SYSCTRL->PCLKSR.bit.DFLLLCKF) {}

      /* Setup GCLK0 using the DFLL @ 48 MHz */
      GCLK->GENCTRL.reg =
          GCLK_GENCTRL_ID(0) |
          GCLK_GENCTRL_SRC_DFLL48M |
          /* Improve the duty cycle. */
          GCLK_GENCTRL_IDC |
          GCLK_GENCTRL_GENEN;

      /* Wait for the write to complete */
      while(GCLK->STATUS.bit.SYNCBUSY);

}
Always Learning
 

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #4 on: July 16, 2021, 09:04:04 am »
I've just noticed I posted the wrong old code for the functions. This is the code that works with the main clock at 8MHz

Code: [Select]
void clock_init()
    {
      SYSCTRL->OSC8M.bit.PRESC = 0;                          // no prescaler (is 8 on reset)
      SYSCTRL->OSC8M.reg |= 1 << SYSCTRL_OSC8M_ENABLE_Pos;   // enable source
   
      GCLK->GENDIV.bit.ID = 0x03;                            // select GCLK_GEN[3]
      GCLK->GENDIV.bit.DIV = 0;                              // no prescaler

      GCLK->GENCTRL.bit.ID = 0x03;                           // select GCLK_GEN[3]
      GCLK->GENCTRL.reg |= GCLK_GENCTRL_SRC_OSC8M;           // OSC8M source
      GCLK->GENCTRL.bit.GENEN = 1;                           // enable generator

      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_SERCOM5_CORE;      // SERCOM5 multiplexer GCLK_PERIPHERAL[n]
      GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK3;           // select multiplexer source GCLK_GEN[3]
      GCLK->CLKCTRL.bit.CLKEN = 1;                           // enable generic clock (for baud rate generation)

      PM->APBCSEL.bit.APBCDIV = 0;                           // no prescaler
      PM->APBCMASK.bit.SERCOM5_ = 1;                         // enable SERCOM5 synchronous clock
    }

    void port_init()
    {

      // assign alternate function (SERCOM5 PAD2 and PAD3) to pin PB22 and PB23
      PORT->Group[1].PINCFG[22].bit.PMUXEN = 1;  // enable alternate function on pin PB22
      PORT->Group[1].PINCFG[23].bit.PMUXEN = 1;  // enable alternate function on pin PB23
      PORT->Group[1].PMUX[11].bit.PMUXE = 0x03;  // pin PB22 is assigned to SERCOM5 PAD2
      PORT->Group[1].PMUX[11].bit.PMUXO = 0x03;  // pin PB23 is assigned to SERCOM5 PAD3
    }

    void USART_init()
    {
      // configure USART 8N1
      SERCOM5->USART.CTRLA.bit.MODE = 0x1;    // USART internal clock
      SERCOM5->USART.CTRLA.bit.CMODE = 0;     // asynchronous mode
      SERCOM5->USART.CTRLA.bit.RXPO = 0x3;    // RX on PAD3
      SERCOM5->USART.CTRLA.bit.TXPO = 0x1;    // TX on PAD2
      SERCOM5->USART.CTRLA.bit.DORD = 1;      // data order: LSB first
      SERCOM5->USART.CTRLB.bit.CHSIZE = 0x0;  // 8-bit character
      SERCOM5->USART.CTRLA.bit.FORM = 0;      // no parity bit
      SERCOM5->USART.CTRLB.bit.SBMODE = 0;    // 1 stop bit
      SERCOM5->USART.BAUD.reg = 50437;        // 115200 baud rate
      SERCOM5->USART.CTRLB.bit.RXEN = 1;      // enable receiver
      SERCOM5->USART.CTRLB.bit.TXEN = 1;      // enable transmitter
      SERCOM5->USART.CTRLA.reg |= 1 << SERCOM_USART_CTRLA_ENABLE_Pos;    // enable USART
    }

When I changed the main CPU's clock (GCLK0) to work at 48MHz that's when it stopped working. I get gibberish on the uart:

« Last Edit: July 16, 2021, 09:18:45 am by reneman »
Always Learning
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11780
  • Country: us
    • Personal site
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #5 on: July 16, 2021, 04:22:21 pm »
I'm lost in what you are doing. Did you change SERCOM clock to 48 MHz? If so, did you recalculate the BAUD value for the new clock?
Alex
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #6 on: July 16, 2021, 04:57:05 pm »
You will have to figure out what your usart has for its clock. If its outputting data then it has some kind of clock, so continuously send out a 'U' and measure what you have ( 1/bitwidth *16 = usart clock freq ). You then can see what your usart clock freq is, and either your clock source is obviously wrong or your baud register value needs changing to account for the clock error.

I have a samd10 xplained mini hooked up, and although I do not have sercom code as I do with other peripherals, I used what I have and then just set the usart registers up as needed. My dfll48m is running in open loop using the flash stored coarse values, and when I switched from osc8m@8MHz to dfll48m I had to figure out why the calculated value did not work. So I continuously output a 'U' and measured it, and it turns out the cpu is running at 40MHZ. Adjusted the baud register value for actual freq and it works ok.

You still need to look at the gclk register requirements for writing/reading (and any register for that matter), as using the bit members will result in a read/modify/write. The results may be ok, but you could end up with reads for a previous id and writes that may not be wanted even if the next write gets the correct value in place. Pretty confusing, so just do as they say- a single 32bit/16bit write as requested by the datasheet.

Code: [Select]
int main(){
    Sysctrl.clockInit( 48_MHz ); //dfll48(open loop)->MAIN/GEN0
    Gclk.generatorUser( Gclk.MAIN, Gclk.SERCOM0 ); //MAIN/GEN0->SERCOM0
    Pm.clockEnable( Pm.SERCOM0 );

    //63019 baud value for 48MHz, actual fcpu is 40MHz so needed a new value
    SERCOM0_REGS->USART_INT.SERCOM_BAUD = 62516;
    SERCOM0_REGS->USART_INT.SERCOM_CTRLB = 0x00010000; //txen
    SERCOM0_REGS->USART_INT.SERCOM_CTRLA = 0x40010006; //lsb,pa10/pad2/tx,intclk,enable
    Pin(PA10).altFunction(ALTFUNC_C); //pa10 alt function c, sercom0/pad2/tx

    for(char c = 'a';;){
        while( (SERCOM0_REGS->USART_INT.SERCOM_INTFLAG & 1) == 0 ){}
        SERCOM0_REGS->USART_INT.SERCOM_DATA = c; //or 'U' for timing
        if( ++c > 'z' ) c = 'a';
        delayMS(48_MHz, 5);
    }
}
 
The following users thanked this post: reneman

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #7 on: July 19, 2021, 08:04:19 am »
I'm lost in what you are doing. Did you change SERCOM clock to 48 MHz? If so, did you recalculate the BAUD value for the new clock?

Yes, I changed the clock to 48MHz, but I did not recalculate the BAUD value. You and cv007 are right on that.
What is the formula to compute the new baud?

I've seen this one on some people's code:
Code: [Select]
uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;where F_CPU would be my clock frequency If I'm not mistaken. Is this the value I need to update for the register SERCOM5->USART.BAUD.reg ? What is the reasoning behind this formula?


On another note and replying to @cv007 I have this line in my USART_Init routine:
Code: [Select]
void USART_init()
    {
      uint32_t baud = 115200;
      // configure USART 8N1
      SERCOM5->USART.CTRLA.bit.MODE = 0x1;    // USART internal clock  <-------
That I'm not sure what it does, but may be setting a different clock or something.

Thanks

EDIT: I posted the sys_init code in previous messages but in case it is relevant and wasn't obvious, I'm running the 48MHz clock in closed-lopp mode
« Last Edit: July 19, 2021, 08:20:05 am by reneman »
Always Learning
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11780
  • Country: us
    • Personal site
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #8 on: July 19, 2021, 08:23:14 am »
I've seen this one on some people's code:
That's my code.

What is the reasoning behind this formula?
Directly transcribed from the datasheet.

That I'm not sure what it does, but may be setting a different clock or something.
It selects the internal clock for the UART as opposed to the external clock supplied on the SCK pin (USART mode only).
Alex
 

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #9 on: July 19, 2021, 09:39:23 am »
I've seen this one on some people's code:
That's my code.

What is the reasoning behind this formula?
Directly transcribed from the datasheet.

That I'm not sure what it does, but may be setting a different clock or something.
It selects the internal clock for the UART as opposed to the external clock supplied on the SCK pin (USART mode only).

How would I proceed to select the 48MHz clock instead?
Always Learning
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11780
  • Country: us
    • Personal site
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #10 on: July 19, 2021, 04:03:25 pm »
What do you mean? You have selected 48 MHz clock, just use calculator and calculate a new BAUD value for that frequency.
Alex
 
The following users thanked this post: reneman

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #11 on: July 19, 2021, 09:58:25 pm »
GCLK CLKCTRL selects your sercom (ID) baud rate generator clock from whatever selection of generators (GEN) you have setup, unless you select the external clock (pin) in the sercom peripheral. The GCLK main clock runs the sercom peripheral (not baud rate generator) through another path, which may need enabling for some peripherals that are not enabled by default (unmask via PM).

You already have the sercom clock setup via gclk, and if you make any mistake in its clock selection process you will not have a working baud rate generator so not too hard pin down if a problem is due to a missing clock (nothing) vs an inaccurate clock (something), assuming tx/rx pin selection is correct.

 
The following users thanked this post: reneman

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #12 on: July 20, 2021, 07:41:54 am »
Thanks to the two.

It's solved. I've ended up using cv007's baud value for 115200 shown in the code above (I tried using the formula fisrt, but for some reason it wasn't computing the proper value) and now it works fine with that hardcoded value.
I had a bit of confussion regarding the two clocks (the one used to generate the bauds as cv007 points, and the one to drive the peripherals)
Always Learning
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #13 on: July 20, 2021, 02:45:30 pm »
Quote
I've ended up using cv007's baud value for 115200 shown in the code above (I tried using the formula fisrt, but for some reason it wasn't computing the proper value) and now it works fine with that hardcoded value.
Seems odd in closed loop mode you would be that far off. I'm using open loop mode, and it appears the stored nvm calibration coarse value gives me ~40MHz (they may be playing it safe in those values, and the 'close to 48MHz' claim gives no definition of 'close'). I don't use that clock for anything that depends on its timing except to get 'fast' cpu speed at times, so have not paid much attention to what I was really getting.

If using closed loop mode with an xtal reference, you should be quite close to 48MHz, and your baud calculation should be valid for that. You can get the clocks to output to a pin, or indirectly measure in some other way. Setting the baud register to 0 and continuously outputting a 'U' is one way, where the bit period is used to calculate the freq (bit freq*16=ref freq). Maybe you are not getting the xtal running (assume you have one connected) and the dfll is 'stuck' using the coarse value which it appears you are setting to get an initial value in the ballpark.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11780
  • Country: us
    • Personal site
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #14 on: July 20, 2021, 05:25:23 pm »
I tried using the formula fisrt, but for some reason it wasn't computing the proper value) and now it works fine with that hardcoded value.
Cases like this is when you stop and investigate what is wrong.  You just plugged some random value that possibly moved your marginal system just enough into the band to function.

The formula works, it represents how hardware designed. If it does not work for you, it means that some of the inputs to the formula are wrong.
Alex
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #15 on: July 20, 2021, 06:54:07 pm »
Your init code has SYSCTRL_DFLLCTRL_MODE, but that is a macro that takes a parameter, at least in my headers (and the latest pack for sad21). Not sure how that would compile, so must be something else.  There are 2 modes, and the define doesn't really indicate what mode means. I would be suspect of this in any case, and since you have a working uart, even if held together by a string, you could dump some of these register values out in sysctrl, starting with DFLLCTRL to see if you are in open or closed loop mode.

      SYSCTRL->DFLLCTRL.reg |=
      /* Closed loop mode */
      SYSCTRL_DFLLCTRL_MODE |
      /* Wait for the frequency to be locked before outputting the clock */
      SYSCTRL_DFLLCTRL_WAITLOCK |
      /* Enable it */
      SYSCTRL_DFLLCTRL_ENABLE;
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11780
  • Country: us
    • Personal site
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #16 on: July 20, 2021, 07:01:53 pm »
Single bit defines from Atmel headers became macros with Microchip headers. I think there are some compatibility defines too.
Alex
 

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #17 on: July 21, 2021, 09:26:58 am »
Quote
I've ended up using cv007's baud value for 115200 shown in the code above (I tried using the formula fisrt, but for some reason it wasn't computing the proper value) and now it works fine with that hardcoded value.
Seems odd in closed loop mode you would be that far off. I'm using open loop mode, and it appears the stored nvm calibration coarse value gives me ~40MHz (they may be playing it safe in those values, and the 'close to 48MHz' claim gives no definition of 'close'). I don't use that clock for anything that depends on its timing except to get 'fast' cpu speed at times, so have not paid much attention to what I was really getting.

If using closed loop mode with an xtal reference, you should be quite close to 48MHz, and your baud calculation should be valid for that. You can get the clocks to output to a pin, or indirectly measure in some other way. Setting the baud register to 0 and continuously outputting a 'U' is one way, where the bit period is used to calculate the freq (bit freq*16=ref freq). Maybe you are not getting the xtal running (assume you have one connected) and the dfll is 'stuck' using the coarse value which it appears you are setting to get an initial value in the ballpark.

To be clear, I used the 63019 baud value for 48MHz, in your code's comments. Not the one you computed for your 40MHz. I assumed that if my clock was well configured and close to 48MHz then it should be fine, and indeed it works.
Always Learning
 

Offline renemanTopic starter

  • Contributor
  • Posts: 21
  • Country: 00
Re: SAMD21g18: Can SERCOM be used with the 48Mhz clock for USART?
« Reply #18 on: July 21, 2021, 09:36:13 am »
I tried using the formula fisrt, but for some reason it wasn't computing the proper value) and now it works fine with that hardcoded value.
Cases like this is when you stop and investigate what is wrong.  You just plugged some random value that possibly moved your marginal system just enough into the band to function.

The formula works, it represents how hardware designed. If it does not work for you, it means that some of the inputs to the formula are wrong.

You are right, and I haven't gave up on the issue.
My assumption is there is something funky going on with division. I've observed other problems in parts of the code that fail when I use a predefined formula in run-time, but work if I Just compute the number with my calculator and then hardcode it into the code.

For example, I Had this code to set up the SysTick interrupt:

Code: [Select]
if ( SysTick_Config( SystemcoreClock / 1000 ) ) //. 1ms interrupts
        {
 
          while ( 1 );
        } //Catch errors.

This code failed when entering the SysTIck_Config function:

Code: [Select]
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible */

  ...
}

Because it returned 1 (reload value impossible). SystemcoreClock definition was this:

Code: [Select]
uint32_t SystemCoreClock=48000000ul;

As you see, something was going on when dividing by 1000 and passing the result to the Systick_config function. Using the actual value however, solved the problem (48000000 / 1000 = 48000)

So this could be something similar, where the result of the formula wasn't 63019 but something different. I'll try to debug the formula now that at least I have a working UART

EDIT
Perhaps division is not the problem. Just tested this code:

Code: [Select]
uint32_t baud = 115200;
  uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;

  char str2[25];
  sprintf(str2,"%d",br);
  Hal.uart_puts(str2);

And the UART output is 50436, which is obviously far away from 63019. F_CPU is the only suspect here, and I couldn't find it in the code. It is apparently in the Makefile I use from @ataradov example repos (no doubt I had missed it).

Code: [Select]
DEFINES += \
  -DDONT_USE_CMSIS_INIT \
  -DF_CPU=8000000 //<----- THIS SHOULD BE 48000000 now 

And the formula now computes the proper BAUD value
« Last Edit: July 21, 2021, 09:45:51 am by reneman »
Always Learning
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf