ข้ามไปยังเนื้อหา

Watchdog

เมื่อจบบทเรียนนี้ คุณจะ

  1. อธิบายหน้าที่ของ watchdog และผลเมื่อไม่ได้รับการป้อนภายในเวลาที่กำหนด
  2. ระบุจุดที่ถูกต้องในการป้อน watchdog ในโปรแกรมที่มีหลาย task และอธิบายว่าทำไมการป้อนใน ISR ของ timer เป็นกับดัก
  3. ออกแบบการบันทึกสาเหตุการรีเซ็ตเพื่อให้รู้ว่า watchdog ทำงานเมื่อใด

ใช้เวลาประมาณ 70 นาที (แนวคิด 15 · ฝึก 25 · แล็บ 25 · เช็ก 5)

ทวนจากบทก่อนหน้าสองข้อ

  1. ตัวเฝ้าการค้างของเรดาร์ใน SDK รายงาน 0 ครั้งทั้งที่ task ค้าง เพราะมันอยู่ตรงไหน (บทเรียน 3.2)
  2. ISR ของ timer ยังทำงานได้ไหม ถ้า task ทุกตัวติดอยู่ในลูปไม่รู้จบ (บทเรียน 4.1 และ 4.2)

เปิด examples/09_watchdog_sim.c ระบบจำลองมีสาม task และ watchdog ที่รีเซ็ตเมื่อไม่ถูกป้อนเกิน 10 tick ที่ tick 40 task sensor ค้าง แล้วลองสองกลยุทธ์: A ป้อนใน ISR ของ timer ทุก tick, B ให้ผู้ดูแลป้อนเฉพาะเมื่อทุก task รายงานความคืบหน้าครบ ทายก่อนรัน ว่ากลยุทธ์ A จะรีเซ็ตที่ tick ไหน

Terminal window
gcc -std=c11 -Wall -Wextra -o watchdog_sim examples/09_watchdog_sim.c
./watchdog_sim

กลยุทธ์ A ไม่รีเซ็ตเลย ทั้งที่ task ค้างไปแล้ว 60 tick เพราะ ISR ของ timer ไม่รู้และไม่สนว่า task เป็นอย่างไร กลยุทธ์ B รีเซ็ตภายในเวลาที่ตั้ง และบอกได้ด้วยว่า task ไหนคือตัวที่หายไป watchdog ที่ป้อนผิดที่ดีกว่าไม่มีเลยนิดเดียว

watchdog คือตัวนับที่เดินด้วยสัญญาณนาฬิกาของตัวเอง แยกจาก CPU ซอฟต์แวร์ต้อง “ป้อน” (ล้าง) มันเป็นระยะ ถ้าไม่ป้อนจนถึงค่าที่ตั้ง มันรีเซ็ตทั้งชิป แนวคิดคือถ้าซอฟต์แวร์ยังป้อนได้ แปลว่ายังทำงาน ถ้าป้อนไม่ได้ แปลว่าค้าง และการเริ่มใหม่ดีกว่าค้างไปเรื่อย ๆ

PDL มีไดรเวอร์ WDT ใน cy_wdt.h BSP ของบอร์ดนี้ประกาศ CY_IP_MXS22SRSS (cy_device_headers_ns.h) ซึ่งทำให้ใช้ชุดฟังก์ชันแบบ match value ของ PDL ได้ ลำดับการตั้งตามเอกสารของไดรเวอร์คือ

ขั้น ฟังก์ชัน
ปลดล็อกและปิดก่อนแก้ Cy_WDT_Unlock() แล้ว Cy_WDT_Disable()
เลือกสัญญาณนาฬิกา (สำหรับ MXS22SRSS คือ PILO หรือ clk_bak) Cy_WDT_SetClkSource()
ตั้งเวลา Cy_WDT_SetMatch() และถ้าต้องการสั้นลง Cy_WDT_SetIgnoreBits()
เปิด แล้วล็อกกันแก้โดยไม่ตั้งใจ Cy_WDT_Enable() แล้ว Cy_WDT_Lock()
ป้อน Cy_WDT_ClearWatchdog() ซึ่งเอกสารเขียนว่า “Clears (“feeds”) the watchdog, to prevent a XRES device reset”

ข้อเตือนจากเอกสารของ PDL ที่ต้องรู้ก่อนเปิดใช้: เวลาของ watchdog ต้องยาวกว่าเวลาบูตของชิป (“a WDT reset can be generated faster than a device start-up”) การเปลี่ยนค่ามีผลช้าไปสองสามจังหวะของสัญญาณนาฬิกาความถี่ต่ำ และ oscillator ความแม่นยำต่ำต้องเผื่อ margin ให้มาก (เอกสารยกตัวอย่าง ILO ของชิปรุ่นอื่นที่คลาดได้ ±30%) ความแม่นยำของ PILO หรือ clk_bak บนชิปนี้ให้ดูใน datasheet ของชิป และระหว่างดีบัก การหยุด CPU ที่ breakpoint อาจทำให้ watchdog รีเซ็ตกลางทาง (บทเรียน 3.1)

สถานะในแม่แบบของ SDK: เราค้นซอร์สของแม่แบบ mtb-only ที่ commit นี้แล้ว ไม่พบการเปิด hardware watchdog (Cy_WDT_Enable หรือ Cy_WDT_ClearWatchdog) มีเพียง Cy_WDT_Unlock() ในโค้ดตั้งสัญญาณนาฬิกาที่ BSP สร้าง (cycfg_clocks.c บรรทัด 584) ส่วนที่ SDK ใช้จริงคือ watchdog แบบซอฟต์แวร์ของ task ตัวหนึ่ง ซึ่งเป็นหัวข้อถัดไป

watchdog ตรวจได้เฉพาะสิ่งที่ “คนป้อน” รู้ ถ้าป้อนจาก ISR ของ timer มันพิสูจน์ได้แค่ว่า interrupt ของ timer ยังทำงาน ซึ่งมักจริงแม้ task ทุกตัวตาย ถ้าให้แต่ละ task ป้อนเอง task ตัวไหนก็ได้ที่ยังรอดจะป้อนแทนตัวที่ตายไปแล้ว รูปแบบที่ใช้ได้คือ check-in กับผู้ดูแลหนึ่งคน

  1. task ที่สำคัญแต่ละตัวตั้งบิตของตัวเองเมื่อ ทำงานคืบหน้าจริง (อ่านเซนเซอร์ได้ ส่งข้อความได้) ไม่ใช่แค่ตื่นขึ้นมา
  2. ผู้ดูแลเป็น task ที่ความสำคัญต่ำ ตรวจเป็นระยะ ป้อนเฉพาะเมื่อบิตครบ แล้วล้างบิตทั้งหมด
  3. เวลาของ watchdog ยาวกว่ารอบที่ช้าที่สุดของ task ทุกตัว บวก margin

ผู้ดูแลที่ความสำคัญต่ำยังจับอีกอาการได้ฟรี: task ความสำคัญสูงที่วนไม่ปล่อย CPU ผู้ดูแลจะไม่ได้รัน watchdog จึงรีเซ็ต

SDK มีตัวอย่าง watchdog แบบซอฟต์แวร์ของจริง 08_deepcraft_link.c อธิบายว่า task ของ model link เคย “asleep inside its own queue receive with a command outstanding” หลังสลับโมเดลไปเป็นร้อยครั้ง deepcraft_task_watchdog() ต้องถูกเรียกราว 1 Hz “from a context that never blocks” และมันตรวจพบงานค้างแล้วปลุก task ให้ทำต่อ คอมเมนต์เดียวกันยอมรับตรง ๆ ว่า “The watchdog does not fix the underlying fault” watchdog คือการฟื้นตัว ไม่ใช่การแก้บั๊ก ต้องบันทึกทุกครั้งที่มันทำงาน

3. บันทึกสาเหตุการรีเซ็ต: รู้ให้ได้ว่า watchdog ทำงานเมื่อไร

หัวข้อที่มีชื่อว่า “3. บันทึกสาเหตุการรีเซ็ต: รู้ให้ได้ว่า watchdog ทำงานเมื่อไร”

watchdog ที่รีเซ็ตเงียบ ๆ ทำให้ระบบดูเหมือน “บางทีก็บูตใหม่เอง” สิ่งที่ต้องมีคือบันทึกที่รอดข้ามการรีเซ็ต

  • อ่านสาเหตุ Cy_SysLib_GetResetReason() คืนชุดบิต เช่น CY_SYSLIB_RESET_HWWDT (0x0001) CY_SYSLIB_RESET_SOFT (0x0010) CY_SYSLIB_RESET_SWWDT0..3 ของ multi-counter watchdog (ดูตารางใน cy_syslib.h) ทดสอบด้วย & เพราะอาจมีหลายบิตพร้อมกัน BSP ของ SDK เองใช้ฟังก์ชันนี้ตอนตั้งสัญญาณนาฬิกา และเขียนกำกับว่าค่า 0 หมายถึง “POR, XRES, or BOD” (cycfg_clocks.c บรรทัด 571-575) หลังบันทึกแล้วเรียก Cy_SysLib_ClearResetReason() ไม่อย่างนั้นบูตครั้งต่อไปจะเห็นสาเหตุเดิม
  • เก็บร่องรอยไว้ใน RAM ที่ไม่ถูกล้างตอนบูต linker script ของ SDK มี section .noinit ที่ startup ไม่แตะ (pse84_ns_cm33.ld บรรทัด 339-343) ผู้ดูแลเขียน “task ไหนยังไม่ check in” ลงไปทุกรอบ หลังรีเซ็ตจึงรู้ว่าใครค้าง หลังเปิดไฟครั้งแรกค่าใน RAM เป็นขยะ ต้องมี magic number ไว้แยก CM55 ของแม่แบบใช้หลักเดียวกัน มันเขียน fault marker ไว้ที่ที่อยู่คงที่ใน SRAM พร้อมคอมเมนต์ว่า “survives until power cycle”
  • ส่งออก พิมพ์ตอนบูต นับจำนวน และถ้ามีการเชื่อมต่อ ส่งขึ้นระบบที่เก็บ log ได้

examples/09_watchdog_sim.c ทำงานเป็นสามท่า

  • ท่าที่ 1 แต่ละ task ทำงานหนึ่งรอบแล้ว check in ด้วยการตั้งบิต (task sensor ค้างตั้งแต่ tick 40)
  • ท่าที่ 2 กลยุทธ์ A ป้อนทุก tick ไม่ดูอะไร กลยุทธ์ B ป้อนเมื่อบิตครบแล้วล้างบิต
  • ท่าที่ 3 watchdog จำลองนับต่อ ถ้าเกินเวลาพิมพ์ว่ารีเซ็ต พร้อมชื่อ task ที่ขาด check in

ลองแก้แล้วทายก่อนรัน

  1. ในกลยุทธ์ B ลบบรรทัด checkin = 0u; ออก แล้วรัน watchdog ยังจับการค้างได้ไหม เพราะอะไร
  2. เปลี่ยน WDT_TIMEOUT_TICKS เป็น 1 กลยุทธ์ B ทำอะไร นั่นบอกอะไรเรื่องการเลือกเวลาของ watchdog
  3. ทำให้ task ui ทำงานคืบหน้าแค่ทุก 5 tick (check in ทุก 5 tick) แล้วหาค่า WDT_TIMEOUT_TICKS ที่เล็กที่สุดที่ไม่รีเซ็ตผิด

เปิด practice/09_watchdog.c มีช่องให้เติม 5 จุด ผู้ดูแลแบบ check-in และบันทึกการบูตที่รอดข้ามการรีเซ็ต

  1. wd_checkin() ตั้งบิตของ task
  2. supervisor_poll() ไม่ป้อนถ้ายังขาด task
  3. supervisor_poll() ล้างบิตหลังป้อน
  4. boot_record_update() แยกขยะใน RAM ออกด้วย magic
  5. boot_record_update() นับการรีเซ็ตจาก watchdog และเก็บชื่อ task ที่ขาด
Terminal window
gcc -std=c11 -Wall -Wextra -o watchdog practice/09_watchdog.c && ./watchdog

ลองเองก่อนอย่างน้อย 15 นาที แล้วเปิด solution/09_watchdog.c สองจุดที่ควรเทียบ: เฉลยทดสอบสาเหตุการรีเซ็ตด้วย & ไม่ใช่ == และคอมเมนต์เตือนว่า |= ใน wd_checkin() ที่หลาย task เรียกพร้อมกันบนบอร์ดจริง ต้องมี critical section หรือใช้ event group ของ FreeRTOS แทน ซึ่งเป็นความรู้จากบทเรียน 1.3 ที่กลับมาในที่ที่ไม่คาดคิด

ตอบคำถาม 5 ข้อใน quiz.yaml (บนเว็บไซต์อยู่ท้ายหน้านี้) ครอบคลุมเป้าหมายทั้งสามข้อ ตอบถูกตั้งแต่ 4 ข้อขึ้นไปถือว่าจบบทเรียน

งาน: อ่านสาเหตุการรีเซ็ตของบอร์ดจริงในสถานการณ์ต่าง ๆ แล้วออกแบบการป้อน watchdog ให้แม่แบบ

  1. ในโปรเจกต์ของคุณ (บน branch ใหม่ ตามบทเรียน 2.3) เพิ่มสามบรรทัดนี้ใน proj_cm33_ns/main.c ทันทีหลัง init_retarget_io(); (เอกสาร B1 ของ SDK บอกว่าจากจุดนี้ UART พร้อมแล้ว)
    uint32_t reset_reason = Cy_SysLib_GetResetReason();
    printf("[RST] reason=0x%08lx%s\r\n", (unsigned long)reset_reason,
    (reset_reason & CY_SYSLIB_RESET_HWWDT) ? " HWWDT" : "");
    build แล้ว flash
  2. บันทึกค่าที่พิมพ์ในสี่สถานการณ์: ถอดสายเสียบใหม่, กดปุ่มรีเซ็ตบนบอร์ด (ถ้ามี), หลัง make program, และถอดเสียบอีกครั้งหลังจากนั้น ค่าไหนเป็น 0 ค่าไหนมีบิต และบิตค้างข้ามการรีเซ็ตหรือไม่ (โค้ดนี้ยังไม่ล้างสาเหตุ นั่นเป็นส่วนหนึ่งของสิ่งที่คุณสังเกต)
  3. เทียบค่าที่ได้กับตารางใน cy_syslib.h ข้อไหนตรงกับที่คาด ข้อไหนไม่ตรง เขียนสิ่งที่เห็นจริงแยกจากสิ่งที่อนุมาน
  4. ออกแบบ (บนกระดาษ) การเปิด hardware watchdog ให้แม่แบบ: ระบุ task ที่ต้อง check in อย่างน้อยสามตัวจาก task ที่แม่แบบสร้าง (ดู main() ใน proj_cm33_ns/main.c) รอบที่ช้าที่สุดของแต่ละตัว เวลาของ watchdog ที่เลือกพร้อม margin และจุดที่วางผู้ดูแล (หลักสูตรนี้ยังไม่ได้ทดสอบการเปิด hardware watchdog บนแม่แบบนี้ ถ้าคุณลองจริง ให้ทำบน branch แยก และบันทึกผลทั้งหมด รวมผลต่อการดีบัก)

หลักฐานที่เก็บไว้ใน portfolio: diff ของการแก้ main.c ตารางค่าสาเหตุการรีเซ็ตทั้งสี่สถานการณ์พร้อมคำอธิบาย และแบบร่างการออกแบบ watchdog ข้อ 4

  • อ่านหัวข้อ Functional Description, Clearing WDT และ Reset Detection ในหัวไฟล์ของ cy_wdt.h แล้วคำนวณว่าถ้าใช้ match value แบบในเอกสาร ต้องป้อนบ่อยแค่ไหน
  • เปรียบเทียบ watchdog แบบ window (ห้ามป้อนเร็วเกินไปด้วย) กับแบบธรรมดา ใน Watchdog timer (Wikipedia) แบบ window จับบั๊กอะไรได้เพิ่ม

บทถัดไป: บทเรียน 4.4 DMA

  • ระบบที่คุณเคยใช้ ระบบไหนที่ “บูตใหม่เองเป็นบางครั้ง” ตอนนี้คุณจะถามผู้พัฒนาว่าอะไร
  • ถ้า watchdog ช่วยให้ผลิตภัณฑ์ดูเหมือนไม่เคยค้าง ทีมจะรู้ได้อย่างไรว่ามีบั๊กที่ต้องแก้

คำถามทบทวน

ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย

  1. ถ้าเฟิร์มแวร์ไม่ป้อน hardware watchdog ภายในเวลาที่ตั้ง จะเกิดอะไร (เป้าหมายข้อ 1)

    1. watchdog พิมพ์ข้อความเตือนลง console แล้วรอต่อ
    2. ชิปถูกรีเซ็ต และสาเหตุถูกบันทึกให้อ่านได้ด้วย Cy_SysLib_GetResetReason() หลังบูต
    3. task ที่ค้างถูกลบทิ้งอัตโนมัติ ส่วนที่เหลือทำงานต่อ
    4. ไม่มีอะไรเกิดจนกว่าจะถอดไฟ
    ดูเฉลย

    คำตอบ: B. ชิปถูกรีเซ็ต และสาเหตุถูกบันทึกให้อ่านได้ด้วย Cy_SysLib_GetResetReason() หลังบูต

    watchdog รีเซ็ตทั้งชิป ไม่เลือก task เอกสาร cy_wdt.h แนะนำให้ใช้ Cy_SysLib_GetResetReason() ตรวจว่า WDT เป็นสาเหตุหรือไม่ (บิต CY_SYSLIB_RESET_HWWDT)

  2. ข้อใดเป็นข้อควรระวังเมื่อเลือกเวลาของ watchdog ตามเอกสารของ PDL (เลือกได้หลายข้อ) (เป้าหมายข้อ 1)

    1. เวลาของ watchdog ต้องยาวกว่าเวลาบูตของชิป
    2. oscillator ความถี่ต่ำอาจคลาดได้มาก ต้องเผื่อ margin
    3. การเปลี่ยนค่าของ WDT มีผลทันทีในคำสั่งถัดไปเสมอ
    4. ยิ่งสั้นยิ่งดี เพราะจับการค้างได้เร็ว
    ดูเฉลย

    คำตอบ: A. เวลาของ watchdog ต้องยาวกว่าเวลาบูตของชิป · B. oscillator ความถี่ต่ำอาจคลาดได้มาก ต้องเผื่อ margin

    เอกสารเตือนว่า WDT reset อาจเกิดเร็วกว่าเวลาบูต ความแม่นยำของ oscillator ต้องเผื่อ และการเปลี่ยนค่ามีผลช้าไปสองสามจังหวะ เวลาที่สั้นเกินจะทำให้รีเซ็ตผิดเมื่อระบบแค่ช้า

  3. ทำไมการป้อน watchdog ใน ISR ของ timer จึงเป็นกับดัก (เป้าหมายข้อ 2)

    1. เพราะ ISR ป้อน watchdog ไม่ได้ทางเทคนิค
    2. เพราะ ISR ของ timer ยังทำงานแม้ task ทุกตัวค้าง watchdog จึงไม่เคยรีเซ็ตทั้งที่ระบบตายแล้ว
    3. เพราะทำให้ timer ช้าลง
    4. เพราะ watchdog จะรีเซ็ตทุกครั้งที่ interrupt เกิด
    ดูเฉลย

    คำตอบ: B. เพราะ ISR ของ timer ยังทำงานแม้ task ทุกตัวค้าง watchdog จึงไม่เคยรีเซ็ตทั้งที่ระบบตายแล้ว

    คนป้อนต้องพิสูจน์ว่าระบบคืบหน้า ISR ของ timer พิสูจน์ได้แค่ว่า interrupt ยังทำงาน การจำลองในบทนี้แสดงว่ากลยุทธ์นี้ไม่รีเซ็ตเลยแม้ task ค้างไป 60 tick

  4. เรียงขั้นของรูปแบบ check-in กับผู้ดูแลหนึ่งคนในหนึ่งรอบ (เป้าหมายข้อ 2)

    1. ผู้ดูแลล้างบิตทั้งหมดสำหรับรอบหน้า
    2. แต่ละ task ตั้งบิตของตัวเองเมื่อทำงานคืบหน้าจริง
    3. ผู้ดูแลเรียก Cy_WDT_ClearWatchdog()
    4. ผู้ดูแลตรวจว่าบิตของทุก task ครบ
    ดูเฉลย

    ลำดับที่ถูก: B. แต่ละ task ตั้งบิตของตัวเองเมื่อทำงานคืบหน้าจริง → D. ผู้ดูแลตรวจว่าบิตของทุก task ครบ → C. ผู้ดูแลเรียก Cy_WDT_ClearWatchdog() → A. ผู้ดูแลล้างบิตทั้งหมดสำหรับรอบหน้า

    task รายงานความคืบหน้า ผู้ดูแลตรวจว่าครบ ป้อน แล้วล้าง ถ้าไม่ล้าง บิตเก่าจะป้อนแทน task ที่ตายไปแล้วตลอดไป

  5. ออกแบบให้รู้ว่า watchdog รีเซ็ตเมื่อไรและเพราะ task ไหน ข้อใดควรทำ (เลือกได้หลายข้อ) (เป้าหมายข้อ 3)

    1. อ่าน Cy_SysLib_GetResetReason() ตอนบูต ทดสอบบิต CY_SYSLIB_RESET_HWWDT ด้วย & แล้วล้างด้วย Cy_SysLib_ClearResetReason()
    2. ให้ผู้ดูแลเขียน task ที่ยังไม่ check in ลงบันทึกใน section .noinit ทุกรอบ พร้อม magic number
    3. เก็บบันทึกไว้ในตัวแปร .bss ธรรมดา เพราะ startup ไม่แตะมัน
    4. นับจำนวนครั้งที่ watchdog รีเซ็ต แล้วพิมพ์หรือส่งออกตอนบูต
    ดูเฉลย

    คำตอบ: A. อ่าน Cy_SysLib_GetResetReason() ตอนบูต ทดสอบบิต CY_SYSLIB_RESET_HWWDT ด้วย & แล้วล้างด้วย Cy_SysLib_ClearResetReason() · B. ให้ผู้ดูแลเขียน task ที่ยังไม่ check in ลงบันทึกใน section .noinit ทุกรอบ พร้อม magic number · D. นับจำนวนครั้งที่ watchdog รีเซ็ต แล้วพิมพ์หรือส่งออกตอนบูต

    .bss ถูกล้างเป็นศูนย์ตอนบูต บันทึกจึงหาย ต้องใช้ .noinit ที่ linker script ของ SDK เตรียมไว้ และต้องมี magic เพราะหลังเปิดไฟครั้งแรกค่าใน .noinit เป็นขยะ สาเหตุการรีเซ็ตเป็นชุดบิตจึงทดสอบด้วย &

อ้างอิงบทเรียนนี้

ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน

"Watchdog" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0

ข้อความอ้างอิงภาษาอังกฤษ: "The watchdog" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0

ลิงก์บทเรียน: https://tesaiot.github.io/tesa-qualification-program/courses/embedded-c-foundations/m04-peripherals/l03-watchdog/

บทเรียนนี้ดัดแปลงจากต้นฉบับด้านล่าง เมื่ออ้างอิงให้คงเครดิตต้นฉบับไว้ด้วย
https://github.com/tesaiot/tesaiot-pse84-devkit-sdk/tree/ef72c1b658178eee8c38b1e47d28b006f80a59b5 · SDK examples and docs are linked at this commit, not copied into this course. Lessons quote short excerpts (at most 25 lines) with a link to the file at this commit and the credit (Apache-2.0, tesaiot-pse84-devkit-sdk).

วิธีอ้างอิง TESA ฉบับเต็ม

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0

เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA