-
-
Notifications
You must be signed in to change notification settings - Fork 110
/
DCCTimerMEGAAVR.cpp
155 lines (135 loc) · 4.65 KB
/
DCCTimerMEGAAVR.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
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
/*
* © 2022 Paul M. Antoine
* © 2021 Mike S
* © 2021 Harald Barth
* © 2021 Fred Decker
* © 2021 Chris Harlow
* © 2021 David Cutting
* All rights reserved.
*
* This file is part of Asbelos DCC API
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
/* This timer class is used to manage the single timer required to handle the DCC waveform.
* All timer access comes through this class so that it can be compiled for
* various hardware CPU types.
*
* DCCEX works on a single timer interrupt at a regular 58uS interval.
* The DCCWaveform class generates the signals to the motor shield
* based on this timer.
*
* If the motor drivers are BOTH configured to use the correct 2 pins for the architecture,
* (see isPWMPin() function. )
* then this allows us to use a hardware driven pin switching arrangement which is
* achieved by setting the duty cycle of the NEXT clock interrupt to 0% or 100% depending on
* the required pin state. (see setPWM())
* This is more accurate than the software interrupt but at the expense of
* limiting the choice of available pins.
* Fortunately, a standard motor shield on a Mega uses pins that qualify for PWM...
* Other shields may be jumpered to PWM pins or run directly using the software interrupt.
*
* Because the PWM-based waveform is effectively set half a cycle after the software version,
* it is not acceptable to drive the two tracks on different methiods or it would cause
* problems for <1 JOIN> etc.
*
*/
// ATTENTION: this file only compiles on a UnoWifiRev3 or NanoEvery
// Please refer to DCCTimer.h for general comments about how this class works
// This is to avoid repetition and duplication.
#ifdef ARDUINO_ARCH_MEGAAVR
#include "DCCTimer.h"
INTERRUPT_CALLBACK interruptHandler=0;
extern char *__brkval;
extern char *__malloc_heap_start;
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
interruptHandler=callback;
noInterrupts();
ADC0.CTRLC = (ADC0.CTRLC & 0b00110000) | 0b01000011; // speed up analogRead sample time
TCB0.CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled
TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // 8 MHz ~ 0.125 us
TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset
TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag
TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt
TCB0.CNT = 0;
TCB0.CTRLA |= TCB_ENABLE_bm; // start
interrupts();
}
// ISR called by timer interrupt every 58uS
ISR(TCB0_INT_vect){
TCB0.INTFLAGS = TCB_CAPT_bm; // Clear interrupt request flag
interruptHandler();
}
bool DCCTimer::isPWMPin(byte pin) {
(void) pin;
return false; // TODO what are the relevant pins?
}
void DCCTimer::setPWM(byte pin, bool high) {
(void) pin;
(void) high;
// TODO what are the relevant pins?
}
void DCCTimer::clearPWM() {
// Do nothing unless we implent HA
}
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
memcpy(mac,(void *) &SIGROW.SERNUM0,6); // serial number
mac[0] &= 0xFE;
mac[0] |= 0x02;
}
volatile int DCCTimer::minimum_free_memory=__INT_MAX__;
// Return low memory value...
int DCCTimer::getMinimumFreeMemory() {
noInterrupts(); // Disable interrupts to get volatile value
int retval = minimum_free_memory;
interrupts();
return retval;
}
extern char *__brkval;
extern char *__malloc_heap_start;
int DCCTimer::freeMemory() {
char top;
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
}
void DCCTimer::reset() {
CPU_CCP=0xD8;
WDT.CTRLA=0x4;
while(true){}
}
int16_t ADCee::ADCmax() {
return 4095;
}
int ADCee::init(uint8_t pin) {
return analogRead(pin);
}
/*
* Read function ADCee::read(pin) to get value instead of analogRead(pin)
*/
int ADCee::read(uint8_t pin, bool fromISR) {
int current;
if (!fromISR) noInterrupts();
current = analogRead(pin);
if (!fromISR) interrupts();
return current;
}
/*
* Scan function that is called from interrupt
*/
void ADCee::scan() {
}
void ADCee::begin() {
noInterrupts();
interrupts();
}
#endif