📄 buttons.c
字号:
/*
* Copyright (C) 2007 by egnite Software GmbH. All rights reserved.
* Copyright (C) 2008 by egnite GmbH. All rights reserved.
*
* 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 THE COPYRIGHT HOLDERS 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 THE
* COPYRIGHT OWNER 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 buttons.c
* \brief Simple button interface.
*
* Handles 3 toggle switches connected to 3 dedicated GPIO lines. It is
* expected, that the port pin is driven low when the related button is
* pressed. Auto repeat is supported.
*
* The following code sample shows how to use the button interface.
* \code
* #include "buttons.h"
*
* char key;
*
* ButtonInit();
* key = ButtonRead(500);
* \endcode
*
* \note The macro USE_BUTTONS must be set to 1 to activate this code.
*
* \verbatim
* $Id$
* \endverbatim
*/
#include <cfg/os.h>
#include <cfg/clock.h>
#include <arch/arm.h>
#include <dev/irqreg.h>
#include <sys/event.h>
#include <sys/timer.h>
#include "buttons.h"
/*!
* \def BTN_TC_ID
* \brief Button scan timer ID.
*
* Specifies the timer/counter that will be used to generate periodical
* interrupts for scanning the buttons.
*
*! \def BTN_PIO_ID
* \brief PIO ID of the button interface.
*
* All buttons must be connected to the same PIO port.
*
* \def BTN_DOWN_BIT
* \brief PIO bit number of the button labeled DOWN.
*
* This button is used to reduce the volume or move to
* the previous menu item.
*
* If not specified, this button will not be available.
*
* \def BTN_SELECT_BIT
* \brief PIO bit number of the button labeled SELECT.
*
* This button is used to enter menu mode or to select
* the currently displayed menu item.
*
* If not specified, this button will not be available.
*
* \def BTN_UP_BIT
* \brief PIO bit number of the button labeled UP.
*
* This button is used to increase the volume or move to
* the next menu item.
*
* If not specified, this button will not be available.
*/
#if defined(AT91SAM7X_EK) /* Board */
/* Calypso board configuration. */
#define BTN_TC_ID TC1_ID
#define BTN_PIO_ID PIOB_ID
#define BTN_DOWN_BIT 27
//#define BTN_SELECT_BIT 28 //Doesn't work. Reason currently unknown.
#define BTN_UP_BIT 29
#elif defined(AT91SAM9260_EK)
/* Calypso board configuration. */
#define BTN_TC_ID TC1_ID
#define BTN_PIO_ID PIOB_ID
#define BTN_DOWN_BIT 8
#define BTN_SELECT_BIT 9
#define BTN_UP_BIT 10
#elif defined(ELEKTOR_IR1)
/* EIR1 configuration. */
#define BTN_TC_ID TC1_ID
#define BTN_PIO_ID PIOB_ID
#define BTN_DOWN_BIT 24
#define BTN_SELECT_BIT 25
#define BTN_UP_BIT 26
#endif /* Board */
#ifndef BTN_SCAN_FREQ
/*!
*\brief Number of button scans per second.
*/
#define BTN_SCAN_FREQ 128UL
#endif
#ifndef BTN_REPEAT_FIRST
/*!
* \brief Number of scans until the first repeat is triggered.
*/
#define BTN_REPEAT_FIRST 100
#endif
#ifndef BTN_REPEAT_NEXT
/*!
* \brief Number of scans until all following repeats are triggered.
*/
#define BTN_REPEAT_NEXT 25
#endif
/*!
* \def BTN_PIO_PE_REG
* \brief PIO enable register of the button interface.
*
* The compiler evaluates \ref BTN_PIO_ID to determine this register.
*
* \def BTN_PIO_OD_REG
* \brief PIO output disable register of the button interface.
*
* The compiler evaluates \ref BTN_PIO_ID to determine this register.
*
* \def BTN_PIO_PDS_REG
* \brief Pin data status register of the button interface.
*
* The compiler evaluates \ref BTN_PIO_ID to determine this register.
*
* \def BTN_PIO_PUE_REG
* \brief Pull-up enable register of the button interface.
*
* The compiler evaluates \ref BTN_PIO_ID to determine this register.
*/
#if BTN_PIO_ID == PIOB_ID
#define BTN_PIO_PE_REG PIOB_PER
#define BTN_PIO_OD_REG PIOB_ODR
#define BTN_PIO_PDS_REG PIOB_PDSR
#define BTN_PIO_PUE_REG PIOB_PUER
#elif BTN_PIO_ID == PIOA_ID
#define BTN_PIO_PE_REG PIOA_PER
#define BTN_PIO_OD_REG PIOA_ODR
#define BTN_PIO_PDS_REG PIOA_PDSR
#define BTN_PIO_PUE_REG PIOA_PUER
#elif BTN_PIO_ID == PIOC_ID
#define BTN_PIO_PE_REG PIOC_PER
#define BTN_PIO_OD_REG PIOC_ODR
#define BTN_PIO_PDS_REG PIOC_PDSR
#define BTN_PIO_PUE_REG PIOC_PUER
#else
#define BTN_PIO_PE_REG PIO_PER
#define BTN_PIO_OD_REG PIO_ODR
#define BTN_PIO_PDS_REG PIO_PDSR
#define BTN_PIO_PUE_REG PIO_PUER
#endif
/*! \brief PIO bit mask of the SELECT button. */
#ifdef BTN_SELECT_BIT
#define BTN_SELECT _BV(BTN_SELECT_BIT)
#else
#define BTN_SELECT 0
#endif
/*! \brief PIO bit mask of the UP button. */
#ifdef BTN_UP_BIT
#define BTN_UP _BV(BTN_UP_BIT)
#else
#define BTN_UP 0
#endif
/*! \brief PIO bit mask of the DOWN button. */
#ifdef BTN_DOWN_BIT
#define BTN_DOWN _BV(BTN_DOWN_BIT)
#else
#define BTN_DOWN 0
#endif
/*!
* \def sig_BTN_TC
* \brief Interrupt signal of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine the Nut/OS interrupt signal.
*
* \def BTN_TC_CC_REG
* \brief Channel control register of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine this register.
*
* \def BTN_TC_CM_REG
* \brief Channel mode register of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine this register.
*
* \def BTN_TC_IE_REG
* \brief Interrupt enable register of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine this register.
*
* \def BTN_TC_ID_REG
* \brief Interrupt disable register of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine this register.
*
* \def BTN_TC_S_REG
* \brief Status register of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine this register.
*
* \def BTN_TC_RC_REG
* \brief Register C of the button scan timer.
*
* The compiler evaluates \ref BTN_TC_ID to determine this register.
*
*/
#if BTN_TC_ID == TC1_ID
#define sig_BTN_TC sig_TC1
#define BTN_TC_CC_REG TC1_CCR
#define BTN_TC_CM_REG TC1_CMR
#define BTN_TC_IE_REG TC1_IER
#define BTN_TC_ID_REG TC1_IDR
#define BTN_TC_S_REG TC1_SR
#define BTN_TC_RC_REG TC1_RC
#elif BTN_TC_ID == TC2_ID
#define sig_BTN_TC sig_TC2
#define BTN_TC_CC_REG TC2_CCR
#define BTN_TC_CM_REG TC2_CMR
#define BTN_TC_IE_REG TC2_IER
#define BTN_TC_ID_REG TC2_IDR
#define BTN_TC_S_REG TC2_SR
#define BTN_TC_RC_REG TC2_RC
#endif
/*!
* \brief Button event queue.
*
* The button scan timer interrupt handler will post an event to this
* queue whenever a new button had been pressed or the repeat timer
* elapses with a button still pressed.
*/
static HANDLE btn_que;
/*!
* \brief Last button code.
*
* For each button that had been pressed during the last scan interrupt,
* the related bit \ref BTN_SELECT, \ref BTN_DOWN or \ref BTN_UP is set.
*/
static volatile u_int btn_pressed;
#if USE_BUTTONS
/*!
* \brief Button scan timer interrupt handler.
*/
static void ScanTimerInterrupt(void *arg)
{
static u_int btn_prev;
static u_int btn_repeat;
u_int btn_code;
/* Read the negated GPIO line status. Pressing a button drives the pin low. */
btn_code = ~inr(BTN_PIO_PDS_REG) & (BTN_DOWN | BTN_SELECT | BTN_UP);
if (btn_code) {
/* A button has been or still is pressed. */
if (btn_code != btn_prev) {
/* This is a new button. Immediately post an event. */
btn_pressed = btn_code;
NutEventPostFromIrq(&btn_que);
/* Initialize the repeat rate. */
btn_repeat = BTN_REPEAT_FIRST;
}
else if (btn_repeat == 0) {
/* Button is still pressed and the repeat time elapsed.
Post a new event. */
NutEventPostFromIrq(&btn_que);
/* Re-initialize the repeat rate. */
btn_repeat = BTN_REPEAT_NEXT;
}
else {
/* Button is still pressed. Wait until repeat time elapses. */
btn_repeat--;
}
}
/* Keep the last code, so we are able to distinguish whether the user
is still pressing the same or selected a new button. */
btn_prev = btn_code;
}
#endif /* USE_BUTTONS */
/*!
* \brief Initialize the button interface.
*
* Configures and starts a continously running timer interrupt, which scans
* three GPIO lines. A low level on any line indicates, that the related
* button has been pressed by the user.
*
* The application must call ButtonRead() to read the code of a pressed button.
*/
void ButtonInit(void)
{
#if USE_BUTTONS
int dummy;
/* Enable scan timer clock. */
outr(PMC_PCER, _BV(BTN_TC_ID) | _BV(BTN_PIO_ID));
/* Disable the Clock Counter */
outr(BTN_TC_CC_REG, TC_CLKDIS);
/* Disable all interrupts */
outr(BTN_TC_ID_REG, 0xFFFFFFFF);
/* Clear the status register. */
dummy = inr(BTN_TC_S_REG);
/* Select divider and compare trigger */
outr(BTN_TC_CM_REG, TC_CLKS_MCK32 | TC_CPCTRG);
/* Enable the Clock counter */
outr(BTN_TC_CC_REG, TC_CLKEN);
/* Validate the RC compare interrupt */
outr(BTN_TC_IE_REG, TC_CPCS);
/* Register timer interrupt handler. */
NutRegisterIrqHandler(&sig_BTN_TC, ScanTimerInterrupt, 0);
/* Set to lowest priority. */
NutIrqSetPriority(&sig_BTN_TC, 0);
/* Enable timer interrupts */
NutIrqEnable(&sig_BTN_TC);
/* Set compare value for specified scan frequency. */
#if defined(AT91_PLL_MAINCK)
outr(BTN_TC_RC_REG, At91GetMasterClock() / (32 * BTN_SCAN_FREQ));
#else
outr(BTN_TC_RC_REG, NutGetCpuClock() / (32 * BTN_SCAN_FREQ));
#endif
/* Initialize GPIO lines for buttons. */
outr(BTN_PIO_PE_REG, BTN_SELECT | BTN_UP | BTN_DOWN);
outr(BTN_PIO_OD_REG, BTN_SELECT | BTN_UP | BTN_DOWN);
outr(BTN_PIO_PUE_REG, BTN_SELECT | BTN_UP | BTN_DOWN);
/* Software trigger starts the scan timer. */
outr(BTN_TC_CC_REG, TC_SWTRG);
#endif
}
/*!
* \brief Wait until the user pressed a button.
*
* This routine will not handle concurrently pressed buttons, but
* return the button with the highest priority. SELECT has the
* highest and UP has the lowest priority.
*
* \param tmo Maximum number of milliseconds to wait.
*
* \return Key code of the button, either \ref KEYCODE_SELECT,
* \ref KEYCODE_DOWN or \ref KEYCODE_UP. If no button had been
* pressed within the given time, then zero is returned.
*/
char ButtonRead(u_long tmo)
{
/* Wait for a button event from the interrupt handler. */
if (NutEventWait(&btn_que, tmo) == 0) {
/* Copy the volatile value to a local variable. */
u_int pressed = btn_pressed;
if (pressed & BTN_SELECT) {
return KEYCODE_SELECT;
}
if (pressed & BTN_DOWN) {
return KEYCODE_DOWN;
}
if (pressed & BTN_UP) {
return KEYCODE_UP;
}
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -