Skip to content

Instantly share code, notes, and snippets.

@sommersoft
Last active May 20, 2018 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sommersoft/d4dad82efba9ef0421452e690b51b308 to your computer and use it in GitHub Desktop.
Save sommersoft/d4dad82efba9ef0421452e690b51b308 to your computer and use it in GitHub Desktop.
FrequencyIn_MK134
/*
* 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