I want to interface a keypad in stm32 l053r8 with timer interrupt. I have a SysTick_Handler function which I am handling there the switch debouncing of push buttons and every time the push button counter gets equal to 10 I am using a flag in order to check when the button is pressed. So now I have all the rows of the keypad as input and all the columns of the keypad as output. My idea is
Every timer int, it activates a new column and reads the 4 row inputs
Check if a button flag is "1" and send the appropriate message to the screen.
I have an implementation but it is not working.For example in main function I have a condition that checks if the key1 (expect 1 as output in screen) is pressed but it doesn't do anything. Any advice would be great.
void TIM2_IRQHandler(void) {
if (TIM2->SR & TIM_SR_UIF ) {
//Clear Timer 2 Flag
CLEAR_BIT(TIM2->SR, TIM_SR_UIF);
if (column_counter==4)
column_counter=0;
column_counter++;
keypad_scanning(column_counter); // activate col=1 or 2 or 3 depends on counter.
}
}
void keypad_scanning(uint8_t column_pos) {
switch (column_pos) {
case 1:
SET_BIT(GPIOC->ODR,GPIO_ODR_OD15); // C1 HIGH
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD1); // C2 LOW
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD4); // C3 LOW
if (flag0.fl.f5 && !(flag0.fl.f6) && !(flag0.fl.f7)) {
key0.fl.f1 = 1; // key 1 is pressed.
}
if (!(flag0.fl.f5) && flag0.fl.f6 && !(flag0.fl.f7)) {
key0.fl.f4 = 1; // key 4 is pressed.
}
if (!(flag0.fl.f5) && !(flag0.fl.f6) && flag0.fl.f7) {
key0.fl.f7 = 1; // key 7 is pressed.
}
break;
case 2:
CLEAR_BIT(GPIOC->ODR,GPIO_ODR_OD15); // C1 LOW
SET_BIT(GPIOA->ODR,GPIO_ODR_OD1); // C2 HIGH
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD4); // C3 LOW
if (flag0.fl.f5 && !(flag0.fl.f6) && !(flag0.fl.f7)) {
key0.fl.f2 = 1; // key 2 is pressed.
}
if (!(flag0.fl.f5) && flag0.fl.f6 && !(flag0.fl.f7)) {
key0.fl.f5 = 1; // key 5 is pressed.
}
if (!(flag0.fl.f5) && !(flag0.fl.f6) && flag0.fl.f7) {
key0.fl.f8 = 1; // key 8 is pressed.
}
break;
case 3:
CLEAR_BIT(GPIOC->ODR,GPIO_ODR_OD15); // C1 LOW
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD1); // C2 LOW
SET_BIT(GPIOA->ODR,GPIO_ODR_OD4); // C3 HIGH
if (flag0.fl.f5 && !(flag0.fl.f6) && !(flag0.fl.f7)) {
key0.fl.f3 = 1; // key 3 is pressed.
}
if (!(flag0.fl.f5) && flag0.fl.f6 && !(flag0.fl.f7)) {
key0.fl.f6 = 1; // key 6 is pressed.
}
if (!(flag0.fl.f5) && !(flag0.fl.f6) && flag0.fl.f7) {
key0.fl.f9 = 1; // key 9 is pressed.
}
break;
}
}
void SysTick_Handler(void) {
//------------------------------
//key 1 sampling ( ROW 1 FLAG)
if(!flag0.fl.f5) {
if(!KEY1_READ()) {
if(key1_counter == 10)
flag0.fl.f5 = 1; // R1 is HIGH
key1_counter = 0;
} else {
if(key1_counter < 10) //10 ms sampling
key1_counter ++;
}
}
//------------------------------
//------------------------------
//key 2 sampling (ROW 2 FLAG)
if(!flag0.fl.f6) {
if(!KEY2_READ()) {
if(key2_counter == 10)
flag0.fl.f6 = 1; // R2 is HIGH
key2_counter = 0;
} else {
if(key2_counter < 10) //10 ms sampling
key2_counter ++;
}
}
//------------------------------
//------------------------------
//key 3 sampling (ROW 3 FLAG)
if(!flag0.fl.f7) {
if(!KEY3_READ()) {
if(key3_counter == 10)
flag0.fl.f7 = 1; // R3 is HIGH
key3_counter = 0;
} else {
if(key3_counter < 10) //10 ms sampling
key3_counter ++;
}
}
//------------------------------
}
First, your title says 4x4 but the code appears to be for 3x3.
Next, an electrical signal can propagate through a key matrix almost instantaneously. You should do do every column one straight after the other, you don't need a timer to do that. Just do: column high, read row, column low, next column high, etc.
After that, you only read the state of the key if it is not already pressed. You try to debounce the rising edge, but once the key is pressed it seems to stay pressed for ever, you never look for the falling edge when the key is released. Falling edges can bounce too, so you need to have a count there too.
Also, you are checking for the state where only one row is active with if(x && !y && !z). What if the user presses more than one button at a time? For each flag you only need to check for one input.
These points won't fix everything, but hopefully will get you further along the way!
I suspect the following code to cause you some problems.
if(!KEY1_READ()){
if(key1_counter == 10)
What are the chances that you will release the button when counter is exactly 10?
You seem to want an on-release event, so I would write the scanning code like so:
if (KEY1_READ()) {
key1_counter++;
} else {
if (key1_counter > 10) {
flag0.fl.f5 = 1;
}
key1_counter = 0;
}
Don't forget to reset the flag after you're done processing it.
I found the solution for that. First of all thank all of you that gave me an advice and post a question. I used the timer (TIM2) and created an interrupt every 1ms. For each interrupt I was giving in the 3 column, which was my output ports, a sequence of binary numbers, while having all my input ports(rows) grounded. First sequence was 1 0 0, second 0 1 0 and third 0 0 1. After that sequence I was reading the inputs(rows) in order to detect which one was set at high("1"), with that I was able to detect which key was pressed by rasing a flag. All of that is similar to the code I 've posted but I used different register for make the outputs(columns) goes from high(logic "1") to ground(logic "0"), that is the BSRR register. For detection if a button is pressed I use the debounce method.
Related
I want to detect the release of a switch in a matrix of keys. Consider a 4*4 keys matrix, a scan by a microcontroller can easily be done to detect a key PRESS. However, I want to detect also the releases when they occur.
Is there a more efficient way to do so than just doing another scan on each state change interrupt for the unpressed ones?
Matrix keyboard scanning does not require any interrupt. You should create scanning function that will be called periodically. The best way is to store each key as a bit in some variable - you have 4x4 keyboard so uint16_t will perfetly fit. XOR operator is perfect for key change detection. Also don't forget to implement some key debouncing functionality.
I'd suggest following "pseudo" code or something similar (I hope there is no bug in it):
#define ROW_COUNT 4
#define COL_COUNT 4
#define DEBOUNCE_TIMEOUT 10 // In milliseconds
typedef uint16_t KeyState;
// This function returns current keyboard state and
// fills upKeys with keys that were pressed and downKeys with keys that were released
// It expects that key down is logic H, up is logic L
KeyState KeyboardState(KeyState* upKeys, KeyState* downKeys)
{
static KeyState lastState = 0;
static KeyState lastValidState = 0;
static Timestamp lastTimestamp = 0;
KeyState currentState = 0;
uint8_t x, y;
// Set defaults
if (upKeys)
*upKeys = 0;
if (downKeys)
*downKeys = 0;
for(y = 0; y < ROW_COUNT; y++)
{
// Set appropriate row pin to H
SetRowPinH(y);
// Just to be sure that any parasitic capacitance gets charged
SleepMicro(10);
for(x = 0; x < COL_COUNT; x++)
{
// Check key pressed
if (IsColPinSet(x))
currentState |= 1 << ((y * COL_COUNT) + x);
}
// And don't forget to set row pin back to L
SetRowPinL(y);
}
// Now lets do debouncing - this is important for good functionality
// Matrix state should not change for some time to accept result
if (lastState == currentState)
{
// Check if time has passed
if ((TimestampNowMilli() - lastTimestampMilli) >= DEBOUNCE_TIMEOUT)
{
// Let's detect up/down changes first - it's easy, just use XOR for change detection
if (upKeys)
*upKeys = currentState & (lastValidState ^ currentState);
if (downKeys)
*downKeys = (~currentState) & (lastValidState ^ currentState);
lastValidState = currentState;
}
}
else
{
// Current state differs from previous - reset timer
lastTimestampMilli = TimestampNowMilli();
}
lastState = currentState;
return lastValidState;
}
int main()
{
KeyState upKeys;
KeyState downKeys;
KeyState currentKeys;
while(1)
{
currentKeys = KeyboardState(&upKeys, &downKeys);
if (upKeys & 0x0001)
printf("Key 1 pressed\n");
if (downKeys & 0x0001)
printf("Key 1 released\n");
}
}
To turn on the led the switch is to be pressed for 2 times,turn off the led the switch is to be pressed 3 times within 10 second?can any one explain the logic how to implement it?
I checked the status of a switch in delay function, but it's not working
#include<reg51.h>
void delay_ms(unsigned int );
int count=0;
sbit sw=P2^0;
sbit led=P1^0;
main()
{
while(1)
{
if(sw==0)
{
delay_ms(10000);
}
}
}
void delay_ms(unsigned int i)
{
int j;
for(i;i>0;i--)
for(j=122;j>0;j--)
{
if(sw==0)
{
while(sw==0);
count++;
if(count==2)
led=1;
if(count==3)
led=0;
}
}
}
expected result- switch pressed 2 times should turn on led and
switch pressed 3 times should turn off led (within 10 sec)
actual result- switch pressed 2 times should turn on led and
switch pressed 3 times should turn off led (but works without any time limit)
An alternative and more flexible solution (in an application where you might need to do other work concurrently), is to timestamp the switch events and compare the current event time with the previous timestamps.
Given a regular time source (assuming the standard clock() here but the logic is valid regardless) :
#include <time.h>
#include <string.h> // for memset()
#include <stdbool.h>
bool isSwitchEvent()
{
sbit sw = P2^0 ;
bit current_sw = sw ;
static bit previous_sw = sw ;
// NOTE: Ignoring switch bounce issues - different question
// Switch event when state *changed* to 1->0...
bool switch_event = (current_sw == 0 && previous_sw == 1) ;
previous_sw = current_sw ;
return switch_event ;
}
#define SWITCH_PERIOD CLOCKS_PER_SEC * 10
int main()
{
sbit led = P1^0;
// Initialise so that first switch event is guaranteed to be at
// least then 10 seconds later than the initial timestamps.
clock_t switch_timestamp[3] = {-SWITCH_PERIOD,
-SWITCH_PERIOD,
-SWITCH_PERIOD } ;
unsigned i = 0 ;
bit led_state = 0 ;
bool change = false ;
for(;;)
{
// On switch event...
if( isSwitchEvent() )
{
// Timestamp event
switch_timestamp[i] = clock() ;
// If LED off, and previous event was less
// than 10 seconds ago...
if( led_state = 0 &&
switch_timestamp[i] - switch_timestamp[(i+2)%3] < SWITCH_PERIOD )
{
// LED on
led_state = 1 ;
change = true ;
}
// else if LED on, and previous two events were less
// than 10 seconds ago...
else if( led_state = 1 &&
switch_timestamp[i] - switch_timestamp[(i+1)%3] < SWITCH_PERIOD )
{
// LED off
led_state = 0 ;
change = true ;
}
// If LED state change...
if( change )
{
// Set output to the prevailing LED state
led = led_state ;
// Make timestamps older that SWITCH_PERIOD so
// previous events are not to be counted
// for next LED state change.
switch_timestamp[0] = clock() - SWITCH_PERIOD ;
switch_timestamp[1] = switch_timestamp[0] ;
switch_timestamp[2] = switch_timestamp[0] ;
// Next timestamp
i++ ;
i %= 3 ;
}
}
// You can do other work here (so long as
// it does not take so long as to miss a
// switch event).
}
return 0 ;
}
The issue is that you are not providing any debounce for your switch. Also where is count being initialized? I don't see it.
You need to have a delay when you detect the switch closed because it will "bounce" open and closed many times in the first few milliseconds. I would delay for at least 10mS before checking to see if the switch has been opened and then you can change your count.
UPDATE BASED ON OP COMMENTS:
Well again, this would only work in simulation. Switches are not that good. But I do see your issue: You don't reset count in your main(). Try:
main()
{
while(1)
{
count = 0;
if(sw==0)
{
delay_ms(10000);
}
}
}
I'm currently working with the MSP430G2553, which is coded in C.
For some reason I seem to be awful at coding basic For and While loops, and I can't figure out how to make a While loop take longer to complete after every iteration.
Basically, I have an LED blinking at 100ms on startup.
When I hold a button, I want to make the LED blink slower and slower the longer I hold the button.
When I let go, the LED should keep its slowed-down blinking rate.
Then, a second button will reset the LED blink-rate back to 100ms.
Right now, I'm able to slow down the LED blinking when I hold the button, but it does not keep going slower. Honestly, I'm not sure how to go about doing this so I made an account here and posted.
for(;;) //loop forever
{
if((P1IN & BIT3)==BIT3) //if button is not pressed
{
i = 0;
a = 4000; //At 10000, 4 cycles per second, or 250ms delay. 4000 cycles = 100ms or 10Hz delay.
P1OUT ^= BIT0 + BIT6; //two LEDs
while(i < a) //delays LED blinking until while-loop is complete.
{
i = i + 1;
}
}
else if((P1IN & BIT3)!=BIT3) //if button is pressed
{
i = 0;
a = 10000;
P1OUT ^= BIT0 + BIT6;
while(i < a) //delays LED blinking until while-loop is complete.
{
a = a + 2;
i = i + 1;
}
}
}
You would need to keep a "global" ( outside of the for scope ) delay counter to keep track of the last button press, or the delay
int button1Pressed = 0; // "global" flag
for(;;)
{
if((P1IN & BIT3) != BIT3) // if button pressed
{
button1Pressed = 1;
}
if((P1IN & BIT4) != BIT4) // hypothetical button 2 press
{
button1Pressed = 0;
}
int delay = 4000;
if(button1Pressed)
{
delay = 10000;
}
while(delay>0) {
delay = delay - 1;
}
}
I'm working on an embedded (avr) project, and basically I want to print out a few different things based on how long a pin has been pressed down. I can't figure out what happens as the value passes through and satisfies if statements along the way (button is still pressed so counter increments).
The setup is as follows:
if overflows is between 7-48 (button pressed for 30ms-200ms), print out a '.'
if overflows is greater than 48 (button pressed for greater than 200ms), print out a '-'
if overflows is greater than 97 (button has not been pressed in over 400ms), print out a ' '
My current code is as follows:
static inline void isr(char type) {
static unsigned int overflows = 0;
static unsigned char idx = 0;
if (type == 'e') { // edge captured
if (TCCR1B & 0x40) { // rising edge
if (overflows < 7) {
// do nothing
} else if (overflows < 49) {
buffer[idx++] = '.';
size++;
} else {
buffer[idx++] = '-';
size++;
}
}
overflows = 0; // restart counting overflows at each edge
} else { // overflow occured
overflows++;
if (buffer[idx-1] != ' ' && !(TCCR1B & 0x40) && overflows > 97) {
buffer[idx++] = ' ';
size++;
}
}
I'm not sure if this is correct though, since it would seem that there would always be a '.' preceding a '-', since as the overflow value is incremented, it satisfies the <49 condition.
Any thoughts?
if you want to count no.of times switch pressed, you can use while loop.
for example,
if(sw==0) //sw is switch connected with I/O pin
{
while(sw==0)
{
led=1; //LED is output
delay(); // use delay function
led=0;
delay();
count++;
}
}
by using while loop, you can avoid from getting multiple times switch pressed. if you make one on and off switch, the count will be increased by one.
I'm not sure if this is correct though, since it would seem that there would always be a '.' preceding a '-', since as the overflow
value is incremented, it satisfies the <49 condition.
But the tests are performed only on the rising edge - when both type == 'e' and (TCCR1B & 0x40) != 0. So the overflow count is evaluated only when the button is released, so you will not see the intermediate . before -.
So to answer your question, a conditional statement or block is executed when the condition is true. Here you have nested conditionals, so all preceding conditions must be true for the inner condition to be evaluated.
According to your description I assume you have a button on your pin, so in this case the first thing what I would recommend before doing anything is to implement a debounce for the button signal.
I would solve the problem something like this:
#define BUTTON_UNKNOWN 0
#define BUTTON_DOWN 1
#define BUTTON_UP 2
#define FALSE 0
#define TRUE 1
static inline unsigned char debounce( unsigned char current_state)
{
static unsigned char ret_value;
unsigned char state_changed = FALSE;
// Counter for number of equal states
static unsigned char count = 0;
// Keeps track of current (de-bounced) state
static unsigned char button_state = 0;
// Check if button is high or low for the moment
if (current_state != button_state)
{
// Button state is about to be changed, increase counter
count++;
if (count >= 3)
{
// The button have not bounced for four checks, change state
button_state = current_state;
// If the button was pressed (not released), tell main so
count = 0;
state_changed = TRUE;
}
}
else
{
state_changed = FALSE;
// Reset counter
count = 0;
}
//if butten press or release detected
if (state_changed == TRUE)
{
//check for the current state of the button
if (current_state != 0)
{
ret_value = BUTTON_DOWN;
}
else
{
ret_value = BUTTON_UP;
}
}
return ret_value;
}
int main(void)
{
//perform proper initialization of your pin
unsigned char button = BUTTON_UNKNOWN;
unsigned char cycle_count = 0;
unsigned char idx = 0;
unsigned char buffer[255];
unsigned char current_state;
unsigned char no_activity;
//unsigned char current_state = (~BUTTON_PIN & BUTTON_MASK) != 0;
while(1)
{
//read the button state
current_state = (~BUTTON_PIN & BUTTON_MASK) != 0;
// Update button_state
button = debounce(current_state);
// Check if the button is pressed.
if (button == BUTTON_DOWN)
{
//count cycles in which the button was pressed
//one cycle is 10 ms - see delay below
cycle_count++;
}
else
{
//check if the button was pressed before
if ((button != BUTTON_UNKNOWN) && (cycle_count != 0))
{
//if button was pressed for 200ms
if (cycle_count <= 20)
{
buffer[idx] = '.';
idx++;
}
else
{
//if the button was pressed between 200 and 400 ms
if ((cycle_count > 20) && (cycle_count <= 40))
{
buffer[idx] = '-';
idx++;
}
//the button was pressed for more than 400ms, uncomment if you need it
/*else
{
buffer[idx] = ' ';
idx++;
}*/
}
//reset counting mechanism
cycle_count = 0;
no_activity = 0;
}
else
{
no_activity++;
if (no_activity >= 40)
{
buffer[idx] = ' ';
idx++;
no_activity = 0;
}
}
}
// Delay for a while so we don’t check to button too often
_delay_ms(10);
}
}
You can adapt this code according to your needs:
change the line current_state = (~BUTTON_PIN & BUTTON_MASK) != 0; so that to read your pin state, and change the declaration of the buffer to suite your needs unsigned char buffer[255];
Please note that due to the debounce the time measured is not exactly 200ms ( it is 200ms + 2*debounce_time = 260ms (debounce time is 3 cycles, each cycle is 10ms, see delay at the end), but you may compensate these errors by reducing the contants in the cycle_count comparisons in the end.
Hope this helps!
If you really stick to your solution, then to avoid the problem what you experience is to not evaluate the overflow value continuously, do not try to determine the length of the button push on the fly, try instead to measure the length of the push and after that evaluate it and put the char in the buffer. You need to wait for the 'release button' and then evaluate how much time the button was pressed. Something like this:
static inline void isr(char type) {
static unsigned int overflows = 0;
static unsigned char idx = 0;
unsiged char button_status;
if (type == 'e') { // edge captured
if (TCCR1B & 0x40) { // rising edge
//perform a debounce otherwise wont be good
button_status = 1;
}
overflows = 0; // restart counting overflows at each edge
} else { // overflow occured
overflows++;
//if button was pressed and its now released evaluate result
if (!(TCCR1B & 0x40) && (button_status == 1))
{
if (overflows < 7) {
// do nothing
} else if (overflows < 49) {
buffer[idx++] = '.';
size++;
} else {
buffer[idx++] = '-';
size++;
}
button_status = 0;
}
if (buffer[idx-1] != ' ' && !(TCCR1B & 0x40) && overflows > 97) {
buffer[idx++] = ' ';
size++;
}
}
void loop() {
photoCell = analogRead(pin);
time = millis();
if (photoCell >= 400){
timeon = millis();
led = 1;
while (analogRead(pin) >= 400) {
timer = millis() - timeon;
//Serial.print("On");
//Serial.println(timer);
}
}
if (photoCell <= 400) {
timeoff = millis();
led = 0;
while (analogRead(pin) <= 400) {
timer2 = millis() - timeoff;
//Serial.print("Off");
//Serial.println(timer2);
}
}
if (timer >= 175 && timer <= 200 && led == 1) {
Serial.print("Char = ");
Serial.println(".");
codearray[i] = 8;
i++;
}
if (timer >= 580 && timer <= 600 && led == 1) {
Serial.print("Char = ");
Serial.println("-");
codearray[i] = 9;
i++;
}
This is my current code which works fine using a photocell plugged into A0 and an led wired into pin 9. The serial monitor displays whether or not I am flashing a dot or dash, morse corde, based on the timing.
However.. When I add this bit of code
if (codearray[0] == 8 && codearray[1] == 8 &&
codearray[2] == 8 && codearray[3] == 0) {
Serial.print("s");
}
The monitor prints nothing. This bit of code fills out an array I set up so I can print back into alphabetical the morse code that was deciphered. I'm pretty sure my logic is correct.
Looking to see if anyone understands why the 2nd bit of code would conflict with the first or what can be wrong with the analog input or serial monitor.
Your question is off topic here; probably belongs on stack overflow.
But it is obvious that you forgot to call Serial.Begin(9600); in setup(); n.b. 9600 is the arduino default baud rate.