Merge pull request #220 from twadleigh/twadleigh/pal-with-events

Added sync & callback interface to KINETIS/GPIOv1.
This commit is contained in:
Fabien Poussin 2020-08-18 20:13:21 +02:00 committed by GitHub
commit 2da018e0e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 465 additions and 97 deletions

View File

@ -31,6 +31,19 @@
/* Driver local definitions. */
/*===========================================================================*/
#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE)
#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
#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
@ -39,53 +52,226 @@
/* Driver local variables and types. */
/*===========================================================================*/
#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE)
palevent_t _pal_events[TOTAL_PORTS * PADS_PER_PORT];
#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */
/*===========================================================================*/
/* 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) {
#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE)
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;
}
}
#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */
return (port->PDIR & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW;
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE)
/*
* Generic interrupt handler.
*/
static inline void irq_handler(PORT_TypeDef* const port,
palevent_t* events) {
iopadid_t pad;
uint32_t pad_mask;
chSysLockFromISR();
uint32_t isfr = port->ISFR; /* Get pending interrupts on this port. */
port->ISFR = 0xFFFFFFFFU; /* Clear all pending interrupts on this port. */
chSysUnlockFromISR();
/* invoke callbacks for pending interrupts */
for (pad = 0, pad_mask = 1U;
pad < PAL_IOPORTS_WIDTH;
++pad, ++events, pad_mask <<= 1) {
if (events->cb && (isfr & pad_mask)) {
events->cb(events->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(PORTA, _pal_events);
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(PORTB, _pal_events + PAL_IOPORTS_WIDTH);
irq_handler(PORTC, _pal_events + PAL_IOPORTS_WIDTH * 2);
irq_handler(PORTD, _pal_events + PAL_IOPORTS_WIDTH * 3);
irq_handler(PORTE, _pal_events + PAL_IOPORTS_WIDTH * 4);
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(PORTC, _pal_events + PAL_IOPORTS_WIDTH * 2);
irq_handler(PORTD, _pal_events + PAL_IOPORTS_WIDTH * 3);
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(PORTB, _pal_events + PAL_IOPORTS_WIDTH);
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(PORTC, _pal_events + PAL_IOPORTS_WIDTH * 2);
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(PORTD, _pal_events + PAL_IOPORTS_WIDTH * 3);
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(PORTE, _pal_events + PAL_IOPORTS_WIDTH * 4);
OSAL_IRQ_EPILOGUE();
}
#endif /* defined(KINETIS_PORTE_IRQ_VECTOR) */
#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */
#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */
/*===========================================================================*/
/* 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);
}
}
/**
@ -105,7 +291,6 @@ void _pal_lld_writepad(ioportid_t port,
void _pal_lld_setpadmode(ioportid_t port,
uint8_t pad,
iomode_t mode) {
PORT_TypeDef *portcfg = NULL;
osalDbgAssert(pad < PADS_PER_PORT, "pal_lld_setpadmode() #1, invalid pad");
@ -179,67 +364,136 @@ 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) {
return (port->PDIR & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW;
}
int i, j;
/**
* @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) {
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)
/**
* @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 */
/** @} */

View File

@ -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,105 @@ 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 /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */
#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);
#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE)
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);
#endif /* (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) */
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_PAL */
#endif /* HAL_PAL_LLD_H_ */
#endif /* HAL_PAL_LLD_H */
/** @} */