-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathint.cpp
288 lines (245 loc) · 11.2 KB
/
int.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
288
/****************************************************************************
** INCLUDE
****************************************************************************/
#include "global.h"
/****************************************************************************
**GLOBAL VARS
****************************************************************************/
// Encoder LUT
//
// Bit 765 | unused, hold at zero
// Bit 4 | Previous direction | 0 = clockwise | 1 = counterclockwise
// Bit 32 | old encoder reading | B channel A channel
// Bit 10 | new encoder reading | B channel A channel
int8_t enc_lut[32] =
{
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)-1 *(1<<ENC_GAIN), //B Rise with A=0: -1 (Counter Clockwise)
(int8_t)+1 *(1<<ENC_GAIN), //A Rise with B=0: +1 (Clockwise)
(int8_t)+2 *(1<<ENC_GAIN), //A Rise B Rise: double event +2
(int8_t)+1 *(1<<ENC_GAIN), //B Fall with A=0: +1 (Clockwise)
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)+2 *(1<<ENC_GAIN), //A Rise B Fall: double event +2
(int8_t)-1 *(1<<ENC_GAIN), //A Rise with B=1: -1 (Counter Clockwise)
(int8_t)-1 *(1<<ENC_GAIN), //A Fall with B=0: -1 (Counter Clockwise)
(int8_t)+2 *(1<<ENC_GAIN), //A Fall B Rise: double event +2
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)+1 *(1<<ENC_GAIN), //B Rise with A=1: +1 (Clockwise)
(int8_t)+2 *(1<<ENC_GAIN), //A Fall B Fall: double event +2
(int8_t)+1 *(1<<ENC_GAIN), //A Fall with B=1: +1 (Clockwise)
(int8_t)-1 *(1<<ENC_GAIN), //B Fall with A=1: -1 (Counter Clockwise)
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)-1 *(1<<ENC_GAIN), //B Rise with A=0: -1 (Counter Clockwise)
(int8_t)+1 *(1<<ENC_GAIN), //A Rise with B=0: +1 (Clockwise)
(int8_t)-2 *(1<<ENC_GAIN), //A Rise B Rise: double event -2
(int8_t)+1 *(1<<ENC_GAIN), //B Fall with A=0: +1 (Clockwise)
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)-2 *(1<<ENC_GAIN), //A Rise B Fall: double event -2
(int8_t)-1 *(1<<ENC_GAIN), //A Rise with B=1: -1 (Counter Clockwise)
(int8_t)-1 *(1<<ENC_GAIN), //A Fall with B=0: -1 (Counter Clockwise)
(int8_t)-2 *(1<<ENC_GAIN), //A Fall B Rise: double event -2
(int8_t)+0 *(1<<ENC_GAIN), //No Change
(int8_t)+1 *(1<<ENC_GAIN), //B Rise with A=1: +1 (Clockwise)
(int8_t)-2 *(1<<ENC_GAIN), //A Fall B Fall: double event -2
(int8_t)+1 *(1<<ENC_GAIN), //A Fall with B=1: +1 (Clockwise)
(int8_t)-1 *(1<<ENC_GAIN), //B Fall with A=1: -1 (Counter Clockwise)
(int8_t)+0 *(1<<ENC_GAIN) //No Change
};
/****************************************************************************
** INTERRUPT SERVICE ROUTINE
*****************************************************************************
** In the AT4809 ISR flags have to be cleared manually
****************************************************************************/
/****************************************************************************
** RTC Periodic Interrupt
*****************************************************************************
** Periodic interrupt generated by the RTC from it's independent clock source
****************************************************************************/
ISR( RTC_PIT_vect )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//----------------------------------------------------------------
// INIT
//----------------------------------------------------------------
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Set the System Tick
g_isr_flags.system_tick = true;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
//Manually clear the interrupt flag
RTC.PITINTFLAGS = RTC_PI_bm;
}
/****************************************************************************
** USART3 RX Interrupt
*****************************************************************************
**
****************************************************************************/
ISR( USART3_RXC_vect )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//Temp var
uint8_t rx_data_tmp;
//----------------------------------------------------------------
// INIT
//----------------------------------------------------------------
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Fetch the data and clear the interrupt flag
rx_data_tmp = USART3.RXDATAL;
//Push byte into RS485 buffer for processing
AT_BUF_PUSH_SAFER( rpi_rx_buf, rx_data_tmp );
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
}
/****************************************************************************
** Function
** quad_encoder_decoder
****************************************************************************/
//! @brief Decode four quadrature encoder channels on an edge on each of the channel
//! @details
//! PIN | ENC0 | ENC1 | ENC2 | ENC3 |
//! -------------------------------------
//! CHA | PC0 | PC2 | PC4 | PC6 |
//! CHB | PC1 | PC3 | PC5 | PC7 |
//!
//! FEATURES:
//!
//! combined ISR
//! option 1 was to write four smaller ISRs one for channel
//! option 2 was to write one bigger ISR triggered by each edge
//! which one is better depends on ISR overhead and load distribution between channels
//! since all encoders go at the same speed, it make sense to write just one routine?
//!
//! global sync
//! ISR only updates a local smaller faster counter all of the times.
//! synchronization between this counter and the main routine only happens when
//! the main routine is not sampling it, when the main routine is requesting it or when local counters are getting too full
//! this feature is meant to reduce the overhead of the ISR encoder routine significantly by not updating 32bit registers
//!
//! double event
//! A LUT allows handling of tricky double events that happen when the ISR can't keep up and skip a beat
//! double event can be handled with no error in count and allow to warn the main that the encoders are getting out of hand
//! and stalling the micro controller
//!
//! ALGORITHM:
//! >Fetch new pin configuration
//! >For each channel
//! >Build an index to the encoder LUT
//! >Decode the increment to be added to the 16b relative counter
//! >Save direction and detect double events to raise warnings
//! >Write new configuration into old configuration
/***************************************************************************/
void quad_encoder_decoder( uint8_t enc_in )
{
//----------------------------------------------------------------
// STATICS
//----------------------------------------------------------------
//relative encoder counters
static int8_t enc_cnt[ENC_NUM] = { 0 };
//Memory of the previous direction of the encoders. each bit is one encoder channel. false=+ true=-
static uint8_t enc_dir = (uint8_t)0x00;
//Memory of previous encoder pin configuration. Initialize to current one at first cycle
static uint8_t enc_pin_old = enc_in;
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//Fetch pin configuration
uint8_t enc_pin = enc_in;
//Counter used to scan the encoders
uint8_t t;
//index to the LUT
uint8_t index;
//increment decoded from the LUT
int8_t increment;
//temporary error counter
bool f_err = false;
//this flag is used to detect when a preventive overflow update is required
bool f_update;
//----------------------------------------------------------------
// INIT
//----------------------------------------------------------------
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//For: each encoder channel
for (t = 0;t < ENC_NUM;t++)
{
//! Build address to the encoder LUT
// | 4 | 3 | 2 | 1 | 0
// | dir | old B | old A | B | A
//Inject new AB and clear index
index = (((enc_pin) >> (2*t)) & 0x03);
//Inject old AB
index |= ((t==0)?((enc_pin_old<<2)&0x0c):(((enc_pin_old) >> (2*(t-1))) & 0x0c));
//Inject old direction
index |= (((enc_dir) << (4 -t)) & 0x10);
//! Decode the increment through the LUT
//Use the index as address for the encoder LUT, applying the complex truth table
increment = enc_lut[ index ];
//! Apply increment to local relative memory and compute special
//Apply increment
enc_cnt[t] += increment;
//Compute new direction flag and write it back to the correct bit of the encoder direction memory
enc_dir = (enc_dir & INV_MASK(t)) | (((increment < 0) & 0x01) << t);
//Detect if a double event happened and remember it. Serves as over speed warning
f_err |= ((increment == +2) || (increment == -2));
//overflow update flag. if at least a counter is getting dangerously large
f_update |= ((enc_cnt[t] >= ENC_UPDATE_TH) || (enc_cnt[t] <= -ENC_UPDATE_TH));
} //End For: each encoder channel
//! Write back double event error flag
g_isr_flags.enc_double_event |= f_err;
//! Write back ISR counters to global 32bit counters
//Only write back if main requests it, if at least one counter is above threshold. withhold if
if ((g_isr_flags.enc_sem == false) && ((g_isr_flags.enc_double_event == true) || (g_isr_flags.enc_updt == true)))
{
//notify the main that sync happened
g_isr_flags.enc_updt = false;
//For: each encoder channel
for (t = 0;t < ENC_NUM;t++)
{
//Synchronize with the global 32b counters
g_enc_cnt[t] += enc_cnt[t];
//Clear the local 8b counters
enc_cnt[t] = 0;
} //End For: each encoder channel
}
//Save pin configuration
enc_pin_old = enc_pin;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return;
} //End function: quad_encoder_decoder
/****************************************************************************
** ISR
** PORTC_PORT_vect
****************************************************************************/
//! @brief
//! @details
//! Any edge on any pin in PORTC will trigger this interrupt
//! Call the quad channel encoder decoder routine
//! Do it as call because the routine can be called from elsewhere
/***************************************************************************/
ISR( PORTC_PORT_vect )
{
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//quad channel encoder decoder routine
quad_encoder_decoder( PORTC.IN );
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
//Clear the Interrupt Flags of PORTC
PORTC.INTFLAGS = (uint8_t)0xff;
} //End ISR: PORTC_PORT_vect