Last active
May 20, 2018 17:56
-
-
Save sommersoft/d4dad82efba9ef0421452e690b51b308 to your computer and use it in GitHub Desktop.
FrequencyIn_MK134
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 "include/sam.h" | |
#include "atmel_start_pins.h" | |
#include "mpconfigport.h" | |
#include "py/runtime.h" | |
#include "samd21_pins.h" | |
#include "timers.h" | |
#include "events.h" | |
#include "shared-bindings/microcontroller/__init__.h" | |
#include "common-hal/pulseio/__init__.h" | |
#include "shared-bindings/pulseio/FrequencyIn.h" | |
#include "peripheral_clk_config.h" | |
#ifdef SAMD21 | |
#include "hpl/gclk/hpl_gclk_base.h" | |
#endif | |
static pulseio_frequencyin_obj_t *active_frequencyins[TC_INST_NUM]; | |
void frequencyin_interrupt_handler(uint8_t index) { | |
Tc* tc = tc_insts[index]; | |
if (!tc->COUNT16.INTFLAG.bit.MC0) return; // false trigger | |
pulseio_frequencyin_obj_t* self = active_frequencyins[index]; | |
// since we use EVACT.PPW, the Period is put into the CC[0] register | |
// and that's all we need. CC[1] will contain the Pulse Width. | |
self->frequency = tc->COUNT16.CC[0].bit.CC; | |
// we'll read CC[1] to make sure all of the INTFLAG registers are | |
// also cleared. CC[1] will contain the Pulse Width measurement, which | |
// we won't use. of course, we could use it in the future (replace PulseIn?). | |
self->pulse_width = tc->COUNT16.CC[1].bit.CC; | |
} | |
void common_hal_pulseio_frequencyin_construct(pulseio_frequencyin_obj_t* self, const mcu_pin_obj_t* pin) { | |
if (!pin->has_extint) { | |
mp_raise_RuntimeError("No hardware support on pin"); | |
} | |
// if SAMD21, make sure that the extint isn't already in use for the EIC | |
#ifdef SAMD21 | |
uint32_t mask = 1 << pin->extint_channel; | |
if (eic_get_enable() == 1 && | |
((EIC->INTENSET.vec.EXTINT & mask) != 0 || | |
(EIC->EVCTRL.vec.EXTINTEO & mask) != 0)) { | |
mp_raise_RuntimeError("EXTINT channel already in use"); | |
} | |
#endif | |
// 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("All timers in use"); | |
} | |
self->tc_index = index; | |
self->pin = pin->pin; | |
self->channel = pin->extint_channel; | |
#ifdef SAMD21 | |
self->TC_IRQ = TC3_IRQn + index; | |
#endif | |
#ifdef SAMD51 | |
self->TC_IRQ = TC0_IRQn + index; | |
#endif | |
active_frequencyins[index] = self; | |
// We use GCLK0 for SAMD21 and GCLK1 for SAMD51 because they both run at 48mhz making our | |
// math the same across the boards. | |
#ifdef SAMD21 | |
turn_on_clocks(true, index, 0); | |
#endif | |
#ifdef SAMD51 | |
turn_on_clocks(true, index, 1); | |
#endif | |
// Ensure EIC is on for SAMD21, since we can't capture TC straight from the pin | |
#ifdef SAMD21 // start SAMD21 | |
if (eic_get_enable() == 0) { | |
PM->APBAMASK.bit.EIC_ = true; | |
_gclk_enable_channel(EIC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); | |
} else { | |
eic_set_enable(false); | |
} | |
uint8_t sense_setting = EIC_CONFIG_FILTEN0 | 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); | |
masked_value = EIC->EVCTRL.vec.EXTINTEO; | |
EIC->EVCTRL.vec.EXTINTEO = masked_value | (1 << self->channel); | |
NVIC_DisableIRQ(EIC_IRQn); | |
NVIC_ClearPendingIRQ(EIC_IRQn); | |
NVIC_EnableIRQ(EIC_IRQn); | |
eic_set_enable(true); | |
// datasheet says: "The TC should be disabled before | |
// the TC is reset in order to avoid undefined behavior" | |
tc_set_enable(tc, false); | |
tc_reset(tc); // this sets all of tc's registers to default; will need to change to capture | |
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | | |
TC_CTRLA_PRESCALER_DIV1 | | |
TC_CTRLA_WAVEGEN_NFRQ; | |
// Setup TC capture registers. | |
tc->COUNT16.EVCTRL.bit.TCEI = 1; | |
tc->COUNT16.EVCTRL.bit.EVACT = TC_EVCTRL_EVACT_PPW_Val; | |
tc->COUNT16.CTRLC.bit.CPTEN0 = 1; | |
//tc->COUNT16.CTRLC.bit.CPTEN1 = 1; | |
tc->COUNT16.INTENSET.bit.MC0 = 1; | |
NVIC_EnableIRQ(self->TC_IRQ); | |
// Turn on EVSYS | |
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; | |
uint8_t evsys_channel = find_async_event_channel(); | |
connect_event_user_to_channel((EVSYS_ID_USER_TC3_EVU + index), evsys_channel); | |
init_async_event_channel(evsys_channel, (EVSYS_ID_GEN_EIC_EXTINT_0 + self->channel)); | |
self->event_channel = evsys_channel; | |
#endif // end SAMD21 | |
#ifdef SAMD51 // start SAMD51 | |
// datasheet says: "The TC should be disabled before | |
// the TC is reset in order to avoid undefined behavior" | |
tc_set_enable(tc, false); | |
tc_reset(tc); // this sets all of tc's registers to default; will need to change to capture | |
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_PRESCALER_DIV1; | |
tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_NFRQ; // check wavegen for capture | |
// TODO: setup capture registers. | |
// 1. setup PPW: EVCTRL.EVACT = PPW | |
// 2. enable the Capture Channel x Enable bit group: CTRLA.CAPTENx | |
// 3. enable capture from IO pin: CTRLA.COPENx | |
#endif // end SAMD51 | |
tc_set_enable(tc, true); | |
#ifdef SAMD21 | |
gpio_set_pin_function(pin->pin, GPIO_PIN_FUNCTION_A); | |
#endif | |
#ifdef SAMD51 | |
gpio_set_pin_function(pin->pin, GPIO_PIN_FUNCTION_E); | |
#endif | |
} | |
bool common_hal_pulseio_frequencyin_deinited(pulseio_frequencyin_obj_t* self) { | |
return self->pin == NO_PIN; | |
} | |
void common_hal_pulseio_frequencyin_deinit(pulseio_frequencyin_obj_t* self) { | |
if (common_hal_pulseio_frequencyin_deinited(self)) { | |
return; | |
} | |
reset_pin(self->pin); | |
// turn off EIC & EVSYS utilized by this TC | |
#ifdef SAMD21 | |
disable_event_channel(self->event_channel); | |
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 | (0 << self->channel); | |
// check if any other objects are using the EIC; if not, turn it off | |
if (EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) { | |
eic_set_enable(false); | |
PM->APBAMASK.bit.EIC_ = false; | |
hri_gclk_write_CLKCTRL_reg(GCLK, GCLK_CLKCTRL_ID(EIC_GCLK_ID)); | |
NVIC_DisableIRQ(EIC_IRQn); | |
NVIC_ClearPendingIRQ(EIC_IRQn); | |
} | |
#endif | |
// turn off the TC we were using | |
Tc *tc = tc_insts[self->tc_index]; | |
tc_set_enable(tc, false); | |
tc_reset(tc); | |
active_frequencyins[self->tc_index] = NULL; | |
self->tc_index = 0xff; | |
self->pin = NO_PIN; | |
} | |
uint16_t common_hal_pulseio_frequencyin_get_item(pulseio_frequencyin_obj_t* self) { | |
//common_hal_mcu_disable_interrupts(); | |
//uint16_t value = CONF_CPU_FREQUENCY / self->frequency; | |
//common_hal_mcu_enable_interrupts(); | |
//return value; | |
return self->frequency; | |
} | |
void common_hal_pulseio_frequencyin_pause(pulseio_frequencyin_obj_t* self) { | |
Tc *tc = tc_insts[self->tc_index]; | |
if (!tc->COUNT16.EVCTRL.bit.TCEI) { | |
return; | |
} | |
tc->COUNT16.EVCTRL.bit.TCEI = 0; | |
return; | |
} | |
void common_hal_pulseio_frequencyin_resume(pulseio_frequencyin_obj_t* self) { | |
Tc *tc = tc_insts[self->tc_index]; | |
if (tc->COUNT16.EVCTRL.bit.TCEI) { | |
return; | |
} | |
tc->COUNT16.EVCTRL.bit.TCEI = 1; | |
return; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment