> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/basic-mcu-interfacing/workshops/user-switch.md).

# User Switch

## Lab 2: User Switch — อ่านปุ่มกด & Debounce

#### วัตถุประสงค์

* **GPIO Input**: อ่านค่าจาก Digital Input
* **Debounce**: ป้องกันการอ่านค่าผิดพลาดจาก Contact Bounce
* **State Machine**: เปลี่ยน LED state เมื่อกดปุ่ม

#### จะได้เรียนรู้อะไร?

* **GPIO Read**: Cy\_GPIO\_Read()
* **Active LOW**: ปุ่มกดลง = 0
* **Debounce Pattern**: Software debounce
* **Static Variables**: เก็บ state ระหว่าง function calls

#### **Hardware Information**

| Component           | Port/Pin          | หมายเหตุ                |
| ------------------- | ----------------- | ----------------------- |
| User LED1           | CYBSP\_USER\_LED1 | Active HIGH (HIGH = ON) |
| User Button 1 (SW2) | CYBSP\_USER\_BTN1 | Active LOW (กดลง = 0)   |
| User Button 2 (SW4) | CYBSP\_USER\_BTN2 | Active LOW (กดลง = 0)   |

***

### Lab 2.1 : อ่านปุ่มกดแบบ Polling

#### **เพิ่ม Button Macros:**

```c
/* เพิ่มหลัง LED macros */

/* Button Configuration */
#define USER_BTN_PORT       CYBSP_USER_BTN1_PORT
#define USER_BTN_PIN        CYBSP_USER_BTN1_PIN

/* Button Read Macro - Active LOW */
#define IS_BUTTON_PRESSED() (Cy_GPIO_Read(USER_BTN_PORT, USER_BTN_PIN) == 0)
```

#### **แก้ไขฟังก์ชั่น Main :**

```c
int main(void)
{
    cy_rslt_t result = CY_RSLT_SUCCESS;
    
    /* Initialize */
    result = cybsp_init();
    if (CY_RSLT_SUCCESS != result) { handle_app_error(); }

    __enable_irq();
    init_retarget_io();

    printf("\x1b[2J\x1b[;H");
    printf("*** PSoC Edge: Button Demo ***\r\n");
    printf("Press SW2 to toggle LED\r\n\n");

    Cy_SysEnableCM55(MXCM55, CM55_APP_BOOT_ADDR, CM55_BOOT_WAIT_TIME_USEC);

    /* Main loop */
    for(;;)
    {
        /* ตรวจสอบปุ่ม */
        if (IS_BUTTON_PRESSED())
        {
            printf("Button pressed!\r\n");
        } else
        {
            printf("Button NOT pressed!\r\n");
        }

        /* หน่วงเวลาเล็กน้อยเพื่อลด CPU load */
        Cy_SysLib_Delay(10);
    }
}
```

***

### Lab 2.2 : Debounce แบบง่าย (Edge Detection)

**WHY: ทำไมต้องรู้?**

* **ปัญหา:** ปุ่มกดมีสัญญาณกระเด้ง (bouncing) ทำให้ detect หลายครั้ง (5-10 ครั้ง!)
* **ประโยชน์:** อ่านค่าปุ่มได้ถูกต้อง, ไม่มี false triggers
* **Hardware:** Mechanical switch มี contact bounce 10-50ms

```
ปุ่มกดจริงๆ:     ___████████████___
                 ↑ กด        ↑ ปล่อย

สัญญาณที่ MCU เห็น: ___█_█_██_█████___
                    ↑ Bouncing!
```

**CAUTION: ข้อควรระวัง**

| ข้อผิดพลาด       | ผลกระทบ               | วิธีป้องกัน              |
| ---------------- | --------------------- | ------------------------ |
| Delay สั้นเกินไป | ยัง bounce อยู่       | ใช้ 20-50ms              |
| Delay นานเกินไป  | ผู้ใช้รู้สึกว่า laggy | ไม่ควรเกิน 100ms         |
| Debounce ใน ISR  | Block interrupt นาน   | ใช้ timer-based debounce |

#### เพิ่ม debouncing และ Macro functions

```c
#define DEBOUNCE_DELAY_MS           (50U)

/* LED Control Macros */
#define LED_ON()                    Cy_GPIO_Clr(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
#define LED_OFF()                   Cy_GPIO_Set(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
#define LED_TOGGLE()                Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)

/*******************************************************************************
* Function Prototypes
*******************************************************************************/
static bool read_button_with_debounce(void);

/*******************************************************************************
* Global Variables
*******************************************************************************/
static uint32_t button_press_count = 0;
```

#### สร้างฟังก์ชัน Debounce

{% hint style="info" %}
**ด้วยเทคนิค Delay-based Debounce**:

1. ตรวจจับ rising edge (ปุ่มถูกกด)
2. รอ 50ms ด้วย `Cy_SysLib_Delay()`
3. อ่านค่าปุ่มอีกครั้งเพื่อยืนยัน
4. ถ้ายังกดอยู่ = valid press
   {% endhint %}

```c
/*******************************************************************************
* Function Name: read_button_with_debounce
********************************************************************************
* Summary:
*   อ่านค่าปุ่มพร้อม debounce เพื่อป้องกันการ bounce
*
* Parameters:
*   None
*
* Return:
*   true ถ้าปุ่มถูกกด (และ debounced แล้ว), false ถ้าไม่ได้กด
*
* Note:
*   ใช้ static variables เพื่อเก็บ state ระหว่าง calls
*******************************************************************************/
static bool read_button_with_debounce(void)
{
    static bool last_state = false;

    bool current_state = IS_BUTTON_PRESSED();

    /* Detect rising edge (button just pressed) */
    if (current_state && !last_state) {
        /* Wait for debounce period */
        Cy_SysLib_Delay(DEBOUNCE_DELAY_MS);

        /* Re-read to confirm button is still pressed */
        if (IS_BUTTON_PRESSED()) {
            last_state = true;
            return true;  /* Valid press detected */
        }
    }

    /* Update state when button is released */
    if (!current_state) {
        last_state = false;
    }

    return false;
}

```

#### แก้ไข Main Loop

```c
int main(void)
{
    cy_rslt_t result = CY_RSLT_SUCCESS;

    /* Initialize */
    result = cybsp_init();
    if (CY_RSLT_SUCCESS != result) { handle_app_error(); }

    __enable_irq();
    init_retarget_io();

    printf("\x1b[2J\x1b[;H");
    printf("*** PSoC Edge: Button Demo ***\r\n");
    printf("Press SW2 to toggle LED\r\n\n");

    Cy_SysEnableCM55(MXCM55, CM55_APP_BOOT_ADDR, CM55_BOOT_WAIT_TIME_USEC);

    /* Main loop */
    for(;;)
    {
       if (read_button_with_debounce()) {
          button_press_count++;  // เพิ่ม counter
          LED_TOGGLE();
          printf("Button pressed! Count: %lu, LED toggled.\r\n",
                  (unsigned long)button_press_count);
    }
    Cy_SysLib_Delay(10);
}
}
```

#### Debounce Flowchart

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FCdKMiBkhqQO0ktb9Cmlp%2FEmbedded%20Systems%20and%20IoT-Page-7.drawio.png?alt=media&amp;token=8a4efdb7-847a-432c-bb44-d90d91cd9e72" alt=""><figcaption></figcaption></figure>

***

### ทำความเข้าใจการเรียกใช้ Static Variables

```c
/* static ภายใน function = เก็บค่าระหว่าง calls */

void example(void)
{
    static int count = 0;  /* Initialize ครั้งเดียวตอนเริ่มโปรแกรม */
    count++;
    printf("Called %d times\n", count);
}

/* เรียก example() 3 ครั้ง:
 * Called 1 times
 * Called 2 times
 * Called 3 times
 */

/* ถ้าไม่ใช้ static: */
void bad_example(void)
{
    int count = 0;  /* Initialize ทุกครั้งที่เรียก! */
    count++;
    printf("Called %d times\n", count);
}

/* เรียก bad_example() 3 ครั้ง:
 * Called 1 times
 * Called 1 times
 * Called 1 times
 */
```

#### ✍️ Exercise

1. **Press Counter** : นับจำนวนครั้งที่กดปุ่มและแสดงผล

{% hint style="info" %}
**Hint**

ประกาศตัวแปร `uint32_t press_count = 0;` แล้วทำการเพิ่มค่า `press_count++;` ในลูป
{% endhint %}

2. **Long Press Detection** : ตรวจจับการกดค้าง (> 1 วินาที) และทำ action แตกต่าง

{% hint style="info" %}
**Hint**

เรียกใช้ฟังก์ชันนี้

```c
/* Long Press Detection using elapsed_time counter */
bool read_long_press(uint32_t hold_time_ms, uint32_t elapsed_time)
{
    static uint32_t press_start = 0;
    static bool counting = false;

    if (IS_BUTTON_PRESSED())
    {
        if (!counting)
        {
            counting = true;
            press_start = elapsed_time;
        }
        else if ((elapsed_time - press_start) >= hold_time_ms)
        {
            counting = false;  /* Reset for next detection */
            return true;       /* Long press detected! */
        }
    }
    else
    {
        counting = false;
    }

    return false;
}
```

{% endhint %}

***
