Fix SAMD51 pulsein

This commit is contained in:
Scott Shawcroft 2020-03-13 17:21:15 -07:00
parent 7100d5e485
commit 76ca13b6ed
No known key found for this signature in database
GPG Key ID: 9349BC7E64B1921E
5 changed files with 96 additions and 33 deletions

View File

@ -34,14 +34,32 @@
#include "background.h"
#include "eic_handler.h"
#include "mpconfigport.h"
#include "timer_handler.h"
#include "py/gc.h"
#include "py/runtime.h"
#include "samd/external_interrupts.h"
#include "samd/pins.h"
#include "samd/timers.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/pulseio/PulseIn.h"
#include "supervisor/shared/translate.h"
// This timer is shared amongst all PulseIn objects as a higher resolution clock.
static uint8_t refcount = 0;
static uint8_t pulsein_tc_index = 0xff;
static uint32_t overflow_count = 0;
void pulsein_timer_interrupt_handler(uint8_t index) {
if (index != pulsein_tc_index) return;
overflow_count++;
Tc* tc = tc_insts[index];
if (!tc->COUNT16.INTFLAG.bit.OVF) return;
// Clear the interrupt bit.
tc->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF;
}
static void pulsein_set_config(pulseio_pulsein_obj_t* self, bool first_edge) {
uint32_t sense_setting;
if (!first_edge) {
@ -59,13 +77,10 @@ static void pulsein_set_config(pulseio_pulsein_obj_t* self, bool first_edge) {
void pulsein_interrupt_handler(uint8_t channel) {
// Grab the current time first.
uint32_t current_us = 0;
uint64_t current_ms = 0;
// current_tick(&current_ms, &current_us);
uint32_t current_overflow = overflow_count;
Tc* tc = tc_insts[pulsein_tc_index];
uint32_t current_count = tc->COUNT16.COUNT.reg;
// current_tick gives us the remaining us until the next tick but we want the number since the
// last ms.
current_us = 1000 - current_us;
pulseio_pulsein_obj_t* self = get_eic_channel_data(channel);
if (!background_tasks_ok() || self->errored_too_fast) {
self->errored_too_fast = true;
@ -76,17 +91,11 @@ void pulsein_interrupt_handler(uint8_t channel) {
self->first_edge = false;
pulsein_set_config(self, false);
} else {
uint32_t ms_diff = current_ms - self->last_ms;
uint16_t us_diff = current_us - self->last_us;
uint32_t total_diff = us_diff;
if (self->last_us > current_us) {
total_diff = 1000 + current_us - self->last_us;
if (ms_diff > 1) {
total_diff += (ms_diff - 1) * 1000;
}
} else {
total_diff += ms_diff * 1000;
}
uint32_t total_diff = current_count + 0xffff * (current_overflow - self->last_overflow) - self->last_count;
#ifdef SAMD21
total_diff /= 3;
#endif
// Cap duration at 16 bits.
uint16_t duration = 0xffff;
if (total_diff < duration) {
duration = total_diff;
@ -100,8 +109,8 @@ void pulsein_interrupt_handler(uint8_t channel) {
self->start++;
}
}
self->last_ms = current_ms;
self->last_us = current_us;
self->last_overflow = current_overflow;
self->last_count = current_count;
}
void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
@ -124,10 +133,57 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
self->start = 0;
self->len = 0;
self->first_edge = true;
self->last_us = 0;
self->last_ms = 0;
self->last_overflow = 0;
self->last_count = 0;
self->errored_too_fast = false;
if (refcount == 0) {
// Find a spare timer.
Tc *tc = NULL;
int8_t index = TC_INST_NUM - 1;
for (; index >= 0; index--) {
if (tc_insts[index]->COUNT16.CTRLA.bit.ENABLE == 0) {
tc = tc_insts[index];
break;
}
}
if (tc == NULL) {
mp_raise_RuntimeError(translate("All timers in use"));
}
pulsein_tc_index = index;
set_timer_handler(true, index, TC_HANDLER_PULSEIN);
#ifdef SAMD21
// We use GCLK0 for SAMD21 which is 48mhz. We prescale it to 3mhz.
turn_on_clocks(true, index, 0);
#endif
#ifdef SAMD51
// We use GCLK5 for SAMD51 because it runs at 2mhz and we can use it for a 1mhz clock,
// 1us per tick.
turn_on_clocks(true, index, 5);
#endif
#ifdef SAMD21
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 |
TC_CTRLA_PRESCALER_DIV16 |
TC_CTRLA_WAVEGEN_NFRQ;
#endif
#ifdef SAMD51
tc_reset(tc);
tc_set_enable(tc, false);
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_PRESCALER_DIV2;
tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_NFRQ;
#endif
tc_set_enable(tc, true);
tc->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;
overflow_count = 0;
}
refcount++;
set_eic_channel_data(pin->extint_channel, (void*) self);
// Check to see if the EIC is enabled and start it up if its not.'
@ -156,6 +212,12 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) {
set_eic_handler(self->channel, EIC_HANDLER_NO_INTERRUPT);
turn_off_eic_channel(self->channel);
reset_pin_number(self->pin);
refcount--;
if (refcount == 0) {
tc_reset(tc_insts[pulsein_tc_index]);
pulsein_tc_index = 0xff;
}
self->pin = NO_PIN;
}
@ -183,8 +245,8 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self,
// Reconfigure the pin and make sure its set to detect the first edge.
self->first_edge = true;
self->last_ms = 0;
self->last_us = 0;
self->last_overflow = 0;
self->last_count = 0;
gpio_set_pin_function(self->pin, GPIO_PIN_FUNCTION_A);
uint32_t mask = 1 << self->channel;
// Clear previous interrupt state and re-enable it.

View File

@ -41,13 +41,14 @@ typedef struct {
volatile uint16_t start;
volatile uint16_t len;
volatile bool first_edge;
volatile uint64_t last_ms;
volatile uint16_t last_us;
volatile uint32_t last_overflow;
volatile uint16_t last_count;
volatile bool errored_too_fast;
} pulseio_pulsein_obj_t;
void pulsein_reset(void);
void pulsein_interrupt_handler(uint8_t channel);
void pulsein_timer_interrupt_handler(uint8_t index);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEIN_H

View File

@ -29,6 +29,7 @@
#include "timer_handler.h"
#include "common-hal/pulseio/PulseIn.h"
#include "common-hal/pulseio/PulseOut.h"
#include "shared-module/_pew/PewPew.h"
#include "common-hal/frequencyio/FrequencyIn.h"
@ -47,6 +48,11 @@ void shared_timer_handler(bool is_tc, uint8_t index) {
if (is_tc) {
uint8_t handler = tc_handler[index];
switch(handler) {
case TC_HANDLER_PULSEIN:
#if CIRCUITPY_PULSEIO
pulsein_timer_interrupt_handler(index);
#endif
break;
case TC_HANDLER_PULSEOUT:
#if CIRCUITPY_PULSEIO
pulseout_interrupt_handler(index);

View File

@ -30,6 +30,7 @@
#define TC_HANDLER_PULSEOUT 0x1
#define TC_HANDLER_PEW 0x2
#define TC_HANDLER_FREQUENCYIN 0x3
#define TC_HANDLER_PULSEIN 0x4
void set_timer_handler(bool is_tc, uint8_t index, uint8_t timer_handler);
void shared_timer_handler(bool is_tc, uint8_t index);

View File

@ -88,14 +88,7 @@ static void _pulsein_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action
self->first_edge = false;
}
}else {
// Wrapped around a number of times.
uint32_t total_diff = 0xffff;
// Wrapped around once so
if (self->last_overflow == current_overflow - 1) {
total_diff = current_count + (0xffffffff - self->last_count);
} else if (self->last_overflow == current_overflow) {
total_diff = current_count - self->last_count;
}
uint32_t total_diff = current_count + 0xffff * (current_overflow - self->last_overflow) - self->last_count;
// Cap duration at 16 bits.
uint16_t duration = 0xffff;