blob: 46db33557428d1d18616e4971e70257c5ad66503 [file] [log] [blame]
/***************************************************************************//**
* @file em_gpio.c
* @brief General Purpose IO (GPIO) peripheral API
* devices.
* @version 4.2.1
*******************************************************************************
* @section License
* <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
*******************************************************************************
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
* obligation to support this Software. Silicon Labs is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Silicon Labs will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
******************************************************************************/
#include "em_gpio.h"
#if defined(GPIO_COUNT) && (GPIO_COUNT > 0)
/***************************************************************************//**
* @addtogroup EM_Library
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup GPIO
* @brief General Purpose Input/Output (GPIO) API
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/** Validation of pin typically usable in assert statements. */
#define GPIO_DRIVEMODE_VALID(mode) ((mode) <= 3)
#define GPIO_STRENGHT_VALID(strenght) (!((strenght) & \
~(_GPIO_P_CTRL_DRIVESTRENGTH_MASK \
| _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK)))
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Sets the pin location of the debug pins (Serial Wire interface).
*
* @note
* Changing the pins used for debugging uncontrolled, may result in a lockout.
*
* @param[in] location
* The debug pin location to use (0-3).
******************************************************************************/
void GPIO_DbgLocationSet(unsigned int location)
{
#if defined ( _GPIO_ROUTE_SWLOCATION_MASK )
EFM_ASSERT(location < AFCHANLOC_MAX);
GPIO->ROUTE = (GPIO->ROUTE & ~_GPIO_ROUTE_SWLOCATION_MASK) |
(location << _GPIO_ROUTE_SWLOCATION_SHIFT);
#else
(void)location;
#endif
}
#if defined (_GPIO_P_CTRL_DRIVEMODE_MASK)
/***************************************************************************//**
* @brief
* Sets the drive mode for a GPIO port.
*
* @param[in] port
* The GPIO port to access.
*
* @param[in] mode
* Drive mode to use for port.
******************************************************************************/
void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode)
{
EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_DRIVEMODE_VALID(mode));
GPIO->P[port].CTRL = (GPIO->P[port].CTRL & ~(_GPIO_P_CTRL_DRIVEMODE_MASK))
| (mode << _GPIO_P_CTRL_DRIVEMODE_SHIFT);
}
#endif
#if defined (_GPIO_P_CTRL_DRIVESTRENGTH_MASK)
/***************************************************************************//**
* @brief
* Sets the drive strength for a GPIO port.
*
* @param[in] port
* The GPIO port to access.
*
* @param[in] strength
* Drive strength to use for port.
******************************************************************************/
void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port,
GPIO_DriveStrength_TypeDef strength)
{
EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_STRENGHT_VALID(strength));
BUS_RegMaskedWrite(&GPIO->P[port].CTRL,
_GPIO_P_CTRL_DRIVESTRENGTH_MASK | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK,
strength);
}
#endif
/***************************************************************************//**
* @brief
* Configure GPIO interrupt.
*
* @details
* If reconfiguring a GPIO interrupt that is already enabled, it is generally
* recommended to disable it first, see GPIO_Disable().
*
* The actual GPIO interrupt handler must be in place before enabling the
* interrupt.
*
* Notice that any pending interrupt for the selected pin is cleared by this
* function.
*
* @note
* A certain pin number can only be associated with one port. Ie, if GPIO
* interrupt 1 is assigned to port A/pin 1, then it is not possibly to use
* pin 1 from any other ports for interrupts. Please refer to the reference
* manual.
*
* @param[in] port
* The port to associate with @p pin.
*
* @param[in] pin
* The GPIO interrupt number (= port pin).
*
* @param[in] risingEdge
* Set to true if interrupts shall be enabled on rising edge, otherwise false.
*
* @param[in] fallingEdge
* Set to true if interrupts shall be enabled on falling edge, otherwise false.
*
* @param[in] enable
* Set to true if interrupt shall be enabled after configuration completed,
* false to leave disabled. See GPIO_IntDisable() and GPIO_IntEnable().
******************************************************************************/
void GPIO_IntConfig(GPIO_Port_TypeDef port,
unsigned int pin,
bool risingEdge,
bool fallingEdge,
bool enable)
{
uint32_t tmp;
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
/* There are two registers controlling the interrupt configuration:
* The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls
* pins 8-15. */
if (pin < 8)
{
BUS_RegMaskedWrite(&GPIO->EXTIPSELL,
0xF << (4 * pin),
port << (4 * pin));
}
else
{
tmp = pin - 8;
BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
0xF << (4 * tmp),
port << (4 * tmp));
}
/* Enable/disable rising edge */
BUS_RegBitWrite(&(GPIO->EXTIRISE), pin, risingEdge);
/* Enable/disable falling edge */
BUS_RegBitWrite(&(GPIO->EXTIFALL), pin, fallingEdge);
/* Clear any pending interrupt */
GPIO->IFC = 1 << pin;
/* Finally enable/disable interrupt */
BUS_RegBitWrite(&(GPIO->IEN), pin, enable);
}
/***************************************************************************//**
* @brief
* Set the mode for a GPIO pin.
*
* @param[in] port
* The GPIO port to access.
*
* @param[in] pin
* The pin number in the port.
*
* @param[in] mode
* The desired pin mode.
*
* @param[in] out
* Value to set for pin in DOUT register. The DOUT setting is important for
* even some input mode configurations, determining pull-up/down direction.
******************************************************************************/
void GPIO_PinModeSet(GPIO_Port_TypeDef port,
unsigned int pin,
GPIO_Mode_TypeDef mode,
unsigned int out)
{
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
/* If disabling pin, do not modify DOUT in order to reduce chance for */
/* glitch/spike (may not be sufficient precaution in all use cases) */
if (mode != gpioModeDisabled)
{
if (out)
{
GPIO_PinOutSet(port, pin);
}
else
{
GPIO_PinOutClear(port, pin);
}
}
/* There are two registers controlling the pins for each port. The MODEL
* register controls pins 0-7 and MODEH controls pins 8-15. */
if (pin < 8)
{
BUS_RegMaskedWrite(&GPIO->P[port].MODEL,
0xF << (pin * 4),
mode << (pin * 4));
}
else
{
BUS_RegMaskedWrite(&GPIO->P[port].MODEH,
0xF << ((pin - 8) * 4),
mode << ((pin - 8) * 4));
}
if (mode == gpioModeDisabled)
{
if (out)
{
GPIO_PinOutSet(port, pin);
}
else
{
GPIO_PinOutClear(port, pin);
}
}
}
#if defined( _GPIO_EM4WUEN_MASK )
/**************************************************************************//**
* @brief
* Enable GPIO pin wake-up from EM4. When the function exits,
* EM4 mode can be safely entered.
*
* @note
* It is assumed that the GPIO pin modes are set correctly.
* Valid modes are @ref gpioModeInput and @ref gpioModeInputPull.
*
* @param[in] pinmask
* Bitmask containing the bitwise logic OR of which GPIO pin(s) to enable.
* Refer to Reference Manuals for pinmask to GPIO port/pin mapping.
* @param[in] polaritymask
* Bitmask containing the bitwise logic OR of GPIO pin(s) wake-up polarity.
* Refer to Reference Manuals for pinmask to GPIO port/pin mapping.
*****************************************************************************/
void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask)
{
EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0);
#if defined( _GPIO_EM4WUPOL_MASK )
EFM_ASSERT((polaritymask & ~_GPIO_EM4WUPOL_MASK) == 0);
GPIO->EM4WUPOL &= ~pinmask; /* Set wakeup polarity */
GPIO->EM4WUPOL |= pinmask & polaritymask;
#elif defined( _GPIO_EXTILEVEL_MASK )
EFM_ASSERT((polaritymask & ~_GPIO_EXTILEVEL_MASK) == 0);
GPIO->EXTILEVEL &= ~pinmask;
GPIO->EXTILEVEL |= pinmask & polaritymask;
#endif
GPIO->EM4WUEN |= pinmask; /* Enable wakeup */
GPIO_EM4SetPinRetention(true); /* Enable pin retention */
#if defined( _GPIO_CMD_EM4WUCLR_MASK )
GPIO->CMD = GPIO_CMD_EM4WUCLR; /* Clear wake-up logic */
#elif defined( _GPIO_IFC_EM4WU_MASK )
GPIO_IntClear(pinmask);
#endif
}
#endif
/** @} (end addtogroup GPIO) */
/** @} (end addtogroup EM_Library) */
#endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */