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
}
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
}
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);
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM0_GCLK_ID_CORE) |
GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);
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;
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;
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);
}
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
}
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);
}
}
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?
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?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.I've seen this one on some people's code:
What is the reasoning behind this formula?
That I'm not sure what it does, but may be setting a different clock or something.
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).
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 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.
QuoteI'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.
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.
if ( SysTick_Config( SystemcoreClock / 1000 ) ) //. 1ms interrupts
{
while ( 1 );
} //Catch errors.
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
...
}
uint32_t SystemCoreClock=48000000ul;
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);
DEFINES += \
-DDONT_USE_CMSIS_INIT \
-DF_CPU=8000000 //<----- THIS SHOULD BE 48000000 now