robobuggy / arduino / InterruptRCRecieverReference / PinChangeInt.h @ c5d6b0e8
History | View | Annotate | Download (20.6 KB)
1 | c5d6b0e8 | msebek | // We use 4-character tabstops, so IN VIM: <esc>:set ts=4 and <esc>:set sw=4
|
---|---|---|---|
2 | // ...that's: ESCAPE key, colon key, then "s-e-t SPACE key t-s-=-4"
|
||
3 | //
|
||
4 | /*
|
||
5 | * This is the PinChangeInt library for the Arduino.
|
||
6 | |||
7 | See google code project for latest, bugs and info http://code.google.com/p/arduino-pinchangeint/
|
||
8 | For more information Refer to avr-gcc header files, arduino source and atmega datasheet.
|
||
9 | |||
10 | This library was inspired by and derived from "johnboiles" (it seems)
|
||
11 | PCInt Arduino Playground example here: http://www.arduino.cc/playground/Main/PcInt
|
||
12 | If you are the original author, please let us know at the google code page
|
||
13 |
|
||
14 | It provides an extension to the interrupt support for arduino by
|
||
15 | adding pin change interrupts, giving a way for users to have
|
||
16 | interrupts drive off of any pin.
|
||
17 | |||
18 | This program is free software: you can redistribute it and/or modify
|
||
19 | it under the terms of the GNU General Public License as published by
|
||
20 | the Free Software Foundation, either version 3 of the License, or
|
||
21 | (at your option) any later version.
|
||
22 | |||
23 | This program is distributed in the hope that it will be useful,
|
||
24 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
26 | GNU General Public License for more details.
|
||
27 | |||
28 | You should have received a copy of the GNU General Public License
|
||
29 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
30 | (the file gpl.txt is included with the library's zip package)
|
||
31 | */
|
||
32 | //-------- define these in your sketch, if applicable ----------------------------------------------------------
|
||
33 | //-------- These must go in your sketch ahead of the #include <PinChangeInt.h> statement -----------------------
|
||
34 | // You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
|
||
35 | // on any one or two of the three ports. If only a single port remains, the handler will be declared inline
|
||
36 | // reducing the size and latency of the handler.
|
||
37 | // #define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
|
||
38 | // #define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts
|
||
39 | // #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts
|
||
40 | // --- Mega support ---
|
||
41 | // #define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
|
||
42 | // #define NO_PORTJ_PINCHANGES // to indicate that port c will not be used for pin change interrupts
|
||
43 | // #define NO_PORTK_PINCHANGES // to indicate that port d will not be used for pin change interrupts
|
||
44 | // In the Mega, there is no Port C, no Port D. Instead, you get Port J and Port K. Port B remains.
|
||
45 | // Port J, however, is practically useless because there is only 1 pin available for interrupts. Most
|
||
46 | // of the Port J pins are not even connected to a header connection. // </end> "Mega Support" notes
|
||
47 | // --- Sanguino, Mioduino support ---
|
||
48 | // #define NO_PORTA_PINCHANGES // to indicate that port a will not be used for pin change interrupts
|
||
49 | // --------------------
|
||
50 | //
|
||
51 | // Other preprocessor directives...
|
||
52 | // You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine
|
||
53 | // slightly by declaring that you don't care if the static variables PCintPort::pinState and/or
|
||
54 | // PCintPort::arduinoPin are set and made available to your interrupt routine.
|
||
55 | // #define NO_PIN_STATE // to indicate that you don't need the pinState
|
||
56 | // #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin
|
||
57 | // #define DISABLE_PCINT_MULTI_SERVICE // to limit the handler to servicing a single interrupt per invocation.
|
||
58 | // #define GET_PCINT_VERSION // to enable the uint16_t getPCIintVersion () function.
|
||
59 | // The following is intended for testing purposes. If defined, then a whole host of static variables can be read
|
||
60 | // in your interrupt subroutine. It is not defined by default, and you DO NOT want to define this in
|
||
61 | // Production code!:
|
||
62 | // #define PINMODE
|
||
63 | //-------- define the above in your sketch, if applicable ------------------------------------------------------
|
||
64 | |||
65 | /*
|
||
66 | PinChangeInt.h
|
||
67 | ---- VERSIONS --- (NOTE TO SELF: Update the PCINT_VERSION define, below) -----------------
|
||
68 | Version 2.19 (beta) Tue Nov 20 07:33:37 CST 2012
|
||
69 | Version 2.17 (beta) Sat Nov 17 09:46:50 CST 2012
|
||
70 | Version 2.11 (beta) Mon Nov 12 09:33:06 CST 2012
|
||
71 | |||
72 | Version 2.01 (beta) Thu Jun 28 12:35:48 CDT 2012
|
||
73 | |||
74 | Version 1.72 Wed Mar 14 18:57:55 CDT 2012
|
||
75 | |||
76 | Version 1.71beta Sat Mar 10 12:57:05 CST 2012
|
||
77 | |||
78 | Version 1.6beta Fri Feb 10 08:48:35 CST 2012
|
||
79 | |||
80 | Version 1.51 Sun Feb 5 23:28:02 CST 2012
|
||
81 | |||
82 | Version 1.5 Thu Feb 2 18:09:49 CST 2012
|
||
83 | |||
84 | Version 1.4 Tue Jan 10 09:41:14 CST 2012
|
||
85 | |||
86 | Version 1.3 Sat Dec 3 22:56:20 CST 2011
|
||
87 | |||
88 | Version 1.2 Sat Dec 3 Sat Dec 3 09:15:52 CST 2011
|
||
89 | |||
90 | Version 1.1 Sat Dec 3 00:06:03 CST 2011
|
||
91 | */
|
||
92 | |||
93 | #ifndef PinChangeInt_h
|
||
94 | #define PinChangeInt_h
|
||
95 | |||
96 | #define PCINT_VERSION 2190 // This number MUST agree with the version number, above. |
||
97 | |||
98 | #include "stddef.h" |
||
99 | |||
100 | // Thanks to Maurice Beelen, nms277, Akesson Karlpetter, and Orly Andico for these fixes.
|
||
101 | #if defined(ARDUINO) && ARDUINO >= 100 |
||
102 | #include <Arduino.h> |
||
103 | #include <new.h> |
||
104 | #include <wiring_private.h> // cby and sbi defined here |
||
105 | #else
|
||
106 | #include <WProgram.h> |
||
107 | #include <pins_arduino.h> |
||
108 | #ifndef LIBCALL_PINCHANGEINT
|
||
109 | #include "../cppfix/cppfix.h" |
||
110 | #endif
|
||
111 | #endif
|
||
112 | |||
113 | |||
114 | #undef DEBUG
|
||
115 | |||
116 | /*
|
||
117 | * Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
|
||
118 | * The PCINT corresponding to the pin must be enabled and masked, and
|
||
119 | * an ISR routine provided. Since PCINTs are per port, not per pin, the ISR
|
||
120 | * must use some logic to actually implement a per-pin interrupt service.
|
||
121 | */
|
||
122 | |||
123 | /* Pin to interrupt map:
|
||
124 | * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
|
||
125 | * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
|
||
126 | * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
|
||
127 | */
|
||
128 | |||
129 | #undef INLINE_PCINT
|
||
130 | #define INLINE_PCINT
|
||
131 | // Thanks to cserveny...@gmail.com for MEGA support!
|
||
132 | #if defined __AVR_ATmega2560__ || defined __AVR_ATmega1280__ || defined __AVR_ATmega1281__ || defined __AVR_ATmega2561__ || defined __AVR_ATmega640__
|
||
133 | #define __USE_PORT_JK
|
||
134 | // Mega does not have PORTA, C or D
|
||
135 | #define NO_PORTA_PINCHANGES
|
||
136 | #define NO_PORTC_PINCHANGES
|
||
137 | #define NO_PORTD_PINCHANGES
|
||
138 | #if ((defined(NO_PORTB_PINCHANGES) && defined(NO_PORTJ_PINCHANGES)) || \
|
||
139 | (defined(NO_PORTJ_PINCHANGES) && defined(NO_PORTK_PINCHANGES)) || \ |
||
140 | (defined(NO_PORTK_PINCHANGES) && defined(NO_PORTB_PINCHANGES))) |
||
141 | #define INLINE_PCINT inline |
||
142 | #endif
|
||
143 | #else
|
||
144 | #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
|
||
145 | #ifndef NO_PORTA_PINCHANGES
|
||
146 | #define __USE_PORT_A
|
||
147 | #endif
|
||
148 | #else
|
||
149 | #define NO_PORTA_PINCHANGES
|
||
150 | #endif
|
||
151 | // if defined only D .OR. only C .OR. only B .OR. only A, then inline it
|
||
152 | #if ( (defined(NO_PORTA_PINCHANGES) && defined(NO_PORTB_PINCHANGES) && defined(NO_PORTC_PINCHANGES)) || \
|
||
153 | (defined(NO_PORTA_PINCHANGES) && defined(NO_PORTB_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) || \ |
||
154 | (defined(NO_PORTA_PINCHANGES) && defined(NO_PORTC_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) || \ |
||
155 | (defined(NO_PORTB_PINCHANGES) && defined(NO_PORTC_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) ) |
||
156 | #define INLINE_PCINT inline |
||
157 | #endif
|
||
158 | #endif
|
||
159 | |||
160 | // Provide drop in compatibility with johnboiles PCInt project at
|
||
161 | // http://www.arduino.cc/playground/Main/PcInt
|
||
162 | #define PCdetachInterrupt(pin) PCintPort::detachInterrupt(pin)
|
||
163 | #define PCattachInterrupt(pin,userFunc,mode) PCintPort::attachInterrupt(pin, userFunc,mode)
|
||
164 | #define PCgetArduinoPin() PCintPort::getArduinoPin()
|
||
165 | |||
166 | |||
167 | typedef void (*PCIntvoidFuncPtr)(void); |
||
168 | |||
169 | class PCintPort { |
||
170 | public:
|
||
171 | PCintPort(int index,int pcindex, volatile uint8_t& maskReg) : |
||
172 | portInputReg(*portInputRegister(index)), |
||
173 | portPCMask(maskReg), |
||
174 | PCICRbit(1 << pcindex),
|
||
175 | portRisingPins(0),
|
||
176 | portFallingPins(0),
|
||
177 | firstPin(NULL)
|
||
178 | #ifdef PINMODE
|
||
179 | ,intrCount(0)
|
||
180 | #endif
|
||
181 | { |
||
182 | #ifdef FLASH
|
||
183 | ledsetup(); |
||
184 | #endif
|
||
185 | } |
||
186 | volatile uint8_t& portInputReg;
|
||
187 | static int8_t attachInterrupt(uint8_t pin, PCIntvoidFuncPtr userFunc, int mode); |
||
188 | static void detachInterrupt(uint8_t pin); |
||
189 | INLINE_PCINT void PCint();
|
||
190 | static volatile uint8_t curr; |
||
191 | #ifndef NO_PIN_NUMBER
|
||
192 | static volatile uint8_t arduinoPin; |
||
193 | #endif
|
||
194 | #ifndef NO_PIN_STATE
|
||
195 | static volatile uint8_t pinState; |
||
196 | #endif
|
||
197 | #ifdef PINMODE
|
||
198 | static volatile uint8_t pinmode; |
||
199 | static volatile uint8_t s_portRisingPins; |
||
200 | static volatile uint8_t s_portFallingPins; |
||
201 | static volatile uint8_t s_lastPinView; |
||
202 | static volatile uint8_t s_pmask; |
||
203 | static volatile char s_PORT; |
||
204 | static volatile uint8_t s_changedPins; |
||
205 | static volatile uint8_t s_portRisingPins_nCurr; |
||
206 | static volatile uint8_t s_portFallingPins_nNCurr; |
||
207 | static volatile uint8_t s_currXORlastPinView; |
||
208 | volatile uint8_t intrCount;
|
||
209 | static volatile uint8_t s_count; |
||
210 | static volatile uint8_t pcint_multi; |
||
211 | static volatile uint8_t PCIFRbug; |
||
212 | #endif
|
||
213 | #ifdef FLASH
|
||
214 | static void ledsetup(void); |
||
215 | #endif
|
||
216 | |||
217 | protected:
|
||
218 | class PCintPin { |
||
219 | public:
|
||
220 | PCintPin() : |
||
221 | PCintFunc((PCIntvoidFuncPtr)NULL),
|
||
222 | mode(0) {}
|
||
223 | PCIntvoidFuncPtr PCintFunc; |
||
224 | uint8_t mode; |
||
225 | uint8_t mask; |
||
226 | uint8_t arduinoPin; |
||
227 | PCintPin* next; |
||
228 | }; |
||
229 | void enable(PCintPin* pin, PCIntvoidFuncPtr userFunc, uint8_t mode);
|
||
230 | int8_t addPin(uint8_t arduinoPin,PCIntvoidFuncPtr userFunc, uint8_t mode); |
||
231 | volatile uint8_t& portPCMask;
|
||
232 | const uint8_t PCICRbit;
|
||
233 | volatile uint8_t portRisingPins;
|
||
234 | volatile uint8_t portFallingPins;
|
||
235 | volatile uint8_t lastPinView;
|
||
236 | PCintPin* firstPin; |
||
237 | }; |
||
238 | |||
239 | #ifndef LIBCALL_PINCHANGEINT // LIBCALL_PINCHANGEINT *********************************************** |
||
240 | volatile uint8_t PCintPort::curr=0; |
||
241 | #ifndef NO_PIN_NUMBER
|
||
242 | volatile uint8_t PCintPort::arduinoPin=0; |
||
243 | #endif
|
||
244 | #ifndef NO_PIN_STATE
|
||
245 | volatile uint8_t PCintPort::pinState=0; |
||
246 | #endif
|
||
247 | #ifdef PINMODE
|
||
248 | volatile uint8_t PCintPort::pinmode=0; |
||
249 | volatile uint8_t PCintPort::s_portRisingPins=0; |
||
250 | volatile uint8_t PCintPort::s_portFallingPins=0; |
||
251 | volatile uint8_t PCintPort::s_lastPinView=0; |
||
252 | volatile uint8_t PCintPort::s_pmask=0; |
||
253 | volatile char PCintPort::s_PORT='x'; |
||
254 | volatile uint8_t PCintPort::s_changedPins=0; |
||
255 | volatile uint8_t PCintPort::s_portRisingPins_nCurr=0; |
||
256 | volatile uint8_t PCintPort::s_portFallingPins_nNCurr=0; |
||
257 | volatile uint8_t PCintPort::s_currXORlastPinView=0; |
||
258 | volatile uint8_t PCintPort::s_count=0; |
||
259 | volatile uint8_t PCintPort::pcint_multi=0; |
||
260 | volatile uint8_t PCintPort::PCIFRbug=0; |
||
261 | #endif
|
||
262 | |||
263 | #ifdef FLASH
|
||
264 | #define PINLED 13 |
||
265 | volatile uint8_t *led_port;
|
||
266 | uint8_t led_mask; |
||
267 | uint8_t not_led_mask; |
||
268 | boolean ledsetup_run=false;
|
||
269 | void PCintPort::ledsetup(void) { |
||
270 | if (! ledsetup_run) {
|
||
271 | led_port=portOutputRegister(digitalPinToPort(PINLED)); |
||
272 | led_mask=digitalPinToBitMask(PINLED); |
||
273 | not_led_mask=led_mask^0xFF;
|
||
274 | pinMode(PINLED, OUTPUT); digitalWrite(PINLED, LOW); |
||
275 | ledsetup_run=true;
|
||
276 | } |
||
277 | }; |
||
278 | #endif
|
||
279 | |||
280 | |||
281 | // ATMEGA 644
|
||
282 | //
|
||
283 | #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) // Sanguino, Mosquino uino bobino bonanafannafofino, me my momino... |
||
284 | |||
285 | #ifndef NO_PORTA_PINCHANGES
|
||
286 | PCintPort portA=PCintPort(1, 0,PCMSK0); // port PB==2 (from Arduino.h, Arduino version 1.0) |
||
287 | #endif
|
||
288 | #ifndef NO_PORTB_PINCHANGES
|
||
289 | PCintPort portB=PCintPort(2, 1,PCMSK1); // port PB==2 (from Arduino.h, Arduino version 1.0) |
||
290 | #endif
|
||
291 | #ifndef NO_PORTC_PINCHANGES
|
||
292 | PCintPort portC=PCintPort(3, 2,PCMSK2); // port PC==3 (also in pins_arduino.c, Arduino version 022) |
||
293 | #endif
|
||
294 | #ifndef NO_PORTD_PINCHANGES
|
||
295 | PCintPort portD=PCintPort(4, 3,PCMSK3); // port PD==4 |
||
296 | #endif
|
||
297 | |||
298 | #else // others |
||
299 | |||
300 | #ifndef NO_PORTB_PINCHANGES
|
||
301 | PCintPort portB=PCintPort(2, 0,PCMSK0); // port PB==2 (from Arduino.h, Arduino version 1.0) |
||
302 | #endif
|
||
303 | #ifndef NO_PORTC_PINCHANGES // note: no PORTC on MEGA |
||
304 | PCintPort portC=PCintPort(3, 1,PCMSK1); // port PC==3 (also in pins_arduino.c, Arduino version 022) |
||
305 | #endif
|
||
306 | #ifndef NO_PORTD_PINCHANGES // note: no PORTD on MEGA |
||
307 | PCintPort portD=PCintPort(4, 2,PCMSK2); // port PD==4 |
||
308 | #endif
|
||
309 | |||
310 | #endif // defined __AVR_ATmega644__ |
||
311 | |||
312 | #ifdef __USE_PORT_JK
|
||
313 | #ifndef NO_PORTJ_PINCHANGES
|
||
314 | PCintPort portJ=PCintPort(10,1,PCMSK1); // port PJ==10 |
||
315 | #endif
|
||
316 | #ifndef NO_PORTK_PINCHANGES
|
||
317 | PCintPort portK=PCintPort(11,2,PCMSK2); // port PK==11 |
||
318 | #endif
|
||
319 | #endif // USE_PORT_JK |
||
320 | |||
321 | static PCintPort *lookupPortNumToPort( int portNum ) { |
||
322 | PCintPort *port = NULL;
|
||
323 | |||
324 | switch (portNum) {
|
||
325 | #ifndef NO_PORTA_PINCHANGES
|
||
326 | case 1: |
||
327 | port=&portA; |
||
328 | break;
|
||
329 | #endif
|
||
330 | #ifndef NO_PORTB_PINCHANGES
|
||
331 | case 2: |
||
332 | port=&portB; |
||
333 | break;
|
||
334 | #endif
|
||
335 | #ifndef NO_PORTC_PINCHANGES
|
||
336 | case 3: |
||
337 | port=&portC; |
||
338 | break;
|
||
339 | #endif
|
||
340 | #ifndef NO_PORTD_PINCHANGES
|
||
341 | case 4: |
||
342 | port=&portD; |
||
343 | break;
|
||
344 | #endif
|
||
345 | #ifdef __USE_PORT_JK
|
||
346 | |||
347 | #ifndef NO_PORTJ_PINCHANGES
|
||
348 | case 10: |
||
349 | port=&portJ; |
||
350 | break;
|
||
351 | #endif
|
||
352 | |||
353 | #ifndef NO_PORTK_PINCHANGES
|
||
354 | case 11: |
||
355 | port=&portK; |
||
356 | break;
|
||
357 | #endif
|
||
358 | |||
359 | #endif
|
||
360 | } |
||
361 | |||
362 | return port;
|
||
363 | } |
||
364 | |||
365 | |||
366 | void PCintPort::enable(PCintPin* p, PCIntvoidFuncPtr userFunc, uint8_t mode) {
|
||
367 | // Enable the pin for interrupts by adding to the PCMSKx register.
|
||
368 | // ...The final steps; at this point the interrupt is enabled on this pin.
|
||
369 | p->mode=mode; |
||
370 | p->PCintFunc=userFunc; |
||
371 | portPCMask |= p->mask; |
||
372 | if ((p->mode == RISING) || (p->mode == CHANGE)) portRisingPins |= p->mask;
|
||
373 | if ((p->mode == FALLING) || (p->mode == CHANGE)) portFallingPins |= p->mask;
|
||
374 | PCICR |= PCICRbit; |
||
375 | } |
||
376 | |||
377 | int8_t PCintPort::addPin(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, uint8_t mode) |
||
378 | { |
||
379 | PCintPin* tmp; |
||
380 | |||
381 | // Add to linked list, starting with firstPin. If pin already exists, just enable.
|
||
382 | if (firstPin != NULL) { |
||
383 | tmp=firstPin; |
||
384 | do {
|
||
385 | if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); } |
||
386 | if (tmp->next == NULL) break; |
||
387 | tmp=tmp->next; |
||
388 | } while (true); |
||
389 | } |
||
390 | |||
391 | // Create pin p: fill in the data.
|
||
392 | PCintPin* p=new PCintPin; |
||
393 | if (p == NULL) return(-1); |
||
394 | p->arduinoPin=arduinoPin; |
||
395 | p->mode = mode; |
||
396 | p->next=NULL;
|
||
397 | p->mask = digitalPinToBitMask(arduinoPin); // the mask
|
||
398 | |||
399 | if (firstPin == NULL) firstPin=p; |
||
400 | else tmp->next=p;
|
||
401 | |||
402 | #ifdef DEBUG
|
||
403 | Serial.print("addPin. pin given: "); Serial.print(arduinoPin, DEC);
|
||
404 | int addr = (int) p; |
||
405 | Serial.print(" instance addr: "); Serial.println(addr, HEX);
|
||
406 | Serial.print("userFunc addr: "); Serial.println((int)p->PCintFunc, HEX); |
||
407 | #endif
|
||
408 | |||
409 | enable(p, userFunc, mode); |
||
410 | #ifdef DEBUG
|
||
411 | Serial.print("addPin. pin given: "); Serial.print(arduinoPin, DEC), Serial.print (" pin stored: "); |
||
412 | int addr = (int) p; |
||
413 | Serial.print(" instance addr: "); Serial.println(addr, HEX);
|
||
414 | #endif
|
||
415 | return(1); |
||
416 | } |
||
417 | |||
418 | /*
|
||
419 | * attach an interrupt to a specific pin using pin change interrupts.
|
||
420 | */
|
||
421 | int8_t PCintPort::attachInterrupt(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, int mode)
|
||
422 | { |
||
423 | PCintPort *port; |
||
424 | uint8_t portNum = digitalPinToPort(arduinoPin); |
||
425 | if ((portNum == NOT_A_PORT) || (userFunc == NULL)) return(-1); |
||
426 | |||
427 | port=lookupPortNumToPort(portNum); |
||
428 | // Added by GreyGnome... must set the initial value of lastPinView for it to be correct on the 1st interrupt.
|
||
429 | // ...but even then, how do you define "correct"? Ultimately, the user must specify (not provisioned for yet).
|
||
430 | port->lastPinView=port->portInputReg; |
||
431 | |||
432 | #ifdef DEBUG
|
||
433 | Serial.print("attachInterrupt FUNC: "); Serial.println(arduinoPin, DEC);
|
||
434 | #endif
|
||
435 | // map pin to PCIR register
|
||
436 | return(port->addPin(arduinoPin,userFunc,mode));
|
||
437 | } |
||
438 | |||
439 | void PCintPort::detachInterrupt(uint8_t arduinoPin)
|
||
440 | { |
||
441 | PCintPort *port; |
||
442 | PCintPin* current; |
||
443 | uint8_t mask; |
||
444 | #ifdef DEBUG
|
||
445 | Serial.print("detachInterrupt: "); Serial.println(arduinoPin, DEC);
|
||
446 | #endif
|
||
447 | uint8_t portNum = digitalPinToPort(arduinoPin); |
||
448 | if (portNum == NOT_A_PORT) return; |
||
449 | port=lookupPortNumToPort(portNum); |
||
450 | mask=digitalPinToBitMask(arduinoPin); |
||
451 | current=port->firstPin; |
||
452 | //PCintPin* prev=NULL;
|
||
453 | while (current) {
|
||
454 | if (current->mask == mask) { // found the target |
||
455 | uint8_t oldSREG = SREG; |
||
456 | cli(); // disable interrupts
|
||
457 | port->portPCMask &= ~mask; // disable the mask entry.
|
||
458 | if (port->portPCMask == 0) PCICR &= ~(port->PCICRbit); |
||
459 | port->portRisingPins &= ~current->mask; port->portFallingPins &= ~current->mask; |
||
460 | // Link the previous' next to the found next. Then remove the found.
|
||
461 | //if (prev != NULL) prev->next=current->next; // linked list skips over current.
|
||
462 | //else firstPin=current->next; // at the first pin; save the new first pin
|
||
463 | SREG = oldSREG; // Restore register; reenables interrupts
|
||
464 | return;
|
||
465 | } |
||
466 | //prev=current;
|
||
467 | current=current->next; |
||
468 | } |
||
469 | } |
||
470 | |||
471 | // common code for isr handler. "port" is the PCINT number.
|
||
472 | // there isn't really a good way to back-map ports and masks to pins.
|
||
473 | void PCintPort::PCint() {
|
||
474 | uint8_t thisChangedPin; //MIKE
|
||
475 | |||
476 | #ifdef FLASH
|
||
477 | if (*led_port & led_mask) *led_port&=not_led_mask;
|
||
478 | else *led_port|=led_mask;
|
||
479 | #endif
|
||
480 | #ifndef DISABLE_PCINT_MULTI_SERVICE
|
||
481 | uint8_t pcifr; |
||
482 | while (true) { |
||
483 | #endif
|
||
484 | // get the pin states for the indicated port.
|
||
485 | #ifdef PINMODE
|
||
486 | PCintPort::s_lastPinView=lastPinView; |
||
487 | intrCount++; |
||
488 | PCintPort::s_count=intrCount; |
||
489 | #endif
|
||
490 | // OLD v. 2.01 technique: Test 1: 3163; Test 7: 3993
|
||
491 | // From robtillaart online: ------------ (starting v. 2.11beta)
|
||
492 | // uint8_t changedPins = PCintPort::curr ^ lastPinView;
|
||
493 | // lastPinView = PCintPort::curr;
|
||
494 | // uint8_t fastMask = changedPins & ((portRisingPins & PCintPort::curr ) | ( portFallingPins & ~PCintPort::curr ));
|
||
495 | // NEW v. 2.11 technique: Test 1: 3270 Test 7: 3987
|
||
496 | // -------------------------------------
|
||
497 | // was: uint8_t changedPins = PCintPort::curr ^ lastPinView;
|
||
498 | // makes test 6 of the PinChangeIntSpeedTest go from 3867 to 3923. Not good.
|
||
499 | uint8_t changedPins = (PCintPort::curr ^ lastPinView) & |
||
500 | ((portRisingPins & PCintPort::curr ) | ( portFallingPins & ~PCintPort::curr )); |
||
501 | |||
502 | #ifdef PINMODE
|
||
503 | PCintPort::s_currXORlastPinView=PCintPort::curr ^ lastPinView; |
||
504 | PCintPort::s_portRisingPins_nCurr=portRisingPins & PCintPort::curr; |
||
505 | PCintPort::s_portFallingPins_nNCurr=portFallingPins & ~PCintPort::curr; |
||
506 | #endif
|
||
507 | lastPinView = PCintPort::curr; |
||
508 | |||
509 | PCintPin* p = firstPin; |
||
510 | while (p) {
|
||
511 | // Trigger interrupt if the bit is high and it's set to trigger on mode RISING or CHANGE
|
||
512 | // Trigger interrupt if the bit is low and it's set to trigger on mode FALLING or CHANGE
|
||
513 | thisChangedPin=p->mask & changedPins; // PinChangeIntSpeedTest makes this 3673... weird. But GOOD!!!
|
||
514 | if (p->mask & changedPins) {
|
||
515 | #ifndef NO_PIN_STATE
|
||
516 | PCintPort::pinState=PCintPort::curr & p->mask ? HIGH : LOW; |
||
517 | #endif
|
||
518 | #ifndef NO_PIN_NUMBER
|
||
519 | PCintPort::arduinoPin=p->arduinoPin; |
||
520 | #endif
|
||
521 | #ifdef PINMODE
|
||
522 | PCintPort::pinmode=p->mode; |
||
523 | PCintPort::s_portRisingPins=portRisingPins; |
||
524 | PCintPort::s_portFallingPins=portFallingPins; |
||
525 | PCintPort::s_pmask=p->mask; |
||
526 | PCintPort::s_changedPins=changedPins; |
||
527 | #endif
|
||
528 | p->PCintFunc(); |
||
529 | } |
||
530 | p=p->next; |
||
531 | } |
||
532 | #ifndef DISABLE_PCINT_MULTI_SERVICE
|
||
533 | pcifr = PCIFR & PCICRbit; |
||
534 | if (pcifr == 0) break; |
||
535 | PCIFR |= PCICRbit; |
||
536 | #ifdef PINMODE
|
||
537 | PCintPort::pcint_multi++; |
||
538 | if (PCIFR & PCICRbit) PCintPort::PCIFRbug=1; // PCIFR & PCICRbit should ALWAYS be 0 here! |
||
539 | #endif
|
||
540 | PCintPort::curr=portInputReg; |
||
541 | } |
||
542 | #endif
|
||
543 | } |
||
544 | |||
545 | #ifndef NO_PORTA_PINCHANGES
|
||
546 | ISR(PCINT0_vect) { |
||
547 | #ifdef PINMODE
|
||
548 | PCintPort::s_PORT='A';
|
||
549 | #endif
|
||
550 | PCintPort::curr = portA.portInputReg; |
||
551 | portA.PCint(); |
||
552 | } |
||
553 | #define PORTBVECT PCINT1_vect
|
||
554 | #define PORTCVECT PCINT2_vect
|
||
555 | #define PORTDVECT PCINT3_vect
|
||
556 | #else
|
||
557 | #define PORTBVECT PCINT0_vect
|
||
558 | #define PORTCVECT PCINT1_vect
|
||
559 | #define PORTDVECT PCINT2_vect
|
||
560 | #endif
|
||
561 | |||
562 | #ifndef NO_PORTB_PINCHANGES
|
||
563 | ISR(PORTBVECT) { |
||
564 | #ifdef PINMODE
|
||
565 | PCintPort::s_PORT='B';
|
||
566 | #endif
|
||
567 | PCintPort::curr = portB.portInputReg; |
||
568 | portB.PCint(); |
||
569 | } |
||
570 | #endif
|
||
571 | |||
572 | #ifndef NO_PORTC_PINCHANGES
|
||
573 | ISR(PORTCVECT) { |
||
574 | #ifdef PINMODE
|
||
575 | PCintPort::s_PORT='C';
|
||
576 | #endif
|
||
577 | PCintPort::curr = portC.portInputReg; |
||
578 | portC.PCint(); |
||
579 | } |
||
580 | #endif
|
||
581 | |||
582 | #ifndef NO_PORTD_PINCHANGES
|
||
583 | ISR(PORTDVECT){ |
||
584 | #ifdef PINMODE
|
||
585 | PCintPort::s_PORT='D';
|
||
586 | #endif
|
||
587 | PCintPort::curr = portD.portInputReg; |
||
588 | portD.PCint(); |
||
589 | } |
||
590 | #endif
|
||
591 | |||
592 | #ifdef __USE_PORT_JK
|
||
593 | #ifndef NO_PORTJ_PINCHANGES
|
||
594 | ISR(PCINT1_vect) { |
||
595 | #ifdef PINMODE
|
||
596 | PCintPort::s_PORT='J';
|
||
597 | #endif
|
||
598 | PCintPort::curr = portJ.portInputReg; |
||
599 | portJ.PCint(); |
||
600 | } |
||
601 | #endif
|
||
602 | |||
603 | #ifndef NO_PORTK_PINCHANGES
|
||
604 | ISR(PCINT2_vect){ |
||
605 | #ifdef PINMODE
|
||
606 | PCintPort::s_PORT='K';
|
||
607 | #endif
|
||
608 | PCintPort::curr = portK.portInputReg; |
||
609 | portK.PCint(); |
||
610 | } |
||
611 | #endif
|
||
612 | |||
613 | #endif // __USE_PORT_JK |
||
614 | |||
615 | #ifdef GET_PCINT_VERSION
|
||
616 | uint16_t getPCIntVersion () { |
||
617 | return ((uint16_t) PCINT_VERSION);
|
||
618 | } |
||
619 | #endif // GET_PCINT_VERSION |
||
620 | #endif // #ifndef LIBCALL_PINCHANGEINT ************************************************************* |
||
621 | #endif // #ifndef PinChangeInt_h ******************************************************************* |