⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 switches.c

📁 这项工程将让您把自己的MP3播放器平台
💻 C
字号:
//++
//switches.c - driver for the rotary encoder and push button ...
//
// Copyright (C) 2005 by Spare Time Gizmos.  All rights reserved.
//
// This file is part of the Spare Time Gizmos' MP3 Player firmware.
//
// This firmware is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA  02111-1307  USA.
//
//DESCRIPTION:
//   This module implements a simplistic driver for the player's sole user
// interface control - the rotary encoder, which also includes a push button.
// The interface presented to the rest of the code is equally simplistic - the
// encoder has an associated single byte variable which counts from -128..+127.
// Each count represents one click of the encoder, and positive counts are CW
// rotation; negative counts are CCW.  The push button also has an associated
// variable which counts the time, in 10ms units, that the button is held down.
// If the button is up, this count will be zero.  This counter, rather than a
// simple flag, allows the background code to differentiate a "quick push and
// release" operation from a "push and hold".
//
//   The implementation is a simple interrupt driven polling operation driven
// by timer 0 interrupts.  Using another, separate, timer for this operation
// is grossly wasteful; we could just as well piggy back off the same timer
// used in the timer.c module, but the 8051's timer 0 is unused and would just
// go to waste if we don't give it something to do.  Besides, using a separate
// timer makes the code easier by eliminating an unnecessary dependency between
// this module and the timer.  It also makes it easy to adjust the polling
// interval without affecting the timer.
//
//   Let's think a minute about the polling interval.  The rotary encoder used
// as 16 steps per rotation, and one of the encoder outputs will change with
// each step.  A little fooling around with an oscilloscope (I love my Tek!)
// reveals that the shortest pulse I can generate is around 4ms, but remember
// that the other phase changed in the middle of that, so we're talking about
// a sampling interval on the order of 2ms.  As a practical matter we could
// probably get by with twice that for real world usage.  For the push button
// we want a slower polling rate, something around 10-15ms, so we arrange to
// poll the push button every fourth or eighth (or whatever) interrupt.
//
//   Notice that the polled approach gives us switch debouncing for "free".
// If the rotary encoder is only polled, say, every 4ms, then any bounce events
// shorter than that will never be noticed.  For an optical encoder this is
// moot (since they don't bounce!), but optical encoders are expensive and the
// much cheaper mechanical encoders certainly do bounce.  This also explains
// why we don't want to poll the push button too fast - 2ms, for example, is
// within the bouncing time range for a mechanical push button.
//
//REVISION HISTORY:
// dd-mmm-yy    who     description
//  9-Jun-05	RLA	New file.
// 12-Oct-05	RLA	Make SwitchesInterrupt() PUBLIC for SDCC.
//--

// Include files...
#include <stdio.h>		// needed so DBGOUT(()) can find printf!
#include "standard.h"		// standard types - BYTE, WORD, BOOL, etc
#include "reg66x.h"		// declarations for Philips 89C66x processors
#include "player.h"		// project wide (hardware configuration) declarations
#include "post.h"		// power on self test (POST) failure codes
#include "switches.h"		// declarations for this module

// These are the public interface for this module that I promised...
PUBLIC signed char DATA g_nEncoderCount;// number of clicks encoder has been turned
PUBLIC BYTE DATA g_bButtonTime;		// time (10 ms units button has been pressed

// Local variables for this module...
PRIVATE BYTE DATA m_bLastEncoderState;	// two bits with the last encoder state
PRIVATE BYTE DATA m_bButtonPollDelay;	// count down until the button is polled



//++
//--
PUBLIC void SwitchesInterrupt (void) interrupt TIMER0_VECTOR
{
  BYTE bCurrentState;

  // First, reset and restart the timer...
  TH0 = HIBYTE(TIMER0_RELOAD);  TL0 = LOBYTE(TIMER0_RELOAD);  TR0 = 1;

  // Turn the current encoder state into a two bit value (A,B) ...
  bCurrentState = ENCODER_A ? 2 : 0;
  if (ENCODER_B) bCurrentState |= 1;

  //   This is basically a giant (well, sixteen entries) table that figures
  // out what the encoder has done based on its current state and its last
  // known state.  There are four possibilities - 1) rotated CCW, 2) rotated CW,
  // 3) not changed, or 4) illegal.  Since the encoder uses gray code, any legal
  // state change will change only 1 bit; any state change that changes two bits
  // is illegal and (probably) indicates that we aren't sampling fast enough and
  // missed an encoder change.
  //
  //   This may seem like a wasteful implementation, but C51 is smart enough
  // to generate a jump table for this case statement, so the actual number of
  // instructions that are executed in any one case is fairly small...
  switch (((m_bLastEncoderState<<2) | bCurrentState) & 0xF) {

    // CCW (counter clockwise) rotation cases...
    case 0x1:	// 00 -> 01
    case 0x7:	// 01 -> 11
    case 0x8:	// 10 -> 00
    case 0xE:	// 11 -> 10
	if (g_nEncoderCount != -128) --g_nEncoderCount;  break;

    // CW (clockwise) rotation cases...
    case 0x2:	/* 00 -> 10 */
    case 0x4:	/* 01 -> 00 */
    case 0xB:	/* 10 -> 11 */
    case 0xD:   /* 11 -> 01 */
        if (g_nEncoderCount != 127) ++g_nEncoderCount;  break;

    // No change (i.e. new state == old state) cases...
    case 0x0:	/* 00 -> 00 */
    case 0x5:   /* 01 -> 01 */
    case 0xA:	/* 10 -> 10 */
    case 0xF:	/* 11 -> 11 */
        break;

    // And illegal (two bits changed) cases...
    case 0x3:	/* 00 -> 11 */
    case 0x6:	/* 01 -> 10 */
    case 0x9:	/* 10 -> 01 */
    case 0xC:	/* 11 -> 00 */
	break; //POST(PC_ENCODER, FALSE);
  }

  // And now save the current state for next time around..
  m_bLastEncoderState = bCurrentState;

  // If we've had enough interrupts, then it's time to sample the push button too ...
  if (--m_bButtonPollDelay == 0) {
    m_bButtonPollDelay = BUTTON_COUNT;
    //   If the button is down now, then increment the button down timer up to 255,
    // where we stop.  Never allow the down timer to roll over from 255 to zero, 
    // because that makes it look like the button is released!  However, if the button
    // really is released, then zero the button timer for real.  Remember that the
    // PUSH_BUTTON input is active low (i.e. 0 -> button pressed).
    if (!PUSH_BUTTON) {
      if (g_bButtonTime != 255) ++g_bButtonTime;
    } else
      g_bButtonTime = 0;
  }
}


//++
//--
PUBLIC void InitializeSwitches (void)
{
  // Initialize the local variables...
  m_bButtonPollDelay = BUTTON_COUNT;
  g_nEncoderCount = g_bButtonTime = 0;
  m_bLastEncoderState = ENCODER_A ? 2 : 0;
  if (ENCODER_B) m_bLastEncoderState |= 1;
  //   All we really need to do to initialize timer 0 is to set the mode, enable
  // timer 0 interrupts, and then set the timer 0 flag.  This will cause an
  // immediate interrupt, which will promptly reload the counter registers...
  TMOD = (TMOD & 0xF0) | 0x01;  TF0 = 1;  ET0 = 1;
  DBGOUT(("Encoder and button initialized ...\n"));
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -