diff --git a/os/hal/ports/AVR/avr_pins.h b/os/hal/ports/AVR/avr_pins.h index b89325aff..bcd5a10e2 100644 --- a/os/hal/ports/AVR/avr_pins.h +++ b/os/hal/ports/AVR/avr_pins.h @@ -91,4 +91,69 @@ #endif /* AVR_ADC_USE_ADC1 */ +#if AVR_EXT_USE_PCINT0 +#if defined(__AVR_ATmega162__) + #define PCINT0_PIN PINA +#elif defined(__AVR_ATmega328P__) || \ + defined(__AVR_ATmega1280__) || \ + defined(__AVR_ATmega2560__) + #define PCINT0_PIN PINB +#else + #warning "Device not supported by EXT driver" +#endif +#endif /* AVR_EXT_USE_PCINT0 */ + +#if AVR_EXT_USE_PCINT1 +#if defined(__AVR_ATmega162__) || \ + defined(__AVR_ATmega328P__) + #define PCINT1_PIN PINC +#elif defined(__AVR_ATmega1280__) || \ + defined(__AVR_ATmega2560__) + #define PCINT1_PIN PINE +#else + #warning "Device not supported by EXT driver" +#endif +#endif /* AVR_EXT_USE_PCINT1 */ + +#if AVR_EXT_USE_PCINT2 +#if defined(__AVR_ATmega1280__) || \ + defined(__AVR_ATmega2560__) + #define PCINT2_PIN PINK +#else + #warning "Device not supported by EXT driver" +#endif +#endif /* AVR_EXT_USE_PCINT2 */ + +#if AVR_EXT_USE_PCINT3 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT3 */ + +#if AVR_EXT_USE_PCINT4 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT4 */ + +#if AVR_EXT_USE_PCINT5 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT5 */ + +#if AVR_EXT_USE_PCINT6 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT6 */ + +#if AVR_EXT_USE_PCINT7 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT7 */ + +#if AVR_EXT_USE_PCINT8 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT8 */ + +#if AVR_EXT_USE_PCINT9 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT9 */ + +#if AVR_EXT_USE_PCINT10 +#warning "Device not supported by EXT driver" +#endif /* AVR_EXT_USE_PCINT10 */ + #endif /* _AVR_PINS_H_ */ diff --git a/os/hal/ports/AVR/hal_ext_lld.c b/os/hal/ports/AVR/hal_ext_lld.c index 225bd7e15..6eccf924a 100644 --- a/os/hal/ports/AVR/hal_ext_lld.c +++ b/os/hal/ports/AVR/hal_ext_lld.c @@ -1,5 +1,7 @@ /* - ChibiOS - Copyright (C) 2016 Theodore Ateba + EXT Low Level Driver for ChibiOS + Copyright (C) 2015 Igor Stoppa + Copyright (C) 2016 Theodore Ateba Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,8 +17,8 @@ */ /** - * @file hal_ext_lld.c - * @brief AVR EXT subsystem low level driver source. + * @file AVR/hal_ext_lld.c + * @brief AVR EXT subsystem low level driver source. * * @addtogroup EXT * @{ @@ -30,12 +32,69 @@ /* Driver local definitions. */ /*===========================================================================*/ +#define EXT_EICRA_LOW_LEVEL 0 +#define EXT_EICRA_BOTH_EDGES 1 +#define EXT_EICRA_FALLING_EDGE 2 +#define EXT_EICRA_RISING_EDGE 3 + +/** + * @brief Declares the isr for the ext channel specified + * + * @param[in] channel number of the channel + * + * @notapi + */ +#define declare_extint_isr(channel) \ +OSAL_IRQ_HANDLER(INT##channel##_vect) \ +{ \ + OSAL_IRQ_PROLOGUE(); \ + EXTD1.config->channels[EXT_INT##channel##_CHANNEL]. \ + cb(&EXTD1, EXT_INT##channel##_CHANNEL); \ + OSAL_IRQ_EPILOGUE(); \ +} + +/** + * @brief Declares the isr for the pc channel specified + * + * @param[in] port number of the port + * + * @notapi + */ +#define declare_pcint_isr(index) \ +OSAL_IRQ_HANDLER(PCINT##index##_vect) { \ + uint8_t changed_pins; \ + uint8_t i; \ + \ + OSAL_IRQ_PROLOGUE(); \ + EXTD1.pc_current_values[index] = (*(PINS[index])) & (*(PCMSK[index])); \ + \ + /* XOR to find the changed pin(s) */ \ + changed_pins = EXTD1.pc_current_values[index] ^ EXTD1.pc_old_values[index];\ + \ + for (i = 0; i < 8; i++) { \ + if (changed_pins & (1 << i)) { \ + const EXTChannelConfig *chn = \ + &EXTD1.config->channels[EXT_PCINT##index##_INDEX + i]; \ + \ + if (((chn->mode & EXT_CH_MODE_RISING_EDGE) && \ + ((EXTD1.pc_current_values[index] & (1 << i)) > 0)) || \ + ((chn->mode & EXT_CH_MODE_FALLING_EDGE) && \ + ((EXTD1.pc_current_values[index] & (1 << i)) == 0))) { \ + chn->cb(&EXTD1, EXT_PCINT##index##_INDEX + i - EXT_PCINT_MIN_INDEX); \ + } \ + } \ + } \ + \ + EXTD1.pc_old_values[index] = EXTD1.pc_current_values[index]; \ + OSAL_IRQ_EPILOGUE(); \ +} + /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ /** - * @brief EXTD1 driver identifier. + * @brief EXT1 driver identifier. */ EXTDriver EXTD1; @@ -43,224 +102,365 @@ EXTDriver EXTD1; /* Driver local variables and types. */ /*===========================================================================*/ +#if EXT_PC_NUM_PORTS > 0 +/** + * @brief Vector with addresses of Ports available. + */ +volatile uint8_t * const PINS[EXT_PC_NUM_PORTS] = { +#if AVR_EXT_USE_PCINT0 + (volatile uint8_t *const)&PCINT0_PIN, +#endif +#if AVR_EXT_USE_PCINT1 + (volatile uint8_t *const)&PCINT1_PIN, +#endif +#if AVR_EXT_USE_PCINT2 +(volatile uint8_t *const)&PCINT2_PIN, +#endif +#if AVR_EXT_USE_PCINT3 +(volatile uint8_t *const)&PCINT3_PIN, +#endif +#if AVR_EXT_USE_PCINT4 +(volatile uint8_t *const)&PCINT4_PIN, +#endif +#if AVR_EXT_USE_PCINT5 +(volatile uint8_t *const)&PCINT5_PIN, +#endif +#if AVR_EXT_USE_PCINT6 +(volatile uint8_t *const)&PCINT6_PIN, +#endif +#if AVR_EXT_USE_PCINT7 +(volatile uint8_t *const)&PCINT7_PIN, +#endif +#if AVR_EXT_USE_PCINT8 +(volatile uint8_t *const)&PCINT8_PIN, +#endif +#if AVR_EXT_USE_PCINT9 +(volatile uint8_t *const)&PCINT9_PIN, +#endif +#if AVR_EXT_USE_PCINT10 +(volatile uint8_t *const)&PCINT10_PIN, +#endif +}; + +/** + * @brief Vector with addresses of Port Masks available. + */ +volatile uint8_t * const PCMSK[EXT_PC_NUM_PORTS] = { +#if AVR_EXT_USE_PCINT0 + (volatile uint8_t *const)&PCMSK0, +#endif +#if AVR_EXT_USE_PCINT1 + (volatile uint8_t *const)&PCMSK1, +#endif +#if AVR_EXT_USE_PCINT2 + (volatile uint8_t *const)&PCMSK2, +#endif +#if AVR_EXT_USE_PCINT3 + (volatile uint8_t *const)&PCMSK3, +#endif +#if AVR_EXT_USE_PCINT4 + (volatile uint8_t *const)&PCMSK4, +#endif +#if AVR_EXT_USE_PCINT5 + (volatile uint8_t *const)&PCMSK5, +#endif +#if AVR_EXT_USE_PCINT6 + (volatile uint8_t *const)&PCMSK6, +#endif +#if AVR_EXT_USE_PCINT7 + (volatile uint8_t *const)&PCMSK7, +#endif +#if AVR_EXT_USE_PCINT8 + (volatile uint8_t *const)&PCMSK8, +#endif +#if AVR_EXT_USE_PCINT9 + (volatile uint8_t *const)&PCMSK9, +#endif +#if AVR_EXT_USE_PCINT10 + (volatile uint8_t *const)&PCMSK10, +#endif +}; +#endif + /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ +#if EXT_PC_NUM_PORTS > 0 /** - * @brief Set the INTx interrupt trigger front or state. + * @brief Configures and activates the Pin Change inputs. * - * @param[in] channel the channel to configure - * @param[in] edge the front or state to configure + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi */ -void ext_lld_set_intx_edges(expchannel_t channel, uint8_t edge) { -#if AVR_EXT_USE_INT0 || defined(__DOXYGEN__) - if (channel == INT0) { - if (edge == EXT_CH_MODE_RISING_EDGE) { - EICRA |= (1 << 0); - EICRA |= (1 << 1); - } - else if (edge == EXT_CH_MODE_FALLING_EDGE) { - EICRA &= ~(1 << 0); - EICRA |= (1 << 1); - } - else if (edge == EXT_CH_MODE_BOTH_EDGES) { - EICRA |= (1 << 0); - EICRA &= ~(1 << 1); - } - else if (edge == EXT_CH_MODE_LOW_LEVEL) { - EICRA &= ~(1 << 0); - EICRA &= ~(1 << 1); +static void start_pc(EXTDriver *extp) { + uint8_t icr = 0; + uint8_t i; + + /* For every pin */ + for (i = 0; i < EXT_PC_NUM_CHANNELS; i++) { + uint8_t mode = extp->config->channels[i + EXT_PC_MIN_CHANNEL].mode; + + /* Only start if autostart and not disabled */ + if ((mode & EXT_CH_MODE_AUTOSTART) && ((mode & EXT_CH_MODE_EDGES_MASK) != EXT_CH_MODE_DISABLED)) { + (*(PCMSK[i/8])) |= _BV(i & 0x07); } } -#endif -#if AVR_EXT_USE_INT1 || defined(__DOXYGEN__) - if (channel == INT1) { - if (edge == EXT_CH_MODE_RISING_EDGE) { - EICRA |= (1 << 2); - EICRA |= (1 << 3); - } - else if (edge == EXT_CH_MODE_FALLING_EDGE) { - EICRA &= ~(1 << 2); - EICRA |= (1 << 3); - } - else if (edge == EXT_CH_MODE_BOTH_EDGES) { - EICRA |= (1 << 2); - EICRA &= ~(1 << 3); - } - else if (edge == EXT_CH_MODE_LOW_LEVEL) { - EICRA &= ~(1 << 2); - EICRA &= ~(1 << 3); - } - } -#endif -#if AVR_EXT_USE_INT2 || defined(__DOXYGEN__) - if (channel == INT2) { - if (edge == EXT_CH_MODE_RISING_EDGE) { - EICRA |= (1 << 4); - EICRA |= (1 << 5); - } - else if (edge == EXT_CH_MODE_FALLING_EDGE) { - EICRA &= ~(1 << 4); - EICRA |= (1 << 5); - } - else if (edge == EXT_CH_MODE_BOTH_EDGES) { - EICRA |= (1 << 4); - EICRA &= ~(1 << 5); - } - else if (edge == EXT_CH_MODE_LOW_LEVEL) { - EICRA &= ~(1 << 4); - EICRA &= ~(1 << 5); - } - } -#endif -#if AVR_EXT_USE_INT3 || defined(__DOXYGEN__) - if (channel == INT3) { - if (edge == EXT_CH_MODE_RISING_EDGE) { - EICRA |= (1 << 6); - EICRA |= (1 << 7); - } - else if (edge == EXT_CH_MODE_FALLING_EDGE) { - EICRA &= ~(1 << 6); - EICRA |= (1 << 7); - } - else if (edge == EXT_CH_MODE_BOTH_EDGES) { - EICRA |= (1 << 6); - EICRA &= ~(1 << 7); - } - else if (edge == EXT_CH_MODE_LOW_LEVEL) { - EICRA &= ~(1 << 6); - EICRA &= ~(1 << 7); - } - } -#endif -#if AVR_EXT_USE_INT4 || defined(__DOXYGEN__) - if (channel == INT4) { - if (edge == EXT_CH_MODE_RISING_EDGE) { - EICRB |= (1 << 0); - EICRB |= (1 << 1); - } - else if (edge == EXT_CH_MODE_FALLING_EDGE) { - EICRB &= ~(1 << 0); - EICRB |= (1 << 1); - } - else if (edge == EXT_CH_MODE_BOTH_EDGES) { - EICRB |= (1 << 0); - EICRB &= ~(1 << 1); - } - else if (edge == EXT_CH_MODE_LOW_LEVEL) { - EICRB &= ~(1 << 0); - EICRB &= ~(1 << 1); - } - } -#endif -#if AVR_EXT_USE_INT5 || defined(__DOXYGEN__) - if (channel == INT5) { - if (edge == EXT_CH_MODE_RISING_EDGE) { - EICRB |= (1 << 2); - EICRB |= (1 << 3); - } - else if (edge == EXT_CH_MODE_FALLING_EDGE) { - EICRB &= ~(1 << 2); - EICRB |= (1 << 3); - } - else if (edge == EXT_CH_MODE_BOTH_EDGES) { - EICRB |= (1 << 2); - EICRB &= ~(1 << 3); - } - else if (edge == EXT_CH_MODE_LOW_LEVEL) { - EICRB &= ~(1 << 2); - EICRB &= ~(1 << 3); + + /* For every port */ + for (i = 0; i < EXT_PC_NUM_PORTS; i++) { + /* Only enable interrupt if at least 1 bit in the mask is set */ + if ((*(PCMSK[i])) != 0) { + /* Enable interrupt */ + icr |= (_BV(i)); } } + + /* Enables/disables the peripheral, as requested. */ +#if defined(__AVR_ATmega162__) + GICR &= ~(0x03 << 3); + GICR |= (icr << 3); +#else + PCICR = icr; #endif } +/** + * @brief Deactivates the PC interrupts. + * + * @param[in] extp pointer to the @p EXTDriver object + */ +static void stop_pc(EXTDriver *extp) { + uint8_t i; + (void)extp; + + /* Disable pin change interrupts */ +#if defined(__AVR_ATmega162__) + GICR &= ~(0x03 << 3); +#else + PCICR = 0; +#endif + + /* Clear masks */ + for (i = 0; i < EXT_PC_NUM_PORTS; i++) { + (*(PCMSK[i])) = 0; + } +} +#endif + +#if EXT_INT_NUM_CHANNELS > 0 +/** + * @brief Configures and activates the INT inputs. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +static void start_ext(EXTDriver *extp) { +#if EXT_INT_NUM_CHANNELS < 4 + uint8_t icr = 0; +#else + uint16_t icr = 0; +#endif + uint8_t msk = 0; + for (expchannel_t channel = EXT_INT_MIN_CHANNEL; + channel <= EXT_INT_MAX_CHANNEL; channel++) { + /* Determines the triggering condition for each channel. */ + switch(extp->config->channels[channel].mode & + ~(EXT_CH_MODE_AUTOSTART | EXT_CH_MODE_INTERNAL_PULLUP)) { + case EXT_CH_MODE_LOW_LEVEL: + icr |= (EXT_EICRA_LOW_LEVEL << (2 * (channel - EXT_INT_MIN_CHANNEL))); + break; + case EXT_CH_MODE_BOTH_EDGES: + icr |= (EXT_EICRA_BOTH_EDGES << (2 * (channel - EXT_INT_MIN_CHANNEL))); + break; + case EXT_CH_MODE_RISING_EDGE: + icr |= (EXT_EICRA_RISING_EDGE << (2 * (channel - EXT_INT_MIN_CHANNEL))); + break; + case EXT_CH_MODE_FALLING_EDGE: + icr |= (EXT_EICRA_FALLING_EDGE << (2 * (channel - EXT_INT_MIN_CHANNEL))); + break; + default: osalDbgAssert(FALSE, "unsupported mode"); + } + + /* Determines which channel must be started right away. */ + if (extp->config->channels[channel].mode & EXT_CH_MODE_AUTOSTART) { + msk |= (1 << (channel - EXT_INT_MIN_CHANNEL)); + } + } + /* Configures the peripheral. */ +#if defined(__AVR_ATmega162__) + MCUCR |= (icr & 0x0f); + + icr >>= 4; + osalDbgAssert(((icr & 0x02) == EXT_EICRA_RISING_EDGE) || ((icr & 0x02) == EXT_EICRA_FALLING_EDGE), "INT2 only supports rising or falling edge, not both."); + EMCUCR |= icr & 0x01; + + GICR |= ((msk & 0x03) << 6); + if (icr & 0x01) { + /* Enable INT2 */ + GICR |= (1 << 5); + } +#else + EICRA = icr & 0xff; +#if EXT_INT_NUM_CHANNELS > 4 + EICRB = icr >> 8; +#endif + /* Enables/disables the peripheral, as requested. */ + EIMSK = msk; +#endif +} + +/** + * @brief Deactivates the INT interrupts. + * + * @param[in] extp pointer to the @p EXTDriver object + */ +static void stop_ext(EXTDriver *extp) { + (void)extp; +#if defined(__AVR_ATmega162__) + MCUCR &= ~(0x0f); + EMCUCR &= ~(0x01); + GICR |= ~(0x07 << 5); +#else + EICRA = 0; +#if EXT_INT_NUM_CHANNELS > 4 + EICRB = 0; +#endif + /* Enables/disables the peripheral, as requested. */ + EIMSK = 0; +#endif +} +#endif + /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ -#if AVR_EXT_USE_INT0 || defined(__DOXYGEN__) -/** - * @brief EXTI[INT0] interrupt handler. - * - * @isr +/* + * Interrupt handlers for PC-type interrupts. */ -OSAL_IRQ_HANDLER(INT0_vect) { - OSAL_IRQ_PROLOGUE(); - EXTD1.config->channels[INT0].cb(&EXTD1, INT0); - OSAL_IRQ_EPILOGUE(); -} +#define EXT_PCINT_MIN_INDEX EXT_PC_MIN_PORT + +#if 0 < EXT_PC_NUM_PORTS +#define EXT_PCINT0_INDEX EXT_PCINT_MIN_INDEX +declare_pcint_isr(0); +#endif +#if 1 < EXT_PC_NUM_PORTS +#define EXT_PCINT1_INDEX (EXT_PCINT0_INDEX + 1) +declare_pcint_isr(1); +#endif +#if 2 < EXT_PC_NUM_PORTS +#define EXT_PCINT2_INDEX (EXT_PCINT1_INDEX + 1) +declare_pcint_isr(2); +#endif +#if 3 < EXT_PC_NUM_PORTS +#define EXT_PCINT3_INDEX (EXT_PCINT2_INDEX + 1) +declare_pcint_isr(3); +#endif +#if 4 < EXT_PC_NUM_PORTS +#define EXT_PCINT4_INDEX (EXT_PCINT3_INDEX + 1) +declare_pcint_isr(4); +#endif +#if 5 < EXT_PC_NUM_PORTS +#define EXT_PCINT5_INDEX (EXT_PCINT4_INDEX + 1) +declare_pcint_isr(5); +#endif +#if 6 < EXT_PC_NUM_PORTS +#define EXT_PCINT6_INDEX (EXT_PCINT5_INDEX + 1) +declare_pcint_isr(6); +#endif +#if 7 < EXT_PC_NUM_PORTS +#define EXT_PCINT7_INDEX (EXT_PCINT6_INDEX + 1) +declare_pcint_isr(7); +#endif +#if 8 < EXT_PC_NUM_PORTS +#define EXT_PCINT8_INDEX (EXT_PCINT7_INDEX + 1) +declare_pcint_isr(8); +#endif +#if 9 < EXT_PC_NUM_PORTS +#define EXT_PCINT9_INDEX (EXT_PCINT8_INDEX + 1) +declare_pcint_isr(9); #endif -#if AVR_EXT_USE_INT1 || defined(__DOXYGEN__) -/** - * @brief EXTI[INT1] interrupt handler. - * - * @isr +/* + * Interrupt handlers for INT-type interrupts. */ -OSAL_IRQ_HANDLER(INT1_vect) { - OSAL_IRQ_PROLOGUE(); - EXTD1.config->channels[INT1].cb(&EXTD1, INT1); - OSAL_IRQ_EPILOGUE(); -} +#if 0 < EXT_INT_NUM_CHANNELS +declare_extint_isr(0); #endif - -#if AVR_EXT_USE_INT2 || defined(__DOXYGEN__) -/** - * @brief EXTI[INT2] interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(INT2_vect) { - OSAL_IRQ_PROLOGUE(); - EXTD1.config->channels[INT2].cb(&EXTD1, INT2); - OSAL_IRQ_EPILOGUE(); -} +#if 1 < EXT_INT_NUM_CHANNELS +declare_extint_isr(1); #endif - -#if AVR_EXT_USE_INT3 || defined(__DOXYGEN__) -/** - * @brief EXTI[INT3] interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(INT3_vect) { - OSAL_IRQ_PROLOGUE(); - EXTD1.config->channels[INT3].cb(&EXTD1, INT3); - OSAL_IRQ_EPILOGUE(); -} +#if 2 < EXT_INT_NUM_CHANNELS +declare_extint_isr(2); #endif - -#if AVR_EXT_USE_INT4 || defined(__DOXYGEN__) -/** - * @brief EXTI[INT4] interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(INT4_vect) { - OSAL_IRQ_PROLOGUE(); - EXTD1.config->channels[INT4].cb(&EXTD1, INT4); - OSAL_IRQ_EPILOGUE(); -} +#if 3 < EXT_INT_NUM_CHANNELS +declare_extint_isr(3); #endif -#if AVR_EXT_USE_INT5 || defined(__DOXYGEN__) -/** - * @brief EXTI[INT5] interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(INT5_vect) { - OSAL_IRQ_PROLOGUE(); - EXTD1.config->channels[INT5].cb(&EXTD1, INT5); - OSAL_IRQ_EPILOGUE(); -} +#if 4 < EXT_INT_NUM_CHANNELS +declare_extint_isr(4); +#endif +#if 5 < EXT_INT_NUM_CHANNELS +declare_extint_isr(5); #endif /*===========================================================================*/ /* Driver functions. */ /*===========================================================================*/ +/** + * @brief Low level EXT driver initialization. + * + * @notapi + */ +void ext_lld_init(void) { + /* Driver initialization.*/ + extObjectInit(&EXTD1); +#if EXT_PC_NUM_PORTS > 0 + for (int i = 0; i < EXT_PC_NUM_PORTS; i++) { + EXTD1.pc_old_values[i] = 0; + } +#endif +} + +/** + * @brief Configures and activates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_start(EXTDriver *extp) { +#if EXT_INT_NUM_CHANNELS > 0 + start_ext(extp); +#endif +#if EXT_PC_NUM_PORTS > 0 + start_pc(extp); +#endif +} + +/** + * @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) { + /* Disables the peripheral.*/ +#if EXT_INT_NUM_CHANNELS > 0 + stop_ext(extp); +#endif +#if EXT_PC_NUM_PORTS > 0 + stop_pc(extp); +#endif + } +} + /** * @brief Enables an EXT channel. * @@ -270,40 +470,33 @@ OSAL_IRQ_HANDLER(INT5_vect) { * @notapi */ void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { -#if AVR_EXT_USE_INT0 || defined(__DOXYGEN__) - if (channel == INT0) { - EIMSK |= 1 << INT0; - ext_lld_set_intx_edges(channel, extp->config->channels[channel].mode); + (void)extp; +#if EXT_PC_NUM_CHANNELS > 0 + if (EXT_PC_MIN_CHANNEL <= channel && channel <= EXT_PC_MAX_CHANNEL) { + uint8_t port = (channel - EXT_PC_MIN_CHANNEL) / 8; + + /* Enable bit in mask */ + (*(PCMSK[port])) |= _BV((channel - EXT_PC_MIN_CHANNEL) % 8); + + /* Always enable interrupt */ +#if defined(__AVR_ATmega162__) + GICR |= (_BV(port) << 3); +#else + PCICR |= _BV(port); +#endif } #endif -#if AVR_EXT_USE_INT1 || defined(__DOXYGEN__) - if (channel == INT1) { - EIMSK |= 1 << INT1; - ext_lld_set_intx_edges(channel, extp->config->channels[channel].mode); - } +#if EXT_PC_NUM_CHANNELS > 0 && EXT_INT_NUM_CHANNELS > 0 + else #endif -#if AVR_EXT_USE_INT2 || defined(__DOXYGEN__) - if (channel == INT2) { - EIMSK |= 1 << INT2; - ext_lld_set_intx_edges(channel, extp->config->channels[channel].mode); - } +#if EXT_INT_NUM_CHANNELS > 0 + if (channel <= EXT_INT_MAX_CHANNEL) { +#if defined(__AVR_ATmega162__) + GICR |= ((1 << channel) << 5); +#else + /* Enables/disables the peripheral, as requested. */ + EIMSK |= (1 << channel); #endif -#if AVR_EXT_USE_INT3 || defined(__DOXYGEN__) - if (channel == INT3) { - EIMSK |= 1 << INT3; - ext_lld_set_intx_edges(channel, extp->config->channels[channel].mode); - } -#endif -#if AVR_EXT_USE_INT4 || defined(__DOXYGEN__) - if (channel == INT4) { - EIMSK |= 1 << INT4; - ext_lld_set_intx_edges(channel, extp->config->channels[channel].mode); - } -#endif -#if AVR_EXT_USE_INT5 || defined(__DOXYGEN__) - if (channel == INT5) { - EIMSK |= 1 << INT5; - ext_lld_set_intx_edges(channel, extp->config->channels[channel].mode); } #endif } @@ -311,80 +504,43 @@ void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { /** * @brief Disables an EXT channel. * - * @param[in] extp pinter to the @p EXTDriver object + * @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) { -#if AVR_EXT_USE_INT0 || defined(__DOXYGEN__) - if (channel == INT0) - EIMSK &= ~(1 << INT0); -#endif -#if AVR_EXT_USE_INT1 || defined(__DOXYGEN__) - if (channel == INT1) - EIMSK &= ~(1 << INT1); -#endif -#if AVR_EXT_USE_INT2 || defined(__DOXYGEN__) - if (channel == INT2) - EIMSK &= ~(1 << INT2); -#endif -#if AVR_EXT_USE_INT3 || defined(__DOXYGEN__) - if (channel == INT3) - EIMSK &= ~(1 << INT3); -#endif -#if AVR_EXT_USE_INT4 || defined(__DOXYGEN__) - if (channel == INT4) - EIMSK &= ~(1 << INT4); -#endif -#if AVR_EXT_USE_INT5 || defined(__DOXYGEN__) - if (channel == INT5) - EIMSK &= ~(1 << INT5); -#endif -} + (void)extp; +#if EXT_PC_NUM_CHANNELS > 0 + if (EXT_PC_MIN_CHANNEL <= channel && channel <= EXT_PC_MAX_CHANNEL) { + uint8_t port = (channel - EXT_PC_MIN_CHANNEL) / 8; -/** - * @brief Low level EXT driver initialization. - * - * @notapi - */ -void ext_lld_init(void) { - /* Driver initialization.*/ - extObjectInit(&EXTD1); -} + /* Clear bit in mask */ + (*(PCMSK[port])) &= ~(_BV((channel - EXT_PC_MIN_CHANNEL) % 8)); -/** - * @brief Configures and activates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_start(EXTDriver *extp) { - expchannel_t line; - - if (extp->state == EXT_STOP) - osalSysUnlock(); - - /* Configuration of automatic channels. */ - for (line = 0; line < EXT_MAX_CHANNELS; line++) { - if (extp->config->channels[line].mode & EXT_CH_MODE_AUTOSTART) - ext_lld_channel_enable(extp, line); - else - ext_lld_channel_disable(extp, line); + /* Disable interrupt if no bits are set */ + if ((*(PCMSK[port])) == 0) { +#if defined(__AVR_ATmega162__) + GICR &= ~(_BV(port) << 3); +#else + PCICR |= ~(_BV(port)); +#endif + } } -} - -/** - * @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) - osalSysLock(); +#endif +#if EXT_PC_NUM_CHANNELS > 0 && EXT_INT_NUM_CHANNELS > 0 + else +#endif +#if EXT_INT_NUM_CHANNELS > 0 + if (channel <= EXT_INT_MAX_CHANNEL) { +#if defined(__AVR_ATmega162__) + GICR &= ~((1 << channel) << 5); +#else + /* Enables/disables the peripheral, as requested. */ + EIMSK &= ~(1 << channel); +#endif + } +#endif } #endif /* HAL_USE_EXT */ diff --git a/os/hal/ports/AVR/hal_ext_lld.h b/os/hal/ports/AVR/hal_ext_lld.h index 883972419..c20c553b0 100644 --- a/os/hal/ports/AVR/hal_ext_lld.h +++ b/os/hal/ports/AVR/hal_ext_lld.h @@ -1,5 +1,7 @@ /* - ChibiOS - Copyright (C) 2016 Theodore Ateba + EXT Low Level Driver for ChibiOS + Copyright (C) 2015 Igor Stoppa + Copyright (C) 2016 Theodore Ateba Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,8 +17,8 @@ */ /** - * @file hal_ext_lld.h - * @brief EXT Driver subsystem low level driver source. + * @file AVR/hal_ext_lld.h + * @brief EXT Driver subsystem low level driver source. * * @addtogroup EXT * @{ @@ -32,48 +34,451 @@ /*===========================================================================*/ /** - * @brief Maximum number of EXT channels. + * @brief Level-driven irq generation. */ -#define AVR_INT_NUM_LINES 6 /**< INT0 to INT5 */ - -/** - * @brief Available number of EXT channels. - */ -#define EXT_MAX_CHANNELS AVR_INT_NUM_LINES +#define EXT_CH_MODE_LEVELS_MASK 8U /**< @brief Mask of levels field. */ +#undef EXT_CH_MODE_LOW_LEVEL +#define EXT_CH_MODE_LOW_LEVEL 8U /**< @brief Trigger on Low level. */ +#define EXT_CH_MODE_INTERNAL_PULLUP 16U /**< @brief Use internal pullup. */ /*===========================================================================*/ /* Driver pre-compile time settings. */ /*===========================================================================*/ +/** + * @name AVR configuration options + * @{ + */ +/** + * @brief INT0 support enable switch. + * @details If set to @p TRUE the support for INT0 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_INT0) || defined(__DOXYGEN__) +#define AVR_EXT_USE_INT0 FALSE +#endif + +/** + * @brief INT1 support enable switch. + * @details If set to @p TRUE the support for INT1 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_INT1) || defined(__DOXYGEN__) +#define AVR_EXT_USE_INT1 FALSE +#endif + +/** + * @brief INT2 support enable switch. + * @details If set to @p TRUE the support for INT2 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_INT2) || defined(__DOXYGEN__) +#define AVR_EXT_USE_INT2 FALSE +#endif + +/** + * @brief INT3 support enable switch. + * @details If set to @p TRUE the support for INT3 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_INT3) || defined(__DOXYGEN__) +#define AVR_EXT_USE_INT3 FALSE +#endif + +/** + * @brief INT4 support enable switch. + * @details If set to @p TRUE the support for INT4 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_INT4) || defined(__DOXYGEN__) +#define AVR_EXT_USE_INT4 FALSE +#endif + +/** + * @brief INT5 support enable switch. + * @details If set to @p TRUE the support for INT5 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_INT5) || defined(__DOXYGEN__) +#define AVR_EXT_USE_INT5 FALSE +#endif + +/** + * @brief PCINT0 support enable switch. + * @details If set to @p TRUE the support for PCINT0 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT0) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT0 FALSE +#endif + +/** + * @brief PCINT1 support enable switch. + * @details If set to @p TRUE the support for PCINT1 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT1) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT1 FALSE +#endif + +/** + * @brief PCINT2 support enable switch. + * @details If set to @p TRUE the support for PCINT2 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT2) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT2 FALSE +#endif + +/** + * @brief PCINT3 support enable switch. + * @details If set to @p TRUE the support for PCINT3 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT3) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT3 FALSE +#endif + +/** + * @brief PCINT4 support enable switch. + * @details If set to @p TRUE the support for PCINT4 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT4) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT4 FALSE +#endif + +/** + * @brief PCINT5 support enable switch. + * @details If set to @p TRUE the support for PCINT5 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT5) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT5 FALSE +#endif + +/** + * @brief PCINT6 support enable switch. + * @details If set to @p TRUE the support for PCINT6 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT6) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT6 FALSE +#endif + +/** + * @brief PCINT7 support enable switch. + * @details If set to @p TRUE the support for PCINT7 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT7) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT7 FALSE +#endif + +/** + * @brief PCINT8 support enable switch. + * @details If set to @p TRUE the support for PCINT8 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT8) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT8 FALSE +#endif + +/** + * @brief PCINT9 support enable switch. + * @details If set to @p TRUE the support for PCINT9 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT9) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT9 FALSE +#endif + +/** + * @brief PCINT10 support enable switch. + * @details If set to @p TRUE the support for PCINT10 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_EXT_USE_PCINT10) || defined(__DOXYGEN__) +#define AVR_EXT_USE_PCINT10 FALSE +#endif +/** @} */ + /*===========================================================================*/ /* Derived constants and error checks. */ /*===========================================================================*/ +#if !defined(INT0_vect) && AVR_EXT_USE_INT0 +#error "INT0 is not present in the selected device" +#endif + +#if !defined(INT1_vect) && AVR_EXT_USE_INT1 +#error "INT1 is not present in the selected device" +#endif + +#if !defined(INT2_vect) && AVR_EXT_USE_INT2 +#error "INT2 is not present in the selected device" +#endif + +#if !defined(INT3_vect) && AVR_EXT_USE_INT3 +#error "INT3 is not present in the selected device" +#endif + +#if !defined(INT4_vect) && AVR_EXT_USE_INT4 +#error "INT4 is not present in the selected device" +#endif + +#if !defined(INT5_vect) && AVR_EXT_USE_INT5 +#error "INT5 is not present in the selected device" +#endif + +#if !defined(PCINT0_PIN) && AVR_EXT_USE_PCINT0 +#error "PCINT0 is not present in the selected device" +#endif + +#if !defined(PCINT1_PIN) && AVR_EXT_USE_PCINT1 +#error "PCINT1 is not present in the selected device" +#endif + +#if !defined(PCINT2_PIN) && AVR_EXT_USE_PCINT2 +#error "PCINT2 is not present in the selected device" +#endif + +#if !defined(PCINT3_PIN) && AVR_EXT_USE_PCINT3 +#error "PCINT3 is not present in the selected device" +#endif + +#if !defined(PCINT4_PIN) && AVR_EXT_USE_PCINT4 +#error "PCINT4 is not present in the selected device" +#endif + +#if !defined(PCINT5_PIN) && AVR_EXT_USE_PCINT5 +#error "PCINT5 is not present in the selected device" +#endif + +#if !defined(PCINT6_PIN) && AVR_EXT_USE_PCINT6 +#error "PCINT6 is not present in the selected device" +#endif + +#if !defined(PCINT7_PIN) && AVR_EXT_USE_PCINT7 +#error "PCINT7 is not present in the selected device" +#endif + +#if !defined(PCINT8_PIN) && AVR_EXT_USE_PCINT8 +#error "PCINT8 is not present in the selected device" +#endif + +#if !defined(PCINT9_PIN) && AVR_EXT_USE_PCINT9 +#error "PCINT9 is not present in the selected device" +#endif + +#if !defined(PCINT10_PIN) && AVR_EXT_USE_PCINT10 +#error "PCINT10 is not present in the selected device" +#endif + +/** + * @brief Indexes of INT channels. + */ +#define EXT_INT_MIN_CHANNEL 0 + +#if AVR_EXT_USE_INT0 +#define EXT_INT0_PRESENT 1 +#define EXT_INT0_CHANNEL EXT_INT_MIN_CHANNEL +#else +#define EXT_INT0_PRESENT 0 +#define EXT_INT0_CHANNEL (EXT_INT_MIN_CHANNEL - 1) +#endif + +#if AVR_EXT_USE_INT1 +#define EXT_INT1_PRESENT 1 +#define EXT_INT1_CHANNEL (EXT_INT0_CHANNEL + 1) +#else +#define EXT_INT1_PRESENT 0 +#define EXT_INT1_CHANNEL EXT_INT0_CHANNEL +#endif + +#if AVR_EXT_USE_INT2 +#define EXT_INT2_PRESENT 1 +#define EXT_INT2_CHANNEL (EXT_INT1_CHANNEL + 1) +#else +#define EXT_INT2_PRESENT 0 +#define EXT_INT2_CHANNEL EXT_INT1_CHANNEL +#endif + +#if AVR_EXT_USE_INT3 +#define EXT_INT3_PRESENT 1 +#define EXT_INT3_CHANNEL (EXT_INT2_CHANNEL + 1) +#else +#define EXT_INT3_PRESENT 0 +#define EXT_INT3_CHANNEL EXT_INT2_CHANNEL +#endif + +#if AVR_EXT_USE_INT4 +#define EXT_INT4_PRESENT 1 +#define EXT_INT4_CHANNEL (EXT_INT3_CHANNEL + 1) +#else +#define EXT_INT4_PRESENT 0 +#define EXT_INT4_CHANNEL EXT_INT3_CHANNEL +#endif + +#if AVR_EXT_USE_INT5 +#define EXT_INT5_PRESENT 1 +#define EXT_INT5_CHANNEL (EXT_INT4_CHANNEL + 1) +#else +#define EXT_INT5_PRESENT 0 +#define EXT_INT5_CHANNEL EXT_INT4_CHANNEL +#endif + +#define EXT_INT_NUM_CHANNELS \ + (EXT_INT0_PRESENT + EXT_INT1_PRESENT + EXT_INT2_PRESENT + \ + EXT_INT3_PRESENT + EXT_INT4_PRESENT + EXT_INT5_PRESENT) + +#if EXT_INT_NUM_CHANNELS > 0 +#define EXT_INT_MAX_CHANNEL (EXT_INT_MIN_CHANNEL + EXT_INT_NUM_CHANNELS - 1) +#else +#define EXT_INT_MAX_CHANNEL 0 +#endif + +/** + * @brief Indexes of PC channels. + */ +#define EXT_PC_MIN_PORT EXT_INT_NUM_CHANNELS + +#if AVR_EXT_USE_PCINT0 +#define PORTA_PRESENT 1 +#define PORTA_INDEX EXT_PC_MIN_PORT +#else +#define PORTA_PRESENT 0 +#define PORTA_INDEX (EXT_PC_MIN_PORT - 1) +#endif + +#if AVR_EXT_USE_PCINT1 +#define PORTB_PRESENT 1 +#define PORTB_INDEX (PORTA_INDEX + 1) +#else +#define PORTB_PRESENT 0 +#define PORTB_INDEX PORTA_INDEX +#endif + +#if AVR_EXT_USE_PCINT2 +#define PORTC_PRESENT 1 +#define PORTC_INDEX (PORTB_INDEX + 1) +#else +#define PORTC_PRESENT 0 +#define PORTC_INDEX PORTB_INDEX +#endif + +#if AVR_EXT_USE_PCINT3 +#define PORTD_PRESENT 1 +#define PORTD_INDEX (PORTC_INDEX + 1) +#else +#define PORTD_PRESENT 0 +#define PORTD_INDEX PORTC_INDEX +#endif + +#if AVR_EXT_USE_PCINT4 +#define PORTE_PRESENT 1 +#define PORTE_INDEX (PORTD_INDEX + 1) +#else +#define PORTE_PRESENT 0 +#define PORTE_INDEX PORTD_INDEX +#endif + +#if AVR_EXT_USE_PCINT5 +#define PORTF_PRESENT 1 +#define PORTF_INDEX (PORTE_INDEX + 1) +#else +#define PORTF_PRESENT 0 +#define PORTF_INDEX PORTE_INDEX +#endif + +#if AVR_EXT_USE_PCINT6 +#define PORTG_PRESENT 1 +#define PORTG_INDEX (PORTF_INDEX + 1) +#else +#define PORTG_PRESENT 0 +#define PORTG_INDEX PORTF_INDEX +#endif + +#if AVR_EXT_USE_PCINT7 +#define PORTH_PRESENT 1 +#define PORTH_INDEX (PORTG_INDEX + 1) +#else +#define PORTH_PRESENT 0 +#define PORTH_INDEX PORTG_INDEX +#endif + +#if AVR_EXT_USE_PCINT8 +#define PORTI_PRESENT 1 +#define PORTI_INDEX (PORTH_INDEX + 1) +#else +#define PORTI_PRESENT 0 +#define PORTI_INDEX PORTH_INDEX +#endif + +#if AVR_EXT_USE_PCINT9 +#define PORTJ_PRESENT 1 +#define PORTJ_INDEX (PORTI_INDEX + 1) +#else +#define PORTJ_PRESENT 0 +#define PORTJ_INDEX PORTI_INDEX +#endif + +#if AVR_EXT_USE_PCINT10 +#define PORTK_PRESENT 1 +#define PORTK_INDEX (PORTJ_INDEX + 1) +#else +#define PORTK_PRESENT 0 +#define PORTK_INDEX PORTJ_INDEX +#endif + +/** + * @brief Available number of PC ports. + */ + +#define EXT_PC_NUM_PORTS \ + (PORTA_PRESENT + PORTB_PRESENT + PORTC_PRESENT + PORTD_PRESENT + \ + PORTE_PRESENT + PORTF_PRESENT + PORTG_PRESENT + PORTH_PRESENT + \ + PORTI_PRESENT + PORTJ_PRESENT + PORTK_PRESENT) + +#if EXT_PC_NUM_PORTS > 0 +#define EXT_PC_MAX_PORT (EXT_PC_MIN_PORT + EXT_PC_NUM_PORTS - 1) +#else +#define EXT_PC_MAX_PORT 0 +#endif + +#define EXT_PC_NUM_CHANNELS (EXT_PC_NUM_PORTS * 8) + +#define EXT_TOTAL_CHANNELS (EXT_INT_NUM_CHANNELS + EXT_PC_NUM_CHANNELS) +#define EXT_MAX_CHANNELS EXT_TOTAL_CHANNELS +#define EXT_PC_MIN_CHANNEL EXT_INT_NUM_CHANNELS +#define EXT_PC_MAX_CHANNEL (EXT_PC_MIN_CHANNEL + EXT_PC_NUM_CHANNELS - 1) + /*===========================================================================*/ /* Driver data structures and types. */ /*===========================================================================*/ /** - * @brief EXT channel identifier. + * @brief EXT channel identifier. */ typedef uint16_t expchannel_t; /** - * @brief Type of an EXT generic notification callback. + * @brief Type of an EXT generic notification callback. * * @param[in] extp pointer to the @p EXPDriver object triggering the * callback + * @param[in] channel channel being triggered. */ typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); /** - * @brief Channel configuration structure. + * @brief Channel configuration structure. */ typedef struct { /** - * @brief Channel mode. + * @brief Channel mode from HAL. */ - uint32_t mode; + uint8_t mode; /** * @brief Channel callback. */ @@ -81,19 +486,19 @@ typedef struct { } EXTChannelConfig; /** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. + * @brief Driver configuration structure. + * @note It could be empty on some architectures. */ typedef struct { /** * @brief Channel configurations. */ - EXTChannelConfig channels[EXT_MAX_CHANNELS]; + EXTChannelConfig channels[EXT_TOTAL_CHANNELS]; /* End of the mandatory fields.*/ } EXTConfig; /** - * @brief Structure representing an EXT driver. + * @brief Structure representing an EXT driver. */ struct EXTDriver { /** @@ -106,15 +511,29 @@ struct EXTDriver { */ const EXTConfig *config; /* End of the mandatory fields.*/ +#if EXT_PC_NUM_PORTS > 0 + /** + * @brief Current pin values. Only valid for PCINT. + */ + uint8_t pc_current_values[EXT_PC_NUM_PORTS]; + /** + * @brief Previous pin states. Only valid for PCINT. + */ + uint8_t pc_old_values[EXT_PC_NUM_PORTS]; +#endif }; /*===========================================================================*/ /* Driver macros. */ /*===========================================================================*/ +#define ext_port_to_channel(port, bit) \ + ((PORT##port##_INDEX - EXT_PC_MIN_PORT) * 8 + bit) + /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ + extern EXTDriver EXTD1; #ifdef __cplusplus