/* * Copyright (C) 2012 by Ole Reinhardt (ole.reinhardt@embedded-it.de) * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /* * \verbatim * $Id: lpc177x_8x_gpio.c $ * \endverbatim */ #include #include #include #include #include #include #include #include #include /*! * \addtogroup xgNutArchCm3Lpc177x_8xGpio */ /*@{*/ #define NUTGPIOPORT_MAX NUTGPIO_PORT5+1 /*! * \brief Get pin configuration. * * Trying to set undefined ports must be avoided. * If NUTDEBUG is enabled an assertion will be rised. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. * * \return Attribute flags of the pin. */ uint32_t GpioPinConfigGet(int bank, int bit) { uint32_t rc = 0; uint32_t mode; __IO uint32_t *IOCON; NUTASSERT(bank < NUTGPIOPORT_MAX); NUTASSERT((bit < 32) && ((bank != NUTGPIOPORT_MAX - 1) || (bit < 5))); /* Calculate the address of the desired IOCON register */ IOCON = (uint32_t *)(LPC_IOCON_BASE + ((bank * 32 + bit) * sizeof(uint32_t))); /* * See register description of the IOCON registers of each I/O pin. */ mode = *IOCON; /* Query pin direction */ if (CM3BBGET(GPIO_BANKID2BASE(bank), LPC_GPIO_TypeDef, FIODIR, bit)) { rc |= GPIO_CFG_OUTPUT; } /* Query currently set alternate function */ rc |= ((mode >> IOCON_FUNC_POS) & IOCON_FUNC_BITMASK) << GPIO_CFG_PERIPHERAL_POS; if (rc & GPIO_CFG_PERIPHERAL_MASK) { rc |= GPIO_CFG_DISABLED; } switch (mode & IOCON_MODE_BITMASK) { case IOCON_MODE_PLAIN: /* Nothing special */ break; case IOCON_MODE_PULLDOWN: rc |= GPIO_CFG_PULLDOWN; break; case IOCON_MODE_PULLUP: rc |= GPIO_CFG_PULLUP; break; case IOCON_MODE_REPEATER: rc |= GPIO_CFG_REPEATER; break; } if ((mode & IOCON_HYSTERESIS_BITMASK) == IOCON_HYSTERESIS) { rc |= GPIO_CFG_HYSTERESIS; } if ((mode & IOCON_INVERT_BITMASK) == IOCON_INVERTED) { rc |= GPIO_CFG_INVERT; } if ((mode & IOCON_ADMODE_BITMASK) == IOCON_ADMODE) { rc |= GPIO_CFG_ADMODE; } if ((mode & IOCON_GLITCH_FILTER_BITMASK) == IOCON_GLITCH_FILTER) { rc |= GPIO_CFG_DEBOUNCE; } if ((mode & IOCON_SLEW_BITMASK) == IOCON_SLEW) { rc |= GPIO_CFG_SLEWCTRL; } if ((mode & IOCON_ODMODE_BITMASK) == IOCON_ODMODE) { rc |= GPIO_CFG_MULTIDRIVE; } if ((mode & IOCON_DACEN_BITMASK) == IOCON_DACEN) { rc |= GPIO_CFG_DAC_ENABLE; } return rc; } /*! * \brief Set port wide pin configuration. * * \note This function does not check for undefined ports and pins or * invalid attributes. If this is required, use GpioPinConfigSet(). * If NUTDEBUG is enabled accessing an undefined port will rise * an assertion. * * \note Port P0-7 .. P0-9 pins (W-Mode pins) are a little bit special and need bit 7 always be set for normal operation * * \param bank GPIO bank/port number. * \param mask The given attributes are set for a specific pin, if the * corresponding bit in this mask is 1. * \param flags Attribute flags to set. * * \return Always 0. */ int GpioPortConfigSet(int bank, uint32_t mask, uint32_t flags) { uint32_t mode = 0; int i; __IO uint32_t *IOCON; NUTASSERT(bank < NUTGPIOPORT_MAX); /* * See register description of the IOCON registers of each I/O pin. */ mode |= ((flags & GPIO_CFG_PERIPHERAL_MASK) >> GPIO_CFG_PERIPHERAL_POS) & IOCON_FUNC_BITMASK; if (flags & GPIO_CFG_PULLDOWN) { mode |= IOCON_MODE_PULLDOWN; } else if (flags & GPIO_CFG_PULLUP) { mode |= IOCON_MODE_PULLUP; } else if (flags & GPIO_CFG_REPEATER) { mode |= IOCON_MODE_REPEATER; } if (flags & GPIO_CFG_HYSTERESIS) { mode |= IOCON_HYSTERESIS; } if (flags & GPIO_CFG_INVERT) { mode |= IOCON_INVERTED; } if (flags & GPIO_CFG_ADMODE) { mode |= IOCON_ADMODE; } if (flags & GPIO_CFG_DEBOUNCE) { mode |= IOCON_GLITCH_FILTER; } if (flags & GPIO_CFG_SLEWCTRL) { mode |= IOCON_SLEW; } if (flags & GPIO_CFG_MULTIDRIVE) { mode |= IOCON_ODMODE; } if (flags & GPIO_CFG_DAC_ENABLE) { mode |= IOCON_DACEN; } IOCON = (uint32_t *)(LPC_IOCON_BASE + ((bank * 32) * sizeof(uint32_t))); for (i = 0; i < (bank != NUTGPIO_PORT5 ? 32 : 5); i ++) { if (mask & _BV(i)) { if ((bank == NUTGPIO_PORT0) && (i >= 7) && (i <= 9)) { /* Port P0-7 .. P0-9 pins (W-Mode pins) are a little bit special and need bit 7 always be set for normal operation */ mode |= _BV(7); } *(IOCON ++) = mode; } } if (GPIO_CFG_OUTPUT) { CM3REG(GPIO_BANKID2BASE(bank), LPC_GPIO_TypeDef, FIODIR) |= mask; } else { CM3REG(GPIO_BANKID2BASE(bank), LPC_GPIO_TypeDef, FIODIR) &= ~mask; } return 0; } /*! * \brief Set pin configuration. * * Applications may also use this function to make sure, that a specific * attribute is available for a specific pin. * * \note GPIO pins are typically initialized to a safe state after power * up. This routine is not able to determine the consequences of * changing pin configurations. In the worst case you may permanently * damage your hardware by bad pin settings. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. * \param flags Attribute flags. * * \return 0 if all attributes had been set, -1 otherwise. */ int GpioPinConfigSet(int bank, int bit, uint32_t flags) { uint32_t rc = 0; uint32_t mode; __IO uint32_t *IOCON; NUTASSERT(bank < NUTGPIOPORT_MAX); NUTASSERT((bit < 32) && ((bank != NUTGPIOPORT_MAX - 1) || (bit < 5))); /* Calculate the address of the desired IOCON register */ IOCON = (uint32_t *)(LPC_IOCON_BASE + ((bank * 32 + bit) * sizeof(uint32_t))); /* * See register description of the IOCON registers of each I/O pin. */ mode = 0; mode |= ((flags & GPIO_CFG_PERIPHERAL_MASK) >> GPIO_CFG_PERIPHERAL_POS) & IOCON_FUNC_BITMASK; if (flags & GPIO_CFG_PULLDOWN) { mode |= IOCON_MODE_PULLDOWN; } else if (flags & GPIO_CFG_PULLUP) { mode |= IOCON_MODE_PULLUP; } else if (flags & GPIO_CFG_REPEATER) { mode |= IOCON_MODE_REPEATER; } if (flags & GPIO_CFG_HYSTERESIS) { mode |= IOCON_HYSTERESIS; } if (flags & GPIO_CFG_INVERT) { mode |= IOCON_INVERTED; } if (flags & GPIO_CFG_ADMODE) { mode |= IOCON_ADMODE; } if (flags & GPIO_CFG_DEBOUNCE) { mode |= IOCON_GLITCH_FILTER; } if (flags & GPIO_CFG_SLEWCTRL) { mode |= IOCON_SLEW; } if (flags & GPIO_CFG_MULTIDRIVE) { mode |= IOCON_ODMODE; } if (flags & GPIO_CFG_DAC_ENABLE) { mode |= IOCON_DACEN; } if ((bank == NUTGPIO_PORT0) && (bit >= 7) && (bit <= 9)) { /* Port P0-7 .. P0-9 pins (W-Mode pins) are a little bit special and need bit 7 always be set for normal operation */ mode |= _BV(7); } #if defined(MCU_LPC407x_8x) if ((bank == NUTGPIO_PORT1) && (((bit >= 5) && (bit <= 7)) || (bit == 14) || (bit == 16) || (bit == 17))) { /* Port P1-5 .. P1-7, P1-14, P1-16, P1-17 pins (W-Mode pins) are a little bit special and need bit 7 always be set for normal operation */ mode |= _BV(7); } #endif *IOCON = mode; if (flags & GPIO_CFG_OUTPUT) { CM3BBSET(GPIO_BANKID2BASE(bank), LPC_GPIO_TypeDef, FIODIR, bit); } else { CM3BBCLR(GPIO_BANKID2BASE(bank), LPC_GPIO_TypeDef, FIODIR, bit); } if (mode != (*IOCON & (IOCON_FUNC_BITMASK | IOCON_MODE_BITMASK | IOCON_HYSTERESIS_BITMASK | IOCON_INVERT_BITMASK | IOCON_ADMODE_BITMASK | IOCON_GLITCH_FILTER_BITMASK | IOCON_I2C_MODE_BITMASK | IOCON_SLEW_BITMASK | IOCON_ODMODE_BITMASK | IOCON_DACEN_BITMASK))) { rc = -1; } if (CM3BBGET(GPIO_BANKID2BASE(bank), LPC_GPIO_TypeDef, FIODIR, bit) != (flags & GPIO_CFG_OUTPUT) ? 1 : 0) { rc = -1; } return rc; } /*! * \brief Register a GPIO pin interrupt handler. * * Generating interrupts on GPIO pin changes is not supported on all * platforms. In this case dedicated external interrupt pins may * be used with NutRegisterIrqHandler(). * * On the LPC17xx interrupts are triggered on rising, falling or both * edges. Level triggering is not supported. * * After registering, interrupts are disabled. Calling GpioIrqEnable() * is required to activate the interrupt. * * The following code fragment registers an interrupt handler which is * called on a rising edge of bit 4 of the first GPIO port: * \code * #include * * static void PinChange(void *arg) * { * ... * } * * { * ... * GpioPinConfigSet(0, 4, GPIO_CFG_PULLUP); * GpioRegisterIrqHandler(&sig_GPIO0, 4, PinChange, NULL); * GpioIrqSetMode(&sig_GPIO0, 4, NUT_IRQMODE_RISINGEDGE); * GpioIrqEnable(&sig_GPIO0, 4); * ... * } * \endcode * * \param sig Bank/port interrupt to be associated with this handler. * \param bit Bit number of the specified bank/port. * \param handler This routine will be called by Nut/OS, when the specified * pin changes its state. * \param arg Argument to be passed to the interrupt handler routine. * * \return 0 on success, -1 otherwise. */ int GpioRegisterIrqHandler(GPIO_SIGNAL * sig, int bit, void (*handler) (void *), void *arg) { int rc = 0; if (sig->ios_vector == 0) { /* This is the first call. Allocate the vector table. */ sig->ios_vector = malloc(sizeof(GPIO_VECTOR) * 32); if (sig->ios_vector) { memset(sig->ios_vector, 0, sizeof(GPIO_VECTOR) * 32); /* Register our internal PIO interrupt service. */ if (sig_PIO.ir_handler == NULL) { rc = NutRegisterIrqHandler(&sig_PIO, sig->ios_handler, NULL); if (rc == 0) { /* Clear any pending interrupts */ LPC_GPIOINT->IO0IntClr = 0xFFFFFFFF; LPC_GPIOINT->IO2IntClr = 0xFFFFFFFF; rc = NutIrqEnable(&sig_PIO); } } } else { return -1; } } sig->ios_vector[bit].iov_handler = handler; sig->ios_vector[bit].iov_arg = arg; return rc; } /*! * \brief Enable a specified GPIO interrupt. * * A related interrupt handler must have been registered before calling * this function. See GpioRegisterIrqHandler(). * * \param sig Interrupt to enable. * \param bit Bit number of the specified bank/port. * * \return 0 on success, -1 otherwise. */ int GpioIrqEnable(GPIO_SIGNAL * sig, int bit) { return (sig->ios_ctl) (sig, NUT_IRQCTL_ENABLE, NULL, bit); } /*! * \brief Disable a specified GPIO interrupt. * * \param sig Interrupt to disable. * \param bit Bit number of the specified bank/port. * * \return 0 on success, -1 otherwise. */ int GpioIrqDisable(GPIO_SIGNAL * sig, int bit) { return (sig->ios_ctl) (sig, NUT_IRQCTL_DISABLE, NULL, bit); } /*! * \brief Query the status of a specified GPIO interrupt. * * A related interrupt handler must have been registered before calling * this function. See GpioRegisterIrqHandler(). * * \param sig Interrupt to query * \param bit Bit number of the specified bank/port. * * \return 0 if interrupt is disabled, 1 of enabled */ int GpioIrqStatus(GPIO_SIGNAL * sig, int bit) { uint32_t status; (sig->ios_ctl) (sig, NUT_IRQCTL_STATUS, &status, bit); return status; } /*! * \brief Set the GPIO interrupt mode for a pin * * \param sig Interrupt to configure. * \param bit Bit number of the specified bank/port. * \param mode one of the following modes: * NUT_IRQMODE_RISINGEDGE, * NUT_IRQMODE_FALLINGEDGE, * NUT_IRQMODE_BOTHEDGE, * NUT_IRQMODE_NONE, * * \return 0 on success, -1 otherwise. */ int GpioIrqSetMode(GPIO_SIGNAL * sig, int bit, int mode) { return (sig->ios_ctl) (sig, NUT_IRQCTL_SETMODE, &mode, bit); } /*@}*/