Skip to content

Commit d0fb6e7

Browse files
committed
atmel-samd: Add rotary encoder support.
Fixes adafruit#283
1 parent fd71e56 commit d0fb6e7

File tree

21 files changed

+537
-381
lines changed

21 files changed

+537
-381
lines changed

ports/atmel-samd/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,15 @@ SRC_C = \
249249
peripherals/clocks.c \
250250
peripherals/dma.c \
251251
peripherals/events.c \
252+
peripherals/external_interrupts.c \
252253
peripherals/sercom.c \
253254
peripherals/timers.c \
254255
peripherals/$(CHIP_FAMILY)/adc.c \
255256
peripherals/$(CHIP_FAMILY)/cache.c \
256257
peripherals/$(CHIP_FAMILY)/clocks.c \
257258
peripherals/$(CHIP_FAMILY)/dma.c \
258259
peripherals/$(CHIP_FAMILY)/events.c \
260+
peripherals/$(CHIP_FAMILY)/external_interrupts.c \
259261
peripherals/$(CHIP_FAMILY)/pins.c \
260262
peripherals/$(CHIP_FAMILY)/sercom.c \
261263
peripherals/$(CHIP_FAMILY)/timers.c \

ports/atmel-samd/common-hal/busio/SPI.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
#include "supervisor/shared/rgb_led_status.h"
3838

3939
#include "peripherals/dma.h"
40-
//#include "peripherals/pins.h"
4140
#include "peripherals/sercom.h"
4241

4342
void common_hal_busio_spi_construct(busio_spi_obj_t *self,

ports/atmel-samd/common-hal/pulseio/PulseIn.c

Lines changed: 25 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -34,107 +34,45 @@
3434
#include "mpconfigport.h"
3535
#include "py/gc.h"
3636
#include "py/runtime.h"
37+
#include "peripherals/external_interrupts.h"
3738
#include "peripherals/pins.h"
3839
#include "shared-bindings/microcontroller/__init__.h"
3940
#include "shared-bindings/pulseio/PulseIn.h"
4041

41-
#ifdef SAMD21
42-
#include "hpl/gclk/hpl_gclk_base.h"
43-
#endif
44-
4542
#include "tick.h"
4643

47-
static pulseio_pulsein_obj_t *active_pulseins[EIC_EXTINT_NUM];
48-
static uint64_t last_ms[EIC_EXTINT_NUM];
49-
static uint16_t last_us[EIC_EXTINT_NUM];
50-
51-
bool eic_get_enable(void) {
52-
#ifdef SAMD51
53-
return EIC->CTRLA.bit.ENABLE;
54-
#endif
55-
#ifdef SAMD21
56-
return EIC->CTRL.bit.ENABLE;
57-
#endif
58-
}
59-
60-
void eic_set_enable(bool value) {
61-
#ifdef SAMD51
62-
EIC->CTRLA.bit.ENABLE = value;
63-
while (EIC->SYNCBUSY.bit.ENABLE != 0) {}
64-
// This won't actually block long enough in Rev A of SAMD51 and will miss edges in the first
65-
// three cycles of the peripheral clock. See the errata for details. It shouldn't impact us.
66-
#endif
67-
#ifdef SAMD21
68-
EIC->CTRL.bit.ENABLE = value;
69-
while (EIC->STATUS.bit.SYNCBUSY != 0) {}
70-
#endif
71-
}
72-
73-
void eic_reset(void) {
74-
#ifdef SAMD51
75-
EIC->CTRLA.bit.SWRST = true;
76-
while (EIC->SYNCBUSY.bit.SWRST != 0) {}
77-
// This won't actually block long enough in Rev A of SAMD51 and will miss edges in the first
78-
// three cycles of the peripheral clock. See the errata for details. It shouldn't impact us.
79-
#endif
80-
#ifdef SAMD21
81-
EIC->CTRL.bit.SWRST = true;
82-
while (EIC->STATUS.bit.SYNCBUSY != 0) {}
83-
#endif
84-
}
85-
86-
void pulsein_reset(void) {
87-
for (int i = 0; i < EIC_EXTINT_NUM; i++) {
88-
active_pulseins[i] = NULL;
89-
last_ms[i] = 0;
90-
last_us[i] = 0;
91-
#ifdef SAMD51
92-
NVIC_DisableIRQ(EIC_0_IRQn + i);
93-
NVIC_ClearPendingIRQ(EIC_0_IRQn + i);
94-
#endif
95-
}
96-
eic_reset();
97-
#ifdef SAMD21
98-
NVIC_DisableIRQ(EIC_IRQn);
99-
NVIC_ClearPendingIRQ(EIC_IRQn);
100-
#endif
101-
}
102-
10344
static void pulsein_set_config(pulseio_pulsein_obj_t* self, bool first_edge) {
104-
uint8_t sense_setting = EIC_CONFIG_FILTEN0;
45+
uint32_t sense_setting;
10546
if (!first_edge) {
106-
sense_setting |= EIC_CONFIG_SENSE0_BOTH_Val;
47+
sense_setting = EIC_CONFIG_SENSE0_BOTH_Val;
48+
configure_eic_channel(self->channel, sense_setting);
49+
return;
10750
} else if (self->idle_state) {
108-
sense_setting |= EIC_CONFIG_SENSE0_FALL_Val;
51+
sense_setting = EIC_CONFIG_SENSE0_FALL_Val;
10952
} else {
110-
sense_setting |= EIC_CONFIG_SENSE0_RISE_Val;
53+
sense_setting = EIC_CONFIG_SENSE0_RISE_Val;
11154
}
112-
eic_set_enable(false);
113-
uint8_t config_index = self->channel / 8;
114-
uint8_t position = (self->channel % 8) * 4;
115-
uint32_t masked_value = EIC->CONFIG[config_index].reg & ~(0xf << position);
116-
EIC->CONFIG[config_index].reg = masked_value | (sense_setting << position);
117-
eic_set_enable(true);
55+
turn_on_eic_channel(self->channel, sense_setting, EIC_HANDLER_PULSEIN);
11856
}
11957

120-
static void pulsein_interrupt_handler(uint8_t channel) {
58+
void pulsein_interrupt_handler(uint8_t channel) {
12159
// Grab the current time first.
12260
uint32_t current_us;
12361
uint64_t current_ms;
12462
current_tick(&current_ms, &current_us);
12563
// current_tick gives us the remaining us until the next tick but we want the number since the
12664
// last ms.
12765
current_us = 1000 - current_us;
128-
pulseio_pulsein_obj_t* self = active_pulseins[channel];
66+
pulseio_pulsein_obj_t* self = get_eic_channel_data(channel);
12967
if (self->first_edge) {
13068
self->first_edge = false;
13169
pulsein_set_config(self, false);
13270
} else {
133-
uint32_t ms_diff = current_ms - last_ms[self->channel];
134-
uint16_t us_diff = current_us - last_us[self->channel];
71+
uint32_t ms_diff = current_ms - self->last_ms;
72+
uint16_t us_diff = current_us - self->last_us;
13573
uint32_t total_diff = us_diff;
136-
if (last_us[self->channel] > current_us) {
137-
total_diff = 1000 + current_us - last_us[self->channel];
74+
if (self->last_us > current_us) {
75+
total_diff = 1000 + current_us - self->last_us;
13876
if (ms_diff > 1) {
13977
total_diff += (ms_diff - 1) * 1000;
14078
}
@@ -154,26 +92,16 @@ static void pulsein_interrupt_handler(uint8_t channel) {
15492
self->start++;
15593
}
15694
}
157-
last_ms[self->channel] = current_ms;
158-
last_us[self->channel] = current_us;
95+
self->last_ms = current_ms;
96+
self->last_us = current_us;
15997
}
16098

16199
void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
162100
const mcu_pin_obj_t* pin, uint16_t maxlen, bool idle_state) {
163101
if (!pin->has_extint) {
164102
mp_raise_RuntimeError("No hardware support on pin");
165103
}
166-
uint32_t mask = 1 << pin->extint_channel;
167-
if (active_pulseins[pin->extint_channel] != NULL ||
168-
(eic_get_enable() == 1 &&
169-
#ifdef SAMD51
170-
((EIC->INTENSET.bit.EXTINT & mask) != 0 ||
171-
(EIC->EVCTRL.bit.EXTINTEO & mask) != 0))) {
172-
#endif
173-
#ifdef SAMD21
174-
((EIC->INTENSET.vec.EXTINT & mask) != 0 ||
175-
(EIC->EVCTRL.vec.EXTINTEO & mask) != 0))) {
176-
#endif
104+
if (eic_get_enable() && !eic_channel_free(pin->extint_channel)) {
177105
mp_raise_RuntimeError("EXTINT channel already in use");
178106
}
179107

@@ -188,42 +116,22 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
188116
self->start = 0;
189117
self->len = 0;
190118
self->first_edge = true;
119+
self->last_us = 0;
120+
self->last_ms = 0;
191121

192-
active_pulseins[pin->extint_channel] = self;
122+
set_eic_channel_data(pin->extint_channel, (void*) self);
193123

194124
// Check to see if the EIC is enabled and start it up if its not.'
195-
// SAMD51 EIC can only be clocked up to 100mhz so we use the 48mhz clock.
196125
if (eic_get_enable() == 0) {
197-
#ifdef SAMD51
198-
MCLK->APBAMASK.bit.EIC_ = true;
199-
hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID,
200-
GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
201-
#endif
202-
203-
#ifdef SAMD21
204-
PM->APBAMASK.bit.EIC_ = true;
205-
_gclk_enable_channel(EIC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val);
206-
#endif
207-
208-
209-
#ifdef SAMD21
210-
NVIC_DisableIRQ(EIC_IRQn);
211-
NVIC_ClearPendingIRQ(EIC_IRQn);
212-
NVIC_EnableIRQ(EIC_IRQn);
213-
#endif
126+
turn_on_external_interrupt_controller();
214127
}
215128

216129
gpio_set_pin_function(pin->pin, GPIO_PIN_FUNCTION_A);
217130

218-
#ifdef SAMD51
219-
NVIC_DisableIRQ(EIC_0_IRQn + self->channel);
220-
NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel);
221-
NVIC_EnableIRQ(EIC_0_IRQn + self->channel);
222-
#endif
131+
turn_on_cpu_interrupt(self->channel);
223132

224133
// Set config will enable the EIC.
225134
pulsein_set_config(self, true);
226-
EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos;
227135
}
228136

229137
bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) {
@@ -234,39 +142,9 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) {
234142
if (common_hal_pulseio_pulsein_deinited(self)) {
235143
return;
236144
}
237-
uint32_t mask = 1 << self->channel;
238-
EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos;
239-
#ifdef SAMD51
240-
NVIC_DisableIRQ(EIC_0_IRQn + self->channel);
241-
NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel);
242-
#endif
243-
active_pulseins[self->channel] = NULL;
145+
turn_off_eic_channel(self->channel);
244146
reset_pin(self->pin);
245147
self->pin = NO_PIN;
246-
247-
bool all_null = true;
248-
for (uint8_t i = 0; all_null && i < 16; i++) {
249-
all_null = all_null && active_pulseins[i] == NULL;
250-
}
251-
#ifdef SAMD21
252-
if (all_null && EIC->INTENSET.reg == 0) {
253-
NVIC_DisableIRQ(EIC_IRQn);
254-
NVIC_ClearPendingIRQ(EIC_IRQn);
255-
}
256-
#endif
257-
// Test if all channels are null and deinit everything if they are.
258-
if (all_null && EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) {
259-
eic_set_enable(false);
260-
#ifdef SAMD51
261-
MCLK->APBAMASK.bit.EIC_ = false;
262-
hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, 0);
263-
#endif
264-
265-
#ifdef SAMD21
266-
PM->APBAMASK.bit.EIC_ = false;
267-
hri_gclk_write_CLKCTRL_reg(GCLK, GCLK_CLKCTRL_ID(EIC_GCLK_ID));
268-
#endif
269-
}
270148
}
271149

272150
void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) {
@@ -289,9 +167,9 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self,
289167
}
290168

291169
// Reconfigure the pin and make sure its set to detect the first edge.
292-
last_ms[self->channel] = 0;
293-
last_us[self->channel] = 0;
294170
self->first_edge = true;
171+
self->last_ms = 0;
172+
self->last_us = 0;
295173
gpio_set_pin_function(self->pin, GPIO_PIN_FUNCTION_A);
296174
uint32_t mask = 1 << self->channel;
297175
// Clear previous interrupt state and re-enable it.
@@ -343,69 +221,3 @@ uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self,
343221
common_hal_mcu_enable_interrupts();
344222
return value;
345223
}
346-
347-
void external_interrupt_handler(uint8_t channel) {
348-
pulsein_interrupt_handler(channel);
349-
EIC->INTFLAG.reg = (1 << channel) << EIC_INTFLAG_EXTINT_Pos;
350-
}
351-
352-
#ifdef SAMD21
353-
void EIC_Handler(void) {
354-
for (uint8_t i = 0; i < 16; i++) {
355-
if ((EIC->INTFLAG.vec.EXTINT & (1 << i)) != 0) {
356-
external_interrupt_handler(i);
357-
}
358-
}
359-
}
360-
#endif
361-
362-
#ifdef SAMD51
363-
void EIC_0_Handler(void) {
364-
external_interrupt_handler(0);
365-
}
366-
void EIC_1_Handler(void) {
367-
external_interrupt_handler(1);
368-
}
369-
void EIC_2_Handler(void) {
370-
external_interrupt_handler(2);
371-
}
372-
void EIC_3_Handler(void) {
373-
external_interrupt_handler(3);
374-
}
375-
void EIC_4_Handler(void) {
376-
external_interrupt_handler(4);
377-
}
378-
void EIC_5_Handler(void) {
379-
external_interrupt_handler(5);
380-
}
381-
void EIC_6_Handler(void) {
382-
external_interrupt_handler(6);
383-
}
384-
void EIC_7_Handler(void) {
385-
external_interrupt_handler(7);
386-
}
387-
void EIC_8_Handler(void) {
388-
external_interrupt_handler(8);
389-
}
390-
void EIC_9_Handler(void) {
391-
external_interrupt_handler(9);
392-
}
393-
void EIC_10_Handler(void) {
394-
external_interrupt_handler(10);
395-
}
396-
void EIC_11_Handler(void) {
397-
external_interrupt_handler(11);
398-
}
399-
void EIC_12_Handler(void) {
400-
external_interrupt_handler(12);
401-
}
402-
void EIC_13_Handler(void) {
403-
external_interrupt_handler(13);
404-
}
405-
void EIC_14_Handler(void) {
406-
external_interrupt_handler(14);
407-
}
408-
void EIC_15_Handler(void) {
409-
external_interrupt_handler(15);
410-
}
411-
#endif

ports/atmel-samd/common-hal/pulseio/PulseIn.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ typedef struct {
4141
volatile uint16_t start;
4242
volatile uint16_t len;
4343
volatile bool first_edge;
44+
volatile uint64_t last_ms;
45+
volatile uint16_t last_us;
4446
} pulseio_pulsein_obj_t;
4547

4648
void pulsein_reset(void);
4749

50+
void pulsein_interrupt_handler(uint8_t channel);
51+
4852
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEIN_H

0 commit comments

Comments
 (0)