diff --git a/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c index 3cba3080..189fc458 100644 --- a/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c +++ b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c @@ -31,6 +31,17 @@ /* 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. */ /*===========================================================================*/ @@ -39,53 +50,235 @@ /* Driver local variables and types. */ /*===========================================================================*/ +#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) +palevent_t _pal_events[TOTAL_PORTS * PADS_PER_PORT]; +#endif + /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ -/** - * @brief Reads a logical state from an I/O pad. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @return The logical state. - * @retval PAL_LOW low logical state. - * @retval PAL_HIGH high logical state. - * - * @notapi - */ -uint8_t _pal_lld_readpad(ioportid_t port, - uint8_t pad) { +static inline PORT_TypeDef* _pal_lld_ext_port(ioportid_t port) { + switch ((uint32_t)port) { + case GPIOA_BASE: + return PORTA; + case GPIOB_BASE: + return PORTB; + case GPIOC_BASE: + return PORTC; + case GPIOD_BASE: + return PORTD; + case GPIOE_BASE: + return PORTE; + default: + return NULL; + } +} - return (port->PDIR & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW; +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/* + * Generic interrupt handler. + */ +static inline void irq_handler(ioportid_t ioport, + PORT_TypeDef * const port) { + unsigned pin; + uint32_t isfr = port->ISFR; + + /* Clear all pending interrupts on this port. */ + port->ISFR = 0xFFFFFFFF; + + for (pin = 0; pin < PAL_IOPORTS_WIDTH; pin++) { + if (isfr & (1 << pin)) { + palevent_t* e = _pal_lld_get_pad_event(ioport, pin); + if (e && e->cb) { + e->cb(e->arg); + } + } + } } /** - * @brief Writes a logical state on an output pad. - * @note This function is not meant to be invoked directly by the - * application code. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. + * @brief PORTA interrupt handler. * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @param[in] bit logical value, the value must be @p PAL_LOW or - * @p PAL_HIGH + * @isr + */ +#if defined(KINETIS_PORTA_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTA_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT1, PORTA); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTA_IRQ_VECTOR) */ + +#if KINETIS_EXT_HAS_COMMON_BCDE_IRQ + +#if defined(KINETIS_PORTD_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT2, PORTB); + irq_handler(IOPORT3, PORTC); + irq_handler(IOPORT4, PORTD); + irq_handler(IOPORT5, PORTE); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTD_IRQ_VECTOR) */ + +#elif KINETIS_EXT_HAS_COMMON_CD_IRQ /* KINETIS_EXT_HAS_COMMON_BCDE_IRQ */ + +#if defined(KINETIS_PORTD_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT3, PORTC); + irq_handler(IOPORT4, PORTD); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTD_IRQ_VECTOR) */ + + +#else /* KINETIS_EXT_HAS_COMMON_CD_IRQ */ + +/** + * @brief PORTB interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTB_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTB_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT2, PORTB); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_EXT_PORTB_IRQ_VECTOR */ + +/** + * @brief PORTC interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTC_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTC_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT3, PORTC); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTC_IRQ_VECTOR) */ + +/** + * @brief PORTD interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTD_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT4, PORTD); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTD_IRQ_VECTOR) */ + +/** + * @brief PORTE interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTE_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTE_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(IOPORT5, PORTE); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTE_IRQ_VECTOR) */ + +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Kinetis I/O ports configuration. + * @details Ports A-E clocks enabled. + * + * @param[in] config the Kinetis ports configuration * * @notapi */ -void _pal_lld_writepad(ioportid_t port, - uint8_t pad, - uint8_t bit) { +void _pal_lld_init(const PALConfig *config) { + int i, j; - if (bit == PAL_HIGH) - port->PDOR |= ((uint32_t) 1 << pad); - else - port->PDOR &= ~((uint32_t) 1 << pad); + /* Enable clocking on all Ports */ + SIM->SCGC5 |= SIM_SCGC5_PORTA | + SIM_SCGC5_PORTB | + SIM_SCGC5_PORTC | + SIM_SCGC5_PORTD | + SIM_SCGC5_PORTE; + + /* Initial PORT and GPIO setup */ + for (i = 0; i < TOTAL_PORTS; i++) { + for (j = 0; j < PADS_PER_PORT; j++) { + pal_lld_setpadmode(config->ports[i].port, + j, + config->ports[i].pads[j]); +#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) + _pal_init_event(PADS_PER_PORT * i + j); +#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */ + } + } + +#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) + nvicEnableVector(PINA_IRQn, KINETIS_EXT_PORTA_IRQ_PRIORITY); +#if KINETIS_EXT_HAS_COMMON_BCDE_IRQ + nvicEnableVector(PINBCDE_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); +#elif KINETIS_EXT_HAS_COMMON_CD_IRQ /* KINETIS_EXT_HAS_COMMON_BCDE_IRQ */ + nvicEnableVector(PINCD_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); +#else /* KINETIS_EXT_HAS_COMMON_CD_IRQ */ + nvicEnableVector(PINB_IRQn, KINETIS_EXT_PORTB_IRQ_PRIORITY); + nvicEnableVector(PINC_IRQn, KINETIS_EXT_PORTC_IRQ_PRIORITY); + nvicEnableVector(PIND_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); + nvicEnableVector(PINE_IRQn, KINETIS_EXT_PORTE_IRQ_PRIORITY); +#endif /* KINETIS_EXT_HAS_COMMON_BCDE_IRQ */ +#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */ +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) { + + int i; + + (void)mask; + + for (i = 0; i < PADS_PER_PORT; i++) { + pal_lld_setpadmode(port, i, mode); + } } /** @@ -179,67 +372,138 @@ void _pal_lld_setpadmode(ioportid_t port, } } -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - /** - * @brief Kinetis I/O ports configuration. - * @details Ports A-E clocks enabled. + * @brief Reads a logical state from an I/O pad. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. * - * @param[in] config the Kinetis ports configuration + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @return The logical state. + * @retval PAL_LOW low logical state. + * @retval PAL_HIGH high logical state. * * @notapi */ -void _pal_lld_init(const PALConfig *config) { +uint8_t _pal_lld_readpad(ioportid_t port, + uint8_t pad) { - int i, j; + return (port->PDIR & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW; +} - /* Enable clocking on all Ports */ - SIM->SCGC5 |= SIM_SCGC5_PORTA | - SIM_SCGC5_PORTB | - SIM_SCGC5_PORTC | - SIM_SCGC5_PORTD | - SIM_SCGC5_PORTE; +/** + * @brief Writes a logical state on an output pad. + * @note This function is not meant to be invoked directly by the + * application code. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +void _pal_lld_writepad(ioportid_t port, + uint8_t pad, + uint8_t bit) { - /* Initial PORT and GPIO setup */ - for (i = 0; i < TOTAL_PORTS; i++) { - for (j = 0; j < PADS_PER_PORT; j++) { - pal_lld_setpadmode(config->ports[i].port, - j, - config->ports[i].pads[j]); + if (bit == PAL_HIGH) + port->PDOR |= ((uint32_t) 1 << pad); + else + port->PDOR &= ~((uint32_t) 1 << pad); +} + +#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) +/** + * @brief Returns a PAL event structure associated to a pad. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +palevent_t* _pal_lld_get_pad_event(ioportid_t port, + iopadid_t pad) { + switch ((uint32_t)port) { + case GPIOA_BASE: + return &_pal_events[pad]; + case GPIOB_BASE: + return &_pal_events[PADS_PER_PORT + pad]; + case GPIOC_BASE: + return &_pal_events[PADS_PER_PORT * 2 + pad]; + case GPIOD_BASE: + return &_pal_events[PADS_PER_PORT * 3 + pad]; + case GPIOE_BASE: + return &_pal_events[PADS_PER_PORT * 4 + pad]; + default: + return NULL; } - } } /** - * @brief Pads mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. + * @brief Enables a pad event. * - * @param[in] port the port identifier - * @param[in] mask the group mask - * @param[in] mode the mode + * @param[in] port port containing pad whose event is to be enabled + * @param[in] pad pad whose event is to be enabled + * @param[in] mode mode of the event * * @notapi */ -void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode) { - - int i; - - (void)mask; - - for (i = 0; i < PADS_PER_PORT; i++) { - pal_lld_setpadmode(port, i, mode); - } +void _pal_lld_enablepadevent(ioportid_t ioport, + iopadid_t pad, + iomode_t mode) { + PORT_TypeDef *port = _pal_lld_ext_port(ioport); + iomode_t pcr_mode; + switch(mode) { + case PAL_EVENT_MODE_RISING_EDGE: + pcr_mode = PCR_IRQC_RISING_EDGE; + break; + case PAL_EVENT_MODE_FALLING_EDGE: + pcr_mode = PCR_IRQC_FALLING_EDGE; + break; + case PAL_EVENT_MODE_BOTH_EDGES: + pcr_mode = PCR_IRQC_EITHER_EDGE; + break; + case PAL_EVENT_MODE_DISABLED: + pcr_mode = PCR_IRQC_DISABLED; + break; + default: + return; + } + port->PCR[pad] |= PORTx_PCRn_IRQC(pcr_mode); } +/** + * @brief Disables a pad event + * + * @param[in] ioport port containing pad whose event is to be disabled + * @param[in] pad pad whose event is to be disabled + * + * @notapi + */ +void _pal_lld_disablepadevent(ioportid_t ioport, iopadid_t pad) { + _pal_lld_enablepadevent(ioport, pad, PAL_EVENT_MODE_DISABLED); +} + +/** + * @brief Returns whether a pad event is enabled + * + * @param[in] ioport port containing the pad + * @param[in] pad pad whose event is to be queried + * + * @notapi + */ +bool _pal_lld_ispadeventenabled(ioportid_t ioport, iopadid_t pad) { + PORT_TypeDef *port = _pal_lld_ext_port(ioport); + uint32_t pcr = port->PCR[pad]; + return ((pcr & PORTx_PCRn_IRQC_MASK) >> PORTx_PCRn_IRQC_SHIFT) != + PCR_IRQC_DISABLED; +} +#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */ #endif /* HAL_USE_PAL */ /** @} */ diff --git a/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h index 376c0a63..2847ce9e 100644 --- a/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h +++ b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h @@ -22,10 +22,10 @@ * @{ */ -#ifndef HAL_PAL_LLD_H_ -#define HAL_PAL_LLD_H_ +#ifndef HAL_PAL_LLD_H +#define HAL_PAL_LLD_H -#if HAL_USE_PAL || defined(__DOXYGEN__) +#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) /*===========================================================================*/ /* Unsupported modes and specific modes */ @@ -45,19 +45,59 @@ /* I/O Ports Types and constants. */ /*===========================================================================*/ -#define TOTAL_PORTS 5 -#define PADS_PER_PORT 32 +/** + * @name Configuration options + * @{ + */ +/** + * @brief PORTA interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTA_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTB interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTB_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTB_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTC interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTC_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTD interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTD_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTD_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTE interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTE_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTE_IRQ_PRIORITY 3 +#endif +/** @} */ + +#define TOTAL_PORTS 5U +#define PADS_PER_PORT 32U /** * @brief Width, in bits, of an I/O port. */ -#define PAL_IOPORTS_WIDTH 32 +#define PAL_IOPORTS_WIDTH 32U /** * @brief Whole port mask. * @brief This macro specifies all the valid bits into a port. */ -#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFF) +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFFU) /** * @brief Digital I/O port sized unsigned type. @@ -398,31 +438,103 @@ typedef struct { #define pal_lld_setpadmode(port, pad, mode) \ _pal_lld_setpadmode(port, pad, mode) +/** + * @brief Returns a PAL event structure associated to a pad. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_get_pad_event(port, pad) \ + _pal_lld_get_pad_event(port, pad) + +/** + * @brief Returns a PAL event structure associated to a line. + * + * @param[in] line line identifier + * + * @notapi + */ +#define pal_lld_get_line_event(line) \ + pal_lld_get_pad_event(PAL_PORT(line), PAL_PAD(line)) + +/** + * @brief Pad event enable. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] mode pad event mode + * + * @notapi + */ +#define pal_lld_enablepadevent(port, pad, mode) \ + _pal_lld_enablepadevent(port, pad, mode) + +/** + * @brief Pad event disable. + * @details This function disables previously programmed event callbacks. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_disablepadevent(port, pad) \ + _pal_lld_disablepadevent(port, pad) + +/** + * @brief Pad event enable check. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @return Pad event status. + * @retval false if the pad event is disabled. + * @retval true if the pad event is enabled. + * + * @notapi + */ +#define pal_lld_ispadeventenabled(port, pad) \ + _pal_lld_ispadeventenabled(port, pad) + #if !defined(__DOXYGEN__) extern const PALConfig pal_default_config; +#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) +extern palevent_t _pal_events[TOTAL_PORTS * PADS_PER_PORT]; +#endif #endif #ifdef __cplusplus extern "C" { #endif - void _pal_lld_init(const PALConfig *config); - void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); + void _pal_lld_setpadmode(ioportid_t port, + uint8_t pad, iomode_t mode); - void _pal_lld_setpadmode(ioportid_t port, + uint8_t _pal_lld_readpad(ioportid_t port, + uint8_t pad); + void _pal_lld_writepad(ioportid_t port, uint8_t pad, - iomode_t mode); - uint8_t _pal_lld_readpad(ioportid_t port, - uint8_t pad); - void _pal_lld_writepad(ioportid_t port, - uint8_t pad, - uint8_t bit); + uint8_t bit); + palevent_t* _pal_lld_get_pad_event(ioportid_t port, + iopadid_t pad); + void _pal_lld_enablepadevent(ioportid_t port, + iopadid_t pad, + iomode_t mode); + void _pal_lld_disablepadevent(ioportid_t port, + iopadid_t pad); + bool _pal_lld_ispadeventenabled(ioportid_t port, + iopadid_t pad); #ifdef __cplusplus } #endif #endif /* HAL_USE_PAL */ -#endif /* HAL_PAL_LLD_H_ */ +#endif /* HAL_PAL_LLD_H */ /** @} */