Skip to content

Commit e4883c8

Browse files
committed
cpu/atmega: WIP implementation of pin change interrupts
1 parent 24476e0 commit e4883c8

3 files changed

Lines changed: 159 additions & 22 deletions

File tree

boards/common/arduino-atmega/include/board_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extern "C" {
7070
PCMSK2 |= (1 << PCINT23); \
7171
} while (0)
7272
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT2_vect
73+
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM PCINT2_vect_num
7374
#define AVR_CONTEXT_SWAP_TRIGGER PORTD ^= (1 << PD7)
7475
#endif
7576

@@ -80,6 +81,7 @@ extern "C" {
8081
PCMSK1 |= (1 << PCINT15); \
8182
} while (0)
8283
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT1_vect
84+
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM PCINT1_vect_num
8385
#define AVR_CONTEXT_SWAP_TRIGGER PORTJ ^= (1 << PJ6)
8486
#endif
8587

boards/waspmote-pro/include/board.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ extern "C" {
157157
} while (0)
158158
/** @cond INTERNAL */
159159
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT0_vect
160+
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM PCINT0_vect_num
160161
#define AVR_CONTEXT_SWAP_TRIGGER PORTB ^= (1 << PB5)
161162
/** @endcond */
162163

cpu/atmega_common/periph/gpio.c

Lines changed: 156 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/*
22
* Copyright (C) 2015 HAW Hamburg
33
* 2016 INRIA
4-
4+
* 2017 TU Braunschweig, IBR
5+
*
56
*
67
* This file is subject to the terms and conditions of the GNU Lesser
78
* General Public License v2.1. See the file LICENSE in the top level
@@ -19,6 +20,7 @@
1920
* @author René Herthel <rene-herthel@outlook.de>
2021
* @author Francisco Acosta <francisco.acosta@inria.fr>
2122
* @author Laurent Navet <laurent.navet@gmail.com>
23+
* @author Robert Hartung <hartung@ibr.cs.tu-bs.de>
2224
*
2325
* @}
2426
*/
@@ -31,6 +33,7 @@
3133
#include "cpu.h"
3234
#include "periph/gpio.h"
3335
#include "periph_conf.h"
36+
#include "board.h"
3437

3538
#define GPIO_BASE_PORT_A (0x20)
3639
#define GPIO_OFFSET_PORT_H (0xCB)
@@ -57,7 +60,28 @@
5760
#define GPIO_EXT_INT_NUMOF (2U)
5861
#endif
5962

63+
#ifdef AVR_USE_PCINT
64+
#if defined(PCINT3_vect)
65+
#define GPIO_PC_INT_NUMOF (32U)
66+
#elif defined(PCINT2_vect)
67+
#define GPIO_PC_INT_NUMOF (24U)
68+
#elif defined(PCINT1_vect)
69+
#define GPIO_PC_INT_NUMOF (16U)
70+
#elif defined(PCINT0_vect)
71+
#define GPIO_PC_INT_NUMOF (8U)
72+
#endif
73+
#endif
74+
75+
/* holds the callback and argument for regular interrupts */
6076
static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF];
77+
#ifdef AVR_USE_PCINT
78+
/* holds the callback and argument for pin change interrupts */
79+
static gpio_isr_ctx_t pcint[GPIO_PC_INT_NUMOF];
80+
/* stores the configured flank for the respective pcint */
81+
static gpio_flank_t pcint_flank[GPIO_PC_INT_NUMOF];
82+
/* stores the last state of each port */
83+
static uint8_t pcint_state[GPIO_PC_INT_NUMOF / 8];
84+
#endif
6185

6286
/**
6387
* @brief Extract the pin number of the given pin
@@ -134,16 +158,65 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
134158
gpio_cb_t cb, void *arg)
135159
{
136160
uint8_t pin_num = _pin_num(pin);
161+
uint8_t port_num = _port_num(pin);
137162

138163
if ((_port_num(pin) == PORT_D && pin_num > 3)
139164
#if defined (PORTE)
140-
|| (_port_num(pin) == PORT_E && pin_num < 4)
141-
|| (_port_num(pin) != PORT_D && _port_num(pin) != PORT_E)
142-
#elif defined(CPU_ATMEGA328P)
143-
|| (pin_num < 2) || (_port_num(pin) != PORT_D)
165+
|| (_port_num(pin) == PORT_E && pin_num < 4)
166+
|| (_port_num(pin) != PORT_D && _port_num(pin) != PORT_E)
167+
#elif defined(CPU_ATMEGA328P) /* INT0: PD2, INT1: PD3 */
168+
|| (pin_num < 2) || (_port_num(pin) != PORT_D)
169+
#elif defined(CPU_ATMEGA1284P) /* INT0: PD2, INT1: PD3, INT2: PB2 */
170+
|| ((pin_num < 2) && (_port_num(pin) == PORT_D))
171+
|| ((pin_num < 2) && (_port_num(pin) == PORT_B))
172+
|| ((pin_num > 2) && (_port_num(pin) == PORT_B))
173+
|| (_port_num(pin) == PORT_C)
174+
|| (_port_num(pin) == PORT_A)
175+
#endif
176+
|| ((mode != GPIO_IN) && (mode != GPIO_IN_PU))) {
177+
178+
/* If pin change interrupts are enabled, enable mask and interrupt */
179+
#ifdef GPIO_PC_INT_NUMOF
180+
gpio_init(pin, mode);
181+
cli();
182+
switch (_port_num(pin)) {
183+
case 0:
184+
PCMSK0 |= (1 << pin_num);
185+
PCICR |= (1 << PCIE0);
186+
break;
187+
case 1:
188+
PCMSK1 |= (1 << pin_num);
189+
PCICR |= (1 << PCIE1);
190+
break;
191+
#ifdef PCIE2
192+
case 2:
193+
PCMSK2 |= (1 << pin_num);
194+
PCICR |= (1 << PCIE2);
195+
break;
144196
#endif
145-
|| ((mode != GPIO_IN) && (mode != GPIO_IN_PU))) {
197+
#ifdef PCIE3
198+
case 3:
199+
PCMSK3 |= (1 << pin_num);
200+
PCICR |= (1 << PCIE3);
201+
break;
202+
#endif
203+
default:
204+
return -1;
205+
break;
206+
}
207+
/* set configuration */
208+
int pcint_num = _port_num(pin) * 8 + pin_num;
209+
pcint[pcint_num].cb = cb;
210+
pcint[pcint_num].arg = arg;
211+
pcint_flank[pcint_num] = flank;
212+
/* store current value of the port */
213+
pcint_state[_port_num(pin)] = (_SFR_MEM8(_pin_addr( GPIO_PIN( _port_num(pin), pin_num ) )));
214+
/* enable global interrupt flag */
215+
sei();
216+
return 0;
217+
#else /* if no pin change interrupts are used */
146218
return -1;
219+
#endif /* GPIO_PC_INT_NUMOF */
147220
}
148221

149222
gpio_init(pin, mode);
@@ -237,61 +310,122 @@ void gpio_write(gpio_t pin, int value)
237310
}
238311
}
239312

313+
#ifdef GPIO_PC_INT_NUMOF
314+
/* inline function that is used by the PCINT ISR */
315+
static inline void pcint_handler(uint8_t port_num, volatile uint8_t *mask_reg)
316+
{
317+
uint8_t pin_num = 0;
318+
/* calculate changed bits */
319+
uint8_t state = _SFR_MEM8(_pin_addr(GPIO_PIN(port_num, 1)));
320+
/* get pins that changed */
321+
uint8_t change = pcint_state[port_num] ^ state;
322+
323+
/* apply mask to change */
324+
change &= *mask_reg;
325+
/* loop through all changed pins with enabled pcint */
326+
while (change > 0) {
327+
/* check if this pin is enabled & has changed */
328+
if (change & 0x1) {
329+
uint8_t pin_mask = (1 << pin_num);
330+
gpio_flank_t flank = pcint_flank[ port_num * 8 + pin_num ];
331+
/* trigger only on correct flank */
332+
if (flank == GPIO_BOTH || ((state & pin_mask) && flank == GPIO_RISING) || (!(state & pin_mask) && flank == GPIO_FALLING)) {
333+
/* finally execute callback routine */
334+
__enter_isr();
335+
pcint[port_num * 8 + pin_num].cb( pcint[port_num * 8 + pin_num].arg );
336+
__exit_isr();
337+
}
338+
}
339+
change = change >> 1;
340+
pin_num++;
341+
}
342+
343+
/* store current state */
344+
pcint_state[port_num] = state;
345+
}
346+
347+
#ifndef AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM
348+
#error gpio.c requires the definition of AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM
349+
#endif
350+
351+
/*
352+
* PCINT0 is always defined, if GPIO_PC_INT_NUMOF is defined
353+
*/
354+
#if PCINT0_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM
355+
ISR(PCINT0_vect, ISR_BLOCK) {
356+
pcint_handler(0, &PCMSK0);
357+
}
358+
#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */
359+
#if defined(PCINT1_vect)
360+
#if PCINT1_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM
361+
ISR(PCINT1_vect, ISR_BLOCK) {
362+
pcint_handler(1, &PCMSK1);
363+
}
364+
#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */
365+
#endif /* PCINT1_vect */
366+
#if defined(PCINT2_vect)
367+
#if PCINT2_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM
368+
ISR(PCINT2_vect, ISR_BLOCK) {
369+
pcint_handler(2, &PCMSK2);
370+
}
371+
#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */
372+
#endif /* PCINT2_vect */
373+
#if defined(PCINT3_vect)
374+
#if PCINT3_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM
375+
ISR(PCINT3_vect, ISR_BLOCK) {
376+
pcint_handler(3, &PCMSK3);
377+
}
378+
#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */
379+
#endif /* PCINT3_vect */
380+
#endif /* GPIO_PC_INT_NUMOF */
381+
240382
static inline void irq_handler(uint8_t pin_num)
241383
{
242384
__enter_isr();
243385
config[pin_num].cb(config[pin_num].arg);
244386
__exit_isr();
245387
}
246388

247-
ISR(INT0_vect, ISR_BLOCK)
248-
{
389+
ISR(INT0_vect, ISR_BLOCK){
249390
irq_handler(0); /**< predefined interrupt pin */
250391
}
251392

252-
ISR(INT1_vect, ISR_BLOCK)
253-
{
393+
ISR(INT1_vect, ISR_BLOCK){
254394
irq_handler(1); /**< predefined interrupt pin */
255395
}
256396

257397
#if defined(INT2_vect)
258-
ISR(INT2_vect, ISR_BLOCK)
259-
{
398+
ISR(INT2_vect, ISR_BLOCK){
260399
irq_handler(2); /**< predefined interrupt pin */
261400
}
262401
#endif
263402

264403
#if defined(INT3_vect)
265-
ISR(INT3_vect, ISR_BLOCK)
266-
{
404+
ISR(INT3_vect, ISR_BLOCK){
267405
irq_handler(3); /**< predefined interrupt pin */
268406
}
269407
#endif
270408

271409
#if defined(INT4_vect)
272-
ISR(INT4_vect, ISR_BLOCK)
273-
{
410+
ISR(INT4_vect, ISR_BLOCK){
274411
irq_handler(4); /**< predefined interrupt pin */
275412
}
276413
#endif
277414

278415
#if defined(INT5_vect)
279-
ISR(INT5_vect, ISR_BLOCK)
280-
{
416+
ISR(INT5_vect, ISR_BLOCK){
281417
irq_handler(5); /**< predefined interrupt pin */
282418
}
283419
#endif
284420

285421
#if defined(INT6_vect)
286-
ISR(INT6_vect, ISR_BLOCK)
287-
{
422+
ISR(INT6_vect, ISR_BLOCK){
288423
irq_handler(6); /**< predefined interrupt pin */
289424
}
290425
#endif
291426

292427
#if defined(INT7_vect)
293-
ISR(INT7_vect, ISR_BLOCK)
294-
{
428+
ISR(INT7_vect, ISR_BLOCK){
295429
irq_handler(7); /**< predefined interrupt pin */
296430
}
297431
#endif

0 commit comments

Comments
 (0)