# Button Status

## Lab 7: Hardware Button Status

### 1. โครงสร้างภาพรวมของ Lab

#### Why? - ทำไมต้องเรียนรู้เรื่องนี้

การอ่านสถานะปุ่มกดเป็นพื้นฐานสำคัญของ Embedded System:

* **Physical Input**: รับ input จากผู้ใช้ผ่านปุ่มจริงบนบอร์ด
* **Polling Pattern**: เรียนรู้ Timer-based polling สำหรับอ่าน Hardware
* **Debouncing**: เข้าใจปัญหา Mechanical Bounce และวิธีกรอง
* **Active Low Logic**: เข้าใจ Hardware logic ของปุ่มกดแบบ Pull-up
* **Industrial Use Case**: Emergency Stop, Mode Select, Start/Stop buttons

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

1. **Button API**: `aic_gpio_button_read_raw(AIC_BTN_USER)` อ่านปุ่มจริง
2. **Timer Polling**: ใช้ `lv_timer_create()` อ่านสถานะทุก 50ms
3. **Active Low Logic**: ปุ่มกด = return 0, ปล่อย = return 1
4. **Debounce Technique**: กรอง noise จาก mechanical switch
5. **UI Feedback**: แสดง LED widget + Label เปลี่ยนสีตามสถานะปุ่ม

#### How? - ทำอย่างไร

1. Initialize GPIO ด้วย `aic_gpio_init()`
2. สร้าง LVGL Timer ที่ poll ทุก 50ms
3. ใน timer callback อ่านสถานะปุ่มด้วย `aic_gpio_button_read_raw()`
4. อัพเดท UI ตามสถานะ: LED widget + Label + สีพื้นหลัง

<figure><img src="/files/36cIa41DeYZIcmpN7LHD" alt=""><figcaption></figcaption></figure>

***

### 2. หลักการทำงาน (Technical Principles)

#### 2.1 Button Hardware Configuration

```
+-------------------------------------------------------------+
|             PSoC Edge E84 Button Configuration               |
+-------------------------------------------------------------+
|                                                              |
|   Button         Pin     Macro           Active Logic        |
|   ---------------------------------------------------------- |
|   USER Button    P8_3    AIC_BTN_USER    Active Low          |
|                                                              |
|   Active Low Circuit:                                        |
|                                                              |
|        3.3V                                                  |
|         |                                                    |
|        [R] Pull-up Resistor (10K)                            |
|         |                                                    |
|    +----+---- GPIO Pin (P8_3)                                |
|    |                                                         |
|   [SW] Button                                                |
|    |                                                         |
|   GND                                                        |
|                                                              |
|   Released: Pin reads HIGH (1) -- pulled up to 3.3V          |
|   Pressed:  Pin reads LOW  (0) -- shorted to GND             |
|                                                              |
|   aic_gpio_button_read_raw() returns:                        |
|     0 = Pressed (active LOW)                                 |
|     1 = Released                                             |
|                                                              |
+-------------------------------------------------------------+
```

#### 2.2 Timer Polling Architecture

```
+-------------------------------------------------------------+
|                  TIMER POLLING PATTERN                      |
+-------------------------------------------------------------+
|                                                             |
|   LVGL Main Loop                                            |
|   +--------------------------------------------------+      |
|   |  lv_timer_handler()                              |      |
|   |       |                                          |      |
|   |       +---> Check all registered timers          |      |
|   |       |                                          |      |
|   |       +---> [poll_timer] 50ms elapsed?           |      |
|   |                  |                               |      |
|   |             Yes  |                               |      |
|   |                  v                               |      |
|   |          poll_btn_cb()                           |      |
|   |          {                                       |      |
|   |            raw = aic_gpio_button_read_raw()      |      |
|   |            if (raw == 0) -> PRESSED              |      |
|   |            else          -> RELEASED             |      |
|   |            update_ui(state)                      |      |
|   |          }                                       |      |
|   +--------------------------------------------------+      |
|                                                             |
|   Why 50ms interval?                                        |
|   +-- Fast enough: user feels instant response              |
|   +-- Slow enough: does not overload CPU                    |
|   +-- Natural debounce: 50ms > bounce time (~5-20ms)        |
|                                                             |
+-------------------------------------------------------------+
```

#### 2.3 Mechanical Bounce Problem

```
+-------------------------------------------------------------+
|                  SWITCH BOUNCE PROBLEM                       |
+-------------------------------------------------------------+
|                                                              |
|   Ideal Switch Press:                                        |
|                                                              |
|   HIGH --------+                                             |
|                |                                             |
|   LOW          +-----------------------------                |
|                ^                                             |
|              Press                                           |
|                                                              |
|   Real Switch Press (with bounce):                           |
|                                                              |
|   HIGH ----+  +-+  +--+                                      |
|            |  | |  |  |                                      |
|   LOW      +--+ +--+  +-----------------------------         |
|            |<- bounce ->|                                    |
|            ~5-20ms      Stable LOW                           |
|                                                              |
|   Solution: Software Debounce                                |
|   +-- Read button every 50ms (larger than bounce time)       |
|   +-- Require same state for 2-3 consecutive reads           |
|   +-- Only then acknowledge state change                     |
|                                                              |
+-------------------------------------------------------------+
```

#### 2.4 UI Layout

```
+--------------------------------------------------------------+
|           Part 1 Ex7: HW Button Status                       |
+--------------------------------------------------------------+
|                                                              |
|              [ LED Indicator ]                               |
|                                                              |
|              USER BUTTON (SW2)                               |
|                                                              |
|                Released                                      |
|              (text color: green)                             |
|                                                              |
|         Press Count: 0                                       |
|                                                              |
|   [Part II - HW] Press the USER button on the board          |
+--------------------------------------------------------------+
```

***

### 3. ฟังก์ชันสำคัญ (API Reference)

#### 3.1 Button Hardware API (aic-eec.h)

<table><thead><tr><th width="275.77911376953125">Function</th><th width="98.6739501953125">Returns</th><th>Description</th></tr></thead><tbody><tr><td><code>aic_gpio_init()</code></td><td>void</td><td>Initialize GPIO (เรียกครั้งเดียว)</td></tr><tr><td><code>aic_gpio_button_read_raw(btn)</code></td><td>int</td><td>อ่านค่าดิบ: 0=Pressed, 1=Released</td></tr></tbody></table>

#### 3.2 Button Constants

```c
AIC_BTN_USER       // ปุ่ม USER Button (SW2) บน P8_3
```

#### 3.3 LVGL Timer API

| Function                                    | Description          |
| ------------------------------------------- | -------------------- |
| `lv_timer_create(cb, period_ms, user_data)` | สร้าง periodic timer |
| `lv_timer_delete(timer)`                    | ลบ timer             |
| `lv_timer_set_period(timer, new_ms)`        | เปลี่ยน period       |
| `lv_timer_reset(timer)`                     | Reset countdown      |

#### 3.4 LVGL Functions ที่ใช้ในบทนี้

<table><thead><tr><th width="383.06610107421875">Function</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_led_create(parent)</code></td><td>สร้าง LED indicator widget</td></tr><tr><td><code>lv_led_on(led)</code> / <code>lv_led_off(led)</code></td><td>เปิด/ปิด LED indicator</td></tr><tr><td><code>lv_led_set_color(led, color)</code></td><td>ตั้งสี LED</td></tr><tr><td><code>lv_label_set_text(label, text)</code></td><td>ตั้งข้อความ Label</td></tr><tr><td><code>lv_label_set_text_fmt(label, fmt, ...)</code></td><td>ตั้งข้อความแบบ format</td></tr><tr><td><code>lv_obj_set_style_text_color(obj, color, 0)</code></td><td>เปลี่ยนสีข้อความ</td></tr></tbody></table>

***

### 4. โค้ดตัวอย่าง (Code Examples)

#### 4.1 Minimal Example: Read Button and Update Label

```c
#include "aic-eec.h"

static lv_obj_t * status_label;

/* Timer callback: poll button every 50ms */
static void poll_btn_cb(lv_timer_t * t)
{
    int raw = aic_gpio_button_read_raw(AIC_BTN_USER);

    if (raw == 0) {
        /* Pressed (Active Low) */
        lv_label_set_text(status_label, "PRESSED");
        lv_obj_set_style_text_color(status_label,
            lv_palette_main(LV_PALETTE_RED), 0);
    } else {
        /* Released */
        lv_label_set_text(status_label, "Released");
        lv_obj_set_style_text_color(status_label,
            lv_palette_main(LV_PALETTE_GREEN), 0);
    }
}

void minimal_button_read(void)
{
    aic_gpio_init();

    status_label = lv_label_create(lv_screen_active());
    lv_label_set_text(status_label, "Released");
    lv_obj_center(status_label);

    /* สร้าง timer poll ทุก 50ms */
    lv_timer_create(poll_btn_cb, 50, NULL);
}
```

#### 4.2 Full Example: Button Status with Debounce + Press Counter

```c
#include "aic-eec.h"

/* ===== Global Variables ===== */
static lv_obj_t * led_indicator;
static lv_obj_t * status_label;
static lv_obj_t * count_label;

static int press_count = 0;
static bool prev_pressed = false;    /* สถานะรอบก่อน สำหรับ edge detection */
static int debounce_count = 0;       /* นับจำนวนครั้งที่อ่านค่าเดิมติดกัน */

#define DEBOUNCE_THRESHOLD  3  /* ต้องอ่านค่าเดิม 3 ครั้งติด (3x50ms = 150ms) */

/* ===== Timer Callback: Poll Button ===== */
static void poll_btn_cb(lv_timer_t * t)
{
    int raw = aic_gpio_button_read_raw(AIC_BTN_USER);
    bool is_pressed = (raw == 0);  /* Active Low: 0 = pressed */

    /* ===== Software Debounce ===== */
    static bool stable_state = false;
    static bool last_raw = false;

    if (is_pressed == last_raw) {
        debounce_count++;
    } else {
        debounce_count = 0;
    }
    last_raw = is_pressed;

    /* ยังไม่เสถียร -> ข้ามไป */
    if (debounce_count < DEBOUNCE_THRESHOLD) return;

    /* สถานะเสถียรแล้ว */
    stable_state = is_pressed;

    /* ===== Update UI ===== */
    if (stable_state) {
        /* PRESSED */
        lv_led_on(led_indicator);
        lv_label_set_text(status_label, "PRESSED");
        lv_obj_set_style_text_color(status_label,
            lv_palette_main(LV_PALETTE_RED), 0);
    } else {
        /* RELEASED */
        lv_led_off(led_indicator);
        lv_label_set_text(status_label, "Released");
        lv_obj_set_style_text_color(status_label,
            lv_palette_main(LV_PALETTE_GREEN), 0);
    }

    /* ===== Edge Detection: นับ Press ===== */
    if (stable_state && !prev_pressed) {
        /* Rising edge: just pressed */
        press_count++;
        lv_label_set_text_fmt(count_label, "Press Count: %d", press_count);
    }
    prev_pressed = stable_state;
}

/* ===== Main Function ===== */
void part1_ex7_hw_button_status(void)
{
    /* ============================== */
    /*    HARDWARE INITIALIZATION     */
    /* ============================== */
    aic_gpio_init();

    /* ============================== */
    /*         LVGL UI SETUP          */
    /* ============================== */
    lv_obj_t * scr = lv_screen_active();
    lv_obj_set_style_bg_color(scr, lv_color_hex(0x1a1a2e), 0);

    /* --- Title --- */
    lv_obj_t * title = lv_label_create(scr);
    lv_label_set_text(title, "Lab 7: HW Button Status");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);

    /* --- Container --- */
    lv_obj_t * cont = lv_obj_create(scr);
    lv_obj_set_size(cont, 350, 180);
    lv_obj_align(cont, LV_ALIGN_CENTER, 0, 10);
    lv_obj_set_style_bg_color(cont, lv_color_hex(0x16213e), 0);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER,
                          LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_row(cont, 12, 0);
    lv_obj_set_style_pad_all(cont, 15, 0);

    /* --- LED Indicator --- */
    led_indicator = lv_led_create(cont);
    lv_led_set_color(led_indicator, lv_palette_main(LV_PALETTE_CYAN));
    lv_obj_set_size(led_indicator, 50, 50);
    lv_led_off(led_indicator);

    /* --- Button Name --- */
    lv_obj_t * btn_name = lv_label_create(cont);
    lv_label_set_text(btn_name, "USER BUTTON (SW2)");
    lv_obj_set_style_text_color(btn_name, lv_color_hex(0xCCCCCC), 0);

    /* --- Status Label --- */
    status_label = lv_label_create(cont);
    lv_label_set_text(status_label, "Released");
    lv_obj_set_style_text_color(status_label,
        lv_palette_main(LV_PALETTE_GREEN), 0);
    lv_obj_set_style_text_font(status_label, &lv_font_montserrat_24, 0);

    /* --- Press Counter --- */
    count_label = lv_label_create(cont);
    lv_label_set_text(count_label, "Press Count: 0");
    lv_obj_set_style_text_color(count_label, lv_color_hex(0x00BFFF), 0);

    /* --- Footer --- */
    lv_obj_t * footer = lv_label_create(scr);
    lv_label_set_text(footer, "[Part II - HW] Press the USER button on the board");
    lv_obj_set_style_text_color(footer, lv_color_hex(0x888888), 0);
    lv_obj_align(footer, LV_ALIGN_BOTTOM_MID, 0, -10);

    /* ============================== */
    /*    START POLLING TIMER         */
    /* ============================== */
    lv_timer_create(poll_btn_cb, 50, NULL);
}
```

#### 4.3 อธิบายโค้ดทีละขั้นตอน

<table><thead><tr><th width="92.6328125">ขั้นตอน</th><th width="357.593017578125">โค้ด</th><th>คำอธิบาย</th></tr></thead><tbody><tr><td>1</td><td><code>aic_gpio_init()</code></td><td>เริ่มต้น GPIO subsystem</td></tr><tr><td>2</td><td><code>lv_timer_create(poll_btn_cb, 50, NULL)</code></td><td>สร้าง timer poll ทุก 50ms</td></tr><tr><td>3</td><td><code>aic_gpio_button_read_raw(AIC_BTN_USER)</code></td><td>อ่านค่าดิบ (0=pressed)</td></tr><tr><td>4</td><td>Debounce check</td><td>นับค่าเดิมติดกัน 3 ครั้ง = เสถียร</td></tr><tr><td>5</td><td>Edge detection</td><td><code>stable &#x26;&#x26; !prev</code> = just pressed, นับ press</td></tr><tr><td>6</td><td><code>lv_led_on/off()</code> + <code>lv_label_set_text()</code></td><td>อัพเดท UI ตามสถานะ</td></tr></tbody></table>

***

### 5. องค์ความรู้และเทคนิค

#### 5.1 Pattern: Hardware Polling with LVGL Timer

```c
/* Template สำหรับ poll hardware ใดก็ได้ */
static void hw_poll_cb(lv_timer_t * t)
{
    /* [1] อ่าน hardware */
    int value = read_hardware_sensor();

    /* [2] อัพเดท UI (safe - เราอยู่ใน LVGL context) */
    lv_label_set_text_fmt(my_label, "Value: %d", value);
}

void setup(void) {
    init_hardware();
    /* สร้าง timer สำหรับ polling */
    lv_timer_create(hw_poll_cb, 50, NULL);  /* 50ms interval */
}
```

#### 5.2 Pattern: Edge Detection (ตรวจจับจังหวะกด)

```c
static bool prev_state = false;

static void poll_cb(lv_timer_t * t)
{
    bool current = (aic_gpio_button_read_raw(AIC_BTN_USER) == 0);

    if (current && !prev_state) {
        /* FALLING EDGE: just pressed */
        on_button_press();
    }
    if (!current && prev_state) {
        /* RISING EDGE: just released */
        on_button_release();
    }
    prev_state = current;
}
```

#### 5.3 Pattern: Simple Debounce

```c
#define DEBOUNCE_MS  3  /* consecutive same-reads needed */
static int same_count = 0;
static bool last_raw = false;
static bool stable = false;

static void debounce_poll(lv_timer_t * t)
{
    bool raw = (aic_gpio_button_read_raw(AIC_BTN_USER) == 0);

    if (raw == last_raw) {
        if (++same_count >= DEBOUNCE_MS) {
            stable = raw;  /* สถานะเสถียร */
        }
    } else {
        same_count = 0;    /* เปลี่ยนแปลง -> reset */
    }
    last_raw = raw;

    /* ใช้ stable เท่านั้นสำหรับ logic */
    update_ui(stable);
}
```

#### 5.4 สิ่งที่ต้องระวัง

| หัวข้อ                 | รายละเอียด                                                         |
| ---------------------- | ------------------------------------------------------------------ |
| **Active Low**         | `read_raw()` คืน 0 เมื่อกด, 1 เมื่อปล่อย -- กลับจากสัญชาตญาณ       |
| **LVGL Thread Safety** | อัพเดท UI ได้เฉพาะใน LVGL context (timer callback, event callback) |
| **Timer Period**       | 50ms เป็นค่าที่ดี: เร็วพอสำหรับ UI, ช้าพอไม่กิน CPU                |
| **Debounce**           | ไม่ debounce = นับ press ผิด, ค่า flicker                          |
| **Static Variables**   | ตัวแปร state ต้องเป็น `static` เพราะ callback ถูกเรียกซ้ำหลายครั้ง |

#### 5.5 Application ในอุตสาหกรรม

* **Emergency Stop**: ปุ่มหยุดฉุกเฉินในโรงงาน ต้อง debounce + edge detect
* **Mode Selection**: ปุ่มเลือกโหมดทำงาน (Manual/Auto/Service)
* **Counting**: นับจำนวนชิ้นงานที่ผ่าน sensor (press = detect)

***

### 6. แบบฝึกหัด (Exercises)

#### แบบฝึกหัดที่ 1: Press Counter with Long-Press Detection

**โจทย์**: สร้างระบบนับการกดปุ่มที่แยก Short Press กับ Long Press

**ข้อกำหนด**:

* Short Press (กดน้อยกว่า 1 วินาที): เพิ่ม counter +1
* Long Press (กดค้าง >= 1 วินาที): Reset counter เป็น 0
* แสดงสถานะ: "Short Press!" (สีเขียว) / "Long Press - Reset!" (สีแดง)
* แสดง Press Duration ขณะกดค้างอยู่ (เช่น "Holding: 0.8s")
* LED indicator เปลี่ยนสีตามระยะเวลากด (เขียว -> เหลือง -> แดง)

**Hints**:

* ใช้ตัวแปร `static uint32_t press_start_tick = 0;` เก็บเวลาเริ่มกด
* `lv_tick_get()` ได้ค่า milliseconds ปัจจุบัน
* Long press threshold = 1000ms (20 timer ticks x 50ms)

#### แบบฝึกหัดที่ 2: Button-Triggered LED Toggle with Visual Feedback

**โจทย์**: กดปุ่ม USER แต่ละครั้ง ให้สลับสถานะ LED ตามลำดับ

**ข้อกำหนด**:

* กดครั้งที่ 1: Red LED ON
* กดครั้งที่ 2: Red OFF + Green LED ON
* กดครั้งที่ 3: Green OFF + Blue LED ON
* กดครั้งที่ 4: ปิดหมด แล้วกลับไปครั้งที่ 1
* แสดงสถานะบนจอ: LED ตัวไหนติด + หมายเลขครั้งที่กด
* LVGL LED indicators 3 ตัว แสดงสถานะตรงกับ Hardware LED

**Hints**:

* ใช้ `static int led_mode = 0;` และ `led_mode = (led_mode + 1) % 4;`
* ใช้ Edge Detection เพื่อนับเฉพาะจังหวะกดลง


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/gpio-to-hmi-display/hardware-interfacing-workshops/button-status.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
