diff --git a/include/libopencm3/lm4f/gpio.h b/include/libopencm3/lm4f/gpio.h index c42f0c74..0a3513ae 100644 --- a/include/libopencm3/lm4f/gpio.h +++ b/include/libopencm3/lm4f/gpio.h @@ -6,9 +6,10 @@ * * @version 1.0.0 * + * @author @htmlonly © @endhtmlonly 2011 Gareth McMullin * @author @htmlonly © @endhtmlonly 2013 Alexandru Gagniuc * - * @date 10 March 2013 + * @date 16 March 2013 * * LGPL License Terms @ref lgpl_license */ @@ -69,6 +70,8 @@ * These are usable across all GPIO registers, * except GPIO_LOCK and GPIO_PCTL * ---------------------------------------------------------------------------*/ +/** @defgroup gpio_pin_id GPIO pin identifiers + * @{*/ #define GPIO0 (1 << 0) #define GPIO1 (1 << 1) #define GPIO2 (1 << 2) @@ -78,6 +81,7 @@ #define GPIO6 (1 << 6) #define GPIO7 (1 << 7) #define GPIO_ALL 0xff +/** @} */ /* ============================================================================= * GPIO registers @@ -171,11 +175,157 @@ #define GPIO_PCELL_ID2(port) MMIO32(port + 0xFF8) #define GPIO_PCELL_ID3(port) MMIO32(port + 0xFFC) +/* ============================================================================= + * Convenience enums + * ---------------------------------------------------------------------------*/ +enum gpio_mode { + GPIO_MODE_OUTPUT, /**< Configure pin as output */ + GPIO_MODE_INPUT, /**< Configure pin as input */ + GPIO_MODE_ANALOG, /**< Configure pin as analog function */ +}; +enum gpio_pullup { + GPIO_PUPD_NONE, /**< Do not pull the pin high or low */ + GPIO_PUPD_PULLUP, /**< Pull the pin high */ + GPIO_PUPD_PULLDOWN, /**< Pull the pin low */ +}; + +enum gpio_output_type { + GPIO_OTYPE_PP, /**< Push-pull configuration */ + GPIO_OTYPE_OD, /**< Open drain configuration */ +}; + +enum gpio_drive_strength { + GPIO_DRIVE_2MA, /**< 2mA drive */ + GPIO_DRIVE_4MA, /**< 4mA drive */ + GPIO_DRIVE_8MA, /**< 8mA drive */ + GPIO_DRIVE_8MA_SLEW_CTL,/**< 8mA drive with slew rate control */ +}; +/* ============================================================================= + * Function prototypes + * ---------------------------------------------------------------------------*/ BEGIN_DECLS -void gpio_set(u32 gpioport, u8 gpios); -void gpio_clear(u32 gpioport, u8 gpios); +void gpio_enable_ahb_aperture(void); +void gpio_mode_setup(u32 gpioport, enum gpio_mode mode, enum gpio_pullup pullup, + u8 gpios); +void gpio_set_output_config(u32 gpioport, enum gpio_output_type otype, + enum gpio_drive_strength drive, u8 gpios); +void gpio_set_af(u32 gpioport, u8 alt_func_num, u8 gpios); + +void gpio_toggle(u32 gpioport, u8 gpios); +void gpio_unlock_commit(u32 gpioport, u8 gpios); + +/* Let's keep these ones inlined. GPIO control should be fast */ +/** @ingroup gpio_control + * @{ */ + +/** + * \brief Get status of a Group of Pins (atomic) + * + * Reads the level of the given pins. Bit 0 of the returned data corresponds to + * GPIO0 level, bit 1 to GPIO1 level. and so on. Bits corresponding to masked + * pins (corresponding bit of gpios parameter set to zero) are returned as 0. + * + * This is an atomic operation. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together. + * + * @return The level of the GPIO port. The pins not specified in gpios are + * masked to zero. + */ +static inline u8 gpio_read(u32 gpioport, u8 gpios) +{ + return GPIO_DATA(gpioport)[gpios]; +} + +/** + * \brief Set level of a Group of Pins (atomic) + * + * Sets the level of the given pins. Bit 0 of the data parameter corresponds to + * GPIO0, bit 1 to GPIO1. and so on. Maskedpins (corresponding bit of gpios + * parameter set to zero) are returned not affected. + * + * This is an atomic operation. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together. + * @param[in] data Level to set pin to. Bit 0 of data corresponds to GPIO0, bit + * 1 to GPIO1. and so on. + */ +static inline void gpio_write(u32 gpioport, u8 gpios, u8 data) +{ + /* ipaddr[9:2] mask the bits to be set, hence the array index */ + GPIO_DATA(gpioport)[gpios] = data; +} + +/** + * \brief Set a Group of Pins (atomic) + * + * Set one or more pins of the given GPIO port. This is an atomic operation. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together. + */ +static inline void gpio_set(u32 gpioport, u8 gpios) +{ + gpio_write(gpioport, gpios, 0xff); +} + +/** + * \brief Clear a Group of Pins (atomic) + * + * Clear one or more pins of the given GPIO port. This is an atomic operation. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together. + */ +static inline void gpio_clear(u32 gpioport, u8 gpios) +{ + gpio_write(gpioport, gpios, 0); +} + +/** + * \brief Read level of all pins from a port (atomic) + * + * Read the current value of the given GPIO port. This is an atomic operation. + * + * This is functionally identical to @ref gpio_read (gpioport, GPIO_ALL). + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * + * @return The level of all the pins on the GPIO port. + */ +static inline u8 gpio_port_read(u32 gpioport) +{ + return gpio_read(gpioport, GPIO_ALL); +} + +/** + * \brief Set level of of all pins from a port (atomic) + * + * Set the level of all pins on the given GPIO port. This is an atomic operation. + * + * This is functionally identical to @ref gpio_write (gpioport, GPIO_ALL, data). + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together. + * @param[in] data Level to set pin to. Bit 0 of data corresponds to GPIO0, bit + * 1 to GPIO1. and so on. + */ +static inline void gpio_port_write(u32 gpioport, u8 data) +{ + gpio_write(gpioport, GPIO_ALL, data); +} + + +/** @} */ END_DECLS diff --git a/lib/lm4f/gpio.c b/lib/lm4f/gpio.c index 49a81efc..f69b8562 100644 --- a/lib/lm4f/gpio.c +++ b/lib/lm4f/gpio.c @@ -1,22 +1,8 @@ -/** @defgroup gpio_file General Purpose I/O - * - * @brief LM4F General Purpose I/O - * - * @ingroup LM4Fxx - * - * @version 1.0.0 - * - * @author @htmlonly © @endhtmlonly 2011 Gareth McMullin - * - * @date 10 March 2013 - * - * LGPL License Terms @ref lgpl_license - */ - /* * This file is part of the libopencm3 project. * * Copyright (C) 2011 Gareth McMullin + * Copyright (C) 2013 Alexandru Gagniuc * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -32,20 +18,429 @@ * along with this library. If not, see . */ +/** @defgroup gpio_file GPIO + * + * + * @ingroup LM4Fxx + * + * @version 1.0.0 + * + * @author @htmlonly © @endhtmlonly 2011 Gareth McMullin + * @author @htmlonly © @endhtmlonly 2013 Alexandru Gagniuc + * + * @date 16 March 2013 + * + * LGPL License Terms @ref lgpl_license + * + * @brief libopencm3 LM4F General Purpose I/O + * + * The LM4F GPIO API provides functionality for accessing the GPIO pins of the + * LM4F. + * + * @attention @code An important aspect to consider is that libopencm3 uses the + * AHB aperture for accessing the GPIO registers on the LM4F. The AHB must be + * explicitly enabled with a call to gpio_enable_ahb_aperture() before accessing + * any GPIO functionality. + * @endcode + * + * Please see the individual GPIO modules for more details. To use the GPIO, the + * gpio.h header needs to be included: + * @code{.c} + * #include + * @endcode + */ + /**@{*/ #include +#include -void gpio_set(u32 gpioport, u8 gpios) +/* Value we need to write to unlock the GPIO commit register */ +#define GPIO_LOCK_UNLOCK_CODE 0x4C4F434B + + +/** @defgroup gpio_config GPIO pin configuration + * @ingroup gpio_file + * + * \brief Enabling and configuring GPIO pins + * + * @section gpio_api_enable Enabling GPIO ports + * @attention + * Before accessing GPIO functionality through this API, the AHB aperture for + * GPIO ports must be enabled via a call to @ref gpio_enable_ahb_aperture(). + * Failing to do so will cause a hard fault. + * + * @note + * Once the AHB aperture is enabled, GPIO registers can no longer be accessed + * via the APB aperture. The two apertures are mutually exclusive. + * + * Enabling the AHB aperture only needs to be done once. However, in order to + * access a certain GPIO port, its clock must also be enabled. Enabling the + * GPIO clock needs to be done for every port that will be used. + * + * For example, to enable GPIOA and GPIOD: + * @code{.c} + * // Make sure we can access the GPIO via the AHB aperture + * gpio_enable_ahb_aperture(); + * ... + * // Enable GPIO ports A and D + * periph_clock_enable(RCC_GPIOA); + * periph_clock_enable(RCC_GPIOD); + * @endcode + * + * On reset all ports are configured as digital floating inputs (no pull-up or + * pull-down), except for special function pins. + * + * + * @section gpio_api_in Configuring pins as inputs + * + * Configuring GPIO pins as inputs is done with @ref gpio_mode_setup(), with + * @ref GPIO_MODE_INPUT for the mode parameter. The direction of the pull-up + * must be specified with the same call + * + * For example, PA2, PA3, and PA4 as inputs, with pull-up on PA4: + * @code{.c} + * gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO2 | GPIO3); + * gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO4); + * @endcode + * + * + * @section gpio_api_out Configuring pins as outputs + * + * Output pins have more configuration options than input pins. LM4F pins can be + * configured as either push-pull, or open drain. The drive strength of each pin + * can be adjusted between 2mA, 4mA, or 8mA. Slew-rate control is available when + * the pins are configured to drive 8mA. These extra options can be specified + * with @ref gpio_set_output_config(). + * The default is push-pull configuration with 2mA drive capability. + * + * @note + * @ref gpio_set_output_config() controls different capabilities than the + * similar sounding gpio_set_output_options() from the STM GPIO API. They are + * intentionally named differently to prevent confusion between the two. They + * are API incompatible. + * + * For example, to set PA2 to output push-pull with a drive strength of 8mA: + * @code{.c} + * gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO2); + * gpio_set_output_config(GPIOA, GPIO_OTYPE_PP, GPIO_DRIVE_8MA, GPIO2); + * @endcode + * + * + * @section gpio_api_analog Configuring pins as analog function + * + * Configuring GPIO pins to their analog function is done with + * @ref gpio_mode_setup(), with @ref GPIO_MODE_ANALOG for the mode parameter. + * + * Suppose PD4 and PD5 are the USB pins. To enable their analog functionality + * (USB D+ and D- in this case), use: + * @code + * // Mux USB pins to their analog function + * gpio_mode_setup(GPIOD, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO4 | GPIO5); + * @endcode + * + * @section gpio_api_alf_func Configuring pins as alternate functions + * + * Most pins have alternate functions associated with them. When a pin is set to + * an alternate function, it is multiplexed to one of the dedicated hardware + * peripheral in the chip. The alternate function mapping can be found in the + * part's datasheet, and usually varies between arts of the same family. + * + * Multiplexing a pin, or group of pins to an alternate function is done with + * @ref gpio_set_af(). Because AF0 is not used on the LM4F, passing 0 as the + * alt_func_num parameter will disable the alternate function of the given pins. + * + * @code + * // Mux PB0 and PB1 to AF1 (UART1 TX/RX in this case) + * gpio_set_af(GPIOB, 1, GPIO0 | GPIO1); + * @endcode + * + * @section gpio_api_sfpins Changing configuration of special function pins + * + * On the LM4F, the NMI and JTAG/SWD default to their alternate function. These + * pins cannot normally be committed to GPIO usage. To enable these special + * function pins to be used as GPIO, they must be unlocked. This may be achieved + * via @ref gpio_unlock_commit. Once a special function pin is unlocked, its + * settings may be altered in the usual way. + * + * For example, to unlock the PF0 pin (NMI on the LM4F120): + * @code + * // PF0 is an NMI pin, and needs to be unlocked + * gpio_unlock_commit(GPIOF, GPIO0); + * // Now the pin can be configured + * gpio_mode_setup(RGB_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, btnpins); + * @endcode + */ +/**@{*/ + +/** + * \brief Enable access to GPIO registers via the AHB aperture + * + * All GPIO registers are accessed in libopencm3 via the AHB aperture. It + * provides faster control over the older APB aperture. This aperture must be + * enabled before calling any other gpio_*() function. + * + */ +void gpio_enable_ahb_aperture(void) { - /* ipaddr[9:2] mask the bits to be set, hence the array index */ - GPIO_DATA(gpioport)[gpios] = 0xff; + SYSCTL_GPIOHBCTL = 0xffffffff; } -void gpio_clear(u32 gpioport, u8 gpios) +/** + * \brief Configure a group of pins + * + * Sets the Pin direction, analog/digital mode, and pull-up configuration of + * or a set of GPIO pins on a given GPIO port. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] mode Pin mode (@ref gpio_mode) \n + * - GPIO_MODE_OUTPUT -- Configure pin as output \n + * - GPIO_MODE_INPUT -- Configure pin as input \n + * - GPIO_MODE_ANALOG -- Configure pin as analog function + * @param[in] pullup Pin pullup/pulldown configuration (@ref gpio_pullup) \n + * - GPIO_PUPD_NONE -- Do not pull the pin high or low \n + * - GPIO_PUPD_PULLUP -- Pull the pin high \n + * - GPIO_PUPD_PULLDOWN -- Pull the pin low + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together + */ +void gpio_mode_setup(u32 gpioport, enum gpio_mode mode, enum gpio_pullup pullup, + u8 gpios) { - GPIO_DATA(gpioport)[gpios] = 0; + switch (mode) { + case GPIO_MODE_OUTPUT: + GPIO_DIR(gpioport) |= gpios; + GPIO_DEN(gpioport) |= gpios; + GPIO_AMSEL(gpioport) &= ~gpios; + break; + case GPIO_MODE_INPUT: + GPIO_DIR(gpioport) &= ~gpios; + GPIO_DEN(gpioport) |= gpios; + GPIO_AMSEL(gpioport) &= ~gpios; + break; + case GPIO_MODE_ANALOG: + GPIO_DEN(gpioport) &= ~gpios; + GPIO_AMSEL(gpioport) |= gpios; + break; + default: + /* Don't do anything */ + break; + } + + /* + * Setting a bit in the GPIO_PDR register clears the corresponding bit + * in the GPIO_PUR register, and vice-versa. + */ + switch (pullup) { + case GPIO_PUPD_PULLUP: + GPIO_PUR(gpioport) |= gpios; + break; + case GPIO_PUPD_PULLDOWN: + GPIO_PDR(gpioport) |= gpios; + break; + case GPIO_PUPD_NONE: /* Fall through */ + default: + GPIO_PUR(gpioport) &= ~gpios; + GPIO_PDR(gpioport) &= ~gpios; + break; + } } +/** + * \brief Configure output parameters of a group of pins + * + * Sets the output configuration and drive strength, of or a set of GPIO pins + * for a set of GPIO pins in output mode. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] otype Output driver configuration (@ref gpio_output_type) \n + * - GPIO_OTYPE_PP -- Configure pin driver as push-pull \n + * - GPIO_OTYPE_OD -- Configure pin driver as open drain + * @param[in] drive Pin drive strength (@ref gpio_drive_strength) \n + * - GPIO_DRIVE_2MA -- 2mA drive \n + * - GPIO_DRIVE_4MA -- 4mA drive \n + * - GPIO_DRIVE_8MA -- 8mA drive \n + * - GPIO_DRIVE_8MA_SLEW_CTL -- 8mA drive with slew rate + * control + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together + */ +void gpio_set_output_config(u32 gpioport, enum gpio_output_type otype, + enum gpio_drive_strength drive, u8 gpios) +{ + if (otype == GPIO_OTYPE_OD) + GPIO_ODR(gpioport) |= gpios; + else + GPIO_ODR(gpioport) &= ~gpios; + + /* + * Setting a bit in the GPIO_DRxR register clears the corresponding bit + * in the other GPIO_DRyR registers, and vice-versa. + */ + switch (drive) + { + case GPIO_DRIVE_8MA_SLEW_CTL: + GPIO_DR8R(gpioport) |= gpios; + GPIO_SLR(gpioport) |= gpios; + break; + case GPIO_DRIVE_8MA: + GPIO_DR8R(gpioport) |= gpios; + GPIO_SLR(gpioport) &= ~gpios; + break; + case GPIO_DRIVE_4MA: + GPIO_DR4R(gpioport) |= gpios; + break; + case GPIO_DRIVE_2MA: /* Fall through */ + default: + GPIO_DR2R(gpioport) |= gpios; + break; + } +} + +#define PCTL_AF(pin, af) (af << (pin << 2)) +#define PCTL_MASK(pin) PCTL_AF(pin, 0xf) +/** + * \brief Multiplex group of pins to the given alternate function + * + * Mux the pin or group of pins to the given alternate function. Note that a + * number of pins may be set but only with a single AF number. This is useful + * when one or more of a peripheral's pins are assigned to the same alternate + * function. + * + * Because AF0 is not used on the LM4F, passing 0 as the alt_func_num parameter + * will disable the alternate function of the given pins. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] alt_func_num Pin alternate function number or 0 to disable the + * alternate function multiplexing. + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together + */ +void gpio_set_af(u32 gpioport, u8 alt_func_num, u8 gpios) +{ + u32 pctl32; + u8 pin_mask; + int i; + + /* Did we mean to disable the alternate function? */ + if (alt_func_num == 0) { + GPIO_AFSEL(gpioport) &= ~gpios; + return; + } + + /* Enable the alternate function */ + GPIO_AFSEL(gpioport) |= gpios; + /* Alternate functions are digital */ + GPIO_DEN(gpioport) |= gpios; + + /* Now take care of the actual multiplexing */ + pctl32 = GPIO_PCTL(gpioport); + for (i = 0; i < 8; i++) { + pin_mask = (1 << i); + + if (!(gpios & pin_mask)) + continue; + + pctl32 &= ~PCTL_MASK(i); + pctl32 |= PCTL_AF( i, (alt_func_num & 0xf) ); + } + + GPIO_PCTL(gpioport) = pctl32; +} + +/** + * \brief Unlock the commit control of a special function pin + * + * Unlocks the commit control of the given pin or group of pins. If a pin is a + * JTAG/SWD or NMI, the pin may then be reconfigured as a GPIO pin. If the pin + * is not locked by default, this has no effect. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios @ref gpio_pin_id. Any combination of pins may be specified + * by OR'ing then together. + */ +void gpio_unlock_commit(u32 gpioport, u8 gpios) +{ + /* Unlock the GPIO_CR register */ + GPIO_LOCK(gpioport) = GPIO_LOCK_UNLOCK_CODE; + /* Enable committing changes */ + GPIO_CR(gpioport) |= gpios; + /* Lock the GPIO_CR register */ + GPIO_LOCK(gpioport) = ~GPIO_LOCK_UNLOCK_CODE; +} +/**@}*/ + +/** @defgroup gpio_control GPIO pin control + * @ingroup gpio_file + * + * \brief Controlling GPIO pins + * + * Each I/O port has 8 individually configurable bits. When reading and writing + * data to the GPIO ports, address bits [9:2] mask the pins to be read or + * written. This mechanism makes all GPIO port reads and writes on the LM4F + * atomic operations. The GPIO API takes full advantage of this fact to preserve + * the atomicity of these operations. + * + * Setting or clearing a group of bits can be accomplished with @ref gpio_set() + * and @ref gpio_clear() respectively. These operation use the masking mechanism + * described above to only affect the specified pins. + * + * Sometimes it is more appropriate to read or set the level of a group of pins + * on a port, in one atomic operation. Reading the status can be accomplished + * with @ref gpio_read(). The result is equivalent to reading all the pins, then + * masking only the desired pins; however, the masking is done in hardware, and + * does not require an extra hardware operation. + * + * Writing a group of pins can be accomplished with @ref gpio_write(). The mask + * ('gpios' parameter) is applied in hardware, and the masked pins are not + * affected, regardless of the value of the respective bits written to the GPIO + * port. + * + * Two extra functions are provided, @ref gpio_port_read() and + * @ref gpio_port_write(). They are functionally identical to + * @ref gpio_read (port, GPIO_ALL) and @ref gpio_write (port, GPIO_ALL, val) + * respectively. Hence, they are also atomic. + * + * GPIO pins may be toggled with @ref gpio_toggle(). This function does not + * translate to an atomic operation. + * + * @note + * The @ref gpio_toggle() operation is the only GPIO port operation which is not + * atomic. It involves a read-modify-write cycle. + * + * Suppose PA0, PA1, PA2, and PA3 are to be modified without affecting the other + * pins on port A. This is common when controlling, for example, a 4-bit bus: + * @code{.c} + * // Pins 4,5,6, and 7 are unaffected, regardless of the bits in val + * gpio_write(GPIOA, GPIO0 | GPIO1 | GPIO2 | GPIO3, val); + * // Wait a bit then send the other 4 bits + * wait_a_bit(); + * gpio_write(GPIOA, GPIO0 | GPIO1 | GPIO2 | GPIO3, val >> 4); + * @endcode + * + * Suppose a LED is connected to PD4, and we want to flash the LED for a brief + * period of time: + * @code + * gpio_set(GPIOD, GPIO4); + * wait_a_bit(); + * gpio_set(GPIOD, GPIO4); + * @endcode + */ +/**@{*/ +/** + * \brief Toggle a Group of Pins + * + * Toggle one or more pins of the given GPIO port. + * + * @param[in] gpioport GPIO block register address base @ref gpio_reg_base + * @param[in] gpios Pin identifiers. @ref gpio_pin_id + */ +void gpio_toggle(u32 gpioport, u8 gpios) +{ + /* The mask makes sure we only toggle the GPIOs we want to */ + GPIO_DATA(gpioport)[gpios] ^= GPIO_ALL; +} +/**@}*/ + /**@}*/