Projekte

  |   Rolladensteuerung   |   Belichtungstimer   |   Drehzahlmesser   |   AVRFK   |   Dunstabzugsklappe   |   FB-Orgel   |   Termowächter   |   Erinnermich   |   LED-Dimmer   |  

  |   Timer Template Klasse   |   Tastatur Klasse   |   LCD Klasse   |  


Tastatur Klasse

intxtaste.h

   1:  /********************************************************************
   2:  *     Copyright (C) 2011  Philipp Klostermann
   3:  *
   4:  *    intxtaste.h
   5:  *
   6:  *    This program is free software: you can redistribute it and/or modify
   7:  *    it under the terms of the GNU General Public License as published by
   8:  *    the Free Software Foundation, either version 3 of the License, or
   9:  *    any later version.
  10:  *
  11:  *    This program is distributed in the hope that it will be useful,
  12:  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13:  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14:  *    GNU General Public License for more details.
  15:  *
  16:  *    You should have received a copy of the GNU General Public License
  17:  *    along with this program.  If not, see here
  18:  *
  19:  *	Email the autor: philipp.klostermann@pksl.de
  20:  *	Address: 
  21:  *	 Philipp Klostermann
  22:  *	 Luchert 45
  23:  *	 56593 Horhausen
  24:  *	 Germany
  25:  *********************************************************************/
  26:  
  27:  #ifndef __INTXTASTE_H_INCLUDED__
  28:  #define __INTXTASTE_H_INCLUDED__
  29:  
  30:  // #define INTXTASTE_NOINLINE
  31:  
  32:  //////////////////////////
  33:  // Konfiguration:
  34:  
  35:  // Ist eine Matrix-Tastatur angeschlossen? 
  36:  // (Ansonsten MATRIXKBD auskommentieren bzw. nicht definieren!)
  37:  // #define MATRIXKBD
  38:  
  39:  // Wie heißt das Interrupt Bit im GICR bzw. EIMSK?
  40:  #ifndef TASTE_INTERRUPT
  41:  #define TASTE_INTERRUPT        INT1
  42:  #endif

  43:  
  44:  // Wie viele Millisekunden soll das Prellen maximal dauern können?
  45:  #define TASTE_PRELLTIME_MS     20 // Siehe
  46:  
  47:  //////////////////////////
  48:  // Interne Definitionen:
  49:  
  50:  // Folgende defaults (TASTE_PRESCALER = 3 und TASTE_PRESCALER_TEILER = 64) 
  51:  // nur bei Bedarf in eines der Wertepaare ändern:
  52:  // (1 1), (2 8), (3 64), (4 256), (5 1024)
  53:  #ifndef TASTE_PRESCALER
  54:  #define TASTE_PRESCALER 3
  55:  #endif

  56:  #ifndef TASTE_PRESCALER_TEILER
  57:  #define TASTE_PRESCALER_TEILER 64
  58:  #endif

  59:  
  60:  #if (TASTE_INTERRUPT == INT0)
  61:  	// Welcher Interrupt-Vektor soll verwendet werden?
  62:  	#define TASTE_INTERRUPT_VEKTOR INT0_vect
  63:  	// Wie heißt das Low-Bit der Interrupt Sense Control Bits in MCUCR?
  64:  	#define TASTE_ISC              ISC00
  65:  	// Auf welchem Port liegt der Anschluss dafür?
  66:  	#define TASTE_PORT             PORTD
  67:  	// Wie lautet das entsprechende Eingabe-Register?
  68:  	#define TASTE_PIN              PIND
  69:  	// An welcher Bitposition liegt der Anschluss?
  70:  	#define TASTE_BIT              PD2
  71:  #else

  72:  	// Welcher Interrupt-Vektor soll verwendet werden?
  73:  	#define TASTE_INTERRUPT_VEKTOR INT1_vect
  74:  	// Wie heißt das Low-Bit der Interrupt Sense Control Bits in MCUCR?
  75:  	#define TASTE_ISC              ISC10
  76:  	// Auf welchem Port liegt der Anschluss dafür?
  77:  	#define TASTE_PORT             PORTD
  78:  	// Wie lautet das entsprechende Eingabe-Register?
  79:  	#define TASTE_PIN              PIND
  80:  	// An welcher Bitposition liegt der Anschluss?
  81:  	#define TASTE_BIT              PD3
  82:  #endif

  83:  
  84:  #define TASTE_OVERFLOWS_PRO_S (F_CPU / (256 * TASTE_PRESCALER_TEILER)) // 488
  85:  #define TASTE_ZAEHLE_OVERFLOWS ((TASTE_OVERFLOWS_PRO_S * TASTE_PRELLTIME_MS) / 1000)
  86:  #if (TASTE_ZAEHLE_OVERFLOWS < 1)
  87:  	#error "TASTE_PRELLTIME_MS zu klein. Vielleicht reicht es, den Prescaler zu erhöhen."
  88:  #endif

  89:  #if (TASTE_ZAEHLE_OVERFLOWS > 255)
  90:  	#error "TASTE_PRELLTIME_MS zu groß. Vielleicht reicht es, den Prescaler zu verringern."
  91:  #endif

  92:  
  93:  class CIntXTaste;
  94:  
  95:  extern CIntXTaste* g_pTheIntXTaste;
  96:  
  97:  #ifdef INTXTASTE_NOINLINE
  98:  class CIntXTaste
  99:  {
 100:  public:
 101:  	CIntXTaste();
 102:  	// Gibt true zurück, wenn asm("sleep") aufgerufen werden kann
 103:  	bool SleepOK() const;
 104:  
 105:  #ifdef MATRIXKBD
 106:  	virtual void VerarbeiteTastendruck(uint16_t Tastencode);
 107:  #else // #ifdef MATRIXKBD
 108:  	virtual void VerarbeiteTastendruck();
 109:  #endif

 110:  	virtual void VerarbeiteLoslassen();
 111:  
 112:  	void TasteInterrupt();
 113:  	void TimerInterrupt();
 114:  
 115:  protected: 
 116:  	void SetzeInterruptBeiPegelNull();
 117:  	void SetzeInterruptBeiFlankeNullNachEins();
 118:  	void DeaktiviereTasteInterrupt();
 119:  	void PrellenAbwartenTimerStarten();
 120:  	volatile bool m_bWarteTastendruck;
 121:  	volatile uint8_t m_nWarteZaehler;
 122:  };
 123:  #else // #ifdef INTXTASTE_NOINLINE
 124:  class CIntXTaste
 125:  {
 126:  public:
 127:  	CIntXTaste()
 128:  	{
 129:  		g_pTheIntXTaste = this;
 130:  
 131:  		// Taste mit Pullup versehen:
 132:  		TASTE_PORT |= (1 << TASTE_BIT);
 133:  #ifdef MATRIXKBD
 134:  		// Spalten als Augänge und auf 0:
 135:  		MATRIXKBD_COL_DDR |= MATRIXKBD_COL_MASK;
 136:  		MATRIXKBD_COL_PORT &= ~MATRIXKBD_COL_MASK;
 137:  		// Zeilen als Eingänge mit Pullup:
 138:  		MATRIXKBD_ROW_DDR &= ~MATRIXKBD_ROW_MASK;
 139:  		MATRIXKBD_ROW_PORT |= MATRIXKBD_ROW_MASK;
 140:  #endif

 141:  		m_bWarteTastendruck = 1;
 142:  		m_nWarteZaehler = 0;
 143:  
 144:  		SetzeInterruptBeiPegelNull();
 145:  	}
 146:  	// Gibt true zurück, wenn asm("sleep") aufgerufen werden kann
 147:  	bool SleepOK() const
 148:  	{
 149:  		return ((MCUCR & (3 << TASTE_ISC)) == 0) && m_bWarteTastendruck && (!m_nWarteZaehler);
 150:  	}
 151:  
 152:  	inline void TasteInterrupt()
 153:  	{
 154:  		// Würde gerne Interrupts einschalten, ohne ständig aufgerufen zu werden:
 155:  		DeaktiviereTasteInterrupt();
 156:  		PrellenAbwartenTimerStarten();
 157:  
 158:  		if (m_bWarteTastendruck)
 159:  		{
 160:  #ifndef MATRIXKBD
 161:  			VerarbeiteTastendruck();
 162:  #endif

 163:  		}
 164:  		else
 165:  		{
 166:  			VerarbeiteLoslassen();
 167:  		}
 168:  	}
 169:  	inline void TimerInterrupt()
 170:  	{
 171:  		if (!(--m_nWarteZaehler))
 172:  		{
 173:  #ifdef __AVR_ATmega88__
 174:  			TCCR0B = (0 << CS00); // Timer deaktivieren
 175:  #else

 176:  			TCCR0 = (0 << CS00); // Timer deaktivieren
 177:  #endif

 178:  			if (m_bWarteTastendruck)
 179:  			{
 180:  				// Der Tastendruck wurde verarbeitet und die Taste ist wahrscheinlich noch gedrückt.
 181:  				m_bWarteTastendruck = 0;
 182:  				if (!(TASTE_PIN & (1 << TASTE_BIT)))
 183:  				{
 184:  #ifdef MATRIXKBD
 185:  					TastaturMatrixEinlesen();
 186:  #endif

 187:  					SetzeInterruptBeiFlankeNullNachEins(); // Interrupt beim loslassen
 188:  				}
 189:  				else
 190:  				{
 191:  					// Der unwahrscheinliche Fall ist eingetreten, dass der User innerhalb 100ms losgelassen hat,
 192:  					// was wir nicht per Interrupt mitbekommen haben, da INTx während der Prellphase deaktiviert ist.
 193:  					// Das könnte noch prellen. Also noch eine Warterunde, bis SetzeInterruptBeiPegelNull()
 194:  					// aufgerufen wird.
 195:  					// Dass im Falle einer Matrixtastatur eine zu schnell gedrücke Taste nicht erkannt wird, ist 
 196:  					// die günstigste Variante.
 197:  					PrellenAbwartenTimerStarten();
 198:  					VerarbeiteLoslassen();
 199:  				}
 200:  			}
 201:  			else
 202:  			{
 203:  				// Das Loslassen wurde verarbeitet und die Taste ist wahrscheinlich noch losgelassen.
 204:  				m_bWarteTastendruck = 1;
 205:  				if (TASTE_PIN & (1 << TASTE_BIT))
 206:  				{
 207:  					SetzeInterruptBeiPegelNull(); // Interrupt beim Drücken
 208:  				}
 209:  				else
 210:  				{
 211:  					// Der unwahrscheinliche Fall ist eingetreten, dass der User innerhalb 100ms wieder gedrückt hat,
 212:  					// was wir nicht per Interrupt mitbekommen haben, da INTx während der Prellphase deaktiviert ist.
 213:  					// Das könnte noch prellen. Also noch eine Warterunde, bis SetzeInterruptBeiFlankeNullNachEins()
 214:  					// aufgerufen und ggf. die Matrix-Tastatur eingelesen wird.
 215:  					PrellenAbwartenTimerStarten();
 216:  #ifndef MATRIXKBD
 217:  					VerarbeiteTastendruck();
 218:  #endif

 219:  				}
 220:  			}
 221:  		}
 222:  	}
 223:  
 224:  	virtual void VerarbeiteLoslassen();
 225:  
 226:  #ifdef MATRIXKBD
 227:  	void TastaturMatrixEinlesen()
 228:  	{
 229:  	// Wir nehmen die Spalten von GND weg, und legen hintereinander GND an einzelne Spalten.
 230:  	// Die gerade nicht aktiven Spalten können wir nicht einfach auf High legen, da es sonst
 231:  	// zu einem Kurzschluss kommt, wenn zwei Tasten in der gleichen Zeile gedrückt werden.
 232:  	// Wir können stattdessen die jeweils inaktiven Spaltenausgänge als Eingänge definieren
 233:  	// Dabei sollten die Pullups aktiviert werden, um an diesen Eingängen definiertes
 234:  	// Potential zu haben und so Strom zu sparen.
 235:  		uint16_t erg;
 236:  		uint8_t spalte, zeile, outmask, inmask;
 237:  		erg = 0xff; // Keine Taste
 238:  		for (spalte = 0, outmask = (1 << MATRIXKBD_COL_LSB); spalte < MATRIXKBD_COLS; outmask <<= 1, ++spalte)
 239:  		{
 240:  			MATRIXKBD_COL_DDR = (MATRIXKBD_COL_DDR & (~MATRIXKBD_COL_MASK)) | outmask; // Eine Spalte als Augang..
 241:  			   MATRIXKBD_COL_PORT = (MATRIXKBD_COL_PORT | (MATRIXKBD_COL_MASK)) & ~outmask; // diese Spalte low, die anderen high, also als pullup, da Eingänge.
 242:  			// MATRIXKBD_COL_PORT = (MATRIXKBD_COL_PORT | (~MATRIXKBD_COL_MASK)) & (~outmask); // ..und low. Alle anderen (und die Zeileneingänge) mit Pullup.
 243:  			for (zeile = 0, inmask = (1 << MATRIXKBD_ROW_LSB); zeile < MATRIXKBD_ROWS; inmask <<= 1, ++zeile)
 244:  			{
 245:  				if (!(MATRIXKBD_ROW_PIN & inmask))
 246:  				{
 247:  					// Diese Spalte zieht diese Zeile herunter;
 248:  					erg = (((uint16_t)zeile) << 8) | spalte;
 249:  
 250:  					zeile = MATRIXKBD_ROWS;
 251:  					spalte = MATRIXKBD_COLS;
 252:  				}
 253:  			}
 254:  		}
 255:  		// Port wieder in Ruhezustand setzen:
 256:  		// Spaltenausgänge aktivieren
 257:  		MATRIXKBD_COL_DDR |= MATRIXKBD_COL_MASK;
 258:  		// Pullups an:
 259:  		MATRIXKBD_ROW_PORT |= MATRIXKBD_ROW_MASK;
 260:  		 // und Spaltenausgänge auf GND:
 261:  		MATRIXKBD_COL_PORT &= ~MATRIXKBD_COL_MASK;
 262:  
 263:  		VerarbeiteTastendruck(erg);
 264:  	}
 265:  
 266:  	virtual void VerarbeiteTastendruck(uint16_t Tastencode);
 267:  #else // #ifdef MATRIXKBD
 268:  	virtual void VerarbeiteTastendruck();
 269:  #endif // #else // #ifdef MATRIXKBD
 270:  
 271:  
 272:  protected: 
 273:  	void SetzeInterruptBeiPegelNull()
 274:  	{
 275:  		// bWarteTastendruck wurde auf 1 gesetzt
 276:  		MCUCR &= ~(3 << TASTE_ISC); // Interrupt solange gedrückt ist
 277:  #ifdef __AVR_ATmega88__
 278:  		EIMSK |= (1 << TASTE_INTERRUPT);
 279:  #else

 280:  		GICR |= (1 << TASTE_INTERRUPT);
 281:  #endif

 282:  		sei();
 283:  	}
 284:  	void SetzeInterruptBeiFlankeNullNachEins()
 285:  	{
 286:  		// bWarteTastendruck wurde auf 0 gesetzt
 287:  		MCUCR |= (3 << TASTE_ISC); // Interrupt beim loslassen
 288:  #ifdef __AVR_ATmega88__
 289:  		EIMSK |= (1 << TASTE_INTERRUPT);
 290:  #else

 291:  		GICR |= (1 << TASTE_INTERRUPT);
 292:  #endif

 293:  		sei();
 294:  		MCUCR &= ~(1 << SE); // Falls der Interrupt, der diese Funktion aufgerufen hat, nach dem Setzen der
 295:  							 // SM-Bits aber vor einem sleep-Befehl erfolgt, muss der sleep-Befehl ohne Wirkung bleiben.
 296:  	}
 297:  	void DeaktiviereTasteInterrupt()
 298:  	{
 299:  #ifdef __AVR_ATmega88__
 300:  		EIMSK &= ~(1 << TASTE_INTERRUPT);
 301:  #else

 302:  		GICR &= ~(1 << TASTE_INTERRUPT);
 303:  #endif

 304:  		sei();
 305:  		MCUCR &= ~(1 << SE); // S.o.
 306:  	}
 307:  	void PrellenAbwartenTimerStarten()
 308:  	{
 309:  		TCNT0 = 0;
 310:  		m_nWarteZaehler = TASTE_ZAEHLE_OVERFLOWS;
 311:  #ifdef __AVR_ATmega88__
 312:  		TCCR0B = (TASTE_PRESCALER << CS00); // Teiler = 64
 313:  		TIMSK0 |= (1 << TOIE0);
 314:  #else

 315:  		TCCR0 = (TASTE_PRESCALER << CS00); // Teiler = 64
 316:  		TIMSK |= (1 << TOIE0);
 317:  #endif

 318:  		sei();
 319:  	}
 320:  	volatile bool m_bWarteTastendruck;
 321:  	volatile uint8_t m_nWarteZaehler;
 322:  };
 323:  #endif // #else // #ifdef INTXTASTE_NOINLINE
 324:  #endif // #ifndef __INTXTASTE_H_INCLUDED__