SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
J5 — บอร์ดฐาน QWA309: pot ปุ่ม RGB matrix และ header
variant ที่ใช้ได้
mtb-mpy เท่านั้น (ไม่มีการลิงก์ libbento_mpy.a เมื่อ BENTO_HAS_MPY=0)

โมดูลทั้ง 5 ตัวในบทนี้อยู่ใน bento_libs/claw/kit-tesaiot-pse84-ai/mpy/ ซึ่งเป็นไดเรกทอรีที่ไม่มีอยู่ในแพ็กเกจ mtb-only ตัวบอร์ดฐานเองเป็นฮาร์ดแวร์เดียวกันบนทั้งสอง variant บน mtb-only ให้เข้าถึงมันผ่าน PDL และผ่าน cm55_sensor_poll (จุดเข้าฝั่ง C (ทั้ง 2 แพ็กเกจ))

เป้าหมายของหัวข้อนี้

เมื่อจบบทนี้จะอ่านค่าจากลูกบิดทั้งสี่ ปุ่มทั้งสอง และ RGB matrix ได้ และขับคอนเนกเตอร์ header สำหรับต่อขยายได้ ข้อเท็จจริง 2 ข้อในบทนี้เป็นสาระ และทั้ง 2 ข้อเดาจาก API ไม่ได้: การจับคู่ลูกบิดกับช่องสัญญาณ ไม่ใช่ การจับคู่แบบตรงตัว และหนึ่งใน 2 ปุ่มใช้ขาร่วมกับไฟเลี้ยง USB host หากเข้าใจผิดข้อใดข้อหนึ่ง โค้ดจะคอมไพล์ผ่าน รันได้ แล้วรายงานลูกบิดผิดตัว หรือทำให้จอยสติ๊กใช้ไม่ได้

ลำดับการทำงานจริงของเฟิร์มแวร์

ทุกอย่างอยู่หลัง flag ตัวเดียว pots, buttons, rgbmatrix, header, can และ shield แต่ละตัวห่อไว้ด้วย #if BSP_HAS_QWA309_BASEBOARD ซึ่งบอร์ดนี้ตั้งไว้เป็น 1 (bsp_features.mk:25) บน AI Kit SoM เปล่า ๆ จะไม่มีตัวใดลงทะเบียนเลยและ import pots จะโยน ImportError — ไม่ใช่ error ขณะทำงานที่เกิดจากอุปกรณ์ที่ไม่มีอยู่

2 คอร์แบ่งบอร์ดฐานกันตามความสามารถ ไม่ใช่ตามอุปกรณ์ ค่า pot อ่านจาก CM33_NS ตรงจากรีจิสเตอร์ผลลัพธ์ของ AutAnalog SAR — ไม่ผ่าน IPC หน่วงระดับไมโครวินาที ซึ่งเป็นสิ่งที่ทำให้ตัวพาย (paddle) ที่ 60 fps เป็นไปได้ (mod_qwa309_pots.c:5-15) ส่วน RGB matrix อยู่บนบัส I2C ของ header ที่ CM55 เป็นเจ้าของ การเรียก rgbmatrix.* ทุกครั้งจึงเป็นข้อความ IPC แบบส่งแล้วไม่รอผล และ CM33 ไม่เคยแตะบัสนั้นเลย (mod_rgbmatrix.c:6-13) CapSense ก็เป็นของ CM55 เช่นกัน (ทางแยกของ CapSense: หนึ่ง API สอง backend) ส่วนปุ่มและ header เป็น GPIO ของ CM33_NS ล้วน ๆ

การจับคู่ลูกบิดไม่ใช่การจับคู่แบบตรงตัว

s_pots_knob_ch[] มีค่า { 5, 4, 6, 7 } (mod_qwa309_pots.c:55) ดัชนีเชิงตรรกะ 0 (VR1) อ่านช่อง SAR GPIO ที่ 5 และดัชนี 1 (VR2) อ่านช่อง 4 เหตุผลตามที่ซอร์สระบุ: แผ่นวงจร QWA309 เดินลาย P15.4 (ช่อง 4) ไปที่ลูกบิด VR2 และ P15.5 (ช่อง 5) ไปที่ VR1 — ลายสองเส้นแรกสลับกันบนบอร์ด ยืนยันบนฮาร์ดแวร์แล้ว ส่วน VR3 และ VR4 เดินลายตรง

ตารางนี้ต้องเดินคู่กันไปตลอดกับ s_pot_adc_ch[] ใน proj_cm55/modules/cm55_sensor_poll/cm55_sensor_poll.c:80 ซึ่งมีค่า { 5, 4, 6, 7 } เหมือนกันและคอมเมนต์เหมือนกัน หน้าจอฝั่ง CM55 ชดเชยค่าตั้งแต่ 2026-07-31 ส่วนโมดูลนี้ส่งมอบไปโดยยังไม่ได้ชดเชย pots.read(0) จึงคืนค่าของลูกบิดที่พิมพ์ว่า VR2 จนกระทั่งแก้ไขเมื่อ 2026-08-20 หากจะแก้ตารางใดตารางหนึ่ง ต้องแก้ทั้ง 2 ตาราง มิฉะนั้นหน้าจอกับ REPL จะไม่ตรงกันว่าผู้ใช้กำลังหมุนลูกบิดตัวใด — และแต่ละฝั่งจะสอดคล้องกันเองภายในซึ่งเป็นสิ่งที่ทำให้มองเห็นได้ยาก

CM33_NS เป็นผู้เริ่ม SAR ตอนบูต ไม่ใช่โมดูลนี้ init_potentiometer_adc() (proj_cm33_ns/main.c:142) รัน Cy_AutAnalog_Init(&autonomous_analog_init) และ Cy_AutAnalog_StartAutonomousControl() โดยมี BSP_HAS_POTENTIOMETER เป็น gate (ด่านกั้น) และมีการเรียกที่ main.c:224 ไฟล์ design.modus ของ BSP จัดหา struct ตั้งค่ามาให้แต่ init_cycfg_peripherals() ไม่ได้เรียก API สำหรับ init — ดังนั้นหากตัดขั้นตอนนี้ออก ทุกช่องจะอ่านได้ 0 ตลอดไป และไม่มีสิ่งใดรายงาน error ลำดับการบูตที่ทำให้สิ่งนี้ทำงานได้อยู่ที่ B1 — เดินดูลำดับการบูตของ CM33_NS ไม่ต้องอธิบายซ้ำที่นี่

ค่าศูนย์ไม่ได้พิสูจน์อะไรเลย ช่องที่ยังไม่ได้ตั้งค่าก็อ่านได้ 0 เช่นกัน จึงเป็นเหตุผลที่ pots.ready() มีอยู่: มันเปิดเผย gate ของสถานะผลลัพธ์รายช่อง และทั้ง 4 ช่องต้องผลิตผลลัพธ์ออกมาแล้วในการกวาดรอบล่าสุด (mod_qwa309_pots.c:56-60) ให้ใช้ตอน bring-up ไม่ใช่ใช้ทุกเฟรม

มีตัวกรองสัญญาณกระโดด ตัว SAR บางครั้งให้ค่าตัวอย่างผิดพลาดจนเกือบเป็นศูนย์ pots_read_channel() ปฏิเสธการตกลงมามากกว่า 200 นับจนต่ำกว่า 50 หนึ่งครั้ง และยอมรับค่าที่ต่ำติดต่อกันเป็นครั้งที่สองว่าผู้เล่นหมุนลูกบิดไปสุดปลายจริง ๆ (mod_qwa309_pots.c:66-89) หน้าจอฝั่ง CM55 ใช้กฎเดียวกัน เพื่อให้ทั้ง 2 เส้นทางตรงกัน

ปุ่ม 2 ปุ่ม ซึ่งปุ่มหนึ่งเป็นไฟเลี้ยง USB ด้วย

s_btns[] (mod_qwa309_buttons.c:67-68): ดัชนี 0 คือ SW4 บน P17.5 ดัชนี 1 คือ SW5 บน P17.7 ทั้งคู่เป็นแบบ active-low พร้อม pull-up ภายใน MCU และทั้งคู่ผ่าน debounce ด้วยซอฟต์แวร์โดยใช้หน้าต่างค่านิ่ง 50 ms

มี 2 เรื่องที่คอมเมนต์ส่วนหัวของไฟล์ (mod_qwa309_buttons.c:8-22) บันทึกไว้ แต่ API บอกไม่ได้:

  • ชื่อของปุ่มมีความเป็นมา ชื่อเหล่านี้คือชื่อที่พิมพ์บนแผ่นวงจรรุ่นผลิตจริงเอกสารรุ่นเก่าเรียกเน็ตเดียวกันนี้ว่า SW9/SW10 และหน้า GPIO และ RGB ของระบบเรียกว่า SW5/SW6 สามระบบการตั้งชื่อ ต่อปุ่มหนึ่งคู่ ให้ยึด buttons.name(n) เป็นเกณฑ์
  • P17.5 ยังเป็นขาเปิดใช้ VBUS ของ USB host ด้วย (usbh_config.c) และเน็ตเหล่านี้ใช้ร่วมกับสัญญาณกล้อง DVP การใช้ปุ่มพร้อมกับจอยสติ๊ก USB บนบอร์ดฐานนี้ ไม่รองรับ: การเรียก buttons_hw_init() ครั้งแรกยึด P17.5 ไปเป็นขาอินพุต นี่เป็นการแลกที่ยอมรับแล้ว ไม่ใช่ความหลงลืม — แต่หมายความว่าคำสั่ง import buttons เพียงคำสั่งเดียวทำให้จอยสติ๊กหยุดทำงานได้ และจะไม่มีสิ่งใดแจ้งเตือน

ทีละขั้น

ขั้นที่ 1 — พิสูจน์ว่า SAR ยังมีชีวิตก่อนจะเชื่อค่าที่อ่านได้

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import pots
print(pots.count(), pots.ready())
print(pots.read_all())
สิ่งที่ควรสังเกต
ได้ 4 True แล้วตามด้วยทูเพิล 4 ตัวของจำนวนเต็มในช่วง 0..4095 การที่ ready() เป็น false หมายความว่า SAR ยังกวาดช่อง 4..7 ไม่ครบ — ให้ตรวจว่า BSP_HAS_POTENTIOMETER=1 และ init_potentiometer_adc() ยังทำงานอยู่ใน main() ทูเพิลที่เป็นศูนย์ทั้ง 4 ตัว พร้อมกับ ready() เป็น true หมายถึงลูกบิดทั้งสี่อยู่ที่ปลายสุดข้างเดียวกัน ส่วนทูเพิลศูนย์ทั้งสี่พร้อม ready() เป็น false หมายถึงตัวแปลงสัญญาณไม่เคยเริ่มทำงาน การแยก 2 กรณีนี้ออกจากกันคือเหตุผลทั้งหมดที่ ready() มีอยู่

ขั้นที่ 2 — ยืนยันการจับคู่ลูกบิดด้วยมือตัวเอง

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import pots, time
while True:
print(["%.2f" % pots.norm(i) for i in range(4)])
time.sleep_ms(200)
สิ่งที่ควรสังเกต
หมุนลูกบิดที่พิมพ์ว่า VR1 แล้วตัวเลข ตัวแรก จะขยับ นั่นคือการแก้ไขที่ทำงานถูกต้อง หากตัวเลขตัวที่สองขยับแทน แสดงว่ากำลังรัน build ที่ s_pots_knob_ch[] เป็นการจับคู่แบบตรงตัว — และหน้า pot บน CM55 จะไม่ตรงกับที่เห็น ซึ่งคือสัญญาณบอกเหตุ

ขั้นที่ 3 — อ่านค่าปุ่มทั้งสอง

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import buttons, time
print(buttons.count(), buttons.name(0), buttons.name(1))
while True:
sw4, sw5 = buttons.read()
if sw4 or sw5:
print("SW4" if sw4 else "", "SW5" if sw5 else "")
time.sleep_ms(50)
สิ่งที่ควรสังเกต
ได้ 2 SW4 SW5 แล้วตามด้วยหนึ่งบรรทัดต่อการกดหนึ่งครั้งโดยไม่มีสัญญาณเด้ง — หน้าต่างค่านิ่ง 50 ms อยู่ภายใน buttons_sample() จึงไม่ต้องเพิ่มของตัวเอง buttons.pressed(n) เป็นรูปแบบสำหรับปุ่มเดียว ดัชนีที่อยู่นอกช่วง 0..1 จะโยน ValueError
Warning
การรันโค้ดนี้ทำให้จอยสติ๊ก USB หยุดทำงานจนกว่าจะตัดไฟแล้วจ่ายไฟใหม่ P17.5 คือขาเปิดใช้ VBUS และโค้ดนี้ยึดมันไปเป็นขาอินพุต ให้ตัดสินใจว่าจะใช้บอร์ดนี้เพื่ออะไร

ขั้นที่ 4 — แสดงบางอย่างบน RGB matrix

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import rgbmatrix as m, time
m.clear()
m.pixel(0, 0, m.RED)
m.pixel(15, 7, m.CYAN)
time.sleep(1)
m.score(42, m.GREEN)
time.sleep(1)
m.bar(3, 5, m.YELLOW)
time.sleep(1)
m.scroll("BENTO", m.WHITE, 80)
time.sleep(3)
m.scroll("") # stop the marquee
m.clear()
สิ่งที่ควรสังเกต
พาเนล DFR0522 ทำตามทุกการเรียก pixel() รับ x 0..15 และ y 0..7 score() แสดง 0..9999 ส่วน bar() วาดค่า value เทียบกับ max และ scroll() ส่งข้อความให้ timer บน CM55 ซึ่งรันตัววิ่งข้อความในภาษา C — มันคืนค่าทันทีและวิ่งข้อความต่อไปจนกว่าจะเรียก scroll("")

การเรียกทุกตัวเหล่านี้เป็นแบบ ส่งแล้วไม่รอผล ผ่าน IPC_CMD_UI_RGB_MATRIX และคืนค่า None ไม่มีการตอบรับและไม่มี error หากไม่ได้เสียบพาเนลไว้ การเรียกก็ยังสำเร็จและไม่มีอะไรสว่างขึ้น เป็นโมดูลที่ไม่มีผู้เรียก — ตัวอย่างทุกชิ้นในหัวข้อนี้เขียนขึ้นเอง

ขั้นที่ 5 — ขับคอนเนกเตอร์ header สำหรับต่อขยาย

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import header, time
header.gpio_out(0, 1) # P13.0 high
print(header.name(0), header.gpio_in(1))
header.pwm(3, 25, 1000) # P13.3 at 25% duty, 1 kHz
header.uart_write(b"hello\r\n") # SCB9, P15.1 TX, 115200 8N1
time.sleep_ms(100)
if header.uart_any():
print(header.uart_read(header.uart_any()))
สิ่งที่ควรสังเกต
ดัชนีเชิงตรรกะ 0..5 จับคู่กับ P13.0, P13.3, P13.4, P13.5, P13.6, P13.7 (mod_qwa309_header.c:8-13) pwm() ใช้ตัวนับที่ 5 ของ TCPWM0 บน P13.3 โดยมี P13.4 ถูกขับเป็นสัญญาณเติมเต็มของฮาร์ดแวร์ — ดังนั้น PWM บนขา 3 จึงพาขา 4 ไปด้วย และการเรียก gpio_out(2, ...) หลังจากนั้นจะไปชนกับอุปกรณ์ต่อพ่วง ส่วน UART คือ SCB9 ที่ 115200 8N1 และเป็น UART คนละตัว กับคอนโซล: สิ่งที่เขียนที่นี่จะไม่ปรากฏในเทอร์มินัล เป็นโมดูลที่ไม่มีผู้เรียก ตัวอย่างเขียนขึ้นเอง

โมดูล 2 ตัวที่บทนี้เพียงชี้ให้เห็น

  • can (modcan.c:479) — init, send, recv, deinit, stats
  • shield.motor_v2 (mod_shield_motor_v2.c:313) — speed, run, stop_all และกลุ่มฟังก์ชัน stepper ได้แก่ stepper_attach, stepper_detach, stepper_rpm, stepper_step, stepper_stop, stepper_release พร้อมค่าคงที่ FORWARD/BACKWARD/BRAKE/RELEASE = 1..4 และ SINGLE/DOUBLE/INTERLEAVE/MICROSTEP = 1..4

ทั้งสองลงทะเบียนอยู่ใต้ flag ของบอร์ดฐานตัวเดียวกัน และทั้งสองเป็นเรื่อง ข้างเคียง ของอุปกรณ์ต่อพ่วง — มันขับสิ่งที่เสียบเข้ากับบอร์ด ไม่ใช่สิ่งที่อยู่บนบอร์ด หน้า Motor Shield ที่คู่กับทั้งสองปิดไว้เป็นค่าเริ่มต้น (bsp_features.mk:77) ด้วยเหตุผลว่าบอร์ดส่วนใหญ่ไม่ได้ติดตั้ง shield ไว้ และเมนูที่ขับฮาร์ดแวร์ที่ไม่มีอยู่คือสิ่งรบกวน ให้เปิดกลับได้เป็นราย build ด้วย ENABLE_PAGE_MOTOR_CTRL=1 ทั้ง 2 โมดูลไม่มีผู้เรียกที่ใดเลยในซอร์สที่ส่งมอบ

กับดัก

Warning
การจับคู่ลูกบิดคือ {5, 4, 6, 7} และมันอยู่ใน 2 ไฟล์ mod_qwa309_pots.c:55 และ cm55_sensor_poll.c:80 การแก้ไฟล์หนึ่งโดยไม่แก้อีกไฟล์จะทำให้ REPL และหน้าจอต่างผิดไปคนละทิศทางอย่างสอดคล้องกันเอง
การที่ pots.read(n) คืนค่า 0 นั้นกำกวม จนกว่า pots.ready() จะยืนยันว่าการกวาดเสร็จสิ้นแล้ว
import buttons แลกมาด้วยจอยสติ๊ก USB P17.5 คือขาเปิดใช้ VBUS และ buttons_hw_init() ยึดมันไป
ห้ามเชื่อชื่อปุ่ม 3 ชุดที่ต่างกัน SW4/SW5 คือชื่อที่พิมพ์บนแผ่นวงจรรุ่นผลิตจริง ส่วน SW9/SW10 และ SW5/SW6 ปรากฏในเอกสารรุ่นเก่าและในหน้า GPIO ของระบบให้ถาม buttons.name(n)
rgbmatrix ล้มเหลวไม่ได้ ทุกการเรียกเป็น IPC แบบส่งแล้วไม่รอผลพาเนลที่ไม่ได้เสียบให้ผลเป็นความเงียบ ไม่ใช่ข้อยกเว้น
header.pwm(3, ...) ยึด P13.4 ไปด้วย เอาต์พุตสัญญาณเติมเต็มเป็นเรื่องของฮาร์ดแวร์ ไม่ใช่ข้อตกลงในการเขียนโค้ด
ห้ามเพิ่มเจ้าของ SAR รายที่สอง CM33_NS เป็นผู้เริ่มตัวแปลงสัญญาณ และ CM55 อ่านรีจิสเตอร์ผลลัพธ์ชุดเดียวกัน ทั้งสองเป็นผู้อ่านอย่างเดียวของการแปลงที่วิ่งอิสระ จึงอยู่ร่วมกันได้โดยไม่ต้องมีการจัดสรร (mod_qwa309_pots.c:11-15) การเรียก Cy_AutAnalog_Init() ซ้ำจากที่อื่นทำลายสมดุลนั้น

กล่อง variant

mtb-mpy mtb-only
โมดูล pots, buttons, rgbmatrix, header, can, shield ไม่มี — ไดเรกทอรี mpy/ ของชุดคิตไม่ได้จัดลงแพ็กเกจ
การอ่านค่า pot pots.read_all() บน CM33_NS cm55_pot_read_all() บน CM55 หรือ Cy_AutAnalog_SAR_ReadResult() พร้อมการจับคู่ {5,4,6,7} ชุดเดียวกัน
ปุ่ม buttons.read() ผ่าน debounce แล้ว Cy_GPIO_Read ที่เขียนเองบน P17.5 / P17.7 พร้อม debounce ของตัวเอง
RGB matrix rgbmatrix.* ผ่าน IPC โค้ดฝั่ง CM55 บนบัส I2C ของ header
I/O ของ header header.* PDL: Cy_GPIO, Cy_TCPWM, Cy_SCB_UART
การเริ่ม SAR main.c:224 ทั้งสอง variant main.c:224 ทั้งสอง variant