STM32 Code Examples
ตัวอย่างโค้ดไมโครไพธอนสำหรับ STM32 และการใช้งาน Thonny Python IDE
Last updated
ตัวอย่างโค้ดไมโครไพธอนสำหรับ STM32 และการใช้งาน Thonny Python IDE
Last updated
เนื้อหาในส่วนนี้มีตัวอย่างโค้ดในภาษาไมโครไพธอนเพื่อนำไปทดลองใช้กับบอร์ด STM32F411CE ได้นำเสนอไว้เพื่อเป็นแนวทางการเรียนรู้ และสาธิตให้เห็นว่า ภาษาไมโครไพธอน ซึ่งแม้ว่าเป็นภาษาคอมพิวเตอร์ระดับสูงกว่า C/C++ ก็สามารถนำมาใช้ในการเรียนรู้หลักการทำงานของไมโครคอนโทรลเลอร์ได้เช่นกัน
ผู้อ่านควรมีความรู้พื้นฐานเกี่ยวกับการเขียนโค้ดภาษาไพธอนมาบ้างแล้ว และการทำความเข้าใจโค้ดตัวอย่างสำหรับ STM32 เกี่ยวข้องกับวงจรภายในไมโครคอนโทรลเลอร์ตระกูล STM32F4 และการทำงานของฮาร์ดแวร์ประเภทต่าง ๆ ที่นำมาต่อเพิ่ม
ซอฟต์แวร์ประเภท Python IDE ที่นำมาใช้ในการเขียนโค้ดสำหรับไมโครไพธอน มีอยู่หลายตัวเลือก เอกสารนี้แนะนำการใช้งาน Thonny IDE (Python IDE for beginners) ในเบื้องต้น ซึ่งเป็นซอฟต์แวร์ประเภท Open Source และนำไปติดตั้งใช้งานได้หลายระบบปฏิบัติการ (Windows, Mac OS และ Linux)
ถ้าใช้ Linux เช่น Ubuntu หรือ Raspbian OS (สำหรับบอร์ด Raspberry Pi) ในโหมด GUI Desktop ก็ให้ทำคำสั่งต่อไปปนี้เพื่อติตตั้งใช้งาน (ใช้ Python 3 และคำสั่ง pip3)
$ sudo apt-get install python3-tk
$ pip3 install thonny
และเรียกใช้ โดยทำคำสั่ง
$ thonny &
เริ่มต้นการใช้งานโดยเปิดโปรแกรม Thonny IDE แล้ว ให้ทำเมนูคำสั่ง Run > Select Interpreter เลือก MicroPython (generic) และเลือกพอร์ต (Port) ที่กำลังเชื่อมต่อกับบอร์ด STM32 ในขณะใช้งาน
เมื่อเชื่อมต่อได้แล้ว จะมองเห็นข้อความในบริเวณ Shell (REPL) ในรูปภาพตัวอย่างแสดงให้เห็นว่า สามารถเชื่อมต่อกับไมโครไพธอน (เช่น เวอร์ชัน v1.12) สำหรับบอร์ด STM32F411CE ได้แล้ว ผู้ใช้สามารถลองพิมพ์และรันคำสั่งของไมโครไพธอนผ่าน REPL Shell (>>>)
ถ้าต้องการเขียนโค้ด แนะนำให้สร้างและบันทึกเป็นไฟล์ใหม่ ซึ่งมีสองตัวเลือกคือ เก็บลงในคอมพิวเตอร์ของผู้ใช้ หรือเก็บลงใน Flash Drive ของบอร์ด STM32
ถ้าจะลองรันโค้ดที่เปิดอยู่ในบริเวณ Code Editor ก็ให้กดปุ่ม Run หรือถ้าจะหยุดการทำงานของโค้ดที่กำลังรันอยู่ ก็ให้ปุ่ม Ctrl+C หรือถ้าจะรีเซตบอร์ด (Soft Reboot) ก็ให้กดปุ่ม Ctrl+D
ข้อมูลต่าง ๆ เกี่ยวกับบอร์ด WeAct Studio Mini-F4 (STM32F4x1) สามารถศึกษาได้จาก URL ต่อไปนี้
ตัวอย่างโค้ดแรกนี้ สาธิตการทำงาน LED ที่ต่อกับขา PC13 ของ STM32F411CE กระพริบได้ โดยเว้นระยะเวลา 100 มิลลิวินาที (msec)
ถ้าต้องการหยุดการทำงานของโค้ด ให้กดปุ่ม Ctrl+C เมื่อใช้ Thonny IDE ในการเขียนและรันโค้ดตัวอย่าง
ภายใต้ /flash ซึ่งเป็น Flash Filesystem ของไมโครไพธอน เราจะเห็นไฟล์ชื่อ boot.py และ main.py เมื่อบอร์ดถูกรีเซตและเริ่มทำงาน จะมีการทำคำสั่งต่าง ๆ ในไฟล์ boot.py
ถ้าในไฟล์ boot.py มีคำสั่ง pyb.main('main.py')
จึงจะรันโค้ดในไฟล์ main.py โดยอัตโนมัติ
โค้ดตัวอย่างนี้ สาธิตการใช้คำสั่งจากคลาส pyb.Switch
และ pyb.LED
สำหรับปุ่มกด (Push Button หรือ KEY Switch) และ LED (Blue) ที่มีอยู่บนบอร์ด
หลักการทำงานคือ ให้มีการทำให้ LED กระพริบด้วยอัตราคงที่ (ให้สลับสถานะลอจิก สำหรับเอาต์พุต ทุก ๆ 500 มิลลิวินาที) และทำขั้นตอนตรวจสอบค่า อินพุตจากปุ่มกด ถ้าอ่านค่าได้ True
หมายถึง มีการกดปุ่มค้างไว้ในขณะนั้น ให้จบการทำงานของโปรแกรม
คำสั่ง time.ticks_ms()
ใช้อ่านค่าเวลาของระบบ เป็นเลขจำนวนเต็มบวก และมีหน่วยเป็นมิลลิวินาที
คำสั่ง time.ticks_diff()
ใช้ในการคำนวณผลต่างหรือช่วงเวลา (Time Interval) ระหว่างการบันทึกเวลา 2 ครั้ง (เวลาล่าสุดกับเวลาก่อนหน้านั้น) และมีหน่วยเป็นมิลลิวินาที
หลักการทำงานของโค้ดตัวอย่างนี้คือ ถ้ากดปุ่มแล้วปล่อย จะทำให้เกิดการสลับสถานะลอจิกของ LED หนึ่งครั้ง โดยมีการตรวจสอบสถานะของปุ่มกดโดยอัตโนมัติ และมีการกำหนดฟังก์ชันสำหรับ Callback ซึ่งจะถูกเรียกให้ทำงาน
เมื่อมีเหตุการณ์ที่เกิดขึ้น (Event-triggered) โดยการกดปุ่มแล้วปล่อยในแต่ละครั้ง ในกรณีนี้คือ จะส่งผลทำให้ LED สลับสถานะ แต่ถ้ากดปุ่มค้างไว้สัก 2–3 วินาที จะทำให้จบการทำงานของโปรแกรม หรือถ้ารันโค้ดผ่าน REPL และกดปุ่ม Ctrl+C จะทำให้จบการทำงานของโปรแกรมเช่นกัน
ตัวอย่างถัดไปสาธิตการใช้งาน Hardware Timer ของ STM32F4 จากคลาส pyb.Timer
โดยสามารถเลือกใช้ Timer จากหมายเลข TIM1..TIM11 ที่มีขนาด 16 บิต (ยกเว้น TIM2 และ TIM5 ที่มีขนาด 32 บิต) และมีตัวหารความถี่ (Frequency Divider) ขนาด 16 บิต
ในตัวอย่างนี้ ความถี่ของการนับ (เลือกโหมดการนับขึ้น pyb.Timer.UP
) ได้ถูกตั้งค่าให้เท่ากับ 10 Hz และมีการกำหนดฟังก์ชันให้ทำงานในรูปแบบที่เรียกว่า Callback Function
เมื่อนับได้ครบหนึ่งรอบหรือหนึ่งคาบ จะมีการเรียกฟังก์ชันดังกล่าวให้ทำงานโดยอัตโนมัติ ในกรณีคือ การทำให้ LED สลับสถานะลอจิกหนึ่งครั้ง ดังนั้นเราจะเห็น LED กระพริบในอัตราคงที่
ตัวอย่างถัดไปสาธิตการใช้งาน Software Timer (หรือเรียกว่า Virtual Timer สำหรับไมโครไพธอน) จากคลาส machine.Timer
โดยใช้ FreeRTOS เป็นตัวจัดการเชิงเวลา และจะต้องกำหนดหมายเลข Timer ID ให้เท่ากับ -1
ในตัวอย่างนี้ได้ตั้งค่าให้มีคาบการนับ เท่ากับ 500 มิลลิวินาที เมื่อตัวนับเริ่มนับขึ้นจาก 0 จนครบเวลาหนึ่งคาบของการนับ (Count Overflow Event) จะมีการเรียกฟังก์ชันสำหรับ Callback และจะเกิดซ้ำไปเรื่อย ๆ (Periodic Mode)
ตัวอย่างถัดไปเป็นการใช้งาน Software Timer (ไทม์เมอร์แบบซอฟต์แวร์) จากคลาส machine.Timer
แต่เลือกใช้โหมด One Shot แทน Periodic ซึ่งหมายความว่า ถ้าครบคาบเวลา จะมีการเรียกฟังก์ชันสำหรับ Callback หนึ่งครั้ง และทำเพียงครั้งเดียว (ไม่ทำซ้ำ)
แต่ให้สังเกตว่า การเปิดใช้งาน Software Timer ในกรณีนี้ จะเกิดขึ้นเมื่อมีการกดปุ่มบนบอร์ดหนึ่งครั้ง เมื่อปุ่มถูกกดจะทำให้ฟังก์ชัน start_timer()
ทำงาน แล้วเปิดใช้งานไทม์เมอร์ในโหมด One Shot และจะต้องรอให้ผ่านไปครบหนึ่งคาบก่อน (ซึ่งในกรณีคือ 1000 มิลลิวินาที) จึงจะมีการเรียกฟังก์ชัน led_pulses()
ให้ทำงานในลำดับถัดไป เมื่อทำให้ LED กระพริบไปจนครบ 10 ครั้ง แล้วจึงจบการทำงานของโปรแกรม
ตัวอย่างถัดไปสาธิตการเปิดใช้งาน Hardware Timer ของ STM32F4 จากคลาส pyb.Timer
เพื่อสร้างสัญญาณ PWM (Pulse Width Modulation) ที่จะต้องกำหนดความถี่ (Frequency) ในหน่วยเป็น Hz และความกว้างของพัลส์ (Pulse Width) ในหน่วยเป็นไมโครวินาที (usec) สำหรับช่วงที่ลอจิกของสัญญาณเป็น High (ซึ่งหมายถึง การกำหนดค่า Duty Cycle ของสัญญาณ PWM)
การใช้งาน Hardware Timer เพื่อสร้างสัญญาณ PWM เป็นเอาต์พุตได้ จะต้องเลือกจาก TIM1..TIM11 และเลือกช่องสัญญาณ (Channel) ให้ถูกต้องได้ ในตัวอย่างนี้ เราได้เลือกใช้ TIM4 (ขนาด 16 บิต) ที่มีช่อง CH1 .. CH4 ให้เลือกใช้ได้ ถ้าเลือก TIM4_CH3 จะตรงกับขา PB8 แต่ถ้าเลือก TIM4_CH4 จะตรงกับขา PB9 เป็นต้น (ดูแผนผัง PinOut ของบอร์ด)
ในตัวอย่างนี้ ได้เลือกใช้ TIM4_CH3 สำหรับขา PB8 ซึ่งจะต้องนำไปต่อกับวงจร LED ภายนอก มีการตั้งความถี่ให้เท่ากับ 5 Hz (มีคาบเท่ากับ 200 มิลลิวินาที) และค่า Pulse Width ให้เท่ากับครึ่งหนึ่งของคาบเวลา หรือจะได้ค่า Duty Cycle เท่ากับ 50%
คำสั่งที่เกี่ยวข้องกับ pyb.Timer
เช่น คำสั่ง freq()
จะให้ค่าความถี่ที่ได้ตั้งค่าไว้ใช้ (มีหน่วยเป็น Hz) คำสั่ง prescaler()
จะให้ตัวเลขสำหรับตัวหารความถี่ (Prescaler) และคำสั่ง period()
จะให้ตัวเลขเป็นคาบเวลาของตัวนับ
ถ้าลองรันโค้ดตัวอย่างนี้ เราจะได้ข้อความเอาต์พุตและตัวเลขดังนี้
CPU freq. [Hz]: 96000000
AHB freq. [Hz]: 96000000
APB1 freq. [Hz]: 24000000
APB2 freq. [Hz]: 48000000
prescaler : 624
frequency : 5 [Hz]
source freq.: 48000000 [Hz]
period : 15359 [us]
pulse width : 7679 [us]
จากข้อความเอาต์พุต มีความหมายดังนี้
ความถี่ของซีพียู (CPU) หรือ SysClk เท่ากับ 96 MHz
ความถี่ของการอินเทอร์เฟสด้วยบัส AHB เท่ากับ 96 MHz = SysClk/1
ความถี่ของการอินเทอร์เฟสด้วยบัส APB1 จะได้ 24 MHz = SysClk/4
ความถี่ของการอินเทอร์เฟสด้วยบัส APB2 จะได้ 48 MHz = SysClk/2
ความถี่ของ Timer (TIM4) ได้ตั้งค่าให้นับด้วยความถี่เท่ากับ 5 Hz
ตัวหารความถี่ (Prescaler) เท่ากับ 624 และคาบ (Period) เท่ากับ 15359
จากตัวเลขเหล่านี้ เราสามารถระบุความสัมพันธ์ได้ดังนี้
หรือ คำนวณเป็นตัวเลขได้ดังนี้
ข้อสังเกต: วงจรไทม์เมอร์ TIM4 ภายใน STM32F411CE เชื่อมต่อโดยใช้บัส APB1 ที่มีความถี่ 24 MHz (= 96MHz /4) แต่เนื่องจากว่า APB1 Prescaler=4 ซึ่งมากกว่า 1 จึงมีการเพิ่มความถี่เป็น 2 เท่า สำหรับใช้เป็นความถี่ของตัวนับ (APB Clock Timers) และได้ความถี่เท่ากับ 48 MHz (รายละเอียดศึกษาได้จาก Clock Tree ในเอกสาร Reference Manual) และไฟล์ timer.c
ของ Micropython สำหรับ STM32 port)
ถ้าลองเปลี่ยนจาก TIM4 เป็น TIM1 (และใช้ช่อง CH3 ซึ่งตรงกับขา PA10) ก็สามารถทำงานได้เช่นกัน แต่มีความแตกต่างคือ TIM1 เชื่อมต่อกับบัส APB2 ที่ใช้ความถี่ 48MHz ความถี่ของตัวนับ (เป็น 2 เท่า) จะเท่ากับ 96 MHz และถ้ากำหนดความถี่ให้ได้ 5 Hz เหมือนเดิม จะได้ค่าสำหรับ Period ในกรณีนี้เท่ากับ 30719
ตัวอย่างนี้ สาธิตการทำให้ LED จำนวน 2 ดวง (ที่นำมาต่อวงจรเพิ่มบนเบรดบอร์ด) กระพริบได้ด้วยอัตราคงที่ โดยใช้ Hardware Timer ที่ทำงานในโหมด Output Compare (OC) และกำหนดให้ขา I/O สำหรับ OC Output สลับสถานะได้โดยอัตโนมัติ เมื่อตัวนับมีค่าเท่าค่าเปรียบเทียบที่ได้กำหนดไว้ (Compare Value)
ในตัวอย่างนี้ เราได้เลือกใช้ Timer 3 (TIM3) และช่องสัญญาณ 1 และ 2 (T3_CH1 และ T3_CH2) ซึ่งตรงกับขา PB4 (LED1) และ PB5 (LED2) ตามลำดับ ความถี่ของ TIM3 เท่ากับ 2 Hz และจะทำให้ LED ทั้งสองดวง สลับสถานะทุก ๆ 500 มิลลิวินาที แต่ช่วงเวลาที่เกิดการสลับสถานะจะไม่พร้อมกัน
ตัวอย่างถัดไป สาธิตการใช้ Hardware Timer จากคลาส pyb.Timer
ในโหมด PWM (เลือกใช้ TIM4 ช่องสัญญาณ CH3) ตั้งค่าความถี่ให้เท่ากับ 1000 Hz และปรับค่า Duty Cycle ให้เปลี่ยนแปลงได้โดยใช้ค่าตัวเลขที่คำนวณเก็บไว้ในอาร์เรย์ เพื่อใช้กำหนดความกว้างของพัลส์
การตั้งค่าความถี่ในตัวอย่างนี้ เราไม่ได้กำหนดค่าโดยตรง แต่ใช้อีกวิธีหนึ่งคือ กำหนดค่า Prescaler ให้เท่ากับ 47 และค่า Period ให้เท่ากับ 999
หรือ คำนวณเป็นตัวเลขได้ดังนี้
สัญญาณ PWM ที่ได้ (ขา PB9) จะถูกนำไปใช้ขับวงจร LED และจะเห็นได้ว่า ความสว่างของ LED เปลี่ยนแปลงตามค่า Duty Cycle ของสัญญาณ
ตัวอย่างนี้สาธิตการทำงาน LED จำนวน 3 ดวง กระพริบได้ด้วยอัตราที่ไม่เท่ากัน (เช่น 1 Hz, 2 Hz และ 4 Hz เป็นต้น) โดยใช้ Software Timer เป็นตัวช่วยดำเนินการ
วงจร LED ที่นำมาต่อเพิ่มบนเบรดบอร์ด จำนวน 3 ชุด (ต่อที่ขา PB7, PB8 และ PB9 ตามลำดับ) ทำงานแบบ Active-Low ซึ่งหมายความว่า ถ้าให้เอาต์พุตเป็น 0 จะทำให้ LED อยู่ในสถานะ ON แต่ถ้าเป็น 1 จะได้สถานะเป็น OFF
ตัวอย่างนี้สาธิตการเขียนโค้ดเพื่อตรวจสอบการกดปุ่มภายนอก โดยใช้หลักการทำงานของไมโครคอนโทรลเลอร์ที่เรียกว่า อินเทอร์รัพท์สำหรับขา GPIO (หรือเรียกที่ว่า External Interrupt) และใช้คลาส pyb.ExtInt
มีการจำแนกเหตุการณ์ได้เป็น 3 กรณีคือ ขอบขาขึ้น (Rising Edge) ขอบขาลง (Falling Edge) และเป็นได้ทั้งขอบขาขึ้นและขาลง
ในตัวอย่างนี้ เราเลือกใช้เฉพาะขอบขาลง และใช้ขา PA0 ที่ต่อกับวงจรปุ่มกดภายนอก (ทำงานแบบ Active-Low) เป็นอินพุต ทุกครั้งที่เกิดเหตุการณ์ขอบขาลงที่สัญญาณอินพุต จะมีการเรียกฟังก์ชันสำหรับ Callback ซึ่งจะทำให้ตัวแปร clicked
มีค่าเป็น True
และปิดการทำงานของอินเทอร์รัพท์ดังกล่าวชั่วคราว
ค่าของตัวแปร clicked
จะถูกตรวจสอบในเงื่อนไขสำหรับการทำซ้ำ ถ้ามีค่าเป็นจริง ก็ให้เพิ่มค่าของตัวนับและแสดงข้อความเอาต์พุต จากนั้นกำหนดให้ค่าตัวแปร clicked
เป็น False
และเปิดการทำงานของอินเทอร์รัพท์อีกครั้ง
ข้อสังเกต: การต่อวงจรปุ่มกด เมื่อกดปุ่มแล้วปล่อย อาจเกิดการกระเด้งของปุ่ม (Bouncing) ทำให้เกิดขอบขาขึ้นหรือขาลงที่สัญญาณอินพุตมากกว่าหนึ่งครั้งได้ วิธีแก้ไขปัญหานี้อย่างง่ายในเบื้องต้นคือ เราสามารถเลือกใช้ตัวเก็บประจุ เช่น 0.1uF มาต่อคร่อมที่ขาสัญญาณกับ GND
ตัวอย่างถัดไป สาธิตการประยุกต์ใช้งานอินเทอร์รัพท์ภายนอกที่ขาอินพุต 2 ขา คือ การนำไปต่อกับโมดูลที่เรียกว่า Incremental Rotary Encoder โดยใช้สัญญาณ 2 เส้นคือ A, B (หรือบางที ก็ตั้งชื่อว่า CLK, DATA) ตามลำดับ
เมื่อมีการเปลี่ยนตำแหน่งเชิงมุมของแกนหมุนที่ตัวโมดูล จะทำให้เกิดสัญญาณพัลส์ที่ขา A, B โดยมีเฟสต่างกัน 90 องศา (Phase Shift) ความกว้างของพัลส์ขึ้นอยู่กับอัตราความเร็วในการหมุน และมีทิศทางการหมุนได้สองทิศทาง (หมุนทวนหรือหมุนตามเข็มนาฬิกา)
โมดูลอินพุตประเภทนี้ มีการนำมาใช้เป็นตัวเพิ่มหรือลดค่าของตัวนับหรือระบุการเปลี่ยนตำแหน่ง เช่น การปรับเพิ่มหรือลดระดับเสียง การเปลี่ยนช่องตัวเลข หรือการปรับระดับความสว่างของแสง หรือใช้สำหรับวัดความเร็วเชิงมุมของมอเตอร์ เป็นต้น
ในการตรวจสอบการเปลี่ยนแปลงที่สัญญาณอินพุตทั้งสอง เราสามารถเปิดใช้งานอินเทอร์รัพท์ภายนอกได้ โดยเลือกชนิดของขอบเหตุการณ์เป็นทั้งแบบ Rising และ Falling Edge
ในตัวอย่างนี้ ได้เลือกใช้ขา PB4 และ PB5 ที่นำไปต่อกับโมดูล Rotary Encoder เมื่อมีการเปลี่ยนขอบสัญญาณใด ๆ ที่ขาทั้งสอง จะมีการเรียกฟังก์ชันสำหรับ Callback ที่เกี่ยวข้องกับแต่ละขา และจะตรวจสอบว่า จะต้องเพิ่มหรือลดค่าของตัวนับ (ใช้ตัวแปรชื่อ cnt
)
การหมุนเชิงมุมไปหนึ่งตำแหน่ง จะทำให้ตัวนับ เพิ่มขึ้นหรือลดลง ครั้งละ 4 ดังนั้นค่าของตัวนับจะถูกหารด้วย 4 เพื่อใช้เป็นค่าของตำแหน่ง (pos
) นอกจากนั้นยังมีการกำหนดค่าต่ำสุดและสูงสุดไว้สำหรับค่าของตัวนับ
ตัวอย่างถัดไปสาธิตการใช้ Hardware Timer ในโหมดการนับ โดยใช้สัญญาณอินพุตแบบ Quadrature Encoder เหมือนในกรณีของโมดูล Incremental Rotary Encoder ซึ่งมี 2 ช่องสัญญาณ
ทุกครั้งมีการเปลี่ยนแปลงที่ขาอินพุต จะมีการเพิ่มหรือลดค่าของตัวนับโดยอัตโนมัติ ซึ่งขึ้นอยู่กับทิศทางการหมุน ตัวนับภายในจะมีค่าเพิ่มขึ้นหรือลดลงครั้งละ 4 เมื่อหมุนไปหนึ่งตำแหน่ง ดังนั้นเราจึงหารด้วย 4 แล้วนำผลลัพธ์ที่ได้มาใช้ระบุตำแหน่ง (Position)
นอกจากนั้นยังมีการกำหนดช่วงของตำแหน่งในตัวอย่างนี้ ให้อยู่ในช่วง 0..99 และถ้านับเกิน จะเกิด Rollover โดยอัตโนมัติ เช่น ถ้านับไปถึง 0 ถัดไปจะเป็น 99 หรือในทางตรงข้าม ถ้านับถึง 99 แล้ว ถัดไปคือ 0
เนื่องจากเราได้เลือกใช้ขา PB4 และ PB5 ถ้าจะใช้งานร่วมกับ Timer ก็จะตรงกับ TIM3 และช่อง 1 และ 2 (TIM3_CH1 และ TIM3_CH2)
วงจร Hardware Timer ของ STM32 สามารถทำงานในโหมดที่เรียกว่า Input Capture (IC) ตัวนับจะทำงานด้วยความถี่คงที่ เมื่อมีเหตุการณ์ เช่น ขอบขาขึ้นหรือขาลง ตามที่กำหนดไว้ จะมีการอ่านค่าของตัวนับในขณะนั้น และนำไปเก็บใส่ลงในรีจิสเตอร์ที่เกี่ยวข้อง (Input Capture Register) ด้วยหลักการทำงานในลักษณะ เราสามารถนำมาใช้วัดความกว้างของสัญญาณพัลส์ วัดคาบของสัญญาณแบบมีคาบ เป็นต้น
ในตัวอย่างนี้ เราจะสร้างสัญญาณ PWM สำหรับ R/C Servo ที่ขา PB4 โดยใช้ Timer 3 ช่องหมายเลข 1 (TIM3_CH1) ให้มีความถี่ 50 Hz และมีความกว้างของพัลส์อยู่ในช่วง 1000 ถึง 2000 ไมโครวินาที
ในการทดสอบการทำงานของโค้ด ขา PB4 จะถูกเชื่อมต่อทางไฟฟ้าด้วยสายไฟภายนอก (Jumper Wire) กับขา PB3 เป็นอินพุตสำหรับ Timer 2 (TIM2 มีขนาด 32 บิต) ช่องหมายเลข 2 (TIM2_CH2) ที่ทำงานในโหมด Input Capture ตัวนับ TIM2 จะทำงานด้วยความถี่ 1 MHz (นับขึ้นทุก ๆ 1 ไมโครวินาที)
ในตัวอย่างนี้ เราต้องการจะวัดความกว้างของพัลส์ช่วงที่เป็น High ในหน่วยเป็นไมโครวินาที ทุก ๆ ครั้งที่เกิดขอบขาขึ้น หรือขาลง เราจะอ่านค่าตัวนับที่ถูกบันทึกไว้ตอนเกิดเหตุการณ์ดังกล่าว และนำมาคำนวณหาผลต่างซึ่งจะได้เป็นความกว้างของสัญญาณพัลส์ (Pulse Width)
โมดูล DHT22 (AM2302) ที่ผลิตโดยบริษัท Aosong Electronics (China) เป็นอุปกรณ์ประเภทเซ็นเซอร์สำหรับวัดค่าอุณหภูมิ (Temperature) และความชื้นสัมพัทธ์ (Relative Humidity) ในอากาศ และมีราคาไม่แพง จึงเหมาะสำหรับนำมาฝึกเขียนโปรแกรมไมโครคอนโทรลเลอร์เพื่อการเชื่อมต่อและอ่านข้อมูล อุปกรณ์นี้ใช้เพียงสัญญาณข้อมูลเพียง 1 เส้น แบบสองทิศทาง (Bidirectional Digital I/O)
ไมโครไพธอนสำหรับ STM32 ได้ร่วมไลบรารี dht
มาให้แล้ว สามารถใช้คำสั่งที่เกี่ยวข้องเพื่ออ่านค่าจากโมดูล DHT22 ได้สะดวก
โค้ดตัวอย่างนี้เลือกใช้ขา B5 สำหรับเชื่อมต่อกับขา DATA ของโมดูล DHT22 (ใช้แรงดันไฟเลี้ยง 3.3V) และที่ขา DATA จะต้องมีตัวต้านทานแบบ Pull up เช่น 4.7k โอห์ม ต่อไปยัง VCC
ตัวอย่างอย่างถัดไปสาธิตการอ่านค่าจากโมดูลเซ็นเซอร์สำหรับวัดอุณหภูมิ โดยใช้ไอซี DS18B20 (Datasheet) ของบริษัท MAXIM Integrated และใช้รูปแบบการเชื่อมต่อและสื่อสารข้อมูลที่เรียกว่า OneWire
การต่อใช้งานไอซีหรือโมดูล DS18B20 ให้ใช้แรงดันไฟเลี้ยง +3.3V และที่ขา DATA (DQ) ของโมดูลนี้ ให้ต่อตัวต้านทาน 4.7k หรือ 10k แบบ Pull-up ไปยังขา VCC (+3.3V)
สำหรับการเขียนโค้ด ผู้พัฒนาไมโครไพธอนได้จัดทำไลบรารีสำหรับ DS18B20 ไว้ให้ลองใช้งานแล้ว มีอยู่ 2 ไฟล์ที่จะต้องใช้ร่วมกัน คือ onewire.py และ ds18x20.py ดังนั้นให้ดาวน์โหลดไฟล์ทั้งสองและนำไปใส่ลงใน MicroPython Flash Drive ของบอร์ด STM32 จึงจะสามารถทดลองรันโค้ดตัวอย่างต่อไปนี้ได้
ขั้นตอนการทำงานของโค้ดตัวอย่างเริ่มต้นด้วยการเปิดใช้งานขา B5 เป็นขา DATA สำหรับ OneWire และใช้กับโมดูลเซ็นเซอร์ DS18B20 (ใช้คลาสชื่อ OneWire
และ DS18X20
ตามลำดับ) จากนั้นมีการสแกนหรือตรวจดูว่า พบอุปกรณ์ DS18B20 หรือไม่ (อุปกรณ์แต่ละตัวจะมีหมายเลขที่ไม่ซ้ำกัน เรียกว่า ROM ID หรือ Serial Code มีขนาด 64 บิต) ถ้าพบว่า มีอย่างน้อย 1 ตัว ก็จะทำการอ่านค่าอุณหภูมิ แลัวจะได้ค่าตัวเลขที่มีหน่วยเป็นเซลเซียส (Celsius) มีความละเอียด 0.5°C แสดงผลเป็นข้อความเอาต์พุต
โค้ดตัวอย่างนี้สาธิตการอ่านค่าจากโมดูลเซ็นเซอร์แสงแบบดิจิทัล (Digital Ambient Light Sensor) โดยให้ค่าความสว่าง (ความเข้มแสง) เป็นข้อมูลตัวเลขจำนวนเต็มขนาด 16 บิต และมีหน่วยเป็น "ลักซ์" (Lux)
ไมโครคอนโทรลเลอร์ STM32 จะทำหน้าที่เป็นอุปกรณ์ I2C Master เพื่อสื่อสารกับโมดูล BH1750 ที่เป็นอุปกรณ์ I2C Slave ผ่านทางบัส I2C
ในการสื่อสารข้อมูลด้วบัส I2C จะต้องระบุแอดเดรส (I2C address) ในการสื่อสารข้อมูลกัน โมดูล BH1750 มีแอดเดรสที่เลือกใช้ได้ 2 ค่าคือ 0x23 และ 0x5C
การเขียนโค้ดไมโครไพธอนเพื่อใช้งานบัส I2C นั้น ก็ใช้คำสั่งในกลุ่ม machine.I2C
เช่น คำสั่ง writeto()
และ readfrom()
เพื่อเขียนหรืออ่านข้อมูลจากอุปกรณ์ I2C Slave เป็นต้น
การทำงานของโค้ดตัวอย่าง เริ่มต้นด้วยการเปิดใช้งาน I2C1 ซึ่งเป็น I2C Bus หมายเลข 1 ของ STM32F411CE (หรือจะลองเปลี่ยนไปใช้ I2C2 แทนก็ได้)
I2C1: SCL = PB6 pin, SDA = PB7 pin
I2C2: SCL = PB10 pin, SDA = PB3 pin
จากนั้นจึงเริ่มสแกนหาอุปกรณ์ที่เชื่อมต่อกับบัส I2C และดูว่า มีแอดเดรสอยู่ในรายการของโมดูล BH1750 หรือไม่ ถ้าใช่ ก็ให้เขียนข้อมูลไปยังอุปกรณ์ดังกล่าวเพื่อกำหนดค่าเริ่มต้นในการใช้งาน และหลังจากนั้น จึงเป็นการอ่านค่าจากโมดูล BH1750 แล้วแสดงค่าที่ได้เป็นข้อความโดยใช้คำสั่ง print()
ตัวอย่างถัดไปสาธิตการเขียนโค้ดเพื่อแสดงข้อความบนโมดูลแสดงผลแบบ OLED ที่ใช้ชิป SSD1306 Driver และเชื่อมต่อผ่านทางบัส I2C
โมดูล SSD1306 OLED สามารถนำมาใช้สำหรับแสดงผลแบบกราฟิก Monochrome มีขนาดเล็ก เช่น 128x64, 128x32, 96x32 หรือ 64x48 พิกเซล เป็นต้น และเชื่อมต่อกับไมโครคอนโทรลเลอร์ได้ในสองรูปแบบคือ ใช้บัส I2C หรือ SPI ถ้าใช้ในโหมด I2C อุปกรณ์นี้มีแอดเดรสเท่ากับ 0x3C
ในการเขียนโค้ดไมโครไพธอน ก็ได้มีการพัฒนาไลบรารีสำหรับโมดูลนี้ไว้แล้ว สามารถดาวน์โหลดไฟล์ ssd1306.py จาก Github ของ MicroPython ได้ แล้วนำไปใส่ลงใน MicroPython Flash Drive ซึ่งก็ทำได้ง่าย โดยสำเนำไฟล์ดังกล่าวไปใส่ลงในไดร์ฟชื่อ STM32F411CE
ในโค้ดตัวอย่าง จะเห็นได้ว่า ได้เลือกใช้บัส I2C1 ของ STM32F411CE เพื่อใช้งานร่วมกับโมดูล OLED SSD1306 ที่มีขนาด 128x32 พิกเซล มีการแสดงข้อความ และเลื่อนข้อความไปทางซ้าย
โค้ดตัวอย่างต่อไปนี้ ใช้ทดสอบการทำงานสำหรับโมดูล 0.66" OLED shield for Wemos D1 Mini ที่มีขนาด 64x48 พิกเซล และใช้แอดเดรส 0x3C
โมดูลนี้ใช้แรงดันไฟเลี้ยง 3.3V และขา D1 และ D2 ของโมดูลดังกล่าว ตรงกับขา SCL และ SDA ตามลำดับ
โค้ดในตัวอย่างนี้สาธิตการอ่านค่าจากโมดูลเซ็นเซอร์ SHT31-DIS สำหรับวัดอุณหภูมิและความชื้นสัมพัทธ์ (ใช้ไอซีที่ผลิตโดยบริษัท SENSIRION) และแสดงค่าที่อ่านได้เป็นข้อความโดยใช้โมดูลแสดงผลแบบกราฟิก I2C OLED SSD1306 ทั้งสองอุปกรณ์นี้เชื่อมต่อแบบบัส I2C ( โมดูล SHT31-DIS มีแอดเดรสเท่ากับ 0x44
ในขณะที่โมดูล I2C OLED SSD1306 มีแอดเดรสตรงกับ 0x3C
)
โค้ดไมโครไพธอนที่จะต้องนำไปบันทึกลงในไฟล์ sht3x.py สำหรับ SHT31-DIS เพื่อใช้กับโค้ดตัวอย่างข้างบน
อุปกรณ์ RGB LED ที่ใช้ไอซี WS2812B หรือบางทีก็เรียกว่า NeoPixel ใช้สัญญาณอินพุตรับข้อมูลเพียง 1 เส้น สำหรับการเลื่อนบิตข้อมูลเข้าไปเพื่อใช้ในการกำหนดค่าสี RGB (Red, Green, Blue) อย่างละ 8 บิต หรือ 24 บิต ต่อ RGB หนึ่งตำแหน่ง (พิกเซล) ถ้ามี RGB LED หลายตำแหน่ง เช่น มีลักษณะเป็นแถบเรียงต่อกันแบบอาร์เรย์ (REB LED Strip) ก็จะส่งข้อมูลต่อกันไป
บริษัท WorldSemi ผู้ออกแบบและพัฒนาไอซี WS2812B ได้กำหนดวิธีการส่งข้อมูลหรือโพรโทคอล (Protocol) เอาไว้แล้ว ซึ่งจะต้องเป็นไปตามนั้น มิเช่นนั้นการรับข้อมูลอาจไม่ถูกต้อง หรือไม่ได้รับข้อมูล
อ้างอิงตามเอกสาร Datasheet การจำแนกข้อมูลบิตแต่ละบิต จะใช้ความกว้างของสัญญาณพัลส์ (Pulse) เป็นตัวกำหนด (หน่วยเป็นไมโครวินาที) ดังนี้
บิตที่มีค่าเป็น 1 (ช่วง High กว้างกว่าช่วง Low)
T1H: เริ่มต้นด้วยช่วง High กว้างประมาณ 0.8us และ
T1L: ตามด้วยช่วง Low กว้างประมาณ 0.45us
บิตที่มีค่าเป็น 0 (ช่วง Low กว้างกว่าช่วง High)
T0H: เริ่มต้นด้วยช่วงช่วง High กว้างประมาณ 0.4 usec และ
T0L: ตามด้วยช่วง Low กว้างประมาณ 0.85 usec
และมีความคลาดเคลื่อนได้ไม่เกิน +/- 150 usec
ลำดับของบิตในแต่ละไบต์ จะเป็นแบบ MSB First และลำดับข้อมูลไบต์ที่จะถูกส่งออกไป เป็นแบบ GRB Format คือ ไบต์แรกสำหรับสีเขียว (G) ไบต์ที่สองสำหรับสีแดง (R) และไบต์ที่สามสำหรับสีเขียว (B) เพื่อใช้กับ RGB LED ในตำแหน่งแรก
ในการส่งข้อมูลไบต์เหล่านี้ เราจะใช้วงจร SPI ที่มีให้เลือกใช้มากกว่าหนึ่งชุด (เช่น เลือกใช้ SPI Bus 1 หรือ SPI Bus 2) ของ STM32F411CE เป็นตัวดำเนินการ และข้อมูลจะถูกส่งออกไปผ่านขา SPI MOSI
การส่งข้อมูลโดยใช้ SPI จะต้องกำหนดความถี่ SCLK และเป็นตัวกำหนดความกว้างของเวลาสำหรับแต่ละบิตในหนึ่งไบต์ ถ้ามีข้อมูลหลายไบต์ ก็จะถูกส่งออกไปตามลำดับแบบต่อเนื่องจนกว่าจะครบ
เราจะใช้ข้อมูล 4 บิต สำหรับเป็นตัวกำหนดค่าบิต 0 หรือ 1 ของค่าสีแต่ละบิต ดังนี้
“1110” (ช่วง High กว้างกว่าช่วง Low) หมายถึง บิต 1
“1000” (ช่วง Low กว้างกว่าช่วง High) หมายถึง บิต 0
ถ้าเรามีค่าสี เช่น 3 ไบต์ หรือ 24 บิต (เรียกว่า Color Bits) ต่อหนึ่งตำแหน่ง เราจะต้องสร้างสัญญาณบิตสำหรับ SPI ที่ขา MOSI ทั้งหมด 24*4 = 96 บิต (เรียกว่า SPI Data Bits) หรือ 12 ไบต์
คำถามคือ แต่ละบิตที่ถูกส่งออกไปทาง SPI จะต้องมีความกว้างเท่าไหร่ ? ถ้าเราลองเลือกความถี่ SCK ให้เท่ากับ 3.2 MHz จะได้ความกว้างของบิตเท่ากับ 1/3.2MHz = 0.3125 usec (ไมโครวินาที) และจะได้ความกว้างดังนี้ (หน่วยเป็น usec)
“1110” (บิต 1): T1H=3*0.3125=0.9375 , T1L=0.3125
“1000” (บิต 0): T0H=0.3125, T0L=3*0.3125 = 0.9375
ซึ่งยังถือว่า ค่าเหล่านี้ยังอยู่ในช่วงที่รับได้ (เมื่อพิจารณา +/-150 usec แล้ว)
ลองมาดูโค้ดสาธิตการทำงาน โดยเราจะเลือกใช้บัส SPI หมายเลข 1 และจะต้องใช้ขา A7 ของ STM32F411CE เป็นขาเอาต์พุต นำไปต่อกับขา DIN ของโมดูล WS2812B เลือกใช้แบบ RGB LED Bar มีทั้งหมด 8 ดวง (8 ตำแหน่ง)
โค้ดไมโครไพธอนที่จะต้องนำไปบันทึกลงไฟล์ ws2812b.py เพื่อใช้ร่วมกับโค้ดตัวอย่างข้างบน
โค้ตัวอย่างนี้สาธิตการใช้งานโมดูล SSD1306 OLED ขนาด 128x64 พิกเซล แต่ใช้วิธีการเชื่อมต่อแบบ SPI และเชื่อมต่อขากับขา I/O ของ STM32 ดังนี้
SPI OLED STM32F411CEU6
GND GND
VCC 3.3V
D0 (SCLK) SCK1 / PA5
D1 (MOSI) MOSI1 / PA7
RST (/RESET) PB6
DC (DATA/CMD) PB7
CS (/Chip Select) PA4
หลักการทำงานของโค้ดมีดังนี้ เริ่มต้นด้วยการเลือกใช้ SPI Bus 1 (SPI1) ซึ่งมีขาที่เกี่ยวข้องได้แก่ SCK1, MOSI1, MISO1 มีการตั้งความถี่ของ SPI Bus Frequency ให้เท่ากับ 8 MHz และทำงานโหมด (0,0)
ถัดไปเป็นการใช้งานโมดูล SSD1306_SPI
จากไฟล์ ssd1306.py และให้ใช้ขาของ SPI1 (ใช้แค่ขา SCK1 และ MOSI1) และ I/O อีก 3 ขา สำหรับ RST, DC, CS
ข้อมูลที่จะถูกนำมาแสดงผลบนโมดูลกราฟิก OLED นั้น ได้จากการสุ่มตัวเลขในช่วง 0 ถึง 100 เก็บไว้ในอาร์เรย์ แล้วนำมาแสดงค่าในรูปของกราฟแท่ง
ในการทำงานแต่ละรอบของ while loop จะมีการลบข้อมูลออกหนึ่งค่าที่ตำแหน่งสุดท้ายของอาร์เรย์ สุ่มตัวเลขใหม่และใส่ลงในอาร์เรย์ในตำแหน่งแรก
โค้ดตัวอย่างนี้สาธิตการอ่านค่าจากโมดูลเซ็นเซอร์แสงแบบแอนะล็อก (CJMCU TEMT6000 Ambient Light Sensor Module) ที่มี 3 ขา คือ V (VCC), G (GND), S (SIGNAL)
ขา S เป็นขาสัญญาณเอาต์พุต-แอนะล็อกจากโมดูลดังกล่าว เชื่อมต่อกับขา PA1 ของ STM32 และมีการเปิดใช้งานวงจร ADC (Analog-to-Digtal Converter) ภายใน เพื่อแปลงค่าสัญญาณอินพุต (ระดับแรงดันไฟฟ้าในช่วง 0V ถึง 3.3V) ให้เป็นค่าเลขจำนวนเต็ม
ไมโครไพธอนมีคำสั่งที่เกี่ยวข้องกับ ADC ไว้ให้ใช้งานคือ machine.ADC โดยจะต้องสร้างออบเจกต์จากคลาสดังกล่าว และระบุชื่อขาที่จะใช้งาน (ในกรณีนี้คือ "A1"
) จากนั้นจึงใช้คำสั่ง read_u16()
เพื่ออ่านค่าอินพุตที่ขาดังกล่าว และจะได้เป็นเลขจำนวนเต็มในช่วง 0 ถึง 65535 ค่าที่อ่านได้จะถูกนำมาแสดงผลแบบกราฟแท่งบนจอ OLED และอัปเดตทุก ๆ 0.1 วินาที
เผยแพร่ภายใต้ลิขสิทธิ์ Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)