tinySA/os/hal/ports/KINETIS/LLD/ext_lld.c

360 lines
9.3 KiB
C

/*
ChibiOS/HAL - Copyright (C) 2014 Derek Mulcahy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file KINETIS/LLD/ext_lld.c
* @brief KINETIS EXT subsystem low level driver source.
*
* @addtogroup EXT
* @{
*/
#include "hal.h"
#if HAL_USE_EXT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
#define PCR_IRQC_DISABLED 0x0
#define PCR_IRQC_DMA_RISING_EDGE 0x1
#define PCR_IRQC_DMA_FALLING_EDGE 0x2
#define PCR_IRQC_DMA_EITHER_EDGE 0x3
#define PCR_IRQC_LOGIC_ZERO 0x8
#define PCR_IRQC_RISING_EDGE 0x9
#define PCR_IRQC_FALLING_EDGE 0xA
#define PCR_IRQC_EITHER_EDGE 0xB
#define PCR_IRQC_LOGIC_ONE 0xC
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief EXTD1 driver identifier.
*/
EXTDriver EXTD1;
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/* A channel map for each channel.
*
* The index is the pin number.
* The result is the channel for that pin.
*/
#if KINETIS_EXT_PORTA_WIDTH > 0
uint8_t porta_channel_map[KINETIS_EXT_PORTA_WIDTH];
#endif
#if KINETIS_EXT_PORTB_WIDTH > 0
uint8_t portb_channel_map[KINETIS_EXT_PORTB_WIDTH];
#endif
#if KINETIS_EXT_PORTC_WIDTH > 0
uint8_t portc_channel_map[KINETIS_EXT_PORTC_WIDTH];
#endif
#if KINETIS_EXT_PORTD_WIDTH > 0
uint8_t portd_channel_map[KINETIS_EXT_PORTD_WIDTH];
#endif
#if KINETIS_EXT_PORTE_WIDTH > 0
uint8_t porte_channel_map[KINETIS_EXT_PORTE_WIDTH];
#endif
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Enables EXTI IRQ sources.
*
* @notapi
*/
static void ext_lld_exti_irq_enable(void) {
#if KINETIS_EXT_PORTA_WIDTH > 0
nvicEnableVector(PINA_IRQn, KINETIS_EXT_PORTA_IRQ_PRIORITY);
#endif
#if KINETIS_EXT_PORTB_WIDTH > 0
nvicEnableVector(PINB_IRQn, KINETIS_EXT_PORTB_IRQ_PRIORITY);
#endif
#if KINETIS_EXT_PORTC_WIDTH > 0
nvicEnableVector(PINC_IRQn, KINETIS_EXT_PORTC_IRQ_PRIORITY);
#endif
#if KINETIS_EXT_PORTD_WIDTH > 0
nvicEnableVector(PIND_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY);
#endif
#if KINETIS_EXT_PORTE_WIDTH > 0
nvicEnableVector(PINE_IRQn, KINETIS_EXT_PORTE_IRQ_PRIORITY);
#endif
}
/**
* @brief Disables EXTI IRQ sources.
*
* @notapi
*/
static void ext_lld_exti_irq_disable(void) {
#if KINETIS_EXT_PORTA_WIDTH > 0
nvicDisableVector(PINA_IRQn);
#endif
#if KINETIS_EXT_PORTB_WIDTH > 0
nvicDisableVector(PINB_IRQn);
#endif
#if KINETIS_EXT_PORTC_WIDTH > 0
nvicDisableVector(PINC_IRQn);
#endif
#if KINETIS_EXT_PORTD_WIDTH > 0
nvicDisableVector(PIND_IRQn);
#endif
#if KINETIS_EXT_PORTE_WIDTH > 0
nvicDisableVector(PINE_IRQn);
#endif
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*
* Generic interrupt handler.
*/
static inline void irq_handler(PORT_TypeDef * const port, const unsigned port_width, const uint8_t *channel_map) {
uint32_t isfr = port->ISFR;
/* Clear all pending interrupts on this port. */
port->ISFR = 0xFFFFFFFF;
for (unsigned pin = 0; pin < port_width; pin++) {
if (isfr & (1 << pin)) {
expchannel_t channel = channel_map[pin];
EXTD1.config->channels[channel].cb(&EXTD1, channel);
}
}
}
/**
* @brief PORTA interrupt handler.
*
* @isr
*/
#if defined(KINETIS_PORTA_IRQ_VECTOR) && KINETIS_EXT_PORTA_WIDTH > 0
OSAL_IRQ_HANDLER(KINETIS_PORTA_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
irq_handler(PORTA, KINETIS_EXT_PORTA_WIDTH, porta_channel_map);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_EXT_PORTA_WIDTH > 0 */
/**
* @brief PORTB interrupt handler.
*
* @isr
*/
#if defined(KINETIS_PORTB_IRQ_VECTOR) && KINETIS_EXT_PORTB_WIDTH > 0
OSAL_IRQ_HANDLER(KINETIS_PORTB_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
irq_handler(PORTB, KINETIS_EXT_PORTB_WIDTH, portb_channel_map);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_EXT_PORTB_WIDTH > 0 */
/**
* @brief PORTC interrupt handler.
*
* @isr
*/
#if defined(KINETIS_PORTC_IRQ_VECTOR) && KINETIS_EXT_PORTC_WIDTH > 0
OSAL_IRQ_HANDLER(KINETIS_PORTC_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
irq_handler(PORTC, KINETIS_EXT_PORTC_WIDTH, portc_channel_map);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_EXT_PORTC_WIDTH > 0 */
/**
* @brief PORTD interrupt handler.
*
* @isr
*/
#if defined(KINETIS_PORTD_IRQ_VECTOR) && KINETIS_EXT_PORTD_WIDTH > 0
OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
irq_handler(PORTD, KINETIS_EXT_PORTD_WIDTH, portd_channel_map);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_EXT_PORTD_WIDTH > 0 */
/**
* @brief PORTE interrupt handler.
*
* @isr
*/
#if defined(KINETIS_PORTE_IRQ_VECTOR) && KINETIS_EXT_PORTE_WIDTH > 0
CH_IRQ_HANDLER(KINETIS_PORTE_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
irq_handler(PORTE, KINETIS_EXT_PORTE_WIDTH, porte_channel_map);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_EXT_PORTE_WIDTH > 0 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level EXT driver initialization.
*
* @notapi
*/
void ext_lld_init(void) {
/* Driver initialization.*/
extObjectInit(&EXTD1);
}
/**
* @brief Configures and activates the EXT peripheral.
*
* @param[in] extp pointer to the @p EXTDriver object
*
* @notapi
*/
void ext_lld_start(EXTDriver *extp) {
if (extp->state == EXT_STOP)
ext_lld_exti_irq_enable();
/* Configuration of automatic channels.*/
for (expchannel_t channel = 0; channel < EXT_MAX_CHANNELS; channel++) {
uint32_t mode = extp->config->channels[channel].mode;
PORT_TypeDef *port = extp->config->channels[channel].port;
uint32_t pin = extp->config->channels[channel].pin;
/* Initialize the channel map */
#if KINETIS_EXT_PORTA_WIDTH > 0
if (port == PORTA)
porta_channel_map[pin] = channel;
else
#endif
#if KINETIS_EXT_PORTB_WIDTH > 0
if (port == PORTB)
portb_channel_map[pin] = channel;
else
#endif
#if KINETIS_EXT_PORTC_WIDTH > 0
if (port == PORTC)
portc_channel_map[pin] = channel;
else
#endif
#if KINETIS_EXT_PORTD_WIDTH > 0
if (port == PORTD)
portd_channel_map[pin] = channel;
else
#endif
#if KINETIS_EXT_PORTE_WIDTH > 0
if (port == PORTE)
porte_channel_map[pin] = channel;
else
#endif
{}
if (mode & EXT_CH_MODE_AUTOSTART)
ext_lld_channel_enable(extp, channel);
else if (port != NULL)
ext_lld_channel_disable(extp, channel);
}
}
/**
* @brief Deactivates the EXT peripheral.
*
* @param[in] extp pointer to the @p EXTDriver object
*
* @notapi
*/
void ext_lld_stop(EXTDriver *extp) {
if (extp->state == EXT_ACTIVE)
ext_lld_exti_irq_disable();
}
/**
* @brief Enables an EXT channel.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be enabled
*
* @notapi
*/
void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) {
uint32_t irqc;
uint32_t mode = extp->config->channels[channel].mode;
if (mode & EXT_CH_MODE_RISING_EDGE)
irqc = PCR_IRQC_RISING_EDGE;
else if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE)
irqc = PCR_IRQC_FALLING_EDGE;
else if (extp->config->channels[channel].mode & EXT_CH_MODE_BOTH_EDGES)
irqc = PCR_IRQC_EITHER_EDGE;
else
irqc = PCR_IRQC_DISABLED;
PORT_TypeDef *port = extp->config->channels[channel].port;
uint32_t pin = extp->config->channels[channel].pin;
uint32_t pcr = port->PCR[pin];
/* Clear all the IRQC bits */
pcr &= ~PORTx_PCRn_IRQC_MASK;
/* Set the required IRQC bits */
pcr |= PORTx_PCRn_IRQC(irqc);
port->PCR[pin] = pcr;
}
/**
* @brief Disables an EXT channel.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be disabled
*
* @notapi
*/
void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) {
PORT_TypeDef *port = extp->config->channels[channel].port;
uint32_t pin = extp->config->channels[channel].pin;
port->PCR[pin] |= PORTx_PCRn_IRQC(PCR_IRQC_DISABLED);
}
#endif /* HAL_USE_EXT */
/** @} */