-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmyfunc.cpp
287 lines (224 loc) · 10.3 KB
/
myfunc.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
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*- -----------------------------------------------------------------------------------------------------------------------
* FDL-2 arduino implementation
* 2018-01-17 <[email protected]> Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* - -----------------------------------------------------------------------------------------------------------------------
* - all supporting functions, like pin setup, pin change interrupt handling, etc ------------------------------------------
* special thanks to Jesse Kovarovics http://www.projectfdl.com to make this happen
* - -----------------------------------------------------------------------------------------------------------------------
*/
#include "myfunc.h"
/**
* @brief Constructor to initialize waittimer
*/
waittimer::waittimer() {
}
/**
* @brief Query if the timer has expired
*
* @return 0 if timer is still running, 1 if not.
* If the timer was never set(), return value is 1
*/
uint8_t waittimer::done(void) {
if (!checkTime) return 1; // not armed, so nothing to do
if ((get_millis() - startTime) < checkTime) return 0; // not ready yet
checkTime = 0; // if we are here, timeout was happened
return 1; // return a 1 for done
}
/**
* @brief Start the timer
*
* @param ms Time until timer is done() (unit: ms)
*/
void waittimer::set(uint32_t wait_millis) {
uint8_t armed = (wait_millis) ? 1 : 0;
if (!armed) return;
startTime = get_millis();
checkTime = wait_millis;
}
/**
* @brief Query the remaing time until the timer is done
*
* @return Time until timer is done() (unit: ms)
*/
uint32_t waittimer::remain(void) {
if (!checkTime) return 0;
return (checkTime - (get_millis() - startTime));
}
/* returns the status of the timer
* 0 not active
* 1 active and remaining time is 0 or below, but not progressed via done()
* 2 active and remaining time is above 0 */
uint8_t waittimer::completed(void) {
if (!checkTime) return 0; // not armed, so return not active
else if ((get_millis() - startTime) >= checkTime) return 1; // timer done, but not progressed
else return 2; // time not ready, need some additional time
}
/*-- pin functions --------------------------------------------------------------------------------------------------------
* concept of pin functions is a central definition of pin and interrupt registers as a struct per pin. handover of pin
* information is done by forwarding a pointer to the specific function and within the function all hardware related
* setup and switching is done.
*/
/* set a specific pin as output */
void set_pin_output(uint8_t pin_def) {
uint8_t bit = digitalPinToBitMask(pin_def);
uint8_t port = digitalPinToPort(pin_def);
volatile uint8_t *reg;
reg = portModeRegister(port);
*reg |= bit;
}
/* set the pin as input */
void set_pin_input(uint8_t pin_def) {
uint8_t bit = digitalPinToBitMask(pin_def);
uint8_t port = digitalPinToPort(pin_def);
volatile uint8_t *reg;
reg = portModeRegister(port);
*reg &= ~bit;
}
/* set high level on specific pin */
void set_pin_high(uint8_t pin_def) {
uint8_t bit = digitalPinToBitMask(pin_def);
uint8_t port = digitalPinToPort(pin_def);
volatile uint8_t *out;
out = portOutputRegister(port);
*out |= bit;
}
/* set a low level on a specific pin */
void set_pin_low(uint8_t pin_def) {
uint8_t bit = digitalPinToBitMask(pin_def);
uint8_t port = digitalPinToPort(pin_def);
volatile uint8_t *out;
out = portOutputRegister(port);
*out &= ~bit;
}
/* detect a pin input if it is high or low */
uint8_t get_pin_status(uint8_t pin_def) {
uint8_t bit = digitalPinToBitMask(pin_def);
uint8_t port = digitalPinToPort(pin_def);
if (*portInputRegister(port) & bit) return HIGH;
return LOW;
}
//- -----------------------------------------------------------------------------------------------------------------------
/*-- interrupt functions --------------------------------------------------------------------------------------------------
* based on the same concept as the pin functions. everything pin related is defined in a pin struct, handover of the pin
* is done by forwarding a pointer to the struct. pin definition is done in HAL_<cpu>.h, functions are declared in HAL_<vendor>.h
*/
struct s_pcint_vector {
volatile uint8_t *PINREG;
uint8_t curr;
uint8_t chng;
uint8_t prev;
uint8_t mask;
uint32_t time;
};
volatile s_pcint_vector pcint_vector[pc_interrupt_vectors]; // define a struct for pc int processing
/* function to register a pin interrupt */
void register_PCINT(uint8_t def_pin) {
set_pin_input(def_pin); // set the pin as input
set_pin_high(def_pin); // key is connected against ground, set it high to detect changes
uint8_t vec = digitalPinToPCICRbit(def_pin); // needed for interrupt handling and to sort out the port
uint8_t port = digitalPinToPort(def_pin); // need the pin port to get further information as port register
if (port == NOT_A_PIN) return; // return while port was not found
pcint_vector[vec].PINREG = portInputRegister(port); // remember the input register
pcint_vector[vec].mask |= digitalPinToBitMask(def_pin); // set the pin bit in the bitmask
*digitalPinToPCICR(def_pin) |= _BV(digitalPinToPCICRbit(def_pin)); // pci functions
*digitalPinToPCMSK(def_pin) |= _BV(digitalPinToPCMSKbit(def_pin)); // make the pci active
maintain_PCINT(vec);
//dbg << "x-v:" << vec << ", m:" << pcint_vector[vec].mask << ", r:" << pcint_vector[vec].chng << ", pin:" << def_pin << '\n';
}
/* period check if a pin interrupt had happend */
uint8_t check_PCINT(uint8_t def_pin, uint8_t debounce) {
// need to get vectore 0 - 3, depends on cpu
uint8_t vec = digitalPinToPCICRbit(def_pin); // needed for interrupt handling and to sort out the port
uint8_t bit = digitalPinToBitMask(def_pin); // get the specific bit for the asked pin
uint8_t status = pcint_vector[vec].curr & bit ? 1 : 0; // evaluate the pin status
uint8_t prev = pcint_vector[vec].prev & bit ? 1 : 0; // evaluate the previous pin status
if (status == prev) return status; // check if something had changed since last time
if (debounce && ((get_millis() - pcint_vector[vec].time) < DEBOUNCE)) return prev; // seems there is a change, check if debounce is necassary and done
pcint_vector[vec].prev ^= bit; // if we are here, there was a change and debounce check was passed, remember for next time
if (status) return 3; // pin is 1, old was 0
else return 2; // pin is 0, old was 1
}
void(*pci_ptr)(uint8_t vec, uint8_t pin, uint8_t flag);
/* internal function to handle pin change interrupts */
void maintain_PCINT(uint8_t vec) {
pcint_vector[vec].curr = *pcint_vector[vec].PINREG & pcint_vector[vec].mask; // read the pin port and mask ot unneeded pins
pcint_vector[vec].time = get_millis(); // store the time, if we need to debounce it
if (pcint_vector[vec].chng == pcint_vector[vec].curr) return; // nothing to do while the same status as last time
//dbg << "i-v:" << vec << ", m:" << pcint_vector[vec].mask << ", c:" << pcint_vector[vec].curr << ", n:" << pcint_vector[vec].chng << ", x:" << (pcint_vector[vec].curr ^ pcint_vector[vec].chng) << '\n';
if (pci_ptr) {
uint8_t pin_int = pcint_vector[vec].curr ^ pcint_vector[vec].chng; // evaluate the pin which raised the interrupt
pci_ptr(vec, pin_int, pcint_vector[vec].curr); // callback the interrupt function in user sketch
}
pcint_vector[vec].chng = pcint_vector[vec].curr; // remember the current status to see the change next time
}
/* interrupt vectors to catch pin change interrupts */
#ifdef PCIE0
ISR(PCINT0_vect) {
maintain_PCINT(0);
}
#endif
#ifdef PCIE1
ISR(PCINT1_vect) {
maintain_PCINT(1);
}
#endif
#ifdef PCIE2
ISR(PCINT2_vect) {
maintain_PCINT(2);
}
#endif
#ifdef PCIE3
ISR(PCINT3_vect) {
maintain_PCINT(3);
}
#endif
//- -----------------------------------------------------------------------------------------------------------------------
/*-- timer functions ------------------------------------------------------------------------------------------------------
* as i don't want to depend on the arduino timer while it is not possible to add some time after the arduino was sleeping
* i defined a new timer. to keep it as flexible as possible you can configure the different timers in the arduino by handing
* over the number of the timer you want to utilize. as timer are very vendor related there is the need to have at least
* timer 0 available for all different hardware.
*/
// https://github.com/zkemble/millis/blob/master/millis/
volatile uint32_t milliseconds;
void init_millis_timer0() {
dbg << F("init timer0\n");
//power_timer0_enable();
TCCR0A = _BV(WGM01); // CTC mode
TCCR0B = (_BV(CS01) | _BV(CS00)); // prescaler 64; 16.000.000 / 64 = 250.000 / 2000 = 125
TIMSK0 = _BV(OCIE0A);
OCR0A = ((F_CPU / 64) / 2000);
}
uint32_t get_millis(void) {
uint32_t ms;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
ms = milliseconds;
}
return ms;
}
/*-- eeprom functions -----------------------------------------------------------------------------------------------------
* to make the library more hardware independend all eeprom relevant functions are defined at one point
*/
/* init the eeprom, can be enriched for a serial eeprom as well */
void init_eeprom(void) {
// place the code to init a i2c eeprom
}
/* read a specific eeprom address */
void get_eeprom(uint16_t addr, uint8_t len, void *ptr) {
eeprom_read_block((void*)ptr, (const void*)addr, len); // AVR GCC standard function
}
/* write a block to a specific eeprom address */
void set_eeprom(uint16_t addr, uint8_t len, void *ptr) {
/* update is much faster, while writes only when needed; needs some byte more space
* but otherwise we run in timing issues */
eeprom_update_block((const void*)ptr, (void*)addr, len); // AVR GCC standard function
}
/* and clear the eeprom */
void clear_eeprom(uint16_t addr, uint16_t len) {
uint8_t tB = 0;
if (!len) return;
for (uint16_t l = 0; l < len; l++) { // step through the bytes of eeprom
set_eeprom(addr + l, 1, (void*)&tB);
}
}
//- -----------------------------------------------------------------------------------------------------------------------