> 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/embedded-c-1.md).

# พื้นฐาน Embedded C - ตอนที่ 1

Embedded C Programming Part 1: พื้นฐานสำคัญ

***

### <mark style="color:$success;">ทำไม Embedded C ต่างจาก C ปกติ?</mark>

{% hint style="info" %}
**Desktop C vs Embedded C**

ดังนั้น: ต้องระวังเรื่อง size, timing, และ hardware access
{% endhint %}

{% tabs %}
{% tab title="Desktop C" %}

* มี OS จัดการให้
* int = 32/64-bit
* printf() ใช้ได้เลย
* ไม่ค่อยสนใจ timing
  {% endtab %}

{% tab title="Embedded C" %}

* Memory จำกัด (KB-MB)
* อาจไม่มี OS (Bare-metal)
* int อาจ = 16-bit!
* ต้อง retarget UART
* Timing สำคัญมาก
  {% endtab %}
  {% endtabs %}

### <mark style="color:$success;">ทำไมต้องเข้าใจ Embedded C สำหรับ IoT และ Edge AI?</mark>

การเขียนโปรแกรม Embedded C สำหรับ IoT และ Edge AI แตกต่างจากการเขียน C บนคอมพิวเตอร์ทั่วไป:

1. **ทรัพยากรจำกัด**: RAM และ Flash memory มีจำกัด ต้องเลือกใช้ data types ที่เหมาะสม
2. **Real-time Requirements**: ต้องควบคุม timing แม่นยำ เข้าใจ macro และ inline functions
3. **Hardware Interaction**: ต้องจัดการ GPIO, Sensors, Peripherals ผ่าน HAL/PDL
4. **Power Efficiency**: การเขียนโค้ดที่มีประสิทธิภาพช่วยประหยัดพลังงาน

**Applications ในชีวิตจริง:**

* Smart Home: ควบคุม LED, อ่านสถานะปุ่ม, sensors
* Wearables: อ่านข้อมูล heart rate, accelerometer
* Industrial IoT: ควบคุม motors, valves, monitoring
* Edge AI: pre-processing ข้อมูล sensor ก่อนส่ง ML model

***

## <mark style="color:blue;">ส่วนที่ 1: Data Types</mark>

#### ทำไมต้องใช้ Fixed-Width Types ให้ถูกต้อง?

**เหตุผล:**

1. **Memory Optimization**: ใน Embedded system RAM มีจำกัด (เช่น 512KB - 2MB)
2. **Performance**: การใช้ type ที่เล็กเกินไปอาจต้อง typecast บ่อย
3. **Overflow Prevention**: ใช้ type ที่เล็กเกินไปทำให้เกิด overflow
4. **Sensor Data**: ต้องเลือก type ที่รองรับ range ของ sensor

### 1.1 ปัญหาของ `int` ใน Embedded

```c
/* ❌ ปัญหา: int มีขนาดไม่แน่นอน */
int delay = 1000;  /* 16-bit หรือ 32-bit? */

/* บน PSoC Edge:    int = 4 bytes = 0 to 2,147,483,647 */
/* บน PC 32-bit:    int = 4 bytes = 0 to 2,147,483,647 */
/* บน Arduino Uno:  int = 2 bytes = 0 to 65,535 */

/* ถ้าเขียนโค้ดบน PC แล้วเอาไป compile บน MCU 16-bit อาจ overflow! */
```

### 1.2 Fixed-Width Integer Types (`stdint.h`)

```c
#include <stdint.h>  /* Standard Integer Types */

/* Unsigned (ไม่มีเครื่องหมาย, ค่าบวกเท่านั้น) */
uint8_t   pin_number = 5;       /* 0 to 255           (1 byte)  */
uint16_t  adc_value = 2048;     /* 0 to 65,535        (2 bytes) */
uint32_t  timestamp = 1000000;  /* 0 to 4,294,967,295 (4 bytes) */

/* Signed (มีเครื่องหมาย, ค่าบวกและลบ) */
int8_t    temperature = -20;    /* -128 to 127        (1 byte)  */
int16_t   sensor_raw = -1000;   /* -32,768 to 32,767  (2 bytes) */
int32_t   position = -50000;    /* -2.1B to 2.1B      (4 bytes) */
```

#### Standard Integer Types (`stdint.h`)

<table><thead><tr><th width="96.35296630859375">Type</th><th width="92.05609130859375">Size</th><th width="162.219482421875">Range</th><th width="138.8565673828125">ใช้งาน</th><th>Real Application</th></tr></thead><tbody><tr><td><code>int8_t</code></td><td>1 byte</td><td>-128 to 127</td><td>Signed 8-bit</td><td>Temperature offset (-50°C to +50°C)</td></tr><tr><td><code>uint8_t</code></td><td>1 byte</td><td>0 to 255</td><td>Unsigned 8-bit</td><td>I2C register, LED brightness (0-255)</td></tr><tr><td><code>int16_t</code></td><td>2 bytes</td><td>-32768 to 32767</td><td>Sensor raw data</td><td>IMU accelerometer raw values</td></tr><tr><td><code>uint16_t</code></td><td>2 bytes</td><td>0 to 65535</td><td>ADC values</td><td>12-bit ADC (0-4095), timer counts</td></tr><tr><td><code>int32_t</code></td><td>4 bytes</td><td>-2B to 2B</td><td>Large signed</td><td>Audio samples, GPS coordinates</td></tr><tr><td><code>uint32_t</code></td><td>4 bytes</td><td>0 to 4B</td><td>Timestamps</td><td>System tick count (ms), memory addresses</td></tr><tr><td><code>float</code></td><td>4 bytes</td><td>±3.4e38</td><td>Decimal</td><td>Sensor values in physical units (m/s², °C)</td></tr></tbody></table>

```c
/* Examples from Real IoT Applications */

/* Smart Wearable - Heart Rate Monitor */
uint8_t  heart_rate_bpm = 72;           /* 40-200 BPM */
uint16_t spo2_raw = 2048;               /* Pulse oximeter ADC */

/* IMU Sensor - Motion Detection */
int16_t  accel_x_raw = 16384;           /* ±2g range, BMI270 */
float    accel_x_g = accel_x_raw / 16384.0f;  /* Convert to g */

/* Environmental Sensor */
float    temperature_c = 25.5f;          /* Temperature in Celsius */
float    humidity_pct = 60.2f;           /* Humidity percentage */

/* System Timing */
uint32_t timestamp_ms = 123456789;       /* Milliseconds since boot */
uint32_t uptime_seconds = timestamp_ms / 1000;

/* GPIO and Peripherals */
uint8_t  led_brightness = 128;           /* PWM duty cycle 0-255 */
bool     button_state = true;            /* Button pressed */
```

**การเปรียบเทียบ: int vs uint**

<table><thead><tr><th width="162.67047119140625">Feature</th><th width="289.00714111328125">int16_t (Signed)</th><th>uint16_t (Unsigned)</th></tr></thead><tbody><tr><td>Range</td><td>-32768 to +32767</td><td>0 to 65535</td></tr><tr><td>Use Case</td><td>Temperature, accel</td><td>ADC values, counters</td></tr><tr><td>Overflow</td><td>Wraps to negative</td><td>Wraps to 0</td></tr><tr><td>When to use</td><td>ต้องการค่าลบ</td><td>ต้องการ range กว้างขึ้น 2 เท่า</td></tr></tbody></table>

**Best Practices:**

* ใช้ `stdint.h` types แทน `int`, `short`, `long` (portable)
* เลือก type ที่พอดีกับ data range (ไม่เล็กไป ไม่ใหญ่เกิน)
* ใช้ `uint` สำหรับ count, index, bit operations
* ใช้ `int` สำหรับค่าที่อาจเป็นลบ
* ใช้ `float` เฉพาะเมื่อจำเป็น (ช้ากว่า integer)

**Common Pitfalls:**

```c
/* WRONG: uint8 overflow */
uint8_t counter = 255;
counter++;  /* Now counter = 0! (wraps around) */

/* WRONG: int16 too small for large ADC */
int16_t adc_sum = 0;
for (int i = 0; i < 100; i++) {
    adc_sum += read_adc();  /* Might overflow! */
}

/* CORRECT: Use appropriate type */
int32_t adc_sum = 0;  /* Large enough for sum */

/* WRONG: Using int instead of stdint */
int sensor_value;  /* Size depends on platform! */

/* CORRECT: Use fixed-size type */
int16_t sensor_value;  /* Always 16-bit */
```

### 1.3 ตัวอย่างจากโค้ดจริง (Hello World)

```c
/* จากไฟล์ main.c ของ hello-world example */

#define BLINKY_LED_DELAY_MSEC       (1000U)  /* U = Unsigned */
#define CM55_BOOT_WAIT_TIME_USEC    (10U)

/* ทำไมใช้ U ต่อท้าย? */
/* 1000U หมายถึง unsigned integer literal */
/* ป้องกัน warning เวลาเปรียบเทียบกับ unsigned variable */
```

### 1.4 Boolean Type (stdbool.h)

```c
#include <stdbool.h>  /* true, false, bool */

/* จากโค้ด gpio-interrupt example */
volatile bool gpio_intr_flag = true;

/* volatile บอกว่าค่าอาจเปลี่ยนได้จากภายนอก (เช่น interrupt) */
/* Compiler จะไม่ optimize ตัวแปรนี้ออก */

bool button_pressed = false;

if (button_pressed)  /* เหมือน if (button_pressed == true) */
{
    /* do something */
}
```

### 1.5 Cypress Result Type (cy\_rslt\_t)

```c
/* cy_rslt_t เป็น typedef ที่ Infineon สร้างขึ้น */
/* ใช้เก็บ error code จาก API functions */

cy_rslt_t result;

result = cybsp_init();

/* ตรวจสอบ error */
if (CY_RSLT_SUCCESS != result)
{
    /* Board init failed! */
    handle_app_error();
}

/* CY_RSLT_SUCCESS = 0 หมายถึงสำเร็จ */
/* ค่าอื่นๆ หมายถึง error ต่างๆ */
```

#### Pattern: Error Checking

```c
/* ✓ Good Pattern: ตรวจสอบทุก API call */
cy_rslt_t result = CY_RSLT_SUCCESS;

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

result = init_adc();
if (CY_RSLT_SUCCESS != result)
{
    printf("ADC init failed!\r\n");
    handle_app_error();
}

/* ทำไมเขียน CY_RSLT_SUCCESS != result แทน result != CY_RSLT_SUCCESS? */
/* เพื่อป้องกัน typo: ถ้าเขียน = แทน == compiler จะ error */
/* เรียกว่า "Yoda Conditions" */
```

***

## <mark style="color:blue;">ส่วนที่ 2: Macros</mark>

#### ทำไมต้องใช้ Define/Macro?

**ข้อดี&#x20;*****(**&#xE04;่าคงที่และการทำให้โค้ดอ่านง่าย)***:**

1. **Magic Number Elimination**: ไม่มีตัวเลขลึกลับกระจายอยู่ในโค้ด
2. **Easy Maintenance**: เปลี่ยนค่าที่เดียว แก้ทั้งโปรเจค
3. **Performance**: Macro expand ตอน compile-time (ไม่เสีย runtime)
4. **Hardware Configuration**: กำหนดค่า pins, addresses, timing parameters

**การประยุกต์ใช้จริง:**

* GPIO Pin Configuration (LED, Button, Sensor pins)
* I2C/SPI Addresses และ Registers
* Timing Constants (delays, sampling rates)
* Buffer Sizes และ Thresholds
* Bit Masks สำหรับ register manipulation

### 2.1 #define Basics

```c
/* Macro = text replacement ที่ทำก่อน compile */

/* Syntax: #define NAME value */
/* ไม่มี = และ ; ท้าย */

#define DELAY_MS        (1000U)     /* ✓ ถูก */
#define DELAY_MS =      1000U       /* ❌ ผิด - มี = */
#define DELAY_MS        1000U;      /* ❌ ผิด - มี ; */
```

### 2.2 ทำไมต้องใส่วงเล็บ?

```c
/* ❌ ปัญหาถ้าไม่ใส่วงเล็บ */
#define OFFSET  5 + 3

int result = OFFSET * 2;
/* Preprocessor แทนที่เป็น: int result = 5 + 3 * 2; */
/* ผลลัพธ์ = 5 + 6 = 11 (ไม่ใช่ 16!) */

/* ✓ ใส่วงเล็บ */
#define OFFSET  (5 + 3)

int result = OFFSET * 2;
/* Preprocessor แทนที่เป็น: int result = (5 + 3) * 2; */
/* ผลลัพธ์ = 8 * 2 = 16 (ถูกต้อง) */
```

### 2.3 ตัวอย่างจากโค้ดจริง

```c
/* จาก gpio-interrupt example */
#define DELAY_SHORT_MS           (250U)
#define DELAY_LONG_MS            (500U)
#define LED_BLINK_COUNT          (4U)
#define GPIO_INTERRUPT_PRIORITY  (7U)
#define DEBOUNCE_DELAY_MS        (300U)
#define INIT_COUNT               (0U)

/* GPIO Pin Configuration - From hello-world example */
#define LED_PIN             CYBSP_USER_LED
#define BUTTON_PIN          CYBSP_USER_BTN

/* จาก adc-basic example */
#define SAR_ADC_INDEX           (0U)
#define SAR_ADC_SEQUENCER       (0U)
#define SAR_ADC_CHANNEL         (0U)
#define SAR_ADC_VREF_MV         (1800U)

/* Communication Settings */
#define UART_BAUDRATE       115200
#define I2C_FREQUENCY       400000   /* 400 kHz */

/* Sensor Configuration - From BMI270 driver */
#define SAMPLE_RATE_HZ      100
#define IMU_I2C_ADDRESS     0x68
#define BUFFER_SIZE         256

/* Application Constants */
#define MAX_RETRIES         3
#define TIMEOUT_MS          5000
#define LED_BLINK_DELAY     500
```

### 2.4 Function-Like Macros

**ทำไมใช้ Macro แทน Function?**

* **Performance**: ไม่มี function call overhead
* **Type-generic**: ทำงานกับหลาย types
* **Compile-time**: Expand ตอน compile (ไม่เสีย RAM)

```c
/* Macro ที่รับ parameter เหมือน function */

#define LED_ON()    Cy_GPIO_Set(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
#define LED_OFF()   Cy_GPIO_Clr(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
#define LED_TOGGLE() Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)

/* ใช้งาน */
LED_ON();     /* แทนที่เป็น Cy_GPIO_Set(...) */
LED_TOGGLE(); /* แทนที่เป็น Cy_GPIO_Inv(...) */

/* Min/Max macros - Generic for any type */
#define MIN(a, b)   ((a) < (b) ? (a) : (b))
#define MAX(a, b)   ((a) > (b) ? (a) : (b))

/* Clamp value to range - Limit sensor values */
#define CLAMP(val, min, max)  (MIN(MAX(val, min), max))

/* Bit manipulation macros - Register control */
#define BIT_SET(reg, bit)     ((reg) |= (1U << (bit)))
#define BIT_CLEAR(reg, bit)   ((reg) &= ~(1U << (bit)))
#define BIT_TOGGLE(reg, bit)  ((reg) ^= (1U << (bit)))
#define BIT_CHECK(reg, bit)   (((reg) >> (bit)) & 1U)

/* Array size macro - Prevent buffer overrun */
#define ARRAY_SIZE(arr)       (sizeof(arr) / sizeof((arr)[0]))

/* Conversion macros - Unit conversion */
#define MS_TO_TICKS(ms)       ((ms) * configTICK_RATE_HZ / 1000)
#define DEG_TO_RAD(deg)       ((deg) * 3.14159f / 180.0f)
#define CELSIUS_TO_F(c)       ((c) * 9.0f / 5.0f + 32.0f)

/* Macro with parameter */
#define IS_BUTTON_PRESSED() (Cy_GPIO_Read(USER_BTN_PORT, USER_BTN_PIN) == 0)

if (IS_BUTTON_PRESSED())
{
    LED_TOGGLE();
}
```

#### ข้อควรระวัง: Macro ไม่ใช่ Function

```c
/* ❌ ปัญหา: Macro ไม่มี return type */
#define LED_ON()  Cy_GPIO_Set(PORT, PIN)

/* Cy_GPIO_Set() return void */
if (LED_ON())  /* ❌ Error! ไม่มีค่า return */
{
    /* ... */
}

/* ✓ ถูกต้อง */
LED_ON();  /* เรียกแล้วจบ ไม่ต้องเช็ค return */
```

### 2.5 Macro สำหรับ Array Size

```c
/* Pattern ที่ใช้บ่อยมาก */
#define ARRAY_SIZE(arr)  (sizeof(arr) / sizeof((arr)[0]))

int values[10];
size_t len = ARRAY_SIZE(values);  /* len = 10 */

/* คำอธิบาย: */
/* sizeof(values) = 40 bytes (int = 4 bytes × 10) */
/* sizeof(values[0]) = 4 bytes (หนึ่ง int) */
/* 40 / 4 = 10 elements */
```

***

## <mark style="color:blue;">ส่วนที่ 3: Functions ใน Embedded C</mark>

### 3.1 Function Declaration และ Definition

```c
/* Declaration (Prototype) - บอก compiler ว่ามี function นี้ */
static void gpio_interrupt_handler(void);
cy_rslt_t init_adc(void);
bool read_button_with_debounce(void);

/* Definition - โค้ดจริงของ function */
static void gpio_interrupt_handler(void)
{
    /* ... implementation ... */
}
```

### 3.2 static Keyword

```c
/* static function = ใช้ได้เฉพาะในไฟล์นี้เท่านั้น */

/* จาก sensor_hub_daq_task.c */
static float lsb_to_mps2(int16_t val, int8_t g_range, uint8_t bit_width)
{
    float half_scale = (float)(1u << (bit_width - 1u));
    return ((GRAVITY_EARTH) * val * g_range) / half_scale;
}

/* ทำไมใช้ static? */
/* 1. ป้องกัน function ถูกเรียกจากไฟล์อื่น */
/* 2. Compiler optimize ได้ดีกว่า (inline) */
/* 3. หลีกเลี่ยง naming conflict */

/* ถ้าไม่ใส่ static = global function (เรียกจากไฟล์อื่นได้) */
cy_rslt_t create_sensor_hub_daq_task(void)
{
    /* ... */
}
```

### 3.3 Function Parameters

```c
/* Pass by Value - ส่งค่า (copy) */
void set_delay(uint32_t delay_ms)
{
    /* delay_ms เป็น copy ของ argument */
    /* แก้ไขที่นี่ไม่มีผลกับตัวแปรเดิม */
}

/* Pass by Pointer - ส่ง address */
cy_rslt_t read_adc(int16_t *result)
{
    /* result เป็น pointer ไปยังตัวแปรจริง */
    *result = 1234;  /* เขียนค่าไปยังตัวแปรเดิมได้! */
    return CY_RSLT_SUCCESS;
}

/* การใช้งาน */
int16_t adc_value;
read_adc(&adc_value);  /* ส่ง address ด้วย & */
```

### 3.4 ตัวอย่างจากโค้ดจริง

```c
/* จาก gpio-interrupt example */
cy_en_sysint_status_t Cy_SysInt_Init(
    const cy_stc_sysint_t *config,  /* pointer to config struct */
    cy_israddress userIsr           /* function pointer */
);

/* การใช้งาน */
cy_stc_sysint_t intrCfg = {
    CYBSP_USER_BTN_IRQ,       /* Interrupt source */
    GPIO_INTERRUPT_PRIORITY   /* Interrupt priority */
};

cy_en_sysint_status_t status = Cy_SysInt_Init(&intrCfg,
                                               &gpio_interrupt_handler);

/* &intrCfg = address ของ struct */
/* &gpio_interrupt_handler = address ของ function */
```

### 3.5 Return Values

```c
/* void - ไม่ return อะไร */
void handle_app_error(void)
{
    /* Disable interrupts */
    __disable_irq();

    /* Infinite loop */
    while(1)
    {
        /* Halt here */
    }
}

/* Return status/error code */
cy_rslt_t init_adc(void)
{
    cy_rslt_t result = CY_RSLT_SUCCESS;

    /* ... do initialization ... */

    if (/* something failed */)
    {
        return CY_RSLT_TYPE_ERROR;  /* Early return on error */
    }

    return CY_RSLT_SUCCESS;  /* Success */
}

/* Return computed value */
int16_t read_potentiometer_mv(void)
{
    /* ... */
    return adc_mv;
}

/* Return boolean */
bool read_button_with_debounce(void)
{
    /* ... */
    if (button_pressed)
    {
        return true;
    }
    return false;
}
```

***

## <mark style="color:blue;">ส่วนที่ 4: Conditional Compilation</mark>

### 4.1 #if, #ifdef, #ifndef

```c
/* จาก gpio-interrupt example */

/* ตรวจสอบว่า macro ถูก define หรือไม่ */
#if defined(CYBSP_USER_BTN2_ENABLED)
    Cy_GPIO_ClearInterrupt(CYBSP_USER_BTN2_PORT, CYBSP_USER_BTN2_PIN);
    NVIC_ClearPendingIRQ(CYBSP_USER_BTN2_IRQ);
#endif

/* แบบย่อ */
#ifdef CYBSP_USER_BTN2_ENABLED
    /* BTN2 is enabled */
#else
    /* BTN2 is not enabled */
#endif

/* ตรวจสอบว่าไม่ได้ define */
#ifndef DEBUG_DISABLED
    printf("Debug message\r\n");
#endif
```

### 4.2 การใช้งานจริง

```c
/* เลือก code ตาม board */
#if defined(KIT_PSE84_EVAL_EPC2)
    #define USER_LED_PORT  CYBSP_USER_LED1_PORT
    #define USER_LED_PIN   CYBSP_USER_LED1_PIN
#elif defined(KIT_PSE84_AI)
    #define USER_LED_PORT  CYBSP_LED_RGB_RED_PORT
    #define USER_LED_PIN   CYBSP_LED_RGB_RED_PIN
#else
    #error "Unknown board configuration!"
#endif

/* Debug mode */
#ifdef DEBUG
    #define DBG_PRINTF(...) printf(__VA_ARGS__)
#else
    #define DBG_PRINTF(...) /* Nothing */
#endif

DBG_PRINTF("ADC value: %d\r\n", adc_value);  /* แสดงเฉพาะ Debug mode */
```

***

## <mark style="color:blue;">ส่วนที่ 5: Bit Operations</mark>

### 5.1 Bit Shifting

```c
/* << Left shift - เลื่อน bit ไปทางซ้าย */
/* >> Right shift - เลื่อน bit ไปทางขวา */

/* จาก sensor_hub_daq_task.c */
float half_scale = (float)(1u << (bit_width - 1u));

/* bit_width = 16 */
/* 1u << 15 = 1 เลื่อนไปทางซ้าย 15 ตำแหน่ง */
/* = 0b1000000000000000 = 32768 */

/* การใช้งานทั่วไป */
uint8_t flags = 0;

/* Set bit 3 */
flags |= (1 << 3);   /* flags = 0b00001000 */

/* Clear bit 3 */
flags &= ~(1 << 3);  /* flags = 0b00000000 */

/* Toggle bit 3 */
flags ^= (1 << 3);   /* สลับ bit 3 */

/* Check bit 3 */
if (flags & (1 << 3))
{
    /* bit 3 is set */
}
```

### 5.2 ใช้กับ Register Configuration

```c
/* ตัวอย่าง: GPIO configuration register */

/* Set multiple bits */
GPIO_CFG |= (1 << 4) | (1 << 5);  /* Set bits 4 and 5 */

/* Clear and set (modify field) */
GPIO_CFG &= ~(0x03 << 2);  /* Clear bits 2-3 */
GPIO_CFG |= (0x02 << 2);   /* Set bits 2-3 to value 2 */

/* Mask and extract */
uint8_t field = (GPIO_CFG >> 2) & 0x03;  /* Get bits 2-3 */
```

***

## <mark style="color:blue;">ส่วนที่ 6: Type Casting</mark>

### 6.1 Explicit Casting

```c
/* จาก sensor_hub_daq_task.c */

int16_t raw_value = -1000;
int8_t g_range = 2;
uint8_t bit_width = 16;

/* Cast to float for calculation */
float half_scale = (float)(1u << (bit_width - 1u));
float result = ((GRAVITY_EARTH) * raw_value * g_range) / half_scale;

/* ทำไมต้อง cast? */
/* 1u << 15 = 32768 (integer) */
/* ถ้าไม่ cast เป็น float, การหารจะเป็น integer division */
/* 100 / 32768 = 0 (integer) */
/* (float)100 / 32768 = 0.00305... (correct) */
```

### 6.2 Casting สำหรับ printf

```c
/* printf ใน embedded ต้องระวังเรื่อง format specifier */

float value = 9.81f;

/* ❌ อาจมีปัญหาบาง compiler */
printf("Value: %f\n", value);

/* ✓ ใช้ (double) เพื่อความปลอดภัย */
printf("Value: %9.6f\n", (double)value);

/* %lu สำหรับ uint32_t */
uint32_t count = 1000000;
printf("Count: %lu\n", count);

/* %d สำหรับ int16_t */
int16_t sensor = -500;
printf("Sensor: %d\n", sensor);
```

***

## <mark style="color:blue;">ส่วนที่ 7: การเก็บเวลาด้วย Tick Count</mark>

### 7.1 Tick-Based Timing

```c
/* จากโค้ด debounce */

static uint32_t last_press_time = 0;

bool read_button_with_debounce(void)
{
    uint32_t current_time = Cy_SysTick_GetValue();
    /* หรือใช้ xTaskGetTickCount() ถ้าใช้ FreeRTOS */

    if (current_time - last_press_time > DEBOUNCE_DELAY_MS)
    {
        last_press_time = current_time;
        return true;
    }
    return false;
}
```

### 7.2 Non-Blocking Delay Pattern

```c
/* ❌ Blocking delay - หยุดทุกอย่าง */
LED_ON();
Cy_SysLib_Delay(500);  /* หยุดที่นี่ 500ms */
LED_OFF();

/* ✓ Non-blocking - ทำอย่างอื่นได้ระหว่างรอ */
static uint32_t last_toggle = 0;
uint32_t current_time = Cy_SysTick_GetValue();

if (current_time - last_toggle >= 500)
{
    last_toggle = current_time;
    LED_TOGGLE();
}

/* อ่าน sensor ได้ตลอดเวลา ไม่ต้องรอ LED */
read_sensors();
```

***

## <mark style="color:blue;">ส่วนที่ 8: Volatile Keyword</mark>

### 8.1 ทำไมต้องใช้ volatile?

* **ปัญหา:** Compiler optimize ตัวแปรไว้ใน register ทำให้ main loop ไม่เห็นการเปลี่ยนแปลงจาก ISR
* **ประโยชน์:** บังคับให้อ่าน/เขียนจาก memory จริงทุกครั้ง
* **Hardware:** ISR และ main code ทำงานแยก context กัน

```c
/* จาก gpio-interrupt example */
volatile bool gpio_intr_flag = true;

/* volatile บอก compiler ว่า:
 * "ค่าตัวแปรนี้อาจเปลี่ยนได้ตลอดเวลา
 *  โดยไม่ได้มาจากโค้ดที่เห็น"
 *
 * เช่น: Interrupt handler เปลี่ยนค่า
 *       Hardware register เปลี่ยนค่า
 *       Another thread/core เปลี่ยนค่า
 */

/* ❌ ถ้าไม่ใส่ volatile */
bool flag = true;

while (flag)  /* Compiler อาจ optimize เป็น while(1) */
{
    /* wait */
}

/* ✓ ใส่ volatile */
volatile bool flag = true;

while (flag)  /* Compiler จะอ่านค่าจริงทุกรอบ */
{
    /* wait until ISR clears flag */
}
```

**HOW: ประยุกต์ใช้จริงในอุตสาหกรรม**

* **Smart Home:** Flag ตรวจจับการเปิดประตู/หน้าต่าง (Interrupt-driven)
* **Wearable:** Flag บอกว่าผู้ใช้กดปุ่มบน Smart Watch
* **Industrial:** Status flags จาก Emergency Stop button

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

| ข้อผิดพลาด             | ผลกระทบ                         | วิธีป้องกัน                            |
| ---------------------- | ------------------------------- | -------------------------------------- |
| ลืมใช้ volatile        | Main loop ไม่เห็นการเปลี่ยนแปลง | ใช้กับทุก shared variable              |
| ใช้ volatile มากเกินไป | Performance ลดลง                | ใช้เฉพาะกับ ISR-shared variables       |
| Non-atomic access      | Race condition                  | ใช้ critical section สำหรับ multi-byte |

### 8.2 ใช้กับ Hardware Registers

```c
/* Memory-mapped I/O registers ต้องใช้ volatile */

volatile uint32_t *GPIO_DATA = (volatile uint32_t *)0x40000000;

*GPIO_DATA = 0x01;  /* Write to GPIO */
uint32_t value = *GPIO_DATA;  /* Read from GPIO */

/* ถ้าไม่ใส่ volatile, compiler อาจ:
 * - Cache ค่าไว้ใน register แทนที่จะอ่านจริง
 * - Remove "unnecessary" reads/writes
 */
```

***

## <mark style="color:blue;">ส่วนที่ 9: สรุปภาพรวม</mark>

### 9.1 Embedded C Mindset: ทำไม Embedded C ถึงไม่เหมือน C บน PC

แม้ภาษาเป็น C เหมือนกัน แต่ “บริบท” ต่างกันมาก

**Desktop / PC**

* ทรัพยากรเยอะ (RAM/CPU/Storage)
* OS ช่วยจัดการหลายอย่าง
* `printf()` ใช้ได้ง่าย
* timing ไม่ค่อย critical

**Embedded**

* ทรัพยากรจำกัด (RAM/Flash)
* บางระบบไม่มี OS (bare-metal) หรือใช้ RTOS
* timing สำคัญ (real-time behavior)
* ต้องคุม hardware, interrupt, power

> สิ่งสำคัญ: Embedded code ที่ดีต้อง “คาดเดาได้ (predictable)” และ “ควบคุมได้ (controllable)”

***

### 9.2 สิ่งที่ต้องจำให้ขึ้นใจจากตอน 1 (Key Takeaways)

**(1) Types: ใช้ให้ถูก ตั้งแต่บรรทัดแรก**

* ใช้ `stdint.h` เพื่อให้ type มีขนาดแน่นอน เช่น `uint32_t`, `int16_t`
* ใช้ `stdbool.h` สำหรับ `bool`
* ระวัง overflow โดยเฉพาะ `uint8_t`, `int16_t`
* `float` ใช้เมื่อจำเป็นจริง ๆ (เพราะ cost สูงกว่า integer)

**(2) Macros: ทรงพลัง แต่ต้อง “ระวัง”**

* `#define` เหมาะกับ constant และ configuration
* Function-like macro อันตรายเรื่อง **side effect** (เช่น `x++`)
* ใส่วงเล็บรอบ parameter และ expression เสมอ
* ถ้า logic เริ่มซับซ้อน → ควรขยับไปใช้ `static inline` หรือ function

**(3) Functions: แยกส่วนเพื่อ maintain ได้**

* Prototype กับ definition ต้องชัดเจน
* ใช้ `static` เพื่อจำกัด scope และลด naming conflict
* เข้าใจ pass-by-value vs pointer
* return type ที่เหมาะสมช่วยลด bug (เช่นใช้ `cy_rslt_t`)

**(4) Conditional compilation: แยก debug / feature ให้เป็นระบบ**

* `#ifdef DEBUG` ช่วยให้ debug code ไม่หลุดเข้า production
* ใช้ `#if defined(...)` เพื่อรองรับหลาย board / feature

**(5) Timing: เลี่ยงการ “หยุดโลก” ด้วย blocking delay**

* Blocking delay ทำให้ระบบไม่ responsive
* Tick-based timing และ non-blocking pattern คือพื้นฐานของระบบที่ดี
* ใน RTOS: ใช้ tick count ของ RTOS ตาม pattern ที่เหมาะสม

**(6) Volatile: ต้องใช้เมื่อ “ค่าถูกเปลี่ยนจากภายนอก”**

* ISR / hardware register / multi-thread → ต้องพิจารณา `volatile`
* `volatile` ไม่ได้แปลว่า thread-safe แต่ช่วยกัน compiler optimize ผิด

***

### 9.3 Checklist ก่อนขยับไปตอน 2

ก่อนเรียนตอน 2 ผู้อ่านควร “ทำได้จริง” อย่างน้อย:

* [ ] เลือก type ให้เหมาะกับ sensor data และ counter ได้
* [ ] อธิบายปัญหา overflow ของ `uint8_t` ได้
* [ ] อ่าน macro แล้วบอกได้ว่า expand เป็นอะไร
* [ ] เข้าใจ pointer และส่งค่าออกจาก function ด้วย pointer ได้
* [ ] แยก blocking vs non-blocking timing ได้
* [ ] อธิบายได้ว่าเมื่อไรต้องใช้ `volatile`

***

### 9.4 Next: ตอน 2 จะพาไปอะไร

ตอน 2 จะต่อยอดจากพื้นฐานนี้ไปสู่ “เครื่องมือที่ใช้จริงใน firmware” ได้แก่:

* `struct` / `typedef` เพื่อจัดระเบียบ data
* array + pointer แบบใช้งานจริง
* parsing / packing data (protocol, sensor frame)
* memory layout และ safety basics

> ถ้าตอน 1 คือ “อ่านภาษา C ให้คล่อง”\
> ตอน 2 คือ “เริ่มเขียน firmware แบบเป็นระบบ”

## สรุป: Quick Reference

### Data Types

```c
#include <stdint.h>
#include <stdbool.h>

uint8_t, uint16_t, uint32_t   /* Unsigned */
int8_t, int16_t, int32_t      /* Signed */
bool, true, false              /* Boolean */
cy_rslt_t                      /* Result/Error code */
```

### Macros

```c
#define CONSTANT       (100U)
#define FUNCTION()     some_func()
#define ARRAY_SIZE(a)  (sizeof(a)/sizeof((a)[0]))
```

### Functions

```c
static void internal_func(void);           /* File-local */
cy_rslt_t public_func(uint32_t *output);  /* Returns status */
```

### Error Checking

```c
cy_rslt_t result = some_api();
if (CY_RSLT_SUCCESS != result) { handle_error(); }
```

### Conditional Compilation

```c
#ifdef FEATURE_ENABLED
    /* Feature code */
#endif
```

### Timing

```c
volatile bool flag;
uint32_t tick = Cy_SysTick_GetValue();
if (current - last >= INTERVAL) { ... }
```

***

**กลับไปที่:** Session 01: GPIO Basics **ถัดไป:** C02: Structs, Pointers, Arrays
