key debouncer
This commit is contained in:
		
							
								
								
									
										2
									
								
								src/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
a.out
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										177
									
								
								src/key.c
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								src/key.c
									
									
									
									
									
								
							@ -4,12 +4,58 @@
 | 
			
		||||
 *  Created on: Mar 20, 2019
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
 | 
			
		||||
#ifndef DESKTOP
 | 
			
		||||
#include "stc15.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static uint8_t keys[5];
 | 
			
		||||
#define TOTAL_ROWS 5
 | 
			
		||||
#define M_COLS 4
 | 
			
		||||
 | 
			
		||||
static uint8_t keys[TOTAL_ROWS]; //only bottom nibbles get set
 | 
			
		||||
static uint32_t new_keys_pressed; //bottom 20 bits get set
 | 
			
		||||
static uint8_t last_state[TOTAL_ROWS]; //all used, 2 bits per key
 | 
			
		||||
static int8_t last_count[TOTAL_ROWS][M_COLS]; //all used
 | 
			
		||||
static uint8_t unexpected_count; //count of unexpected transitions
 | 
			
		||||
 | 
			
		||||
static const int8_t COUNT_LIM_LOW  = -30;
 | 
			
		||||
static const int8_t COUNT_LIM_HIGH =  30;
 | 
			
		||||
static const int8_t THRESH_STEADY = 2;
 | 
			
		||||
static const int8_t THRESH_TRANS  = 2;
 | 
			
		||||
 | 
			
		||||
//2-bit state:
 | 
			
		||||
#define STEADY_LOW     0
 | 
			
		||||
#define TRANS_LOW_HIGH 1
 | 
			
		||||
#define STEADY_HIGH    2
 | 
			
		||||
#define TRANS_HIGH_LOW 3
 | 
			
		||||
 | 
			
		||||
#ifdef DESKTOP
 | 
			
		||||
//test functions
 | 
			
		||||
void KeyInit(void){}
 | 
			
		||||
 | 
			
		||||
static void raw_scan(void){
 | 
			
		||||
	static uint8_t i = 0;
 | 
			
		||||
	static const uint8_t data[] = {
 | 
			
		||||
		0,0,0,0,0,0,0,0,0,0,0,0,
 | 
			
		||||
		0,2,0,2,0,2,0,2,0,2,0,2,
 | 
			
		||||
		2,2,0,2,2,0,2,2,0,2,2,0,
 | 
			
		||||
		2,2,2,2,2,2,2,2,2,2,2,2,
 | 
			
		||||
		0,2,0,2,0,2,0,2,0,2,0,2,
 | 
			
		||||
		0,0,2,0,0,2,0,0,2,0,0,2,
 | 
			
		||||
		0,0,0,0,0,0,0,0,0,0,0,0
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	keys[1] = data[i];
 | 
			
		||||
	i++;
 | 
			
		||||
	if (i >= (sizeof(data)/sizeof(data[0]))){
 | 
			
		||||
		i = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void KeyInit(void){
 | 
			
		||||
	//set column drivers bits 7:4 to quasi-bidirectional w/ pullup M[1:0]=b00
 | 
			
		||||
	//and row inputs bits 3:0 to quasi-bidirectional w/ pullup M[1:0]=b00
 | 
			
		||||
@ -29,11 +75,9 @@ void KeyInit(void){
 | 
			
		||||
	P5 |= 0x30; //pull up
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void KeyScan(void){
 | 
			
		||||
static void raw_scan(void){
 | 
			
		||||
	//top row not part of matrix
 | 
			
		||||
	static const uint8_t M_ROWS = 4;
 | 
			
		||||
	static const uint8_t M_COLS = 4;
 | 
			
		||||
	uint8_t i, j;
 | 
			
		||||
 | 
			
		||||
	//scan top row
 | 
			
		||||
@ -62,8 +106,131 @@ void KeyScan(void){
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint8_t* GetKeys(void){
 | 
			
		||||
//based on quick draw/integrator hybrid debounce algorithm from
 | 
			
		||||
//https://summivox.wordpress.com/2016/06/03/keyboard-matrix-scanning-and-debouncing/
 | 
			
		||||
static void debounce(void){
 | 
			
		||||
	uint8_t i, j;
 | 
			
		||||
	new_keys_pressed = 0; //initially
 | 
			
		||||
	for (i = 0; i < TOTAL_ROWS; i++){
 | 
			
		||||
		for (j = 0; j < M_COLS; j++){
 | 
			
		||||
			int8_t new_count;
 | 
			
		||||
			uint8_t new_state;
 | 
			
		||||
			int8_t curr_count = last_count[i][j];
 | 
			
		||||
			uint8_t curr_state = last_state[i] & (0x3 << (j*2));
 | 
			
		||||
			curr_state >>= j*2;
 | 
			
		||||
			new_count = curr_count;
 | 
			
		||||
			new_state = curr_state;
 | 
			
		||||
			//update count
 | 
			
		||||
			if (keys[i] & (1 << j)){
 | 
			
		||||
				if (curr_count < COUNT_LIM_HIGH){
 | 
			
		||||
					new_count = curr_count + 1;
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if (curr_count > COUNT_LIM_LOW){
 | 
			
		||||
					new_count = curr_count - 1;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			//update state
 | 
			
		||||
			switch(curr_state){
 | 
			
		||||
				////////
 | 
			
		||||
				case STEADY_LOW: {
 | 
			
		||||
					if (new_count >= COUNT_LIM_LOW + THRESH_TRANS){
 | 
			
		||||
						new_count = 0;
 | 
			
		||||
						new_state = TRANS_LOW_HIGH;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				////////
 | 
			
		||||
				case TRANS_LOW_HIGH: {
 | 
			
		||||
					if (new_count >= THRESH_STEADY){
 | 
			
		||||
						new_state = STEADY_HIGH;
 | 
			
		||||
						new_count = COUNT_LIM_HIGH;
 | 
			
		||||
					} else if (new_count <= -THRESH_STEADY){
 | 
			
		||||
						new_state = STEADY_LOW;
 | 
			
		||||
						new_count = COUNT_LIM_LOW;
 | 
			
		||||
						unexpected_count++;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				////////
 | 
			
		||||
				case STEADY_HIGH: {
 | 
			
		||||
					if (new_count <= COUNT_LIM_HIGH - THRESH_TRANS){
 | 
			
		||||
						new_count = 0;
 | 
			
		||||
						new_state = TRANS_HIGH_LOW;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				////////
 | 
			
		||||
				case TRANS_HIGH_LOW: {
 | 
			
		||||
					if (new_count <= -THRESH_STEADY){
 | 
			
		||||
						new_state = STEADY_LOW;
 | 
			
		||||
						new_count = COUNT_LIM_LOW;
 | 
			
		||||
					} else if (new_count >= THRESH_STEADY){
 | 
			
		||||
						new_state = STEADY_HIGH;
 | 
			
		||||
						new_count = COUNT_LIM_HIGH;
 | 
			
		||||
						unexpected_count++;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				////////
 | 
			
		||||
				default:
 | 
			
		||||
					new_state = STEADY_LOW;
 | 
			
		||||
					new_count = COUNT_LIM_LOW;
 | 
			
		||||
					unexpected_count++;
 | 
			
		||||
			} //switch(curr_state)
 | 
			
		||||
			//track new keys down
 | 
			
		||||
			if (curr_state != TRANS_LOW_HIGH && new_state == TRANS_LOW_HIGH){
 | 
			
		||||
				new_keys_pressed |= ((uint32_t) 1 << (i*M_COLS + j));
 | 
			
		||||
			}
 | 
			
		||||
			//write back new count
 | 
			
		||||
			last_count[i][j] = new_count;
 | 
			
		||||
			//write back new state
 | 
			
		||||
			last_state[i] = (last_state[i] & ~(0x3 << (j*2))) | (new_state << (j*2));
 | 
			
		||||
		} //for cols
 | 
			
		||||
	} //for rows
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void KeyScan(void){
 | 
			
		||||
	raw_scan();
 | 
			
		||||
	debounce();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uint8_t* DebugGetKeys(void){
 | 
			
		||||
	return keys;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uint32_t GetNewKeys(void){
 | 
			
		||||
	return new_keys_pressed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef DESKTOP
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
const char state_names[4][32] = {
 | 
			
		||||
	"STEADY_LOW"    ,
 | 
			
		||||
	"TRANS_LOW_HIGH",
 | 
			
		||||
	"STEADY_HIGH"   ,
 | 
			
		||||
	"TRANS_HIGH_LOW"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(void){
 | 
			
		||||
	for (int iii = 0; iii < 100; iii++){
 | 
			
		||||
		KeyScan();
 | 
			
		||||
		const uint8_t* keys = DebugGetKeys();
 | 
			
		||||
		static const uint8_t i = 1, j = 1;
 | 
			
		||||
		uint8_t curr_count = last_count[i][j/2] & (0xf << ((j&1)*4));
 | 
			
		||||
		uint8_t curr_state = last_state[i] & (0x3 << (j*2));
 | 
			
		||||
		curr_count >>= (j&1)*4;
 | 
			
		||||
		curr_state >>= j*2;
 | 
			
		||||
		printf("%3d: %x, %14s, %2u, %2lx\n",
 | 
			
		||||
		       iii, keys[1], state_names[curr_state], curr_count, (unsigned long) GetNewKeys());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,8 @@
 | 
			
		||||
 | 
			
		||||
void KeyInit(void);
 | 
			
		||||
void KeyScan(void);
 | 
			
		||||
uint8_t* GetKeys(void);
 | 
			
		||||
const uint8_t* DebugGetKeys(void);
 | 
			
		||||
const uint32_t GetNewKeys(void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* SRC_KEY_H_ */
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										81
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								src/main.c
									
									
									
									
									
								
							@ -10,11 +10,42 @@
 | 
			
		||||
 | 
			
		||||
#define FOSC 11059200
 | 
			
		||||
 | 
			
		||||
// clear wdt
 | 
			
		||||
#define WDT_CLEAR() (WDT_CONTR |= 1 << 4)
 | 
			
		||||
 | 
			
		||||
static const char KEY_MAP[20] = {
 | 
			
		||||
	'c', '<', 'r', 'm',
 | 
			
		||||
	'/', '9', '8', '7',
 | 
			
		||||
	'*', '6', '5', '4',
 | 
			
		||||
	'-', '3', '2', '1',
 | 
			
		||||
	'+', '=', '.', '0'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t NewKeyBuf[4];
 | 
			
		||||
volatile uint8_t new_key_write_i;
 | 
			
		||||
volatile uint8_t new_key_read_i;
 | 
			
		||||
volatile uint8_t new_key_empty;
 | 
			
		||||
#define INCR_NEW_KEY_I(i) i = (i + 1) & 3
 | 
			
		||||
 | 
			
		||||
volatile uint8_t SecCount;
 | 
			
		||||
void timer0_isr() __interrupt 1 __using 1
 | 
			
		||||
{
 | 
			
		||||
//	P3_1 ^= 1;
 | 
			
		||||
	static uint8_t count = 0;
 | 
			
		||||
	static uint8_t min_count = 0, hour_count = 0;
 | 
			
		||||
 | 
			
		||||
	uint32_t new_keys;
 | 
			
		||||
 | 
			
		||||
	//scan keyboard
 | 
			
		||||
	KeyScan();
 | 
			
		||||
	new_keys = GetNewKeys();
 | 
			
		||||
	if (new_keys != 0){
 | 
			
		||||
		if (!new_key_empty && (new_key_write_i == new_key_read_i)){
 | 
			
		||||
			//do not overwrite keymap currently being processed
 | 
			
		||||
			INCR_NEW_KEY_I(new_key_write_i);
 | 
			
		||||
		}
 | 
			
		||||
		NewKeyBuf[new_key_write_i] = new_keys;
 | 
			
		||||
		INCR_NEW_KEY_I(new_key_write_i);
 | 
			
		||||
		new_key_empty = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -41,34 +72,56 @@ char buf[17];
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
	uint8_t* keys;
 | 
			
		||||
	uint8_t j;
 | 
			
		||||
	const uint8_t* keys;
 | 
			
		||||
	uint8_t key_i;
 | 
			
		||||
	Timer0Init(); // display refresh & switch read
 | 
			
		||||
	LCD_Open();
 | 
			
		||||
	KeyInit();
 | 
			
		||||
	P3_4 = 0; //turn on led backlight
 | 
			
		||||
	LCD_OutString("Hello world !!!!");
 | 
			
		||||
	LCD_GoTo(1,0);
 | 
			
		||||
	LCD_OutString(".......");
 | 
			
		||||
 | 
			
		||||
	i = 0;
 | 
			
		||||
	j = 0;
 | 
			
		||||
	// LOOP
 | 
			
		||||
	while (1)
 | 
			
		||||
	{
 | 
			
		||||
		LCD_GoTo(0,0);
 | 
			
		||||
		//scan keyboard
 | 
			
		||||
		KeyScan();
 | 
			
		||||
		keys = GetKeys();
 | 
			
		||||
		//keyboard debug
 | 
			
		||||
		keys = DebugGetKeys();
 | 
			
		||||
		for (key_i = 0; key_i < 5; key_i++){
 | 
			
		||||
			LCD_OutNibble(keys[key_i]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		TERMIO_PutChar(',');
 | 
			
		||||
		//counter
 | 
			
		||||
		LCD_GoTo(1,7);
 | 
			
		||||
		LCD_OutString(u32str(i, buf, 10));
 | 
			
		||||
		i++;
 | 
			
		||||
		if (SecCount == 0){
 | 
			
		||||
			LCD_OutString("  ");
 | 
			
		||||
		} else if (SecCount < 10){
 | 
			
		||||
			TERMIO_PutChar(' ');
 | 
			
		||||
			LCD_OutString(u32str(SecCount, buf, 10));
 | 
			
		||||
		} else {
 | 
			
		||||
			LCD_OutString(u32str(SecCount, buf, 10));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		WDT_CLEAR();
 | 
			
		||||
		///new keys
 | 
			
		||||
		if (!new_key_empty){
 | 
			
		||||
			uint8_t i_key;
 | 
			
		||||
			uint32_t new_keys = NewKeyBuf[new_key_read_i];
 | 
			
		||||
			INCR_NEW_KEY_I(new_key_read_i);
 | 
			
		||||
			if (new_key_read_i == new_key_write_i){
 | 
			
		||||
				new_key_empty = 1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			LCD_GoTo(1,j);
 | 
			
		||||
			for (i_key = 0; i_key < 20; i_key++){
 | 
			
		||||
				if (new_keys & ((uint32_t) 1 << i_key)){
 | 
			
		||||
					TERMIO_PutChar(KEY_MAP[i_key]);
 | 
			
		||||
					j++;
 | 
			
		||||
					j &= 0x0f;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
/* ------------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
@ -17,5 +17,13 @@ void _delay_ms(uint8_t ms);
 | 
			
		||||
char* u32str(uint32_t x, char* buf, uint8_t base);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#define DESKTOP
 | 
			
		||||
#elif _WIN32
 | 
			
		||||
#define DESKTOP
 | 
			
		||||
#elif __APPLE__
 | 
			
		||||
#define DESKTOP
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* SRC_UTILS_H_ */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user