106 lines
4.0 KiB
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;
|
|
}
|