For with fix for disconnection notifications
Fork of nRF51822 by
Diff: nordic/app_common/app_gpiote.c
- Revision:
- 46:2bfbbe290083
- Child:
- 56:a1071b629aa3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nordic/app_common/app_gpiote.c Wed Jul 16 10:54:22 2014 +0100 @@ -0,0 +1,360 @@ +/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "app_gpiote.h" +#include <stdlib.h> +#include <string.h> +#include "app_util.h" +#include "app_util_platform.h" +#include "nrf_error.h" +#include "nrf_gpio.h" + + +/**@brief GPIOTE user type. */ +typedef struct +{ + uint32_t pins_mask; /**< Mask defining which pins user wants to monitor. */ + uint32_t pins_low_to_high_mask; /**< Mask defining which pins will generate events to this user when toggling low->high. */ + uint32_t pins_high_to_low_mask; /**< Mask defining which pins will generate events to this user when toggling high->low. */ + uint32_t sense_high_pins; /**< Mask defining which pins are configured to generate GPIOTE interrupt on transition to high level. */ + app_gpiote_event_handler_t event_handler; /**< Pointer to function to be executed when an event occurs. */ +} gpiote_user_t; + +STATIC_ASSERT(sizeof(gpiote_user_t) <= GPIOTE_USER_NODE_SIZE); +STATIC_ASSERT(sizeof(gpiote_user_t) % 4 == 0); + +static uint32_t m_enabled_users_mask; /**< Mask for tracking which users are enabled. */ +static uint8_t m_user_array_size; /**< Size of user array. */ +static uint8_t m_user_count; /**< Number of registered users. */ +static gpiote_user_t * mp_users = NULL; /**< Array of GPIOTE users. */ + + +/**@brief Function for toggling sense level for specified pins. + * + * @param[in] p_user Pointer to user structure. + * @param[in] pins Bitmask specifying for which pins the sense level is to be toggled. + */ +static void sense_level_toggle(gpiote_user_t * p_user, uint32_t pins) +{ + uint32_t pin_no; + + for (pin_no = 0; pin_no < NO_OF_PINS; pin_no++) + { + uint32_t pin_mask = (1 << pin_no); + + if ((pins & pin_mask) != 0) + { + uint32_t sense; + + // Invert sensing. + if ((p_user->sense_high_pins & pin_mask) == 0) + { + sense = GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos; + p_user->sense_high_pins |= pin_mask; + } + else + { + sense = GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos; + p_user->sense_high_pins &= ~pin_mask; + } + + NRF_GPIO->PIN_CNF[pin_no] &= ~GPIO_PIN_CNF_SENSE_Msk; + NRF_GPIO->PIN_CNF[pin_no] |= sense; + } + } +} + + +/**@brief Function for handling the GPIOTE interrupt. + */ +void GPIOTE_IRQHandler(void) +{ + uint8_t i; + uint32_t pins_changed; + uint32_t pins_state = NRF_GPIO->IN; + + // Clear event. + NRF_GPIOTE->EVENTS_PORT = 0; + + // Check all users. + for (i = 0; i < m_user_count; i++) + { + gpiote_user_t * p_user = &mp_users[i]; + + // Check if user is enabled. + if (((1 << i) & m_enabled_users_mask) != 0) + { + uint32_t transition_pins; + uint32_t event_low_to_high; + uint32_t event_high_to_low; + + // Find set of pins on which there has been a transition. + transition_pins = (pins_state ^ ~p_user->sense_high_pins) & p_user->pins_mask; + + // Toggle SENSE level for all pins that have changed state. + sense_level_toggle(p_user, transition_pins); + + // Second read after setting sense. + // Check if any pins have changed while serving this interrupt. + pins_changed = NRF_GPIO->IN ^ pins_state; + if (pins_changed) + { + // Transition pins detected in late stage. + uint32_t late_transition_pins; + + pins_state |= pins_changed; + + // Find set of pins on which there has been a transition. + late_transition_pins = (pins_state ^ ~p_user->sense_high_pins) & p_user->pins_mask; + + // Toggle SENSE level for all pins that have changed state in last phase. + sense_level_toggle(p_user, late_transition_pins); + + // Update pins that has changed state since the interrupt occurred. + transition_pins |= late_transition_pins; + } + + // Call user event handler if an event has occurred. + event_high_to_low = (~pins_state & p_user->pins_high_to_low_mask) & transition_pins; + event_low_to_high = (pins_state & p_user->pins_low_to_high_mask) & transition_pins; + + if ((event_low_to_high | event_high_to_low) != 0) + { + p_user->event_handler(event_low_to_high, event_high_to_low); + } + } + } +} + + +/**@brief Function for sense disabling for all pins for specified user. + * + * @param[in] user_id User id. + */ +static void pins_sense_disable(app_gpiote_user_id_t user_id) +{ + uint32_t pin_no; + + for (pin_no = 0; pin_no < 32; pin_no++) + { + if ((mp_users[user_id].pins_mask & (1 << pin_no)) != 0) + { + NRF_GPIO->PIN_CNF[pin_no] &= ~GPIO_PIN_CNF_SENSE_Msk; + NRF_GPIO->PIN_CNF[pin_no] |= GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos; + } + } +} + + +uint32_t app_gpiote_init(uint8_t max_users, void * p_buffer) +{ + if (p_buffer == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Check that buffer is correctly aligned. + if (!is_word_aligned(p_buffer)) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Initialize file globals. + mp_users = (gpiote_user_t *)p_buffer; + m_user_array_size = max_users; + m_user_count = 0; + m_enabled_users_mask = 0; + + memset(mp_users, 0, m_user_array_size * sizeof(gpiote_user_t)); + + // Initialize GPIOTE interrupt (will not be enabled until app_gpiote_user_enable() is called). + NRF_GPIOTE->INTENCLR = 0xFFFFFFFF; + + NVIC_ClearPendingIRQ(GPIOTE_IRQn); + NVIC_SetPriority(GPIOTE_IRQn, APP_IRQ_PRIORITY_HIGH); + NVIC_EnableIRQ(GPIOTE_IRQn); + + return NRF_SUCCESS; +} + + +uint32_t app_gpiote_user_register(app_gpiote_user_id_t * p_user_id, + uint32_t pins_low_to_high_mask, + uint32_t pins_high_to_low_mask, + app_gpiote_event_handler_t event_handler) +{ + // Check state and parameters. + if (mp_users == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + if (event_handler == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + if (m_user_count >= m_user_array_size) + { + return NRF_ERROR_NO_MEM; + } + + // Allocate new user. + mp_users[m_user_count].pins_mask = pins_low_to_high_mask | pins_high_to_low_mask; + mp_users[m_user_count].pins_low_to_high_mask = pins_low_to_high_mask; + mp_users[m_user_count].pins_high_to_low_mask = pins_high_to_low_mask; + mp_users[m_user_count].event_handler = event_handler; + + *p_user_id = m_user_count++; + + // Make sure SENSE is disabled for all pins. + pins_sense_disable(*p_user_id); + + return NRF_SUCCESS; +} + + +uint32_t app_gpiote_user_enable(app_gpiote_user_id_t user_id) +{ + uint32_t pin_no; + uint32_t pins_state; + + // Check state and parameters. + if (mp_users == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + if (user_id >= m_user_count) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Clear any pending event. + NRF_GPIOTE->EVENTS_PORT = 0; + pins_state = NRF_GPIO->IN; + + // Enable user. + if (m_enabled_users_mask == 0) + { + NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk; + } + m_enabled_users_mask |= (1 << user_id); + + // Enable sensing for all pins for specified user. + mp_users[user_id].sense_high_pins = 0; + for (pin_no = 0; pin_no < 32; pin_no++) + { + uint32_t pin_mask = (1 << pin_no); + + if ((mp_users[user_id].pins_mask & pin_mask) != 0) + { + uint32_t sense; + + if ((pins_state & pin_mask) != 0) + { + sense = GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos; + } + else + { + sense = GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos; + mp_users[user_id].sense_high_pins |= pin_mask; + } + + NRF_GPIO->PIN_CNF[pin_no] &= ~GPIO_PIN_CNF_SENSE_Msk; + NRF_GPIO->PIN_CNF[pin_no] |= sense; + } + } + + return NRF_SUCCESS; +} + + +uint32_t app_gpiote_user_disable(app_gpiote_user_id_t user_id) +{ + // Check state and parameters. + if (mp_users == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + if (user_id >= m_user_count) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Disable sensing for all pins for specified user. + pins_sense_disable(user_id); + + // Disable user. + m_enabled_users_mask &= ~(1UL << user_id); + if (m_enabled_users_mask == 0) + { + NRF_GPIOTE->INTENCLR = GPIOTE_INTENSET_PORT_Msk; + } + + return NRF_SUCCESS; +} + + +uint32_t app_gpiote_pins_state_get(app_gpiote_user_id_t user_id, uint32_t * p_pins) +{ + gpiote_user_t * p_user; + + // Check state and parameters. + if (mp_users == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + if (user_id >= m_user_count) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Get pins. + p_user = &mp_users[user_id]; + *p_pins = NRF_GPIO->IN & p_user->pins_mask; + + return NRF_SUCCESS; +} + +#if defined(SVCALL_AS_NORMAL_FUNCTION) || defined(SER_CONNECTIVITY) +uint32_t app_gpiote_input_event_handler_register(const uint8_t channel, + const uint32_t pin, + const uint32_t polarity, + app_gpiote_input_event_handler_t event_handler) +{ + (void)sense_level_toggle(NULL, pin); + return NRF_ERROR_NOT_SUPPORTED; +} + +uint32_t app_gpiote_input_event_handler_unregister(const uint8_t channel) +{ + return NRF_ERROR_NOT_SUPPORTED; +} + +uint32_t app_gpiote_end_irq_event_handler_register(app_gpiote_input_event_handler_t event_handler) +{ + return NRF_ERROR_NOT_SUPPORTED; +} + +uint32_t app_gpiote_end_irq_event_handler_unregister(void) +{ + return NRF_ERROR_NOT_SUPPORTED; +} + +uint32_t app_gpiote_enable_interrupts(void) +{ + return NRF_ERROR_NOT_SUPPORTED; +} + +uint32_t app_gpiote_disable_interrupts(void) +{ + return NRF_ERROR_NOT_SUPPORTED; +} +#endif // SVCALL_AS_NORMAL_FUNCTION || SER_CONNECTIVITY