/* * Copyright (C) 2001-2009 by egnite Software GmbH. All rights reserved. * Copyright (C) 2009 by Ulrich Prinz (uprinz2@netscape.net) * * 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 EMBEDDED IT 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 EMBEDDED IT * 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/ * */ /* * \file dev/keys.c * \brief Key handling driver. * * Driver for flexible key, switch and button handling. * * \verbatim * * $Log$ * * Revision 0.3 2009/09/12 ulrichprinz * First checkin, new push button driver example * (currently SAM7X256 is tested only) * * \endverbatim */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KEY_SUPPORT_IOEXP #include #else #define IOXP_PORT0 0x80 #endif //#define DEBUG_KEYS #ifdef DEBUG_KEYS #include #define KPRINTF(args,...) printf(args,##__VA_ARGS__) #else #define KPRINTF(args,...) #endif #include /*! * \addtogroup xgDevKeys */ /*@{*/ // TODO: [UP] This struct could be smaller without increasing addressing overhead typedef struct { void* next; /**< Pointer to next key or NULL on last key */ HANDLE* event; /**< Handle for key event */ void (*callback)(void); /**< Function Pointer if key is activated */ int bank; /**< GPIO bank of key */ int pin; /**< GPIO pin of key */ int lastState; /**< last state sampled from port */ int newState; /**< current state sampled from port */ int fx; /**< Action type of key */ uint32_t fxt; /**< time for action */ uint32_t TimeDown; /**< System time in ms at key down recognized */ } KEYEventT; static KEYEventT *first_key; HANDLE key_tmr = NULL; HANDLE key_evt = NULL; /*! * \brief reads actual state of a key * * Applications can call this functions to determine which key issued * the event to a calling thread. This helps if more than ome key has * been assigned to a single event mutex. If the key was the one which * raised the event, it has bit 1 set. This bit will be cleared on calling * this function. * * \param keyh Handle of the key to query. * * \return bit 0: 1 if key still pressed, bit 1: 1 if key raised the event. */ int NutGetKeyState( HANDLE *keyhp) { KEYEventT *key = (KEYEventT *)keyhp; int rc = -1; if( keyhp==NULL) goto error_out; rc = key->newState; key->newState &= ~KEY_PENDING; error_out: return rc; } /*! * \brief reads the time in ms of how long the key is or was pressed * * Applications can call this functions to determine how long a key * is pressed. * * \param keyh Handle of the key to query. * * \return time key was pressed in ms or -1 in case of error. */ int NutGetKeyTime( HANDLE *keyhp) { KEYEventT *key = (KEYEventT *)keyhp; int rc = -1; if( keyhp==NULL) goto error_out; rc = (int)(NutGetMillis() - key->TimeDown); error_out: return rc; } /*! * \brief Key-Timer callback handler. * * This is an internal function called by NutRegisterKey() and the * EventTimer for key handling. * * \param timer Internal Handle of the key timer. * \param arg Not used. */ void KeyTimerCb(HANDLE timer, void *arg) { NutEventPostAsync(arg); } /*! * \brief Key-Thread. * * This is the internal key thread triggered by the key event timer. * */ THREAD( sys_key, arg) { KEYEventT *key; uint32_t now; #ifdef KEY_SUPPORT_IOEXP int ioxread, ioxstate; #endif NUTASSERT( arg != NULL); NutThreadSetPriority( 16); for(;;) { #ifdef KEY_SUPPORT_IOEXP ioxread = 0; #endif if( NutEventWait( arg, NUT_WAIT_INFINITE)==0) { key = first_key; now = NutGetMillis(); while( key ) { /* * Read in keys from ports */ key->newState &= ~KEY_IS_DOWN; #ifndef KEY_SUPPORT_IOEXP /* Save inverted key state (low-active) */ key->newState |= (GpioPinGet( key->bank, key->pin))?KEY_NOT_PRESSED:KEY_IS_DOWN; #else if( key->bank < IOXP_PORT0) { /* Save inverted key state (low-active) */ key->newState |= (GpioPinGet( key->bank, key->pin))?KEY_NOT_PRESSED:KEY_IS_DOWN; } else { /* read io-expander only on first key connected ** and buffer the result to keep bus silent */ if( ioxread == 0) { IOExpRawRead( key->bank, &ioxstate); ioxread = 1; } /* Save inverted key state (low-active) */ key->newState |= ((ioxstate & (1<pin))?0:1); } #endif /* * Process key status change */ if( (key->newState & KEY_IS_DOWN) > (key->lastState & KEY_IS_DOWN)) { /* key up->down change */ key->TimeDown = now; if( key->fx == KEY_ACTION_DOWN) { KPRINTF("KD %08lx E:%08lx\n", (uint32_t)key, (uint32_t)(key->event)); key->newState |= KEY_PENDING; if( key->event) NutEventPost( key->event); if( key->callback) (*key->callback)(); } } else if( (key->newState & KEY_IS_DOWN) < (key->lastState & KEY_IS_DOWN)) { /* key down->up change */ key->newState &= ~KEY_IS_LOCKED; if( key->fx == KEY_ACTION_UP) { KPRINTF("KU %08lx E:%08lx\n", (uint32_t)key, (uint32_t)(key->event)); key->newState |= (KEY_ACTION_UP | KEY_PENDING); if( key->event) NutEventPost( key->event); if( key->callback) (*key->callback)(); } else if( ( key->fx == KEY_ACTION_SHORT) && ( (now - key->TimeDown) < key->fxt)) { KPRINTF("KS %08lx E:%08lx\n", (uint32_t)key, (uint32_t)(key->event)); key->newState |= (KEY_ACTION_SHORT | KEY_PENDING); if( key->event) NutEventPost( key->event); if( key->callback) (*key->callback)(); } } else if( (key->newState & KEY_IS_DOWN) && (key->fx==KEY_ACTION_HOLD)) { /* key still down */ if( ((now - key->TimeDown) > key->fxt) && ((key->newState & KEY_IS_LOCKED) == 0)) { KPRINTF("KH %08lx E:%08lx\n", (uint32_t)key, (uint32_t)(key->event)); key->newState |= (KEY_ACTION_HOLD | KEY_PENDING); if( key->event) NutEventPost( key->event); if( key->callback) (*key->callback)(); key->newState |= KEY_IS_LOCKED; } } /* Backup new state of key */ key->lastState = key->newState; /* Advance to next key */ key = key->next; } } } } /*! * \brief Internal functionn to setup a GPIO port for reading a connected key. * * \param key Handle to the key, the port will be assigned to. * * \return 0 if key could be set up, -1 if not. */ int InitKEY( KEYEventT *key ) { if( key->bank < IOXP_PORT0) { /* configure standard GPIO pin */ GpioPinConfigSet( key->bank, key->pin, GPIO_CFG_PULLUP|GPIO_CFG_DEBOUNCE); return 0; } #ifdef KEY_SUPPORT_IOEXP else { /* configure externally connected GPIO pin as input */ IOExpPinConfigSet( key->bank, key->pin, 0); return 0; } #endif return -1; } int NutAssignKeyEvt( HANDLE *keyhp, HANDLE *event) { KEYEventT *key; if( keyhp==NULL) return -1; key = (KEYEventT*)keyhp; key->event = event; key->newState &= ~KEY_PENDING; return 0; } int NutAssignKeyFkt( HANDLE *keyhp, void (*callback)(void)) { KEYEventT *key; if( keyhp==NULL) return -1; key = (KEYEventT*)keyhp; key->callback = callback; key->newState &= ~KEY_PENDING; return 0; } /*! * \brief register a key and describe its function * * \param keyh The handle to the LED that should be controlled. * \param event The event handle for locking a task until the key is active. * \param bank The GPIO or PCA9555 port, where key is connected to. * \param pin Pin of the bank, where key is connected to. * \param fx Action of key for releasing the event. * \param fxt Time parameter for action. * * \return 0 on success, -1 on any error. * * \note Following actions can be assigned to a key: * * * * * * *
fx fxtEvent released
>KEY_ACTION_DOWN 0 on Key press
>KEY_ACTION_UP 0 on Key release
>KEY_ACTION_HOLD n on Key pressed for n ms
>KEY_ACTION_SHORTn on Key released before n ms
*/ int NutRegisterKey( HANDLE *keyhp, int bank, int pin, int fx, uint32_t fxt) { KEYEventT *key; NUTASSERT( keyhp!=NULL); /* Check memory constraints and assign memory to new led struct */ key = malloc( sizeof(KEYEventT)); *keyhp = (void*)key; if( key == NULL) { return -1; } /* Preset new key struct */ key->bank = bank; key->pin = pin; key->fx = fx; key->fxt = fxt; key->callback = NULL; key->event = NULL; key->lastState = key->newState = 1; key->TimeDown = 0; /* Initalize key hardware */ InitKEY( key); /* Assign the key to the key chain */ NutEnterCritical(); if( first_key == NULL) { /* it is the first key */ first_key = key; } else { key->next = first_key; first_key = key; } NutExitCritical(); if( key_tmr == NULL) { NutThreadCreate( "sys_key", sys_key, &key_evt, 256); key_tmr = NutTimerStart(10, KeyTimerCb, &key_evt, 0); } KPRINTF("KREG %08lx E:%08lx\n", (uint32_t)key, (uint32_t)(key->event)); return 0; } /*@}*/