-
Notifications
You must be signed in to change notification settings - Fork 1
/
Osc.hpp
217 lines (160 loc) · 5.72 KB
/
Osc.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#pragma once
//Oscillator
/*
Oscillator - set cpu clock source, speed
any fucntion that depends on cpu frequency, can call sysclk() to get current
cpu frequency in Hz, if not already calculated it will be calculated when
called or when cpu source/speed is changed, if external clock/crystal is
used then frequency is calculated using sosc or lprc UNLESS m_extosc_freq
is set in this file by user to anything other than 0
config bits will need to enable clock source switching if changing clock
sources OR changing PLL mul/div values
#pragma config FCKSM = CSECMD
just set frc as clock source in config bits to prevent pll problems after
a reset (where MUL is too high, and reset will seem to lock up chip)
#pragma config FNOSC = FRCDIV
use Osc::sysclk() to retrieve sysclock
any peripheral specifying SOSC as clock source, will also cause use of
Osc::sosc(true) to make sure sosc is turned on, but no check if SOSC is
present (assume if specified, caller must know its available), if peripheral
like rtcc needs a clock but is not specified, then peripheral code will call
sosc() to check if its on already, if on assume its available
if using sosc, just call Osc::sosc(true) early in code (may already be fused
on), so any peripheral needing it can use it
*/
#include <cstdint>
#include "Reg.hpp"
struct Osc : private Reg {
//==== OSCCON ====
enum
DIVS : uint8_t { //upper byte of osccon, spllcon
DIV1, DIV2, DIV4, DIV8, DIV16, DIV32, DIV64, DIV256
};
static auto
frc_div (DIVS) -> void;
static auto
frc_div () -> DIVS;
enum
CNOSC : uint8_t { //cosc/nosc in second byte of osccon
//FRCDIV = 0, SPLL, POSC, SOSC = 4, LPRC
FRC = 0, FRCPLL, POSC, POSCPLL, SOSC, LPRC, FRC16, FRCDIV
};
//get current osc source
static auto
clk_src () -> CNOSC;
//start switch to nosc
static auto
clk_src (CNOSC) -> void;
//lock nosc/oswen until reset
static auto
clk_lock () -> void;
//sleep, wait
static auto
sleep () -> void;
//idle, wait
static auto
idle () -> void;
//clock failed?
static auto
clk_bad () -> bool;
//sosc enable
static auto
sosc (bool) -> void;
//sosc enabled? (if enabled, assume its available for use)
static auto
sosc () -> bool;
// get peripheral bus divider
static auto
pb_div () -> DIVS;
// set peripheral bus divider
static auto
pb_div (DIVS) -> void;
//get pll divider
static auto
pll_odiv () -> DIVS;
enum
IDIVS : uint8_t { //lower byte of devcfg2
IDIV1, IDIV2, IDIV3, IDIV4, IDIV5, IDIV6, IDIV10, IDIV12
};
//get pll input divider
static auto
pll_idiv () -> IDIVS;
enum
PLLMUL : uint8_t {
MUL15, MUL16, MUL17, MUL18, MUL19, MUL20, MUL21, MUL24
};
//get pll multiplier
static auto
pll_mul () -> PLLMUL;
//set pll mul/div, pll src
static auto
pll_set (PLLMUL, DIVS, CNOSC = FRC) -> void;
//==== CLKSTAT ====
enum
CLKRDY : uint32_t {
SOSCRDY = 1<<22, PBDIVRDY = 1<<21
};
//clock ready?
static auto
ready (CLKRDY) -> bool;
//==== OSCTUN ====
//tune value 6bits, -32 to 31
static auto
tun_val (int8_t) -> void;
//get tune value
static auto
tun_val () -> int8_t;
//misc
//get cpu sysclk
static auto
sysclk () -> uint32_t;
//get ext clock
static auto
extclk () -> uint32_t;
//get frc clock
static auto
frcclk () -> uint32_t;
//get peripheral clock
static auto
pbclk () -> uint32_t;
//get peripheral clock
static auto
clk_switch (CNOSC, uint32_t) -> void;
private:
//store calculated cpu freq
static uint32_t m_sysclk;
//store calculated refo in clk
static uint32_t m_extclk;
//store calculated pbclock in clk
static uint32_t m_pbclk;
//store current refo frequency
static const uint32_t m_frcosc_freq = 8000000;
//manually set here if want exact ext freq
static const uint32_t m_extosc_freq = 8000000;
static const uint8_t m_mul_lookup[8];
static const uint8_t m_idiv_lookup[8];
};
/*
func reg calls recalculate
-----------------------------------------------------------------------
frc_div() FRCDIVv sysclk() [m_sysclk]
clk_src() NOSC sysclk() [m_sysclk]
pll_src() PLLICLK refo_clk() [m_refoclk]
pll_set() PLLxxxx pll_src(),clk_src() [m_refoclk][m_sysclk]
refo_src() ROSEL refo_clk() [m_refoclk]
refo_clk() recalculates m_refoclk if set to 0
else returns m_refoclk
re-sets refo freq if m_refo_freq not 0
returns m_refoclk
sysclk() recalculates m_sysclk if set to 0
else returns m_sysclk
returns m_sysclk
//used by anyone to get sysclock
Osc::sysclk()
//used by anyone to get refo frequency
Osc::refo_freq()
//only needed by Osc
Osc::refoclk()
Osc::extclk()
Osc::vcoclk()
*/