# CAPSENSE

## Lab 11: CAPSENSE Hardware (Direct I2C)

### Part 1 - LVGL Basics + CAPSENSE Touch Controller

### 1. โครงสร้างภาพรวม (Overview)

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

การอ่าน Touch Sensor ผ่าน I2C โดยตรงจาก CM55 เป็นทักษะสำคัญสำหรับระบบ Embedded:

* **Direct I2C Access**: CM55 อ่าน PSoC 4000T โดยตรง ไม่ผ่าน CM33 หรือ IPC
* **Shared Bus Management**: จัดการ I2C bus ที่ใช้ร่วมกับ Display Touch Controller
* **Edge Detection**: ตรวจจับเฉพาะเมื่อ state เปลี่ยน เพื่อประหยัด CPU
* **Error Handling**: จัดการ I2C communication error อย่างถูกวิธี
* **Real Hardware**: ต่อยอดจาก Ex10 (Mockup) มาเป็นข้อมูลจริงจาก Sensor

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

1. **I2C Bus Sharing**: ปิด Display Touch ก่อนอ่าน CAPSENSE (shared SCB0)
2. **aic\_capsense API**: `aic_capsense_init()` + `aic_capsense_read()`
3. **Edge Detection Pattern**: เปรียบเทียบ prev vs current state
4. **I2C Error Recovery**: จัดการ 3 สถานะ (OK / Error / No Response)
5. **LED Hardware Control**: BTN0 -> Red, BTN1 -> Green, Slider -> Blue PWM

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

1. เรียก `lv_port_indev_disable_touch()` ปิด Display Touch (คืน I2C bus)
2. Initialize GPIO + PWM + CAPSENSE
3. สร้าง UI เหมือน Ex10 (Slider panel + Button panels)
4. Timer poll 50ms อ่าน I2C + อัพเดท UI เฉพาะเมื่อ state เปลี่ยน

***

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

#### 2.1 I2C Bus Sharing Problem

```
+-------------------------------------------------------------+
|           I2C BUS SHARING (SCB0)                             |
+-------------------------------------------------------------+
|                                                              |
|   SCB0 I2C Bus (Shared!)                                     |
|   +--------------------------------------------------+       |
|   |   [Display Touch]        [PSoC 4000T CAPSENSE]   |       |
|   |    Controller              Address: 0x08         |       |
|   +--------------------------------------------------+       |
|                                                              |
|   PROBLEM: ทั้งสองตัวใช้ I2C bus เดียวกัน!                         |
|   SOLUTION: ปิด touch polling ก่อนเริ่มอ่าน CAPSENSE              |
|                                                              |
|   lv_port_indev_disable_touch()  <-- เรียกก่อน init            |
|                                                              |
+-------------------------------------------------------------+
```

#### 2.2 Data Flow Architecture

```
+-------------------------------------------------------------+
|         DIRECT I2C ARCHITECTURE (CM55 Only)                  |
+-------------------------------------------------------------+
|                                                              |
|   PSoC 4000T              CM55 (LVGL + I2C Master)           |
|   +------------+          +---------------------------+      |
|   | CSB1 (BTN0)|          |                           |      |
|   | CSB2 (BTN1)|--I2C---->| aic_capsense_read(&data)  |      |
|   | CSS1 (Sldr)|  0x08    |    |                      |      |
|   +------------+          |    v                      |      |
|                           | aic_capsense_data_t       |      |
|   Data: 3 bytes           |   .btn0_pressed (0/1)     |      |
|   [BTN0, BTN1, Slider]    |   .btn1_pressed (0/1)     |      |
|                           |   .slider_pos  (0-100)    |      |
|   NOTE: No CM33 needed!   |   .slider_active (0/1)    |      |
|                           |    |                      |      |
|                           |    v                      |      |
|                           | Edge Detect -> UI + LEDs  |      |
|                           +---------------------------+      |
+-------------------------------------------------------------+
```

#### 2.3 Edge Detection Pattern

```
WITHOUT Edge Detection (Bad):
  tick 1: btn0=1 -> update UI  (set TOUCHED)
  tick 2: btn0=1 -> update UI  (set TOUCHED again - unnecessary!)
  tick 3: btn0=1 -> update UI  (waste CPU every 50ms)

WITH Edge Detection (Good):
  tick 1: btn0=1, prev=0 -> CHANGED! update UI
  tick 2: btn0=1, prev=1 -> same, skip
  tick 3: btn0=0, prev=1 -> CHANGED! update UI

Implementation:
  if (btn0 != prev_btn0) {
      /* state changed! update UI */
      prev_btn0 = btn0;
  }
```

#### 2.4 I2C Error Status (Color Coding)

| สถานะ           | สี                | เงื่อนไข                          |
| --------------- | ----------------- | --------------------------------- |
| **OK**          | Green (0x00FF00)  | `aic_capsense_read()` return true |
| **Error**       | Red (0xFF0000)    | เคย connected แต่ตอนนี้ read fail |
| **No Response** | Yellow (0xFFFF00) | ไม่เคย connect สำเร็จเลย          |

#### 2.5 UI Layout

```
+-------------------------------------------------------------+
|          Part 1 Ex11: CAPSENSE (I2C)                        |
|          Mode: Direct I2C (CM55)                            |
+-------------------------------------------------------------+
|  +-- SLIDER (CSS1) ----------------------------- [*] --+    |
|  |  ========================================     (LED)  |    |
|  |  0%                                   100%    Blue   |    |
|  +------------------------------------------------------+    |
|                                                              |
|  +--- BTN0 ---+                          +--- BTN1 ---+     |
|  |   (CSB1)   |                          |   (CSB2)   |     |
|  |   (  *  )  |                          |   (  *  )  |     |
|  |    Red LED  |                          |  Green LED |     |
|  |   Ready     |                          |   Ready    |     |
|  +-------------+                          +-------------+    |
|                                                              |
|  CM55 reads PSoC 4000T directly via I2C (0x08)              |
|  I2C: cnt=150 OK                                            |
+-------------------------------------------------------------+
```

***

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

#### 3.1 CAPSENSE API

| Function                        | Description                     | Return |
| ------------------------------- | ------------------------------- | ------ |
| `aic_capsense_init()`           | Initialize I2C สำหรับ CAPSENSE  | `bool` |
| `aic_capsense_read(&data)`      | อ่านข้อมูลจาก PSoC 4000T        | `bool` |
| `aic_capsense_is_available()`   | ตรวจว่า CAPSENSE ตอบ I2C        | `bool` |
| `lv_port_indev_disable_touch()` | ปิด Display Touch (คืน I2C bus) | `void` |

#### 3.2 CAPSENSE Data Structure

```c
typedef struct {
    bool btn0_pressed;      /* Button 0 (CSB1): true = touched */
    bool btn1_pressed;      /* Button 1 (CSB2): true = touched */
    uint8_t slider_pos;     /* Slider position: 0-100 (%) */
    bool slider_active;     /* Slider is being touched */
} aic_capsense_data_t;
```

#### 3.3 Hardware LED + LVGL Widget API

| Function                                            | Description                      |
| --------------------------------------------------- | -------------------------------- |
| `aic_gpio_led_set(led, state)`                      | ควบคุม LED ON/OFF (Red, Green)   |
| `aic_gpio_pwm_set_brightness(AIC_LED_BLUE, val)`    | ตั้งความสว่าง Blue LED (0-100)   |
| `lv_slider_set_value(slider, val, anim)`            | ตั้งค่า Slider                   |
| `lv_obj_remove_flag(slider, LV_OBJ_FLAG_CLICKABLE)` | ทำให้ Slider read-only           |
| `lv_led_on(led)` / `lv_led_off(led)`                | เปิด/ปิด LED widget              |
| `lv_led_set_brightness(led, val)`                   | ตั้งความสว่าง LED widget (0-255) |

***

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

#### 4.1 Full CAPSENSE Hardware Code

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

#define EX11_NUM_BUTTONS 2

/* ===== UI Elements ===== */
static lv_obj_t * ex11_btn_panels[EX11_NUM_BUTTONS];
static lv_obj_t * ex11_btn_leds[EX11_NUM_BUTTONS];
static lv_obj_t * ex11_btn_status[EX11_NUM_BUTTONS];
static lv_obj_t * ex11_slider;
static lv_obj_t * ex11_slider_value;
static lv_obj_t * ex11_output_led;
static lv_obj_t * ex11_status_label;
static lv_timer_t * ex11_poll_timer;

/* ===== Edge Detection State ===== */
static bool ex11_prev_btn0 = false;
static bool ex11_prev_btn1 = false;
static uint8_t ex11_prev_slider = 0;

/* ===== I2C Status Tracking ===== */
static uint32_t ex11_read_count = 0;
static uint32_t ex11_error_count = 0;
static bool ex11_connected = false;

/* ============================================================ */
/*        HELPER: Update UI with CAPSENSE data                  */
/* ============================================================ */
static void ex11_update_ui(bool btn0, bool btn1,
                           uint8_t slider_pos, bool slider_active)
{
    /* === Button 0 (CSB1) -> Red LED === */
    if (btn0 != ex11_prev_btn0) {
        if (btn0) {
            lv_obj_set_style_bg_color(ex11_btn_panels[0],
                lv_color_hex(0x00AA00), 0);
            lv_led_on(ex11_btn_leds[0]);
            lv_label_set_text(ex11_btn_status[0], "TOUCHED");
            lv_obj_set_style_text_color(ex11_btn_status[0],
                lv_color_hex(0x00FF00), 0);
            aic_gpio_led_set(AIC_LED_RED, true);
        } else {
            lv_obj_set_style_bg_color(ex11_btn_panels[0],
                lv_color_hex(0x333355), 0);
            lv_led_off(ex11_btn_leds[0]);
            lv_label_set_text(ex11_btn_status[0], "Ready");
            lv_obj_set_style_text_color(ex11_btn_status[0],
                lv_color_hex(0x888888), 0);
            aic_gpio_led_set(AIC_LED_RED, false);
        }
        ex11_prev_btn0 = btn0;
    }

    /* === Button 1 (CSB2) -> Green LED === */
    if (btn1 != ex11_prev_btn1) {
        if (btn1) {
            lv_obj_set_style_bg_color(ex11_btn_panels[1],
                lv_color_hex(0x00AA00), 0);
            lv_led_on(ex11_btn_leds[1]);
            lv_label_set_text(ex11_btn_status[1], "TOUCHED");
            lv_obj_set_style_text_color(ex11_btn_status[1],
                lv_color_hex(0x00FF00), 0);
            aic_gpio_led_set(AIC_LED_GREEN, true);
        } else {
            lv_obj_set_style_bg_color(ex11_btn_panels[1],
                lv_color_hex(0x333355), 0);
            lv_led_off(ex11_btn_leds[1]);
            lv_label_set_text(ex11_btn_status[1], "Ready");
            lv_obj_set_style_text_color(ex11_btn_status[1],
                lv_color_hex(0x888888), 0);
            aic_gpio_led_set(AIC_LED_GREEN, false);
        }
        ex11_prev_btn1 = btn1;
    }

    /* === Slider (CSS1) -> Blue LED PWM === */
    if (slider_pos != ex11_prev_slider) {
        lv_slider_set_value(ex11_slider, slider_pos, LV_ANIM_ON);
        lv_label_set_text_fmt(ex11_slider_value, "%d%%", slider_pos);

        uint8_t brightness = (uint8_t)((slider_pos * 255) / 100);
        if (slider_active) {
            lv_led_on(ex11_output_led);
            lv_led_set_brightness(ex11_output_led, brightness);
        } else {
            lv_led_off(ex11_output_led);
        }
        aic_gpio_pwm_set_brightness(AIC_LED_BLUE, slider_pos);
        ex11_prev_slider = slider_pos;
    }
}

/* ============================================================ */
/*         TIMER CALLBACK: Poll I2C every 50ms                  */
/* ============================================================ */
static void ex11_poll_timer_cb(lv_timer_t * timer)
{
    (void)timer;
    aic_capsense_data_t caps_data;

    if (aic_capsense_read(&caps_data)) {
        /* === I2C Read Success === */
        ex11_read_count++;
        if (!ex11_connected) ex11_connected = true;

        /* Status: Green = OK */
        lv_label_set_text_fmt(ex11_status_label,
            "I2C: cnt=%u OK", (unsigned int)ex11_read_count);
        lv_obj_set_style_text_color(ex11_status_label,
            lv_color_hex(0x00FF00), 0);

        /* Update UI with real CAPSENSE data */
        ex11_update_ui(caps_data.btn0_pressed,
                       caps_data.btn1_pressed,
                       caps_data.slider_pos,
                       caps_data.slider_active);
    } else {
        /* === I2C Read Failed === */
        ex11_error_count++;
        if (ex11_connected) {
            lv_label_set_text_fmt(ex11_status_label,
                "I2C Error (err=%u)",
                (unsigned int)ex11_error_count);
            lv_obj_set_style_text_color(ex11_status_label,
                lv_color_hex(0xFF0000), 0);
        } else {
            lv_label_set_text(ex11_status_label,
                "I2C: No response");
            lv_obj_set_style_text_color(ex11_status_label,
                lv_color_hex(0xFFFF00), 0);
        }
    }
}

/* ============================================================ */
/*                      MAIN FUNCTION                           */
/* ============================================================ */
void part1_ex11_capsense_hardware(void)
{
    /* [0] DISABLE DISPLAY TOUCH (MUST be first!) */
    lv_port_indev_disable_touch();

    /* [1] HARDWARE INITIALIZATION */
    aic_gpio_init();
    aic_gpio_pwm_init(AIC_LED_BLUE);
    aic_capsense_init();

    /* Reset edge detection state */
    ex11_prev_btn0 = false;
    ex11_prev_btn1 = false;
    ex11_prev_slider = 0;
    ex11_read_count = 0;
    ex11_error_count = 0;
    ex11_connected = false;

    /* [2] 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, "Part 1 Ex11: CAPSENSE (I2C)");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 8);

    /* Connection Status */
    ex11_status_label = lv_label_create(scr);
    if (aic_capsense_is_available()) {
        lv_label_set_text(ex11_status_label, "Mode: Direct I2C (CM55)");
        lv_obj_set_style_text_color(ex11_status_label,
            lv_color_hex(0x00FF00), 0);
    } else {
        lv_label_set_text(ex11_status_label, "Mode: I2C not available");
        lv_obj_set_style_text_color(ex11_status_label,
            lv_color_hex(0xFFFF00), 0);
    }
    lv_obj_align(ex11_status_label, LV_ALIGN_TOP_MID, 0, 30);

    /* ===== SLIDER PANEL (TOP) ===== */
    lv_obj_t * slider_panel = lv_obj_create(scr);
    lv_obj_set_size(slider_panel, 420, 80);
    lv_obj_align(slider_panel, LV_ALIGN_TOP_MID, 0, 55);
    lv_obj_set_style_bg_color(slider_panel, lv_color_hex(0x0f0f23), 0);
    lv_obj_set_style_pad_all(slider_panel, 8, 0);
    lv_obj_clear_flag(slider_panel, LV_OBJ_FLAG_SCROLLABLE);

    lv_obj_t * slider_title = lv_label_create(slider_panel);
    lv_label_set_text(slider_title, "SLIDER (CSS1)");
    lv_obj_set_style_text_color(slider_title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_align(slider_title, LV_ALIGN_TOP_LEFT, 10, 0);

    ex11_slider_value = lv_label_create(slider_panel);
    lv_label_set_text(ex11_slider_value, "0%");
    lv_obj_set_style_text_color(ex11_slider_value, lv_color_hex(0x00AAFF), 0);
    lv_obj_set_style_text_font(ex11_slider_value, &lv_font_montserrat_16, 0);
    lv_obj_align(ex11_slider_value, LV_ALIGN_TOP_RIGHT, -10, 0);

    ex11_slider = lv_slider_create(slider_panel);
    lv_obj_set_width(ex11_slider, 340);
    lv_obj_set_height(ex11_slider, 25);
    lv_obj_align(ex11_slider, LV_ALIGN_BOTTOM_MID, 0, -8);
    lv_slider_set_range(ex11_slider, 0, 100);
    lv_obj_set_style_bg_color(ex11_slider, lv_color_hex(0x333355), LV_PART_MAIN);
    lv_obj_set_style_bg_color(ex11_slider, lv_color_hex(0x00AAFF), LV_PART_INDICATOR);
    lv_obj_remove_flag(ex11_slider, LV_OBJ_FLAG_CLICKABLE); /* Read-only */

    ex11_output_led = lv_led_create(slider_panel);
    lv_obj_set_size(ex11_output_led, 25, 25);
    lv_obj_align(ex11_output_led, LV_ALIGN_BOTTOM_RIGHT, -5, -8);
    lv_led_set_color(ex11_output_led, lv_palette_main(LV_PALETTE_LIGHT_BLUE));
    lv_led_off(ex11_output_led);

    /* ===== BUTTON PANELS (BOTTOM) ===== */
    const char * btn_names[] = {"BTN0", "BTN1"};
    const char * btn_ids[] = {"(CSB1)", "(CSB2)"};
    uint32_t led_colors[] = {0xff0000, 0x00ff00};
    int btn_x_pos[] = {-110, 110};

    for (int i = 0; i < EX11_NUM_BUTTONS; i++) {
        ex11_btn_panels[i] = lv_obj_create(scr);
        lv_obj_set_size(ex11_btn_panels[i], 140, 150);
        lv_obj_align(ex11_btn_panels[i], LV_ALIGN_BOTTOM_MID,
            btn_x_pos[i], -55);
        lv_obj_set_style_bg_color(ex11_btn_panels[i],
            lv_color_hex(0x333355), 0);
        lv_obj_set_style_border_width(ex11_btn_panels[i], 3, 0);
        lv_obj_set_style_border_color(ex11_btn_panels[i],
            lv_color_hex(0x666699), 0);
        lv_obj_set_style_radius(ex11_btn_panels[i], 10, 0);
        lv_obj_set_style_pad_all(ex11_btn_panels[i], 5, 0);
        lv_obj_clear_flag(ex11_btn_panels[i], LV_OBJ_FLAG_SCROLLABLE);

        lv_obj_t * name = lv_label_create(ex11_btn_panels[i]);
        lv_label_set_text(name, btn_names[i]);
        lv_obj_set_style_text_color(name, lv_color_hex(0xFFFFFF), 0);
        lv_obj_set_style_text_font(name, &lv_font_montserrat_16, 0);
        lv_obj_align(name, LV_ALIGN_TOP_MID, 0, 2);

        lv_obj_t * id_lbl = lv_label_create(ex11_btn_panels[i]);
        lv_label_set_text(id_lbl, btn_ids[i]);
        lv_obj_set_style_text_color(id_lbl, lv_color_hex(0x888888), 0);
        lv_obj_align(id_lbl, LV_ALIGN_TOP_MID, 0, 22);

        ex11_btn_leds[i] = lv_led_create(ex11_btn_panels[i]);
        lv_obj_set_size(ex11_btn_leds[i], 50, 50);
        lv_obj_align(ex11_btn_leds[i], LV_ALIGN_CENTER, 0, 8);
        lv_led_set_color(ex11_btn_leds[i], lv_color_hex(led_colors[i]));
        lv_led_off(ex11_btn_leds[i]);

        ex11_btn_status[i] = lv_label_create(ex11_btn_panels[i]);
        lv_label_set_text(ex11_btn_status[i], "Ready");
        lv_obj_set_style_text_color(ex11_btn_status[i],
            lv_color_hex(0x888888), 0);
        lv_obj_align(ex11_btn_status[i], LV_ALIGN_BOTTOM_MID, 0, -2);
    }

    /* Description + Footer */
    lv_obj_t * desc = lv_label_create(scr);
    lv_label_set_text(desc, "CM55 reads PSoC 4000T directly via I2C (0x08)");
    lv_obj_set_style_text_color(desc, lv_color_hex(0x888888), 0);
    lv_obj_align(desc, LV_ALIGN_BOTTOM_MID, 0, -25);

    aic_create_footer(scr);

    /* [3] START I2C POLLING (50ms = 20Hz) */
    ex11_poll_timer = lv_timer_create(ex11_poll_timer_cb, 50, NULL);
}
```

#### 4.2 Initialization Sequence

```
[0] lv_port_indev_disable_touch()  <-- MUST be first!
     |   ปิด Display Touch เพื่อคืน I2C bus
[1] aic_gpio_init()               GPIO subsystem
[2] aic_gpio_pwm_init(BLUE)       PWM for Blue LED
[3] aic_capsense_init()            I2C for CAPSENSE
[4] Create LVGL UI                Slider + Buttons panels
[5] lv_timer_create(cb, 50)       Start I2C polling

WARNING: ถ้าไม่เรียก [0] ก่อน -> I2C bus conflict!
```

***

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

#### 5.1 Pattern: Shared I2C Bus Resolution

```c
/* PROBLEM: Display Touch + CAPSENSE ใช้ SCB0 I2C ร่วมกัน
 * SOLUTION: ปิด touch ก่อนใช้ CAPSENSE */
void part1_ex11_capsense_hardware(void)
{
    lv_port_indev_disable_touch();  /* Step 1: ปิด Touch */
    aic_capsense_init();            /* Step 2: CAPSENSE ใช้ I2C ได้ */
}
/* ผลกระทบ: หน้าจอจะไม่ตอบสนองต่อการสัมผัส
 * CAPSENSE กลายเป็น input device หลักแทน */
```

#### 5.2 Pattern: Edge Detection for Efficient Updates

```c
static bool prev_btn0 = false;
static uint8_t prev_slider = 0;

static void poll_cb(lv_timer_t * t)
{
    aic_capsense_data_t data;
    if (!aic_capsense_read(&data)) return;

    /* Button: update only on state change */
    if (data.btn0_pressed != prev_btn0) {
        aic_gpio_led_set(AIC_LED_RED, data.btn0_pressed);
        data.btn0_pressed ? lv_led_on(led) : lv_led_off(led);
        prev_btn0 = data.btn0_pressed;
    }

    /* Slider: update only on value change */
    if (data.slider_pos != prev_slider) {
        lv_slider_set_value(slider, data.slider_pos, LV_ANIM_ON);
        aic_gpio_pwm_set_brightness(AIC_LED_BLUE, data.slider_pos);
        prev_slider = data.slider_pos;
    }
}
```

#### 5.3 Pattern: Read-Only Slider

```c
/* Slider แสดงค่าจาก sensor - ผู้ใช้ลากไม่ได้ */
lv_obj_t * slider = lv_slider_create(parent);
lv_obj_remove_flag(slider, LV_OBJ_FLAG_CLICKABLE);  /* Read-only */

/* ใน timer: อัพเดทจาก sensor */
lv_slider_set_value(slider, sensor_value, LV_ANIM_ON);
```

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

| หัวข้อ               | รายละเอียด                                                           |
| -------------------- | -------------------------------------------------------------------- |
| **Touch Disable**    | ต้องเรียก `lv_port_indev_disable_touch()` ก่อน `aic_capsense_init()` |
| **Init Order**       | disable\_touch -> gpio\_init -> pwm\_init -> capsense\_init          |
| **Edge Detection**   | เปรียบเทียบ prev กับ current ก่อน update UI ทุกครั้ง                 |
| **Format Specifier** | ใช้ `(unsigned int)` cast กับ `%u` สำหรับ uint32\_t                  |
| **Read-Only Slider** | ต้อง `lv_obj_remove_flag(slider, LV_OBJ_FLAG_CLICKABLE)`             |
| **I2C Address**      | PSoC 4000T ใช้ address `0x08`                                        |
| **No CM33**          | Ex11 อ่าน I2C โดยตรงจาก CM55 ไม่ต้องใช้ IPC                          |

#### 5.5 Ex10 (Mockup) vs Ex11 (Hardware)

| รายการ             | Ex10 Mockup          | Ex11 Hardware                        |
| ------------------ | -------------------- | ------------------------------------ |
| **Data Source**    | Auto-demo จำลอง      | I2C จาก PSoC 4000T จริง              |
| **Touch Screen**   | ใช้ได้ (Manual mode) | ปิด (disable\_touch)                 |
| **Hardware LED**   | ไม่มี                | BTN0->Red, BTN1->Green, Slider->Blue |
| **Error Handling** | ไม่จำเป็น            | Green/Red/Yellow status              |
| **Use Case**       | พัฒนา UI ไม่มี board | ทดสอบ hardware จริง                  |

#### 5.6 Direct I2C (Ex11) vs IPC (Part 4 Ex9)

| รายการ           | Direct I2C (Ex11)    | IPC (Part 4 Ex9)         |
| ---------------- | -------------------- | ------------------------ |
| **Architecture** | CM55 อ่าน I2C โดยตรง | CM33 อ่าน -> IPC -> CM55 |
| **Complexity**   | ง่าย (single core)   | ซับซ้อน (dual core)      |
| **Touch Screen** | ใช้ไม่ได้            | ใช้ได้ (I2C คนละ bus)    |
| **Latency**      | ต่ำ (direct)         | สูงกว่า (IPC overhead)   |
| **Scalability**  | จำกัด (bus sharing)  | ดี (CM33 จัดการ I/O)     |

***

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

#### แบบฝึกหัดที่ 1: Touch Counter

**โจทย์**: นับจำนวนครั้งที่กด CAPSENSE Button แต่ละปุ่ม

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

* นับ BTN0 touch count (rising edge: Released -> Touched)
* นับ BTN1 touch count แยกกัน
* แสดง "Count: 15" บน panel แต่ละปุ่ม
* แสดง Total touches (BTN0 + BTN1) ที่ footer

**Hints**:

* ใช้ `static uint32_t btn0_count = 0;`
* Rising edge: `if (btn0 != prev_btn0 && btn0)` คือจังหวะกดใหม่

#### แบบฝึกหัดที่ 2: CAPSENSE Color Mixer

**โจทย์**: ใช้ Slider ผสมสี + Buttons เลือก channel R/G/B

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

* BTN0 กด: สลับ channel (Red -> Green -> Blue -> Red ...)
* BTN1 กด: Reset channel ปัจจุบันเป็น 0
* Slider: ปรับค่าสีของ channel ที่เลือก (0-100% -> 0-255)
* แสดง Color preview panel: `lv_color_make(r, g, b)`
* LED hardware แสดง channel ที่กำลังแก้ไข

**Hints**:

* `static uint8_t rgb[3] = {0, 0, 0}; static int ch = 0;`
* `lv_obj_set_style_bg_color(preview, lv_color_make(rgb[0], rgb[1], rgb[2]), 0);`
* `rgb[ch] = (uint8_t)(slider_pos * 255 / 100);`


---

# 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/capsense.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.
