PSoC 4 Single button for 2 functions

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

About a month ago, there was a discussion asking how to modify the sample of Lesson 9 of PSoC 101.

https://community.infineon.com/t5/PSoC-4/PSoC-101-Lesson-9-Timer-Example/m-p/382634#M43753

The poster's intention seemed to be using a Timer to distinct a short push and a long push for different functions or actions.

Although a Timer can have such capability, for making it more flexible, I suggested to use SysTick,

which also saves a Timer for other usage.

IMG_5398.JPG

Although CY8CKIT-042 has 2 switches, SW1 is dedicated for RESET, so only SW2 is available for the user function.

This time the poster wanted to have a short push, and a long push of the button.

With a short push, a function will be called immediately and with a long push (or keep pushing),

another function will be called repeatedly in a certain interval.

We could have "Single Click", "Double Clicks", "Triple Clicks"..., too, but let save it for another day 😉

Now, the schematic

002-schematic.JPG

Pin_Switch (an input pin with dedicated interrupt)

004-Pin_Switch_1.JPG

005-Pin_Switch_2.JPG

Pins (Pin assignment)

003-pins.JPG

Tera Term log

At first I pushed the button twice, then pushed and kept for a while.

Then pushed the button three times, then pushed and kept for while, again.

001-TeraTerm-log.JPG

main.c


#include <project.h>
#include <stdio.h>

#define MAX_SW2_DURATION_MS 10000
#define SW2_SHORT_THRESHOLD 1000

volatile long sw2_duration  = -1 ;
volatile int  sw2_short_flag = 0 ;
volatile int  sw2_long_flag  = 0 ;
volatile int  sw2_held       = 0 ;
volatile long sw2_sec_count  = 0 ;
volatile long sw2_short_threshold = SW2_SHORT_THRESHOLD ; // New

/* print related utilities */
#define STR_LEN 64
char str[STR_LEN+1] ;

void print(char *str)
{
    UART_UartPutString(str) ;    
}

void cls(void)
{
    print("\033c") ; /* reset */
    CyDelay(100) ;
    print("\033[2J") ; /* clear screen */
    CyDelay(100) ;
}

void splash(char *title)
{
    cls() ;
    if (title && *title) {
        print(title) ;
        print(" ") ;
    }
    snprintf(str, STR_LEN, "(%s %s)\n\r", __DATE__, __TIME__) ;
    print(str) ;
}

/* SysTick related functions */
volatile uint32_t tick_count = 0 ;

void set_short_threshold(long new_value) // New
{
    sw2_short_threshold = new_value ;
}

CY_ISR(tick_callback)
{
    tick_count++ ;
    
    if (sw2_duration >= 0) { // if sw2_duration < 0, this stops
        sw2_duration++ ;
        if (sw2_duration >= sw2_short_threshold) { 
            sw2_long_flag = 1 ;
            sw2_held      = 1 ;
            sw2_duration  = 0 ;
        }
    }
}

int find_empty_slot(void)
{
    int result = -1 ;
    uint32_t i ;
    for (i = 0 ; i < CY_SYS_SYST_NUM_OF_CALLBACKS ; i++ ) {
        if (CySysTickGetCallback(i) == NULL) {
            result = i ;
            break ;
        }
    }
    return(result) ;
}

void start_sys_tick_timer(void)
{
    int sys_tick_slot = 0 ;

    sys_tick_slot = find_empty_slot() ;
    if (sys_tick_slot < 0) {
        print("Sorry No empty SysTick Slot available\n\r") ;
        while(1) { } /* halting here */
    } else {
        CySysTickStart() ;
        CySysTickSetCallback(sys_tick_slot, tick_callback) ;
    }
}

CY_ISR(sw2_isr)
{
    Pin_Switch_ClearInterrupt() ;
    if (Pin_Switch_Read()) { /* SW2=High -> released */
        if (sw2_held == 0) {
            sw2_short_flag = 1 ;
        }
        sw2_sec_count = 0 ;
        sw2_held      = 0 ;
        sw2_duration  = -1 ;
    } else { /* SW2=Low -> pushed */
        sw2_duration = 0 ;
    }
}

void init_hardware(void)
{
    /* Enable global interrupts. */
	CyGlobalIntEnable; 
    
    start_sys_tick_timer() ;
    
    sw2_int_ClearPending() ;
    sw2_int_StartEx(sw2_isr) ;
    
    UART_Start() ;
    splash("button double assing test") ;
}

int main(void)
{
    init_hardware() ;
    
    set_short_threshold(1500) ; // now 1.5 sec is the threshold
    
    for(;;)
    {
        if (sw2_short_flag) {
            sw2_short_flag = 0 ;
            print("Button Pushed!\n\r") ;
        }
        if (sw2_long_flag) {
            sw2_long_flag = 0 ;
            sw2_sec_count++   ;
            snprintf(str, STR_LEN, "%ld\n\r", sw2_sec_count) ;
            print(str) ;
        }
    }
}

At the bottom of the previous discussion, 
@Michael_K -san, asked a couple of questions, but as I could not reply to the discussion, let me try to reply here.

(Q1) Is it not possible to replace sw2_duration in the SysTick callback with the tick_count and reset tick_count after SW2_SHORT_TRESHOLD is reached?

I think it is possible, but if we want to use tick_count for other time measure(s),

I'd rather use dedicated variable to keep track of one purpose.

(Q2) And why does sw2_duration have the value -1 in the ISR?

In this example, I assigned three mode(s) on sw2_duration

-1 or negative number:

Counter for SW2 is inactive.

So nothing will be done in the ISR.

0 to sw2 duration: SW2 is in short or first pushed state

If SW2 is released during this state, it will be taken as sw2_short_flag.

sw2 duration or longer : SW2 is in long push state

While in this mode, function for sw2_long_flag is set repeatedly.

BTW, I did not care about overflow of the counter, since long can hold rather large value,

I thought not many will try to keep holding the button for such a long time (more than 20 days!)

 

moto

0 Replies