1 | void ClocksInit(void){
|
2 |
|
3 | uint32_t tempDFLL48CalibrationCoarse; /* used to retrieve DFLL48 coarse calibration value from NVM */
|
4 |
|
5 | /* ----------------------------------------------------------------------------------------------
|
6 | * 1) Set Flash wait states for 48 MHz (per Table 37-40 in data sheet)
|
7 | */
|
8 |
|
9 | NVMCTRL->CTRLB.bit.RWS = 1; /* 1 wait state required @ 3.3V & 48MHz */
|
10 |
|
11 | /* ----------------------------------------------------------------------------------------------
|
12 | * 2) Enable XOSC32K clock (External on-board 32.768kHz oscillator), will be used as DFLL48M reference.
|
13 | */
|
14 |
|
15 | // Configure SYSCTRL->XOSC32K settings
|
16 | SYSCTRL_XOSC32K_Type sysctrl_xosc32k = {
|
17 | .bit.WRTLOCK = 0, /* XOSC32K configuration is not locked */
|
18 | .bit.STARTUP = 0x2, /* 3 cycle start-up time */
|
19 | .bit.ONDEMAND = 0, /* Osc. is always running when enabled */
|
20 | .bit.RUNSTDBY = 0, /* Osc. is disabled in standby sleep mode */
|
21 | .bit.AAMPEN = 0, /* Disable automatic amplitude control */
|
22 | .bit.EN32K = 1, /* 32kHz output is disabled */
|
23 | .bit.XTALEN = 1 /* Crystal connected to XIN32/XOUT32 */
|
24 | };
|
25 |
|
26 | SYSCTRL_OSC32K_Type sysctrl_osc32k = {
|
27 | .bit.WRTLOCK = 0, /* OSC32K configuration is not locked */
|
28 | .bit.STARTUP = 0x2, /* 3 cycle start-up time */
|
29 | .bit.ONDEMAND = 0, /* Osc. is always running when enabled */
|
30 | .bit.RUNSTDBY = 0, /* Osc. is disabled in standby sleep mode */
|
31 | .bit.EN32K = 1, /* 32kHz output is disabled */
|
32 | };
|
33 |
|
34 | // Write these settings
|
35 | // SYSCTRL->XOSC32K.reg = sysctrl_xosc32k.reg;
|
36 | SYSCTRL->OSC32K.reg = sysctrl_osc32k.reg;
|
37 | // Enable the Oscillator - Separate step per data sheet recommendation (sec 17.6.3)
|
38 | SYSCTRL->OSC32K.bit.ENABLE = 1;
|
39 | // SYSCTRL->XOSC32K.bit.ENABLE = 1;
|
40 |
|
41 | // Wait for XOSC32K to stabilize
|
42 | while(!SYSCTRL->PCLKSR.bit.OSC32KRDY);
|
43 |
|
44 | /* ----------------------------------------------------------------------------------------------
|
45 | * 3) Put XOSC32K as source of Generic Clock Generator 1
|
46 | */
|
47 |
|
48 | // Set the Generic Clock Generator 1 output divider to 1
|
49 | // Configure GCLK->GENDIV settings
|
50 | GCLK_GENDIV_Type gclk1_gendiv = {
|
51 | .bit.DIV = 1, /* Set output division factor = 1 */
|
52 | // .bit.ID = GENERIC_CLOCK_GENERATOR_XOSC32K /* Apply division factor to Generator 1 */
|
53 | .bit.ID = 0x04
|
54 | };
|
55 | // Write these settings
|
56 | GCLK->GENDIV.reg = gclk1_gendiv.reg;
|
57 |
|
58 | // Configure Generic Clock Generator 1 with XOSC32K as source
|
59 | GCLK_GENCTRL_Type gclk1_genctrl = {
|
60 | .bit.RUNSTDBY = 0, /* Generic Clock Generator is stopped in stdby */
|
61 | .bit.DIVSEL = 0, /* Use GENDIV.DIV value to divide the generator */
|
62 | .bit.OE = 0, /* Disable generator output to GCLK_IO[1] */
|
63 | .bit.OOV = 0, /* GCLK_IO[1] output value when generator is off */
|
64 | .bit.IDC = 1, /* Generator duty cycle is 50/50 */
|
65 | .bit.GENEN = 1, /* Enable the generator */
|
66 | .bit.SRC = 0x05, /* Generator source: XOSC32K output */
|
67 | // .bit.ID = GENERIC_CLOCK_GENERATOR_XOSC32K /* Generator ID: 1 */
|
68 | .bit.ID = 0x04
|
69 | };
|
70 | // Write these settings
|
71 | GCLK->GENCTRL.reg = gclk1_genctrl.reg;
|
72 | // GENCTRL is Write-Synchronized...so wait for write to complete
|
73 | while(GCLK->STATUS.bit.SYNCBUSY);
|
74 |
|
75 | /* ----------------------------------------------------------------------------------------------
|
76 | * 4) Put Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference)
|
77 | */
|
78 |
|
79 | GCLK_CLKCTRL_Type gclk_clkctrl = {
|
80 | .bit.WRTLOCK = 0, /* Generic Clock is not locked from subsequent writes */
|
81 | .bit.CLKEN = 1, /* Enable the Generic Clock */
|
82 | // .bit.GEN = GENERIC_CLOCK_GENERATOR_XOSC32K, /* Generic Clock Generator 1 is the source */
|
83 | .bit.GEN = 0x04, /* Generic Clock Generator 1 is the source */
|
84 | .bit.ID = 0x00 /* Generic Clock Multiplexer 0 (DFLL48M Reference) */
|
85 | };
|
86 | // Write these settings
|
87 | GCLK->CLKCTRL.reg = gclk_clkctrl.reg;
|
88 |
|
89 | /* ----------------------------------------------------------------------------------------------
|
90 | * 5) Enable DFLL48M clock
|
91 | */
|
92 |
|
93 | // DFLL Configuration in Closed Loop mode, cf product data sheet chapter
|
94 | // 17.6.7.1 - Closed-Loop Operation
|
95 |
|
96 | // Enable the DFLL48M in open loop mode. Without this step, attempts to go into closed loop mode at 48 MHz will
|
97 | // result in Processor Reset (you'll be at the in the Reset_Handler in startup_samd21.c).
|
98 | // PCLKSR.DFLLRDY must be one before writing to the DFLL Control register
|
99 | // Note that the DFLLRDY bit represents status of register synchronization - NOT clock stability
|
100 | // (see Data Sheet 17.6.14 Synchronization for detail)
|
101 | while(!SYSCTRL->PCLKSR.bit.DFLLRDY);
|
102 | SYSCTRL->DFLLCTRL.reg = (uint16_t)(SYSCTRL_DFLLCTRL_ENABLE);
|
103 | while(!SYSCTRL->PCLKSR.bit.DFLLRDY);
|
104 |
|
105 | // Set up the Multiplier, Coarse and Fine steps
|
106 | SYSCTRL_DFLLMUL_Type sysctrl_dfllmul = {
|
107 | .bit.CSTEP = 31, /* Coarse step - use half of the max value (63) */
|
108 | .bit.FSTEP = 511, /* Fine step - use half of the max value (1023) */
|
109 | .bit.MUL = 1465 /* Multiplier = MAIN_CLK_FREQ (48MHz) / EXT_32K_CLK_FREQ (32768 Hz) */
|
110 | };
|
111 | // Write these settings
|
112 | SYSCTRL->DFLLMUL.reg = sysctrl_dfllmul.reg;
|
113 | // Wait for synchronization
|
114 | while(!SYSCTRL->PCLKSR.bit.DFLLRDY);
|
115 |
|
116 | // To reduce lock time, load factory calibrated values into DFLLVAL (cf. Data Sheet 17.6.7.1)
|
117 | // Location of value is defined in Data Sheet Table 10-5. NVM Software Calibration Area Mapping
|
118 |
|
119 | // Get factory calibrated value for "DFLL48M COARSE CAL" from NVM Software Calibration Area
|
120 | tempDFLL48CalibrationCoarse = *(uint32_t*)FUSES_DFLL48M_COARSE_CAL_ADDR;
|
121 | tempDFLL48CalibrationCoarse &= FUSES_DFLL48M_COARSE_CAL_Msk;
|
122 | tempDFLL48CalibrationCoarse = tempDFLL48CalibrationCoarse>>FUSES_DFLL48M_COARSE_CAL_Pos;
|
123 | // Write the coarse calibration value
|
124 | SYSCTRL->DFLLVAL.bit.COARSE = tempDFLL48CalibrationCoarse;
|
125 | // Switch DFLL48M to Closed Loop mode and enable WAITLOCK
|
126 | while(!SYSCTRL->PCLKSR.bit.DFLLRDY);
|
127 | SYSCTRL->DFLLCTRL.reg |= (uint16_t) (SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK);
|
128 |
|
129 | /* ----------------------------------------------------------------------------------------------
|
130 | * 6) Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz.
|
131 | */
|
132 |
|
133 | // Now that DFLL48M is running, switch CLKGEN0 source to it to run the core at 48 MHz.
|
134 | // Enable output of Generic Clock Generator 0 (GCLK_MAIN) to the GCLK_IO[0] GPIO Pin
|
135 | GCLK_GENCTRL_Type gclk_genctrl0 = {
|
136 | .bit.RUNSTDBY = 0, /* Generic Clock Generator is stopped in stdby */
|
137 | .bit.DIVSEL = 0, /* Use GENDIV.DIV value to divide the generator */
|
138 | .bit.OE = 1, /* Enable generator output to GCLK_IO[0] */
|
139 | .bit.OOV = 0, /* GCLK_IO[0] output value when generator is off */
|
140 | .bit.IDC = 1, /* Generator duty cycle is 50/50 */
|
141 | .bit.GENEN = 1, /* Enable the generator */
|
142 | .bit.SRC = 0x07, /* Generator source: DFLL48M output */
|
143 | .bit.ID = GENERIC_CLOCK_GENERATOR_MAIN /* Generator ID: 0 */
|
144 | };
|
145 | GCLK->GENCTRL.reg = gclk_genctrl0.reg;
|
146 | // GENCTRL is Write-Synchronized...so wait for write to complete
|
147 | while(GCLK->STATUS.bit.SYNCBUSY);
|
148 |
|
149 | // Direct the GCLK_IO[0] output to PA28
|
150 | PORT_WRCONFIG_Type port0_wrconfig = {
|
151 | .bit.HWSEL = 1, /* Pin# (28) - falls in the upper half of the 32-pin PORT group */
|
152 | .bit.WRPINCFG = 1, /* Update PINCFGy registers for all pins selected */
|
153 | .bit.WRPMUX = 1, /* Update PMUXn registers for all pins selected */
|
154 | .bit.PMUX = 7, /* Peripheral Function H selected (GCLK_IO[0]) */
|
155 | .bit.PMUXEN = 1, /* Enable peripheral Multiplexer */
|
156 | .bit.PINMASK = (uint16_t)(1 << (28-16)) /* Select the pin(s) to be configured */
|
157 | };
|
158 | // Write these settings
|
159 | PORT->Group[0].WRCONFIG.reg = port0_wrconfig.reg;
|
160 |
|
161 | /* ----------------------------------------------------------------------------------------------
|
162 | * 7) Modify prescaler value of OSC8M to produce 8MHz output
|
163 | */
|
164 |
|
165 | SYSCTRL->OSC8M.bit.PRESC = 0; /* Prescale by 1 */
|
166 | SYSCTRL->OSC8M.bit.ONDEMAND = 0 ; /* Oscillator is always on if enabled */
|
167 |
|
168 | /* ----------------------------------------------------------------------------------------------
|
169 | * 8) Put OSC8M as source for Generic Clock Generator 3
|
170 | */
|
171 |
|
172 | // Set the Generic Clock Generator 3 output divider to 1
|
173 | // Configure GCLK->GENDIV settings
|
174 | GCLK_GENDIV_Type gclk3_gendiv = {
|
175 | .bit.DIV = 1, /* Set output division factor = 1 */
|
176 | .bit.ID = GENERIC_CLOCK_GENERATOR_OSC8M /* Apply division factor to Generator 3 */
|
177 | };
|
178 | // Write these settings
|
179 | GCLK->GENDIV.reg = gclk3_gendiv.reg;
|
180 |
|
181 | // Configure Generic Clock Generator 3 with OSC8M as source
|
182 | GCLK_GENCTRL_Type gclk3_genctrl = {
|
183 | .bit.RUNSTDBY = 0, /* Generic Clock Generator is stopped in stdby */
|
184 | .bit.DIVSEL = 0, /* Use GENDIV.DIV value to divide the generator */
|
185 | .bit.OE = 0, /* Disable generator output to GCLK_IO[1] */
|
186 | .bit.OOV = 0, /* GCLK_IO[2] output value when generator is off */
|
187 | .bit.IDC = 1, /* Generator duty cycle is 50/50 */
|
188 | .bit.GENEN = 1, /* Enable the generator */
|
189 | .bit.SRC = 0x06, /* Generator source: OSC8M output */
|
190 | .bit.ID = GENERIC_CLOCK_GENERATOR_OSC8M /* Generator ID: 3 */
|
191 | };
|
192 | // Write these settings
|
193 | GCLK->GENCTRL.reg = gclk3_genctrl.reg;
|
194 | // GENCTRL is Write-Synchronized...so wait for write to complete
|
195 | while(GCLK->STATUS.bit.SYNCBUSY);
|
196 |
|
197 | /* ----------------------------------------------------------------------------------------------
|
198 | * 9) Set CPU and APBx BUS Clocks to 48MHz
|
199 | */
|
200 | PM->CPUSEL.reg = PM_CPUSEL_CPUDIV_DIV1 ;
|
201 | PM->APBASEL.reg = PM_APBASEL_APBADIV_DIV1_Val ;
|
202 | PM->APBBSEL.reg = PM_APBBSEL_APBBDIV_DIV1_Val ;
|
203 | PM->APBCSEL.reg = PM_APBCSEL_APBCDIV_DIV1_Val ;
|
204 |
|
205 | } // ClockSysInit48M()
|