circuitpython/ports/atmel-samd/peripherals/external_interrupts.c

106 lines
4.0 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 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 "common-hal/pulseio/PulseIn.h"
#include "common-hal/rotaryio/IncrementalEncoder.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "peripherals/external_interrupts.h"
#include "sam.h"
// This structure is used to share per-channel storage amongst all users of external interrupts.
// Without this there would be multiple arrays even though they are disjoint because each channel
// has one user.
static void *channel_data[EIC_EXTINT_NUM];
static uint8_t channel_handler[EIC_EXTINT_NUM];
void external_interrupt_handler(uint8_t channel) {
uint8_t handler = channel_handler[channel];
if (handler == EIC_HANDLER_PULSEIN) {
pulsein_interrupt_handler(channel);
} else if (handler == EIC_HANDLER_INCREMENTAL_ENCODER) {
incrementalencoder_interrupt_handler(channel);
}
EIC->INTFLAG.reg = (1 << channel) << EIC_INTFLAG_EXTINT_Pos;
}
void configure_eic_channel(uint8_t eic_channel, uint32_t sense_setting) {
uint8_t config_index = eic_channel / 8;
uint8_t position = (eic_channel % 8) * 4;
#ifdef SAMD51
eic_set_enable(false);
#endif
common_hal_mcu_disable_interrupts();
uint32_t masked_value = EIC->CONFIG[config_index].reg & ~(0xf << position);
EIC->CONFIG[config_index].reg = masked_value | (sense_setting << position);
common_hal_mcu_enable_interrupts();
#ifdef SAMD51
eic_set_enable(true);
#endif
}
void turn_on_eic_channel(uint8_t eic_channel, uint32_t sense_setting,
uint8_t channel_interrupt_handler) {
// We do very light filtering using majority voting.
sense_setting |= EIC_CONFIG_FILTEN0;
configure_eic_channel(eic_channel, sense_setting);
uint32_t mask = 1 << eic_channel;
EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos;
if (channel_interrupt_handler != EIC_HANDLER_NO_INTERRUPT) {
channel_handler[eic_channel] = channel_interrupt_handler;
turn_on_cpu_interrupt(eic_channel);
}
}
void turn_off_eic_channel(uint8_t eic_channel) {
uint32_t mask = 1 << eic_channel;
EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos;
#ifdef SAMD51
NVIC_DisableIRQ(EIC_0_IRQn + eic_channel);
NVIC_ClearPendingIRQ(EIC_0_IRQn + eic_channel);
#endif
channel_data[eic_channel] = NULL;
#ifdef SAMD21
if (EIC->INTENSET.reg == 0) {
NVIC_DisableIRQ(EIC_IRQn);
NVIC_ClearPendingIRQ(EIC_IRQn);
}
#endif
// Test if all channels are null and deinit everything if they are.
if (EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) {
turn_off_external_interrupt_controller();
}
}
void* get_eic_channel_data(uint8_t eic_channel) {
return channel_data[eic_channel];
}
void set_eic_channel_data(uint8_t eic_channel, void* data) {
channel_data[eic_channel] = data;
}