starting again; initial addition of 'frequencyio.FrequencyIn'

This commit is contained in:
sommersoft 2019-02-16 19:26:24 -06:00
parent 90bc09a31e
commit ea9200a042
5 changed files with 977 additions and 0 deletions

View File

@ -0,0 +1,545 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include "hal/include/hal_gpio.h"
#include "atmel_start_pins.h"
#include "supervisor/shared/translate.h"
#include "mpconfigport.h"
#include "py/runtime.h"
#include "timer_handler.h"
#include "background.h"
#include "samd/clocks.h"
#include "samd/timers.h"
#include "samd/events.h"
#include "samd/pins.h"
#include "samd/external_interrupts.h"
#include "shared-bindings/frequencyio/FrequencyIn.h"
#include "peripheral_clk_config.h"
#include "hpl_gclk_config.h"
#include "tick.h"
#ifdef SAMD21
#include "hpl/gclk/hpl_gclk_base.h"
#endif
static frequencyio_frequencyin_obj_t *active_frequencyins[TC_INST_NUM];
volatile uint8_t reference_tc = 0xff;
#ifdef SAMD51
static uint8_t dpll_gclk;
#endif
void frequencyin_emergency_cancel_capture(uint8_t index) {
frequencyio_frequencyin_obj_t* self = active_frequencyins[index];
NVIC_DisableIRQ(self->TC_IRQ);
NVIC_ClearPendingIRQ(self->TC_IRQ);
#ifdef SAMD21
NVIC_DisableIRQ(EIC_IRQn);
NVIC_ClearPendingIRQ(EIC_IRQn);
#endif
#ifdef SAMD51
NVIC_DisableIRQ(EIC_0_IRQn + self->channel);
NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel);
#endif
common_hal_frequencyio_frequencyin_pause(self); // pause any further captures
NVIC_EnableIRQ(self->TC_IRQ);
#ifdef SAMD21
NVIC_EnableIRQ(EIC_IRQn);
#endif
#ifdef SAMD51
NVIC_EnableIRQ(EIC_0_IRQn + self->channel);
#endif
mp_raise_RuntimeError(translate("Frequency captured is above capability. Capture Paused."));
}
void frequencyin_interrupt_handler(uint8_t index) {
Tc* ref_tc = tc_insts[reference_tc];
if (!ref_tc->COUNT16.INTFLAG.bit.OVF) return; // false trigger
uint32_t current_us;
uint64_t current_ms;
current_tick(&current_ms, &current_us);
for (uint8_t i = 0; i <= (TC_INST_NUM - 1); i++) {
if (active_frequencyins[i] != NULL) {
frequencyio_frequencyin_obj_t* self = active_frequencyins[i];
Tc* tc = tc_insts[self->tc_index];
uint32_t mask = 1 << self->channel;
if ((EIC->INTFLAG.reg & mask) == mask) {
// Make sure capture_period has elapsed before we
// record a new event count.
if (current_ms - self->last_ms >= self->capture_period) {
float new_factor = self->last_us + (1000 - current_us);
self->factor = (current_ms - self->last_ms) + (new_factor / 1000);
self->last_ms = current_ms;
self->last_us = current_us;
#ifdef SAMD51
tc->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val;
while ((tc->COUNT16.SYNCBUSY.bit.COUNT == 1) ||
(tc->COUNT16.CTRLBSET.bit.CMD == TC_CTRLBSET_CMD_READSYNC_Val)) {
}
#endif
uint16_t new_freq = tc->COUNT16.COUNT.reg;
if ((tc->COUNT16.INTFLAG.reg & TC_INTFLAG_OVF) == 1) {
new_freq += 65535;
tc->COUNT16.INTFLAG.reg |= TC_INTFLAG_OVF;
}
self->frequency = new_freq;
#ifdef SAMD51
tc->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_RETRIGGER_Val;
while ((tc->COUNT16.SYNCBUSY.bit.COUNT == 1) ||
(tc->COUNT16.CTRLBSET.bit.CMD == TC_CTRLBSET_CMD_RETRIGGER_Val)) {
}
#endif
}
EIC->INTFLAG.reg |= mask;
}
// Check if we've reached the upper limit of detection
if (!background_tasks_ok() || self->errored_too_fast) {
self->errored_too_fast = true;
frequencyin_emergency_cancel_capture(i);
}
}
}
ref_tc->COUNT16.INTFLAG.reg |= TC_INTFLAG_OVF;
}
void frequencyin_reference_tc_init() {
if (reference_tc == 0xff) {
return;
}
#ifdef SAMD21
turn_on_clocks(true, reference_tc, 0, TC_HANDLER_FREQUENCYIN);
#endif
// use the DPLL we setup so that the reference_tc and freqin_tc(s)
// are using the same clock frequency.
#ifdef SAMD51
if (dpll_gclk == 0xff) {
frequencyin_samd51_start_dpll();
}
turn_on_clocks(true, reference_tc, dpll_gclk, TC_HANDLER_FREQUENCYIN);
#endif
Tc *tc = tc_insts[reference_tc];
tc_set_enable(tc, false);
tc_reset(tc);
#ifdef SAMD21
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_PRESCALER_DIV1;
tc->COUNT16.INTENSET.bit.OVF = 1;
NVIC_EnableIRQ(TC3_IRQn + reference_tc);
#endif
#ifdef SAMD51
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 |
TC_CTRLA_PRESCALER_DIV1;
tc->COUNT16.INTENSET.bit.OVF = 1;
NVIC_EnableIRQ(TC0_IRQn + reference_tc);
#endif
}
bool frequencyin_reference_tc_enabled() {
if (reference_tc == 0xff) {
return false;
}
Tc *tc = tc_insts[reference_tc];
return tc->COUNT16.CTRLA.bit.ENABLE;
}
void frequencyin_reference_tc_enable(bool enable) {
if (reference_tc == 0xff) {
return;
}
Tc *tc = tc_insts[reference_tc];
tc_set_enable(tc, enable);
}
#ifdef SAMD51
void frequencyin_samd51_start_dpll() {
if (clock_get_enabled(0, GCLK_SOURCE_DPLL1)) {
return;
}
uint8_t free_gclk = find_free_gclk(1);
if (free_gclk == 0xff) {
dpll_gclk = 0xff;
return;
}
GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL1].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(free_gclk);
// TC4-7 can only have a max of 100MHz source
// DPLL1 frequency equation with [X]OSC32K as source: 98.304MHz = 32768(2999 + 1 + 0/32)
// Will also enable the Lock Bypass due to low-frequency sources causing DPLL unlocks
// as outlined in the Errata (1.12.1)
OSCCTRL->Dpll[1].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0) | OSCCTRL_DPLLRATIO_LDR(2999);
if (board_has_crystal()) { // we can use XOSC32K directly as the source
OSC32KCTRL->XOSC32K.bit.EN32K = 1;
OSCCTRL->Dpll[1].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK(1) |
OSCCTRL_DPLLCTRLB_LBYPASS;
} else {
// can't use OSCULP32K directly; need to setup a GCLK as a reference,
// which must be done in samd/clocks.c to avoid waiting for sync
return;
//OSC32KCTRL->OSCULP32K.bit.EN32K = 1;
//OSCCTRL->Dpll[1].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK(0);
}
OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
while (!(OSCCTRL->Dpll[1].DPLLSTATUS.bit.LOCK || OSCCTRL->Dpll[1].DPLLSTATUS.bit.CLKRDY)) {}
enable_clock_generator(free_gclk, GCLK_GENCTRL_SRC_DPLL1_Val, 1);
dpll_gclk = free_gclk;
}
void frequencyin_samd51_stop_dpll() {
if (!clock_get_enabled(0, GCLK_SOURCE_DPLL1)) {
return;
}
disable_clock_generator(dpll_gclk);
GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL1].reg = 0;
OSCCTRL->Dpll[1].DPLLCTRLA.reg = 0;
OSCCTRL->Dpll[1].DPLLRATIO.reg = 0;
OSCCTRL->Dpll[1].DPLLCTRLB.reg = 0;
while (OSCCTRL->Dpll[1].DPLLSYNCBUSY.bit.ENABLE) {
}
dpll_gclk = 0xff;
}
#endif
void common_hal_frequencyio_frequencyin_construct(frequencyio_frequencyin_obj_t* self, const mcu_pin_obj_t* pin, const uint16_t capture_period) {
if (!pin->has_extint) {
mp_raise_RuntimeError(translate("No hardware support on pin"));
}
if ((capture_period == 0) || (capture_period > 500)) {
// TODO: find a sutiable message that is already translated
mp_raise_ValueError(translate("Invalid something"));
}
uint32_t mask = 1 << pin->extint_channel;
if (eic_get_enable() == 1 &&
#ifdef SAMD21
((EIC->INTENSET.vec.EXTINT & mask) != 0 || (EIC->EVCTRL.vec.EXTINTEO & mask) != 0)) {
#endif
#ifdef SAMD51
((EIC->INTENSET.bit.EXTINT & mask) != 0 || (EIC->EVCTRL.bit.EXTINTEO & mask) != 0)) {
#endif
mp_raise_RuntimeError(translate("EXTINT channel already in use"));
}
uint8_t timer_index = find_free_timer();
if (timer_index == 0xff) {
mp_raise_RuntimeError(translate("All timers in use"));
}
Tc *tc = tc_insts[timer_index];
self->tc_index = timer_index;
self->pin = pin->number;
self->channel = pin->extint_channel;
self->errored_too_fast = false;
self->last_ms = 0;
self->last_us = 1000;
self->capture_period = capture_period;
#ifdef SAMD21
self->TC_IRQ = TC3_IRQn + timer_index;
#endif
#ifdef SAMD51
self->TC_IRQ = TC0_IRQn + timer_index;
#endif
active_frequencyins[timer_index] = self;
// SAMD21: We use GCLK0 generated from DFLL running at 48mhz
// SAMD51: We use a GCLK generated from DPLL1 running at <100mhz
#ifdef SAMD21
turn_on_clocks(true, timer_index, 0, TC_HANDLER_NO_INTERRUPT);
#endif
#ifdef SAMD51
frequencyin_samd51_start_dpll();
if (dpll_gclk == 0xff && !clock_get_enabled(0, GCLK_SOURCE_DPLL1)) {
common_hal_frequencyio_frequencyin_deinit(self);
mp_raise_RuntimeError(translate("No available clocks"));
}
turn_on_clocks(true, timer_index, dpll_gclk, TC_HANDLER_NO_INTERRUPT);
#endif
// Ensure EIC is on
if (eic_get_enable() == 0) {
turn_on_external_interrupt_controller(); // enables EIC, so disable it after
}
eic_set_enable(false);
uint8_t sense_setting = EIC_CONFIG_SENSE0_HIGH_Val;
uint8_t config_index = self->channel / 8;
uint8_t position = (self->channel % 8) * 4;
uint32_t masked_value = EIC->CONFIG[config_index].reg & ~(0xf << position);
EIC->CONFIG[config_index].reg = masked_value | (sense_setting << position);
#ifdef SAMD21
masked_value = EIC->EVCTRL.vec.EXTINTEO;
EIC->EVCTRL.vec.EXTINTEO = masked_value | (1 << self->channel);
#endif
#ifdef SAMD51
masked_value = EIC->EVCTRL.bit.EXTINTEO;
EIC->EVCTRL.bit.EXTINTEO = masked_value | (1 << self->channel);
EIC->ASYNCH.bit.ASYNCH = 1;
#endif
turn_on_cpu_interrupt(self->channel);
eic_set_enable(true);
// Turn on EVSYS
turn_on_event_system();
uint8_t evsys_channel = find_async_event_channel();
#ifdef SAMD21
connect_event_user_to_channel((EVSYS_ID_USER_TC3_EVU + timer_index), evsys_channel);
#endif
#ifdef SAMD51
connect_event_user_to_channel((EVSYS_ID_USER_TC0_EVU + timer_index), evsys_channel);
#endif
init_async_event_channel(evsys_channel, (EVSYS_ID_GEN_EIC_EXTINT_0 + self->channel));
self->event_channel = evsys_channel;
tc_set_enable(tc, false);
tc_reset(tc);
#ifdef SAMD21
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 |
TC_CTRLA_PRESCALER_DIV1;
tc->COUNT16.EVCTRL.bit.TCEI = 1;
tc->COUNT16.EVCTRL.bit.EVACT = TC_EVCTRL_EVACT_COUNT_Val;
#endif
#ifdef SAMD51
tc->COUNT16.EVCTRL.reg = TC_EVCTRL_EVACT(TC_EVCTRL_EVACT_COUNT_Val) | TC_EVCTRL_TCEI;
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 |
TC_CTRLA_PRESCALER_DIV1;
#endif
NVIC_EnableIRQ(self->TC_IRQ);
gpio_set_pin_function(pin->number, GPIO_PIN_FUNCTION_A);
tc_set_enable(tc, true);
// setup reference TC if not already
if (reference_tc == 0xff) {
reference_tc = find_free_timer();
if (reference_tc == 0xff) {
common_hal_frequencyio_frequencyin_deinit(self);
mp_raise_RuntimeError(translate("All timers in use"));
}
frequencyin_reference_tc_init();
}
if (!frequencyin_reference_tc_enabled()) {
frequencyin_reference_tc_enable(true);
}
}
bool common_hal_frequencyio_frequencyin_deinited(frequencyio_frequencyin_obj_t* self) {
return self->pin == NO_PIN;
}
void common_hal_frequencyio_frequencyin_deinit(frequencyio_frequencyin_obj_t* self) {
if (common_hal_frequencyio_frequencyin_deinited(self)) {
return;
}
reset_pin(self->pin);
// turn off EIC & EVSYS utilized by this TC
disable_event_channel(self->event_channel);
eic_set_enable(false);
#ifdef SAMD21
disable_event_user(EVSYS_ID_USER_TC3_EVU + self->tc_index);
uint32_t masked_value = EIC->EVCTRL.vec.EXTINTEO;
EIC->EVCTRL.vec.EXTINTEO = masked_value ^ (1 << self->channel);
#endif
#ifdef SAMD51
disable_event_user(EVSYS_ID_USER_TC0_EVU + self->tc_index);
uint32_t masked_value = EIC->EVCTRL.bit.EXTINTEO;
EIC->EVCTRL.bit.EXTINTEO = masked_value ^ (1 << self->channel);
NVIC_DisableIRQ(EIC_0_IRQn + self->channel);
NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel);
#endif
eic_set_enable(true);
// check if any other objects are using the EIC; if not, turn it off
if (EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) {
eic_reset();
turn_off_external_interrupt_controller();
}
// turn off the TC we were using
Tc *tc = tc_insts[self->tc_index];
tc_set_enable(tc, false);
tc_reset(tc);
NVIC_DisableIRQ(self->TC_IRQ);
NVIC_ClearPendingIRQ(self->TC_IRQ);
active_frequencyins[self->tc_index] = NULL;
self->tc_index = 0xff;
self->pin = NO_PIN;
bool check_active = false;
for (uint8_t i = 0; i <= (TC_INST_NUM - 1); i++) {
if (active_frequencyins[i] != NULL) {
check_active = true;
}
}
if (!check_active) {
frequencyin_reference_tc_enable(false);
reference_tc = 0xff;
#ifdef SAMD51
frequencyin_samd51_stop_dpll();
#endif
}
}
uint32_t common_hal_frequencyio_frequencyin_get_item(frequencyio_frequencyin_obj_t* self) {
NVIC_DisableIRQ(self->TC_IRQ);
#ifdef SAMD21
NVIC_DisableIRQ(EIC_IRQn);
#endif
#ifdef SAMD51
NVIC_DisableIRQ(EIC_0_IRQn + self->channel);
#endif
// adjust for actual capture period vs base `capture_period`
float frequency_adjustment = 0.0;
if (self->factor > self->capture_period) {
float time_each_event = self->factor / self->frequency; // get the time for each event during actual period
float capture_diff = self->factor - self->capture_period; // get the difference of actual and base periods
// we only need to adjust if the capture_diff can contain 1 or more events
// if so, we add how many events could have occured during the diff time
if (time_each_event > capture_diff) {
frequency_adjustment = capture_diff / time_each_event;
}
}
float value = 1000 / (self->capture_period / (self->frequency + frequency_adjustment));
NVIC_ClearPendingIRQ(self->TC_IRQ);
NVIC_EnableIRQ(self->TC_IRQ);
#ifdef SAMD21
NVIC_ClearPendingIRQ(EIC_IRQn);
NVIC_EnableIRQ(EIC_IRQn);
#endif
#ifdef SAMD51
NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel);
NVIC_EnableIRQ(EIC_0_IRQn + self->channel);
#endif
return value;
}
void common_hal_frequencyio_frequencyin_pause(frequencyio_frequencyin_obj_t* self) {
Tc *tc = tc_insts[self->tc_index];
if (!tc->COUNT16.EVCTRL.bit.TCEI) {
return;
}
tc->COUNT16.EVCTRL.bit.TCEI = 0;
#ifdef SAMD21
uint32_t masked_value = EIC->EVCTRL.vec.EXTINTEO;
EIC->EVCTRL.vec.EXTINTEO = masked_value | (0 << self->channel);
#endif
#ifdef SAMD51
uint32_t masked_value = EIC->EVCTRL.bit.EXTINTEO;
EIC->EVCTRL.bit.EXTINTEO = masked_value | (0 << self->channel);
#endif
return;
}
void common_hal_frequencyio_frequencyin_resume(frequencyio_frequencyin_obj_t* self) {
Tc *tc = tc_insts[self->tc_index];
if (tc->COUNT16.EVCTRL.bit.TCEI) {
return;
}
tc->COUNT16.EVCTRL.bit.TCEI = 1;
#ifdef SAMD21
uint32_t masked_value = EIC->EVCTRL.vec.EXTINTEO;
EIC->EVCTRL.vec.EXTINTEO = masked_value | (1 << self->channel);
#endif
#ifdef SAMD51
uint32_t masked_value = EIC->EVCTRL.bit.EXTINTEO;
EIC->EVCTRL.bit.EXTINTEO = masked_value | (1 << self->channel);
#endif
self->errored_too_fast = false;
return;
}
void common_hal_frequencyio_frequencyin_clear(frequencyio_frequencyin_obj_t* self) {
NVIC_DisableIRQ(self->TC_IRQ);
#ifdef SAMD21
NVIC_DisableIRQ(EIC_IRQn);
#endif
#ifdef SAMD51
NVIC_DisableIRQ(EIC_0_IRQn + self->channel);
#endif
self->frequency = 0;
NVIC_ClearPendingIRQ(self->TC_IRQ);
NVIC_EnableIRQ(self->TC_IRQ);
#ifdef SAMD21
NVIC_ClearPendingIRQ(EIC_IRQn);
NVIC_EnableIRQ(EIC_IRQn);
#endif
#ifdef SAMD51
NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel);
NVIC_EnableIRQ(EIC_0_IRQn + self->channel);
#endif
return;
}
uint16_t common_hal_frequencyio_frequencyin_get_capture_period(frequencyio_frequencyin_obj_t *self) {
return self->capture_period;
}
void common_hal_frequencyio_frequencyin_set_capture_period(frequencyio_frequencyin_obj_t *self, uint16_t capture_period) {
if ((capture_period == 0) || (capture_period > 500)) {
// TODO: find a sutiable message that is already translated
mp_raise_ValueError(translate("Invalid something"));
}
self->capture_period = capture_period;
common_hal_frequencyio_frequencyin_clear(self);
}

View File

@ -0,0 +1,60 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_FREQUENCYIO_FREQUENCYIN_H
#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_FREQUENCYIO_FREQUENCYIN_H
#include "common-hal/microcontroller/Pin.h"
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
uint8_t tc_index;
uint8_t pin;
uint8_t channel;
uint8_t event_channel;
uint32_t frequency;
volatile uint64_t last_ms;
volatile uint32_t last_us;
float factor;
uint32_t capture_period;
uint8_t TC_IRQ;
volatile bool errored_too_fast;
} frequencyio_frequencyin_obj_t;
void frequencyin_interrupt_handler(uint8_t index);
void frequencyin_emergency_cancel_capture(uint8_t index);
void frequencyin_reference_tc_init(void);
void frequencyin_reference_tc_enable(bool enable);
bool frequencyin_reference_tc_enabled(void);
#ifdef SAMD51
void frequencyin_samd51_start_dpll(void);
void frequencyin_samd51_stop_dpll(void);
#endif
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_FREQUENCYIO_FREQUENCYIN_H

View File

@ -0,0 +1,238 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include "lib/utils/context_manager_helpers.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "py/runtime0.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/frequencyio/FrequencyIn.h"
#include "shared-bindings/util.h"
#include "supervisor/shared/translate.h"
//| .. currentmodule:: frequencyio
//|
//| :class:`FrequencyIn` -- Read a frequency signal
//| ========================================================
//|
//| FrequencyIn is used to measure the frequency, in hertz, of a digital signal
//| on an incoming pin. Accuracy has shown to be within 1kHz, if not better.
//| Current maximum detectable frequency is ~512kHz.
//| It will not determine pulse width (use ``PulseIn``).
//|
//| .. class:: FrequencyIn(pin, capture_period=10)
//|
//| Create a FrequencyIn object associated with the given pin.
//|
//| :param ~microcontroller.Pin pin: Pin to read frequency from.
//| :param int capture_period: Keyword argument to set the measurement period, in
//| milliseconds. Default is 10ms; maximum is 500ms.
//|
//| Read the incoming frequency from a pin::
//|
//| import frequencyio
//| import board
//|
//| frequency = frequencyio.FrequencyIn(board.D11)
//|
//| # Loop while printing the detected frequency
//| while True:
//| print(frequency.value)
//|
//| # Optional clear() will reset the value
//| # to zero. Without this, if the incoming
//| # signal stops, the last reading will remain
//| # as the value.
//| frequency.clear()
//|
STATIC mp_obj_t frequencyio_frequencyin_make_new(const mp_obj_type_t *type, size_t n_args,
size_t n_kw, const mp_obj_t *pos_args) {
mp_arg_check_num(n_args, n_kw, 1, 2, true);
frequencyio_frequencyin_obj_t *self = m_new_obj(frequencyio_frequencyin_obj_t);
self->base.type = &frequencyio_frequencyin_type;
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
enum { ARG_pin, ARG_capture_period };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_capture_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 10} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
assert_pin(args[ARG_pin].u_obj, false);
mcu_pin_obj_t* pin = MP_OBJ_TO_PTR(args[ARG_pin].u_obj);
assert_pin_free(pin);
const uint16_t capture_period = args[ARG_capture_period].u_int;
common_hal_frequencyio_frequencyin_construct(self, pin, capture_period);
return MP_OBJ_FROM_PTR(self);
}
//| .. method:: deinit()
//|
//| Deinitialises the FrequencyIn and releases any hardware resources for reuse.
//|
STATIC mp_obj_t frequencyio_frequencyin_deinit(mp_obj_t self_in) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_frequencyio_frequencyin_deinit(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_deinit_obj, frequencyio_frequencyin_deinit);
//| .. method:: __enter__()
//|
//| No-op used by Context Managers.
//|
// Provided by context manager helper.
//| .. method:: __exit__()
//|
//| Automatically deinitializes the hardware when exiting a context. See
//| :ref:`lifetime-and-contextmanagers` for more info.
//|
STATIC mp_obj_t frequencyio_frequencyin_obj___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
common_hal_frequencyio_frequencyin_deinit(args[0]);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(frequencyio_frequencyin___exit___obj, 4, 4, frequencyio_frequencyin_obj___exit__);
//| .. method:: pause()
//|
//| Pause frequency capture.
//|
STATIC mp_obj_t frequencyio_frequencyin_obj_pause(mp_obj_t self_in) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_frequencyio_frequencyin_deinited(self));
common_hal_frequencyio_frequencyin_pause(self);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_pause_obj, frequencyio_frequencyin_obj_pause);
//| .. method:: resume()
//|
//| Resumes frequency capture.
//|
STATIC mp_obj_t frequencyio_frequencyin_obj_resume(mp_obj_t self_in) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_frequencyio_frequencyin_deinited(self));
common_hal_frequencyio_frequencyin_resume(self);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_resume_obj, frequencyio_frequencyin_obj_resume);
//| .. method:: clear()
//|
//| Clears the last detected frequency capture value.
//|
STATIC mp_obj_t frequencyio_frequencyin_obj_clear(mp_obj_t self_in) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_frequencyio_frequencyin_deinited(self));
common_hal_frequencyio_frequencyin_clear(self);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_clear_obj, frequencyio_frequencyin_obj_clear);
//| .. attribute:: capture_period
//|
//| The capture measurement period.
//|
//| .. note:: When setting a new ``capture_period``, all previous capture information is
//| cleared with a call to ``clear()``.
//|
STATIC mp_obj_t frequencyio_frequencyin_obj_get_capture_period(mp_obj_t self_in) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_frequencyio_frequencyin_deinited(self));
return MP_OBJ_NEW_SMALL_INT(common_hal_frequencyio_frequencyin_get_capture_period(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequency_get_capture_period_obj, frequencyio_frequencyin_obj_get_capture_period);
STATIC mp_obj_t frequencyio_frequencyin_obj_set_capture_period(mp_obj_t self_in, mp_obj_t capture_period) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_frequencyio_frequencyin_deinited(self));
common_hal_frequencyio_frequencyin_set_capture_period(self, mp_obj_get_int(capture_period));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(frequencyio_frequency_set_capture_period_obj, frequencyio_frequencyin_obj_set_capture_period);
const mp_obj_property_t frequencyio_frequencyin_capture_period_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&frequencyio_frequency_get_capture_period_obj,
(mp_obj_t)&frequencyio_frequency_set_capture_period_obj,
(mp_obj_t)&mp_const_none_obj},
};
//| .. method:: __get__(index)
//|
//| Returns the value of the last frequency captured.
//|
STATIC mp_obj_t frequencyio_frequencyin_obj_get_value(mp_obj_t self_in) {
frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_frequencyio_frequencyin_deinited(self));
//return MP_OBJ_NEW_SMALL_INT(common_hal_frequencyio_frequencyin_get_item(self));
return mp_obj_new_int_from_float(common_hal_frequencyio_frequencyin_get_item(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_get_value_obj, frequencyio_frequencyin_obj_get_value);
const mp_obj_property_t frequencyio_frequencyin_value_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&frequencyio_frequencyin_get_value_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj},
};
STATIC const mp_rom_map_elem_t frequencyio_frequencyin_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&frequencyio_frequencyin_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&frequencyio_frequencyin___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&frequencyio_frequencyin_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&frequencyio_frequencyin_pause_obj) },
{ MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&frequencyio_frequencyin_resume_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&frequencyio_frequencyin_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_capture_period), MP_ROM_PTR(&frequencyio_frequencyin_capture_period_obj) },
};
STATIC MP_DEFINE_CONST_DICT(frequencyio_frequencyin_locals_dict, frequencyio_frequencyin_locals_dict_table);
const mp_obj_type_t frequencyio_frequencyin_type = {
{ &mp_type_type },
.name = MP_QSTR_frequencyin,
.make_new = frequencyio_frequencyin_make_new,
.locals_dict = (mp_obj_dict_t*)&frequencyio_frequencyin_locals_dict,
};

View File

@ -0,0 +1,46 @@
frequencyio/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO_FREQUENCYIN_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO_FREQUENCYIN_H
#include "common-hal/microcontroller/Pin.h"
#include "common-hal/frequencyio/FrequencyIn.h"
extern const mp_obj_type_t frequencyio_frequencyin_type;
extern void common_hal_frequencyio_frequencyin_construct(frequencyio_frequencyin_obj_t *self,
const mcu_pin_obj_t *pin, uint16_t capture_period);
extern void common_hal_frequencyio_frequencyin_deinit(frequencyio_frequencyin_obj_t *self);
extern bool common_hal_frequencyio_frequencyin_deinited(frequencyio_frequencyin_obj_t *self);
extern void common_hal_frequencyio_frequencyin_pause(frequencyio_frequencyin_obj_t *self);
extern void common_hal_frequencyio_frequencyin_resume(frequencyio_frequencyin_obj_t *self);
extern void common_hal_frequencyio_frequencyin_clear(frequencyio_frequencyin_obj_t *self);
extern uint32_t common_hal_frequencyio_frequencyin_get_item(frequencyio_frequencyin_obj_t *self);
extern uint16_t common_hal_frequencyio_frequencyin_get_capture_period(frequencyio_frequencyin_obj_t *self);
extern void common_hal_frequencyio_frequencyin_set_capture_period(frequencyio_frequencyin_obj_t *self, uint16_t capture_period);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO_FREQUENCYIN_H

View File

@ -0,0 +1,88 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/frequencyio/FrequencyIn.h"
//| :mod:`frequencyio` --- Support for frequency based protocols
//| =============================================================
//|
//| .. module:: frequencyio
//| :synopsis: Support for frequency based protocols
//| :platform: SAMD51
//|
//| The `frequencyio` module contains classes to provide access to basic frequency IO.
//|
//| Libraries
//|
//| .. toctree::
//| :maxdepth: 3
//|
//| FrequencyIn
//|
//| .. warning:: This module is not available in SAMD21 builds. See the
//| :ref:`module-support-matrix` for more info.
//|
//| All classes change hardware state and should be deinitialized when they
//| are no longer needed if the program continues after use. To do so, either
//| call :py:meth:`!deinit` or use a context manager. See
//| :ref:`lifetime-and-contextmanagers` for more info.
//|
//| For example::
//|
//| import frequencyio
//| import time
//| from board import *
//|
//| frequency = frequencyio.FrequencyIn(D13)
//| frequency.capture_period = 15
//| time.sleep(0.1)
//|
//| This example will initialize the the device, set
//| :py:data:`~frequencyio.FrequencyIn.capture_period`, and then sleep 0.1 seconds.
//| CircuitPython will automatically turn off FrequencyIn capture when it resets all
//| hardware after program completion. Use ``deinit()`` or a ``with`` statement
//| to do it yourself.
//|
STATIC const mp_rom_map_elem_t frequencyio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_frequencyio) },
{ MP_ROM_QSTR(MP_QSTR_FrequencyIn), MP_ROM_PTR(&pulseio_frequencyin_type) },
};
STATIC MP_DEFINE_CONST_DICT(frequencyio_module_globals, frequencyio_module_globals_table);
const mp_obj_module_t frequencyio_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&frequencyio_module_globals,
};