Oft muss man Taster oder andere Eingaben in einen Microcontroller einlesen um Aktionen auszulösen. Leider sind Taster "nicht ideale" Schalter, da diese prellen und mitunter erst nach einigen 10ms einen stabilen Logikpegel einnehmen. Man muss Signale also entprellen oder debouncen.

Liest man diese Signale in einen Controller ein, kommt es sicher zu einem Prellen des Signals. Will man eigentlich nur einmal einen Taster betätigen, kann es wahrscheinlich sein, dass mehrere Tastendrücke erkannt werden. Dies ist aber nicht erwünscht. Lösen kann man dies auf zwei Arten. In Hardware durch vorschalten eines Tiefpasses oder Schmitttriggers oder einfacher durch die logische Auswertung der Signale in Software. Dies nennt man auch "debouncen". In Software gibts auch verschiedene Verfahren die alle auch Ihre Nachteile haben. Man könnte z.B. einfach immer ein paar mal den Taster einlesen und bis zu 100ms warten, bevor man den Taster endgültig auswertet. Der Nachteil ist, man muss immer warten ob und wie lange der Taster gedrückt wurde. Warten ist für Controller immer nachteilig, weil der in der Zeit zumeist nicht anderes tun kann.
Hier folgt jetzt ein Programm welches ich von Peter Danneger übernommen habe der diese Debounce Routine für den ATMEGA geschrieben hat. Diese funktioniert wunderbar, ist klein und kompakt. Ich habe diese Routine nur auf den STM32 angepasst.
Wer mehr wissen will kann hier die Details nachlesen.

#include <stdio.h>
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include <stddef.h>
#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include <string.h>
#include <misc.h>
#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"

/*
 * Program to test the TIM2, TIM4 timer and interrupt function for STM32F103RB to read debounced keys press
 * tested with OLIMEXINO STM32 (F103RB)  Board
 * **********************
 * Wolfram Koerver - 2013
 * **********************
 * The Port C is used to read in button pressed events. This uses TIM4 for counting with 10ms.
 * The debounce functions - I have modified to work with STM32 IOs - are invented originally by Peter Danneger
 *
 * Hardware STM32 Olimexino Connections:
 * A0/D15/PC0 == Switch to GND = SW1
 * A1/D16/PC1 == Switch to GND = SW2
 * Serial Port connected to USART2
 * D0 = Input -> STM32 UART
 * D1 = Output from <- STM32 UART
 */

void init_LED(void);
void blink_yellow(void);
void init_timer2(void);
void init_timer4(void);

void delay_us(uint32_t time_us);
void delay_ms(uint32_t time_ms);

void TIM2_IRQHandler(void);
void init_sys_tick(void);

void RCC_Init(void);

void InitUart2(void);
void InitUart3(void);
void USART2_SendString(char* data);

void init_keyport(void);

uint8_t get_key_state(uint8_t key_mask);
uint8_t get_key_rpt(uint8_t key_mask);
uint8_t get_key_short(uint8_t key_mask);

#define uchar unsigned char

uint32_t	ti;

volatile 	uchar 		rfid_bits[54];
volatile 	uchar 		rfid_bytes[9];

char bufferRx[256],receiveRx[256];
uint16_t	RxCounter;



uint8_t usart_receive_array[] = {0xAA, 0xAA, 0xAA};

uint8_t		new_rfid=0; 	//flag to tell there is a new rfid token received

uint16_t	start_bit_count=0,seq_bit_no,b,k,rbit;

uchar			rc5_bit;				// bit value
uint8_t			bit_time_cnts;				// counts number of timer cycles during a bit reception
uint16_t		rc5_tmp;					// shift bits in
uchar			rfid_bit;					// actual received logical bit from RFID stream

volatile uint16_t	rc5_data;				// store result
/********************************** Debounce Variables **********************************/

#define KEY_PIN GPIO_ReadInputData(GPIOC)

#define KEY0            0
#define KEY1            1
#define KEY2            2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)

#define REPEAT_MASK     (1<<KEY0 | 1<<KEY1)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     10                        // every 200ms

volatile uint8_t key_state;                                // debounced and inverted key state:
                                                  // bit = 1: key pressed
volatile uint8_t key_press;                                // key press detect

volatile uint8_t key_rpt;                                  // key long press and repeat

#define sw1	0
#define sw2	1
#define sw3	2
#define sw4	3
/****************************************************************************************/
int main(void)
{

	extern unsigned char			rc5_bit;				// bit value

	char text[10];
	char			out_text[50];

	out_text[49] = '\0';	// make sure string termination

	text[9]	=0x00;
	uint8_t ky;


	SystemInit();					// initialize the core
	RCC_Init();
	InitUart2();
	
	/****************************************/
	/* Watchdog Test - Base frequency 40kHz */
	// These calls initiate the watchdog timer interrupts. If watchdog time expires the CPU is reset
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	IWDG_SetPrescaler(IWDG_Prescaler_256);
	IWDG_SetReload(3000);	// should be about 1/156s * 3000 should be 20s
	IWDG_ReloadCounter();
	IWDG_Enable();
	/****************************************/

	init_LED();						// Init the LED port IO
	init_timer2();					// configure the Timer2 Interrupt
	init_timer4();
	init_keyport();					// configure the key switch input port
	init_extint();					// configure external interrupt

	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
			USART2_SendString("RFID Test Debounce on STM32\n\r");
	rc5_bit=0;						// init RFID INT

    while(1)
    {
    	blink_yellow();						// let the main program do something
		
		if( get_key_short( 1<<sw1 )){

			sprintf(out_text,"Key sw1 %3d  \r",ky);
			USART2_SendString(out_text);
		}
		if( get_key_short( 1<<sw2 )){
			sprintf(out_text,"Key sw2 %3d  \r",ky);
			USART2_SendString(out_text);
		}
			delay_ms(25);
			IWDG_ReloadCounter();		// Retrigger Watchdog Counter otherwise CPU would be restartet
    }
}

void init_keyport(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; 	// enable the firts 4 bits as key switch inputs
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;						// Pin Floating
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;								//Pin Up
	//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;	//Pin Down
	GPIO_Init(GPIOC, &GPIO_InitStructure);
}


void outuchar(uchar val)
{
	char 	buf[3];
	sprintf(buf,"%02X",val);
	buf[3] = '\0';
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
				USART2_SendString(buf);
}
void outbits(uchar val)
{
	char 	buf[2];
	sprintf(buf,"%d",val & 0x1);
	buf[2] = '\0';
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
				USART2_SendString(buf);
}

void RCC_Init(void)
{
	// Route and enable clocks for low and high speed bus
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
}


void TIM2_IRQHandler(void){							// Timer Interrupt handler for TIM2 will be called every 10us
  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);		// Clear interrupt pending bit
  bit_time_cnts++;
}
/**************************************************************************************************************/
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
uint8_t get_key_press( uint8_t key_mask )
{
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  return key_mask;
}

///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed long enough such that the
// key repeat functionality kicks in. After a small setup delay
// the key is reported being pressed in subsequent calls
// to this function. This simulates the user repeatedly
// pressing and releasing the key.
//
uint8_t get_key_rpt( uint8_t key_mask )
{
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  return key_mask;
}

///////////////////////////////////////////////////////////////////
//
// check if a key is pressed right now
//
uint8_t get_key_state( uint8_t key_mask )

{
  key_mask &= key_state;
  return key_mask;
}

///////////////////////////////////////////////////////////////////
//
uint8_t get_key_short( uint8_t key_mask )
{
  return get_key_press( ~key_state & key_mask );
}

///////////////////////////////////////////////////////////////////
//
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}


void USART2_SendString(char* data)
{
	while (*data)
	{
		USART_SendData(USART2, (uint16_t) *data);
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET);
		data++;
	}
}

void TIM4_IRQHandler(void){							// Timer Interrupt handler for TIM4 will be called every 10ms
	 static uint8_t ct0, ct1, rpt;					// KEY-PIN will becalled to read in key button pressed events
	  uint8_t i;
  TIM_ClearITPendingBit(TIM4, TIM_IT_Update);		// Clear interrupt pending bit
  i = key_state ^ ~KEY_PIN;                       	// key changed ?
   ct0 = ~( ct0 & i );                             	// reset or count ct0
   ct1 = ct0 ^ (ct1 & i);                          	// reset or count ct1
   i &= ct0 & ct1;                                 	// count until roll over ?
   key_state ^= i;                                 	// then toggle debounced state
   key_press |= key_state & i;                     	// 0->1: key press detect
   if( (key_state & REPEAT_MASK) == 0 )            	// check repeat function
      rpt = REPEAT_START;                          	// start delay
   if( --rpt == 0 ){
     rpt = REPEAT_NEXT;                            	// repeat delay
     key_rpt |= key_state & REPEAT_MASK;
   }
}

void USART2_IRQHandler(void)
{
  uint8_t c;
  if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  {
    /* Read one byte from the receive data register */
	// This routine jsut reads a byte from serial; only for demonstration purposes
    c = USART_ReceiveData(USART2);
   USART_SendData(USART2, c);				//echo byte back
  }
 }

void InitUart2(void)
{
	// Initialize Structs
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	USART_ClockInitTypeDef USART_ClockInitStructure;

	// USART1 RX-Pin initialize PA3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	// USART1 TX-Pin initialize PA2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	// USART initialize
	USART_ClockStructInit(&USART_ClockInitStructure);
	USART_ClockInit(USART2, &USART_ClockInitStructure);

	USART_InitStructure.USART_BaudRate = 19200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

	USART_Init(USART2, &USART_InitStructure);
	USART_Cmd(USART2, ENABLE);

	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

	  NVIC_InitTypeDef NVIC_InitStructure;
	  /* Enable the USART2 Interrupt for reception of bytes */
	  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	  NVIC_Init(&NVIC_InitStructure);
}

void init_timer2(void)
{
	// SystemCoreClock = 72MHz -> APB1 is maximum 36MHz for TIM2,3,4,5 but will be doubled again to 72MHz when prescaler # 0
	// This example uses TIM2 Timer
	// APB1 clock = SystemCoreClock/2 but s.o.

	TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBase_InitStructure.TIM_Period 			= 9;		// count up to 999 = 1000
	TIM_TimeBase_InitStructure.TIM_Prescaler		= 71;		//72(MHz) -1 Divided by 72
	TIM_TimeBase_InitStructure.TIM_CounterMode 		= TIM_CounterMode_Up;
	// 9,71 = 10us Cycle Time
	// 999,71 = 1ms
	// 1MHz / 500 = 2000 Hz.
	// SystemCoreClock = 72MHz / 72 => 1 MHz Clock with this clock the timer counts up to 999 = 1000 counts =
	// 1/1000 --> 1000 Hz = 1ms cycle time

	TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
	NVIC_Init(&NVIC_InitStructure);
	TIM_Cmd(TIM2, ENABLE);
}
void init_timer4(void)
{
	// SystemCoreClock = 72MHz -> APB1 is maximum 36MHz for TIM4 but will be doubled again to 72MHz when prescaler # 0
	// This example uses TIM4 Timer
	// APB1 clock = SystemCoreClock/2 but s.o.
	TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

	TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

	TIM_TimeBase_InitStructure.TIM_Period 			= 2499;		// count up to 2500
	TIM_TimeBase_InitStructure.TIM_Prescaler		= 143;		//72(MHz) -1 Divided by 144
	TIM_TimeBase_InitStructure.TIM_CounterMode 		= TIM_CounterMode_Up;
	// 9,71 = 10us Cycle Time
	// 999,71 = 1ms
	//2499/143 = 10ms
	// 1MHz / 500 = 2000 Hz.
	// SystemCoreClock = 72MHz / 72 => 1 MHz Clock with this clock the timer counts up to 999 = 1000 counts =
	// 1/1000 --> 1000 Hz = 1ms cycle time

	TIM_TimeBaseInit(TIM4, &TIM_TimeBase_InitStructure);
	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
	NVIC_Init(&NVIC_InitStructure);
	TIM_Cmd(TIM4, ENABLE);
}
void delay_us(uint32_t nus)
{
 uint32_t temp;
 SysTick->LOAD 	= nus * 11;
 SysTick->VAL 	= 0x00;
 SysTick->CTRL 	= 0x01 ;
 do
 {
  temp=SysTick->CTRL;
 }
 while(temp&0x01&&!(temp&(1<<16)));
 SysTick->CTRL = 0x00;
 SysTick->VAL = 0X00;
}


void delay_ms(uint32_t time_ms) //Delay in msec
{
  while (time_ms>0)
  {
	delay_us(1000);
    time_ms--;
  }
}

void init_LED(void)
{
	/* Initialize GPIO Structure, configure clock on peripheral bus
	 * Enable GPIO Pin for yellow LED which
	   Configure direction and clock speed */
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void blink_yellow()
{
	GPIOA->BRR = GPIO_Pin_1;						//reset bit
	GPIOA->BSRR = GPIO_Pin_1;					//set bit
	delay_ms(1);
	GPIOA->BRR = GPIO_Pin_1;						//reset bit
}

void toggle_yellow()
{
	GPIOA->ODR ^= GPIO_Pin_7;						// invert pin
}

Das Beispielprogramm zum Download für den STM32 findet sich hier: