# พื้นฐาน Embedded C - ตอนที่ 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


---

# 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/basic-mcu-interfacing/embedded-c-1.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.
