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: