-
Notifications
You must be signed in to change notification settings - Fork 1
/
timer.cpp
121 lines (108 loc) · 3.51 KB
/
timer.cpp
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
#include "config.h"
#include "debug.h"
#include "ethernet.h"
#include "timing.h"
volatile char pps_int = 0;
static char pps_output_enabled = 0;
static void timer1_setup() {
pmc_enable_periph_clk(ID_TC1); // TC0 Ch1
TC_Configure(TC0, 1,
TC_CMR_TCCLKS_XC0 | // XC0 = TCLK0 = PB26 = pin 22
TC_CMR_WAVSEL_UP_RC | // Count up to RC then reset to 0
TC_CMR_LDRA_RISING // Load RA and generate interrupt on rising edge of TIOA1 (PA2 = pin A7)
);
TC0->TC_CHANNEL[1].TC_IER = TC_IER_LDRAS | TC_IER_CPCS; // Generate interrupt on RA load and 1Hz
TC0->TC_CHANNEL[1].TC_IDR = ~(TC_IER_LDRAS | TC_IER_CPCS);
TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN; // Enable clock
TC0->TC_CHANNEL[1].TC_RC = HZ; // Period = 1 second
NVIC_EnableIRQ(TC1_IRQn); // Enable IRQ (TC1_Handler)
}
static void timer0_setup() {
pmc_enable_periph_clk(ID_TC0);
TC_Configure(TC0, 0,
TC_CMR_TCCLKS_XC0 | // XC0 = TCLK0 = PB26 = pin 22
TC_CMR_WAVSEL_UP_RC | // Count up to RC then reset to 0
TC_CMR_WAVE | // Generate output waveform
TC_CMR_EEVT_XC1 // Let TIOB be an output
);
TC0->TC_CHANNEL[0].TC_IER = 0; // No interrupts
TC0->TC_CHANNEL[0].TC_IDR = ~0; // No interrupts
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN; // Enable clock
TC0->TC_CHANNEL[0].TC_RC = HZ; // Period = 1 second
TC0->TC_CHANNEL[0].TC_RA = HZ - (((uint64_t)HZ * (PPS_OFFSET_NS - PPSOUT_OFFSET_NS)) / 1000000000L) - 1; // Drive high at top of second
TC0->TC_CHANNEL[0].TC_RB = HZ - (((uint64_t)HZ * (PPS_OFFSET_NS - PPSOUT_OFFSET_NS)) / 1000000000L) - 1; // Drive high at top of second
TC0->TC_CHANNEL[0].TC_CMR |= (TC_CMR_BCPB_SET | TC_CMR_BCPC_CLEAR); // Enable pin 13 PPS for GPS feedback
PIO_Configure(
PIOB,
PIO_PERIPH_B,
PIO_PB27B_TIOB0,
PIO_DEFAULT
);
PIO_Configure(
PIOB,
PIO_PERIPH_B,
PIO_PB25B_TIOA0,
PIO_DEFAULT
);
}
static void timers_start() {
// Loading this register resets all three channels of TC0 to 0 and starts them
TC0->TC_BCR = TC_BCR_SYNC;
}
static void timers_sync() {
do { } while (! (TC0->TC_CHANNEL[1].TC_SR & TC_SR_CPCS));
int32_t tgt = TC0->TC_CHANNEL[1].TC_RA + ((uint64_t)HZ * (PPS_OFFSET_NS + PPS_FUDGE_NS)) / 1000000000L - 2;
if (tgt < 0)
tgt += HZ;
if (tgt >= HZ)
tgt -= HZ;
int32_t diff;
do {
diff = TC0->TC_CHANNEL[1].TC_CV - tgt;
} while (diff > 1 || diff < -1);
TC0->TC_BCR = TC_BCR_SYNC;
}
void timers_set_max(uint32_t max) {
TC0->TC_CHANNEL[0].TC_RC = TC0->TC_CHANNEL[1].TC_RC = max;
}
static int jam_sync = 1;
void timers_jam_sync() {
jam_sync = 1;
}
void TC1_Handler() {
uint32_t status = TC0->TC_CHANNEL[1].TC_SR;
if (status & TC_SR_CPCS) { // On RC compare (1Hz)
second_int();
}
if (status & TC_SR_LDRAS) { // On rising edge of PPS
debug("CAPT: ");
uint32_t tm = TC0->TC_CHANNEL[1].TC_RA;
TC0->TC_CHANNEL[1].TC_RB;
debug(tm); debug("\r\n");
if (jam_sync) {
timers_sync();
jam_sync = 0;
} else {
pps_int = 1;
}
}
}
void timer_init() {
pinMode(13, OUTPUT);
pinMode(2, OUTPUT);
timer0_setup();
timer1_setup();
timers_start();
}
void pps_output_enable() {
if (!pps_output_enabled) {
TC0->TC_CHANNEL[0].TC_CMR |= (TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR);
pps_output_enabled = 1;
}
}
void pps_output_disable() {
if (pps_output_enabled) {
TC0->TC_CHANNEL[0].TC_CMR &= ~(TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR);
pps_output_enabled = 0;
}
}