View previous topic :: View next topic |
Author |
Message |
hemnath
Joined: 03 Oct 2012 Posts: 242 Location: chennai
|
short press and long press tactile button |
Posted: Mon Mar 09, 2020 2:47 am |
|
|
Hi,
I have a tactile switch. I want to use this button for multipurpose.
Short press which toggles a LED which is connected to PIN C5. A long press which displays the menu on the LCD display.
Any algorithm or logic, please. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Mon Mar 09, 2020 5:07 am |
|
|
Some options.
1) Use the 'search' option and previous posts. there are several.
2) Check the manual, Q&A section, for 'How do I wait only a specified time for a button press?'
3) Either PCMP or Mr. T posted a 'button.c' driver that might work for you.
Details you need to decide are what is a 'short' press, a 'long' press ? I'd think a maximum of 5 seconds for a 'timeout' or 'cancel button reading' might be a good start. A lot depends on the button quality, debounce, etc. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Mar 10, 2020 6:21 am |
|
|
Or you could try something like standard keyboard method where you recognise the difference between a sharp tap and a longer hold for auto-repeat.
There must be loads of examples out there.
Mike |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Tue Mar 10, 2020 7:12 am |
|
|
The normal way is to decode 'key down', and 'key released' as two separate
events. Then measuring the time between these allows you to decide which
action to take.
As Mike says having an auto repeat when the time is longer than a specific
value is a common action.
A timer based keyboard scanner that decides a key is 'down' when it
is active for two successive scans, avoids bounce problems and gives
reliable contact detection. Then the same applied for 'release' gives the
ability to take different actions according to the count between the events. |
|
|
hemnath
Joined: 03 Oct 2012 Posts: 242 Location: chennai
|
|
Posted: Mon Nov 09, 2020 5:54 am |
|
|
any sample code? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Mon Nov 09, 2020 6:18 am |
|
|
Use the 'search' option, use 'button' as the keyword. Somewhere will be the 'button.c' program.
I KNOW it works ! Have used it for 3-5 years on several projects.
Just copy and save a version, edit YOUR version as required for your needs.
I have a 'short', 'medium', long' version as I needed 3 distinct 'levels' from one switch when I ran out of pins..... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
hemnath
Joined: 03 Oct 2012 Posts: 242 Location: chennai
|
|
Posted: Mon Nov 09, 2020 9:38 pm |
|
|
Code: | #include <18F2620.h>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
// These are the "Bvar" variables required by Button.c.
int8 A4 = 0; // For the button on pin A4
int8 B0 = 0; // For the button on pin B0
// Global "button pressed" variables.
int8 A4_pressed = FALSE;
int8 B0_pressed = FALSE;
#include <Button.c>
// This gives an RTCC interrupt rate of 100.16 Hz
// (approx. 10 ms) to sample and debounce the buttons.
#define RTCC_PRELOAD (256 - 39)
// The buttons are read in this Timer0 (RTCC) isr.
#int_rtcc
void rtcc_isr(void)
{
set_rtcc(RTCC_PRELOAD); // Reload the Timer.
if(button(PIN_B0, 0, 50, 10, B0, 1))
B0_pressed = TRUE;
if(button(PIN_A4, 0, 50, 10, A4, 1))
A4_pressed = TRUE;
}
//================================
void main()
{
printf("\n\rStart: ");
// Setup the RTCC for a 10 ms interrupt rate.
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
while(1)
{
if(A4_pressed == TRUE)
{
A4_pressed = FALSE;
printf("A");
}
if(B0_pressed == TRUE)
{
B0_pressed = FALSE;
printf("B");
}
}
} |
My code is similar to this. Using button.c and interrupt, i can scan the button. But how can i modify the code for short press and long press for the same button ? Please help. |
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Nov 10, 2020 9:11 am |
|
|
A quick and dirty way to try can be that when the button is pressed, stay in a while loop that has, for instance, a 1ms delay and an up-counter that will count up to 1000.
When the loop exits after 1 second (1000x 1 ms) check if the button is still pressed. If still pressed, run Function1() otherwise run Function2().
Not ideal but a good way of testing
Code: |
// Check if button is pressed
if( input( PIN_B0 ) == TRUE )
{
Counter = 0;
// Start a 1 second delay
while( Counter < 1000 )
{
delay_ms( 1 );
Counter ++;
}
// 1 second delay expired, check if button is still pressed
if( input( PIN_B0 ) == TRUE )
{
// Button is still pressed, run function Function1();
}
else
{
// Button is not pressed, run function Function2();
}
}
void Function1( void )
{
// Code here
}
void Function2( void )
{
// Code here
} |
Haven't tested this so play around with it but you get the idea. I use interrupts for external buttons.
Good luck.
Ben |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Tue Nov 10, 2020 10:21 am |
|
|
I'm pretty sure CCS supplies a 'timed pin' in the FAQ section of the manual. I know there's a 'timed RS232' chunk of code.....
Similar to what Ben posted...stays in a loop until EITHER 'something ' happens OR a 'timeout' occours.
Jay |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
|
Posted: Tue Nov 10, 2020 11:38 am |
|
|
My recommendation would be to modify your ISR to handle counting and let your main code look at the count and decide if it was a short or long press based on how high the count is:
Code: |
static unsigned int8 B0_count = 0;
static unsigned int8 A4_count = 0;
// The buttons are read in this Timer0 (RTCC) isr.
#int_rtcc
void rtcc_isr(void)
{
set_rtcc(RTCC_PRELOAD); // Reload the Timer.
if(input_state(PIN_B0)) {
if(B0_count < 255){
B0_count++;
}
}else{
// if the input is low, we reset the count
B0_count = 0;
}
if(input_state(PIN_A4)) {
if(A4_count < 255){
A4_count++;
}
}else{
// if the input is low, we reset the count
A4_count = 0;
}
}
|
Then in your main you can see if the count is at least some value (1 or higher, up to you) to say that it is pressed, then if it is less than some value (say 100 for one second) it is a short press. Otherwise if it is higher, it is a long press.
You could even make an enumeration and function to categorize the button presses:
Code: |
typedef enum {BTTN_NOT_PRESSED, BTTN_SHORT_PRESS, BTTN_LONG_PRESS} button_press_t;
button_press_t check_press(unsigned int8 press_count){
if(press_count > 100){
return BTTN_LONG_PRESS;
}else if(press_count > 5){
return BTTN_SHORT_PRESS;
}else{
return BTTN_NOT_PRESSED;
}
}
|
And that can be called on B0_count and A4_count in your main to see what type of press you currently have. |
|
|
hemnath
Joined: 03 Oct 2012 Posts: 242 Location: chennai
|
|
Posted: Thu Nov 12, 2020 10:21 pm |
|
|
Modified the code. But it is not working. Please check
Code: | #include <18F2620.h>
#fuses INTRC_IO, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)
#define RS PIN_A1
#define EN PIN_A2
#define UP PIN_A5
#define ENTER PIN_C2
#define DOWN PIN_C0
void lcd_init();
void lcd_cmd(unsigned char c);
void lcd_data(unsigned char z);
#include "lcd.c"
static unsigned int8 B0_count = 0;
static unsigned int8 A4_count = 0;
// This gives an RTCC interrupt rate of 100.16 Hz
// (approx. 10 ms) to sample and debounce the buttons.
#define RTCC_PRELOAD (256 - 39)
// The buttons are read in this Timer0 (RTCC) isr.
#int_rtcc
void rtcc_isr(void)
{
set_rtcc(RTCC_PRELOAD); // Reload the Timer.
if(input_state(UP))
{
if(B0_count < 255)
{
B0_count++;
}
}
else
{
// if the input is low, we reset the count
B0_count = 0;
}
if(input_state(DOWN))
{
if(A4_count < 255)
{
A4_count++;
}
}
else
{
// if the input is low, we reset the count
A4_count = 0;
}
}
//================================
void main()
{
// Setup the RTCC for a 10 ms interrupt rate.
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
delay_ms(100);
lcd_init();
lcd_cmd(0x80);
printf(lcd_data, "Welcome");
delay_ms(1000);
lcd_cmd(0x01);
while(1)
{
if(B0_count > 100)
{
lcd_cmd(0x80);
printf(lcd_data, "Long ");
}
else if(B0_count > 5)
{
lcd_cmd(0x80);
printf(lcd_data, "Short ");
}
else
{
lcd_cmd(0x80);
printf(lcd_data, "No Press");
}
delay_ms(100);
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Nov 13, 2020 1:14 am |
|
|
Hemnath wrote: | Modified the code. But it is not working. |
How are your switches connected ? I think it's like this:
Code: |
+5v
|
<
> 4.7K
< ___ Switch
To | _|_|_
PIC -----------------o o------
pin A5 |
--- GND
-
|
The idle state is a logic "1" for this design. A button press gives a logic "0". |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
|
Posted: Fri Nov 13, 2020 8:50 am |
|
|
Yeah, I only guessed which way the buttons were oriented but figured the OP could adjust it if they were reversed. Definitely could be active low instead.
If they are active low, then
Code: | if(input_state(UP)) |
becomes:
Code: | if(0 == input_state(UP)) |
and similar for the other buttons |
|
|
|