For with fix for disconnection notifications
Fork of nRF51822 by
nordic/app_common/app_gpiote.c
- Committer:
- Rohit Grover
- Date:
- 2014-09-02
- Revision:
- 56:a1071b629aa3
- Parent:
- 46:2bfbbe290083
File content as of revision 56:a1071b629aa3:
/* 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. * */ #if NEED_APP_GPIOTE /* disabled by default */ #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 #endif // #if NEED_APP_GPIOTE