# 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);`
