RPi Pico RP2040 Code Examples
โต้ดตัวอย่างสาธิตการใช้งานไมโครไพธอนร่วมกับบอร์ด Raspberry Pi Pico (RP2040) -- เริ่มต้นเขียนเมื่อวันที่ 12 กุมภาพันธ์ พ.ศ. 2564
ในการต่อวงจรเพื่อทดลองการทำงานของโค้ดตัวอย่างโดยใช้บอร์ดไมโครคอนโทรลเลอร์ RPi Pico ร่วมกับวงจรหรือโมดูลต่าง ๆ ควรมีความรู้พื้นฐานในการต่อวงจรและระมัดระวังเพื่อมิให้เกิดความเสียหายกับอุปกรณ์
โดยทั่วไปแล้วบอร์ด RPi Pico ใช้แรงดันไฟเลี้ยงจากพอร์ต USB ซึ่งจะได้ประมาณ 5V แต่ไม่เกิน 5.5V แต่ระดับแรงดันไฟฟ้าที่ขา I/O จะอยู่ที่ 3.3V เท่านั้น และไม่สามารถใช้งานได้กับวงจรที่มีระดับแรงดันไฟฟ้าสูงกว่า เช่น 5V (Not 5V tolerant)
การต่อวงจรที่ขา I/O ของบอร์ด ควรตรวจสอบจาก Pinout Diagram ของบอร์ดทุกครั้ง

รูป: RPi Pico - Pin Reference (Source: Adafruit)

รูป: RPi Pico PinMap
โค้ดตัวอย่างแรก สาธิตการทำให้ LED กระพริบได้ โดยเลือกใช้ LED บนบอร์ด Pico ซึ่งต่อวงจรอยู่กับขา GPIO-25 และสาธิตการใช้คลาส
machine.Pin
ที่เกี่ยวข้องกับขา GPIO ตัวแปร
led
อ้างอิงขา GPIO-25 Pin ที่เปิดใช้งานในทิศทางเอาต์พุต และใช้คำสั่ง led.value()
สำหรับอ่านสถานะลอจิกในขณะนั้น และกำหนดสถานะใหม่ได้เช่นกัน โดยทำคำสั่ง เช่น led.value(0)
หรือ led.value(1)
ถ้าต้องการจบการทำงานของโค้ดนี้เมื่อรันผ่านทาง MicroPython REPL ให้กดคีย์ Ctrl+C
import machine
import utime as time
# use the on-board LED (GP25 pin)
led = machine.Pin( 25, machine.Pin.OUT )
try:
while True:
state = not led.value()
led.value( state )
print('LED state:', state)
time.sleep(0.5)
except KeyboardInterrupt:
pass
led.value(0) # turn off LED
โค้ดตัวอย่างนี้ สาธิตการอ่านค่าจากขา GPIO ที่ต่อรับสัญญาณอินพุตจากวงจรปุ่มกด (Active-Low Push Button) โดยก ำหนดให้ขา GPIO-16 เป็นขาอินพุตและเปิดใช้งาน Internal Pull-Up สำหรับขาดังกล่าว และใช้ตัวแปร
button
ในการอ้างอิงขาดังกล่าว ในตัวอย่างนี้ เมื่อกดปุ่มจะอ่านค่าโดยใช้คำสั่ง button.value()
ได้เป็น 0 หรือลอจิก LOW และจบการทำงานของโปรแกรมimport utime as time
from machine import Pin
BTN_PIN = 16 # use GP16 for Button pin
button = Pin( BTN_PIN, Pin.IN, Pin.PULL_UP )
while True:
if not button.value():
print('pressed')
break
time.sleep(0.1)
หรือจะเปลี่ยนไปใช้วิธีการตรวจสอบอินเทอร์รัพท์ (Interrupt) ที่ขาอินพุตตามตัวอย่างต่อไปนี้ โดยการตรวจสอบดูว่า มีการเปลี่ยนแปลงสถานะที่ขาอินพุตหรือไม่ เหตุการณ์ที่จะทำให้เกิดอินเทอร์รัพท์ในกรณีนี้คือ เมื่อมีการเปลี่ยนจาก HIGH เป็น LOW หรือ Falling-Edge Transition
ตัวแปร
button
ใช้ในการอ้างอิงขา GPIO-16 (GP16) สำหรับวงจรปุ่มกด และการใช้คำสั่ง button.irq()
เป็นการเปิดใช้งานและกำหนดรูปแบบของอินเทอร์รัพท์ เช่น เลือกรูปแบบเป็น Pin.IRQ_FALLING
และระบุฟังก์ชันที่ใช้สำ หรับทำหน้าที่เป็น Callback (Interrupt Handler)import utime as time
from machine import Pin
BTN_PIN = 16 # use GP16 for Button pin
button = Pin( BTN_PIN, Pin.IN, Pin.PULL_UP )
done = False
def button_handler(pin):
global done
# disable external interrupt on the Button pin
button.irq( handler=None )
done = True
button.irq( trigger=Pin.IRQ_FALLING, handler=button_handler )
print('Press the button!')
while not done:
time.sleep(0.1)
ตัวอย่างนี้สาธิตการเปิดใช้งานอินเทอร์รัพท์ที่ขา GPIO-14 (GP14) ที่ต่อกับวงจรปุ่มกดบนเบรดบอร์ด เมื่อมีการกดปุ่มแล้วปล่อยแต่ละครั้ง จะทำให้ LED บนบอร์ดสลับสถานะหนึ่งครั้ง
import utime as time
from machine import Pin
BTN_PIN = 16
LED_PIN = 25
button = Pin( BTN_PIN, Pin.IN, Pin.PULL_UP )
led = Pin( LED_PIN, Pin.OUT )
led.low() # turn the LED off
def button_handler(pin):
global pressed
# disable external interrupt on the Button pin
button.irq( handler=None )
pressed = True
print( time.ticks_ms() ) # show time ticks (in msec)
pressed = False
button.irq( trigger=Pin.IRQ_FALLING, handler=button_handler )
print('Press the button!')
try:
while True:
if pressed:
while not button.value():
time.sleep(0.1)
led.toggle() # toggle LED output
button.irq( trigger=Pin.IRQ_FALLING, handler=button_handler )
pressed = False
time.sleep(0.1)
except KeyboardInterrupt:
pass
led.low() # turn off LED
ตัวอย่างนี้สาธิตการสร้างสัญญาณแบบ PWM (Pulse Width Modulation) และให้เป็นเอาต์พุตที่ขา GPIO-25 กำหนดความถี่ให้เท่ากับ 1000 Hz (1 kHz) และเริ่มต้นให้มีค่า Duty Cycle เป็น 0 การกำหนดค่า Duty Cycle สำหรับ PWM นั้น จะต้องเป็นเลขจำนวนเต็มขนาด 16 บิต อยู่ในช่วง 0..65535
โค้ดตัวอย่างนี้ จะเพิ่มค่า Duty Cycle จาก 0 และไปจนถึงค่าเท่ากับ 255*256 จากนั้นจะลดลงไปจนถึง 0 อีกครั้ง แล้วจะวนซ้ำในลักษณะนี้ไปเรื่อย ๆ
import utime as time
from machine import Pin, PWM
# use onboard LED and PWM output pin
pwm = PWM( Pin(25) )
# set PWM freq. to 1kHz
pwm.freq(1000)
# set counter to 0
cnt = 0
try:
while True:
if cnt < 256:
duty = cnt
else:
duty = 511-cnt
pwm.duty_u16( duty * 256 )
cnt = (cnt+1) % 512
time.sleep(0.01)
except KeyboardInterrupt:
pass
# set duty cycle to 0 (ns)
pwm.duty_ns(0)
# deinitialize the GPIO pin used for PWM output
pwm.deinit()
ถ้าทดสอบการทำงานของโค้ดตัวอย่างนี้กับบอร์ด RPi Pico จะเห็น LED บนบอร์ดค่อย ๆ สว่างขึ้นและดับลงซ้ำไปเรื่อย ๆ
ถ้าจะลองใช้โมดูล RGB LED ที่ใช้ขาสัญญาณควบคุมแบบ 3 ขา ก็สามารถทำได้เช่นกัน โค้ดตัวอย่างนี้สาธิตการใช้ขา GPIO-11, 12, 13 (GP11,GP12,GP13) เป็นขาสัญญาณเอาต์พุต นำไปต่อกับขาสัญญาณของโมดูล RGB LED (Active-High) เพื่อเปลี่ยนค่า Duty Cycle ของสัญญาณแต่ละช่องไปตามลำดับ
import utime as time
from machine import Pin, PWM
pwm_list = [ PWM( Pin(p) ) for p in [11,12,13] ]
for pwm in pwm_list:
pwm.freq(1000) # set freq. to 1kHz
pwm.duty_ns(0) # set duty cycle (high pulse) = 0ns
try:
while True:
time.sleep(0.1)
for pwm in pwm_list:
for cnt in range(512):
if cnt < 256:
duty = cnt
else:
duty = (511-cnt)
pwm.duty_u16( duty * 256 )
time.sleep(0.005)
except KeyboardInterrupt:
pass
for pwm in pwm_list:
pwm.duty_ns(0)
pwm.deinit()

รูป: ตัวอย่างการต่อวงจรบนเบรดบอร์ดโดยใช้โมดูล RGB LED
ตัวอย่างนี้สาธิตการใช้งาน Timer (ตอนนี้ใช้ได้เฉพาะ Software Timer และใช้ Timer id เท่ากับ -1) เพื่อทำคำสั่งหรือเรียกใช้ฟังก์ชันตามคาบเวลาหรือความถี่ที่ได้กำหนดไว้ ซึ่งเป็นเหตุการณ์ที่เกิดขึ้นซ้ำ (Periodic) เช่น การสลับสถานะเอาต์พุตของ LED ที่อัตรา 10 Hz
from machine import Pin, Timer
led = Pin( 25, Pin.OUT ) # use onboard LED
def tick(timer):
global led
led.toggle()
# create the hardware timer object
timer = Timer(-1)
# configure the timer object, 10Hz tick rate, periodic mode
timer.init( freq=10, mode=Timer.PERIODIC, callback=tick )
try:
while True:
pass
except KeyboardInterrupt:
pass
timer.deinit()
ตัวอย่างนี้สาธิตการใช้คำสั่ง
_thread.start_new_thread()
เพื่อสร้าง"เธรด" (Thread) ใหม่ให้ทำงานบน CPU Core อีกอันหนึ่ง โดยให้ทำหน้าที่สลับสถานะลอจิกของ LED ตามข่วงเวลาที่กำหนดไว้แต่มีข้อจำกัดคือ เนื่องจาก MicroPython สำหรับ RP2040 ไม่ได้ทำงานโดยใช้ RTOS หรือระบบปฏิบัติการเวลาจริง ดังนั้นการใช้งาน "เธรด" ใหม่ จ ะถูกกำหนดให้รันบน CPU Core 1 ในขณะที่โค้ดของเธรดหลัก (Main Thread) จะรันบน CPU Core 0
เนื่องจากมีการทำงานพร้อมกันโดยใช้ CPU Core ทั้งสองแกน และต้องมีการใช้วิธีการป้องกันที่เรียกว่า Mutex Lock โดยใช้คำสั่ง
_thread.allocate_lock()
เพื่อสร้าง Lock Object มาใช้งาน และอ้างอิงโดยใช้ตัวแปร lock
จากนั้นจึงใช้คำสั่ง lock.acquire()
และ lock.release()
ตามลำดับ ก่อนและหลังการใช้คำสั่งที่ต้องใช้ทรัพยากรของระบบที่อาจต้องใช้ร่วมกันระหว่างเธรดในระบบ เช ่น การใช้คำสั่ง print()
เพื่อส่งข้อความออกทาง REPL from machine import Pin
import utime as time
import _thread
lock = None
done = False
def led_task(led_pin, n, delay):
global done
lock.acquire()
print( 'LED Task: Thread ID=%d' % _thread.get_ident() )
lock.release()
led = Pin( led_pin, Pin.OUT )
try:
for i in range(2*n):
led.toggle()
time.sleep(delay)
except KeyboardInterrupt:
pass
done = True
lock.acquire()
print('Task done')
lock.release()
# create a lock object
lock = _thread.allocate_lock()
# create a new thread and run it on the second CPU core
# blink the LED on GPIO-25 pin 10 times with 500ms delay time
_thread.start_new_thread( led_task, (25, 10, 0.5) )
try:
lock.acquire()
print( 'Main: Thread ID=%d' %_thread.get_ident() )
lock.release()
while not done:
pass
except KeyboardInterrupt:
pass
finally:
lock.acquire()
print('Main thread done')
lock.release()
โค้ดตัวอย่างนี้สาธิตการใช้งานบัส I2C และเชื่อมต่อกับโมดูล OLED I2C Display (SSD1306 Driver) โดยใช้งานร่วมกับไลบรารี
ssd1306
ซึ่งสามารถดาวน์โหลดไฟล์ ssd1306.py
ได้จาก Github แล้วนำไปใส่ลงในไดรฟ์ของไมโครไพธอนเนื่องจาก RP2040 มีบัส I2C ให้ใช้งาน 2 ชุด ในตัวอย่างนี้ได้เลือกใช้ I2C1 และใช้ขา GPIO-14 และ GPIO 15 ซ ึ่งสามารถใช้เป็นขา I2C1_SDA และ I2C1_SCL ได้
ขนาดของโมดูล OLED I2C ได้เลือกมาลองใช้งาน มีขนาด 128 x 32 พิกเซล และมีแอดเดรสตรงกับ
0x3C
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
# use I2C1 and GPIO 14,15 for SDA and SCL pins
i2c = I2C(1, scl=Pin(15), sda=Pin(14), freq=400000)
dev_addr_list = i2c.scan() # scan I2C devices
for addr in dev_addr_list:
print('Found: 0x{:02x}'.format(addr) )
oled = SSD1306_I2C( 128, 32, i2c, addr=0x3c)
oled.fill(0)
oled.text("Raspberry Pi",5,5)
oled.text("Pico",5,15)
oled.show()
ตัวอย่างนี้เป็นการใช้งานบัส I2C โดยนำบอร์ด RP2040 ไปเชื่อมต่อกับโมดูล SSD1306 OLED Display และโมดูลเซ็นเซอร์แสง BH1750 ทั้งสองโมดูลนี้ใช้บัส I2C1 ร่วมกัน และใช้แรงดันไฟเลี้ยง +3.3V จากบอร์ด RP2040
แอดเดรสของโมดูล SSD1306 และ BH1750 ตรงกับ
0x3C
และ 0x23
ตามลำดับfrom machine import Pin, I2C
import utime as time
from ssd1306 import SSD1306_I2C
BH1750_ADDR = 0x23
SSD1306_ADDR = 0x3C
def bh1750_init( i2c, addr ):
try:
# power on the BH1750
i2c.writeto( addr, bytearray([0x01]) )
# reset the BH1750
i2c.writeto( addr, bytearray([0x07]) )
time.sleep_ms(200)
# set mode to 1.0x high-resolution,
# continuous measurement
i2c.writeto( addr, bytearray([0x10]) )
time.sleep_ms(150)
return True
except Exception:
return False
def bh1750_read( i2c, addr ):
try:
data = i2c.readfrom(addr, 2) # read two bytes
value = (data[0]<<8 | data[1])/(1.2)
return value
except Exception as ex:
print( 'BH1750 reading error:', ex )
return None
# use I2C1 and GPIO 14,15 for SDA and SCL pins
i2c = I2C( 1, scl=Pin(15), sda=Pin(14), freq=400000 )
#print( [hex(addr) for addr in i2c.scan()] )
oled = SSD1306_I2C( 128, 32, i2c, addr=SSD1306_ADDR )
oled.fill(0)
try:
bh1750_init( i2c, BH1750_ADDR )
while True:
# read sensor value
value = int( bh1750_read( i2c, BH1750_ADDR ) )
if value:
text = 'BH1750 (0x{:02x})'.format(BH1750_ADDR)
oled.fill(0)
oled.text(text,5,5)
text = '{:5d} Lux'.format(value)
oled.text(text,20,24)
oled.show()
time.sleep_ms(200)
except KeyboardInterrupt:
pass

รูป: โมดูล BH1750 และ SSD1306 OLED Display ที่ได้นำมาทดลองใช้งาน
ตัวอย่างการสร้างคลาสในภาษาไมโครไพธอนสำหรับ BH1750 แล้วนำมาใช้งาน โดยบันทึกลง ในไฟล์
bh1750.py
from utime import sleep_ms
class BH1750():
"""Micropython BH1750 ambient light sensor driver."""
PWR_OFF = 0x00
PWR_ON = 0x01
RESET = 0x07
CONT_LOWRES = 0x13
CONT_HIRES_1 = 0x10
CONT_HIRES_2 = 0x11
ONCE_HIRES_1 = 0x20
ONCE_HIRES_2 = 0x21
ONCE_LOWRES = 0x23
def __init__(self, bus, addr=0x23):
self.bus = bus
self.addr = addr
self.reset()
def reset(self): # reset sensor
self.on()
self.set_mode(self.RESET)
def off(self): # turn device off
self.set_mode(self.PWR_OFF)
def on(self): # turn device on
self.set_mode(self.PWR_ON)
def set_mode(self, mode): # set sensor mode
self.mode = mode
self.bus.writeto(self.addr, bytes([self.mode]))
def read(self): # read sensor value (luminance in Lux)
sleep_ms(24 if self.mode in (0x13, 0x23) else 180)
data = self.bus.readfrom(self.addr, 2)
factor = 2.0 if self.mode in (0x11, 0x21) else 1.0
return (data[0]<<8 | data[1])/(1.2 * factor)
และโค้ดสาธิตการใช้งานไฟล์
bh1750.py
from machine import Pin, I2C
import utime as time
from bh1750 import BH1750 # import from file bh1750.py
BH1750_ADDR = 0x23
# use I2C1 and GPIO 14,15 for SDA and SCL pins
i2c = I2C( 1, scl=Pin(15), sda=Pin(14), freq=400000 )
print( [hex(addr) for addr in i2c.scan()] )
try:
dev = BH1750(i2c, addr=BH1750_ADDR)
dev.on()
dev.set_mode( BH1750.CONT_HIRES_1 )
while True:
value = int(dev.read()) # read sensor value (luminance in Lux)
text = 'BH1750 (0x{:02x}): '.format(BH1750_ADDR)
text += '{:5d} Lux'.format(value)
print(text)
time.sleep_ms(200)
except KeyboardInterrupt:
pass
โค้ดตัวอย่างนี้สาธิตการเปิดใช้งาน Watchdog Timer (WDT) ซึ่งเป็นวงจรภายใน RP2040 และเมื่อเปิดการทำงานของ WDT แล้ว จะต้องมีการป้อนค่าให้ WDT (WDT feeding) ภายในระยะเวลาที่กำหนด มิฉะนั้นแล้ว วงจร WDT จะรีเซตการทำงานของไมโครไพธอน
ในตัวอย่างนี้ จะต้องมีการต่อวงจรปุ่มกดภายนอกที่ขา GPIO-16 ด้วย และเมื่อโค้ดนี้เริ่มทำงาน จะต้องมีการกดปุ่มค้างไว้ในช่วงเวลาดังกล่าว จึงจะเปิดการทำงานของ WDT
import utime as time
from machine import Pin, WDT
print('Press the button on GPIO-16 to enable WDT.')
button = Pin( 16, mode=Pin.IN, pull=Pin.PULL_UP )
time.sleep_ms(1000)
wdt = None
if button.value() == 0:
# enable WDT with timeout of 2000 msec
wdt = WDT(timeout=2000)
# Note that once the WDT is running the timeout cannot be
# changed and it cannot be stopped either.
if wdt is None:
print('WDT is disabled.')
try:
while wdt is not None:
# feed the WDT to prevent it from resetting the system.
print('feed WDT @{} ms'.format( time.ticks_ms() ) )
wdt.feed()
time.sleep(1.0)
except KeyboardInterrupt:
pass
โค้ดตัวอย่างนี้สาธิตการใช้คำสั่งของโมดูล
uos
เพื่อสอบถามข้อมูลเกี่ยวกับระบบและการใช้งานระบบไฟล์ในหน่วยความจำแฟลช (Flash File System) ของไมโครไพธอนimport uos as os
names = ['sysname', 'nodename', 'release', 'version', 'machine']
results = os.uname()
for name,value in zip(names,results):
print('{:<8s} = "{}"'.format(name, value))
# list all files
files = os.listdir()
print('Python Script File(s):')
for file in files:
if file.endswith('.py'):
print('->',file) # show only Python script file
info = os.statvfs('/')
fs_total = info[0]*info[2]
fs_free = info[0]*info[3]
fs_used = fs_total - fs_free
text = 'File System (total/free/used): {:,} / {:,} / {:,} bytes'
print( text.format(fs_total,fs_free,fs_used) )
ตัวอย่างข้อความเอาต์พุต
sysname = "rp2"
nodename = "rp2"
release = "1.13.0"
version = "v1.13-290-g556ae7914 on 2021-01-21 (GNU 10.2.0 MinSizeRel)"
machine = "Raspberry Pi Pico with RP2040"
Python Script File(s):
-> ssd1306.py
File System (total/free/used): 1,441,792 / 1,425,408 / 16,384 bytes
ตัวอย่างนี้สาธิตการใช้งานวงจร ADC (Analog-to-Digital Converter) ภายใน RP2040 เพื่ออ่านค่าจากเซ็นเซอร์วัดอุหภูมิที่อยู่ภายในชิป และต่อสัญญาณแบบแอนะล็อกเข้าช่องหมายเลข 4 ของวงจร ADC
from machine import ADC
import utime as time
sensor_temp = ADC(4) # use on-chip temperature sensor
conversion_factor = 3.3 / 65535 # 3.3V -> 16-bit value
while True:
reading = sensor_temp.read_u16() * conversion_factor
# Typical value: 0.706V at 27 degrees C
# with a slope of -1.721mV (0.001721) per degree.
temperature = 27 - (reading - 0.706)/0.001721
print('On-chip tempreature: {:.2f} deg.C'.format(temperature) )
time.sleep(2.0)
วงจร ADC ภายใน RP2040 มีช่องอินพุตทั้งหมด 5 ช่อง (ช่องหมายเลข 0 ถึง 4) และมีความละเอียดในการแปลงข้อมูลเท่ากับ 12 บิต (0..4095) แต่ไมโครไพธอนจะแปลงให้เป็นข้อมูลขนาด 16 บิต (0..65535)
ช่องอินพุต 3 ช่องแรกของ ADC บนบอร์ด Pico ตรงกับขา GP26, GP27, GP28 ตามลำดับ แต่ช่องที่ 4 จะใช้สำหรับอ่านแรงดันไฟฟ้าที่ขา VSYS/3 ของบอร์ด (แรงดันไฟฟ้าที่วัดได้จะเท่ากับ VSYS / 3) และถ้าใช้แรงดันไฟเลี้ยงจาก VUSB แรงดันไฟฟ้าที่วัดได้จาก ADC ที่ขา VSYS จะเท่ากับ (VBUS / 3) ซึ่งได้จากวงจรหารความถี่โดยใช้ตัวต้านทาน
ข้อสังเกต: VBUS ต่อผ่านไดโอด Schottky ไปยัง VSYS (ต้องอยู่ในช่วง 1.8V .. 5.5V) และต่อไปยังวงจร Switching Power Supply (RT6150 buck-boost SMPS) เพื่อสร้างแรงดันไฟฟ้า +3.3V
โค้ดตัวอย่างนี้สาธิตการอ่านค่าอินพุต 4 ช่องของ ADC ตามลำดับ
import utime as time
from machine import Pin,ADC
adc_pins = [ 26,27,28,29 ]
adc_units = [ ADC(pin) for pin in adc_pins ]
vsys_adc = adc_units[-1]
try:
values = []
while True:
# read the first 3 ADC inputs
for adc in adc_units[:-1]:
values.append( (adc.read_u16()/65535)*3.3 )
# read the voltage on the VSYS pin
values.append( 3*vsys_adc.read_u16()/65535*3.3 )
print( ['{:.3f}V'.format(v) for v in values] )
values = []
time.sleep(2.0)
except KeyboardInterrupt:
pass
ตัวอย่างนี้สาธิตการใช้งานไลบรารีสำหรับ TM1637 ซึ่งเป็นไอซีควบคุมการทำงานของโมดูล 4-Digit 7-Segment Display
ไฟล์
tm637.py
ซึ่งเป็นไลไบรารีและมีโค้ดที่เขียนด้วยภาษาไมโครไพธอนสำหรับ TM1637 สามารถดาวน์โหลดได้จาก Github ดังนั้นต้องดาวน์โหลดไฟล์นี้แล้วนำไปใส่ไว้ใน File System ของบอร์ด RP2040การทำงานของโค้ดนี้ได้เลือกใช้ขา GP15 และ GP14 สำหรับขาสัญญาณ CLK แลพ DIO ของโมดูล TM1637 ตามลำดับ (ใช้แรงดันไฟเลี้ยง 3.3V) และจะแสดงผลตัวเลขเป็นนาฬิกาที่นับถอยหลัง เริ่มต้นที่ 2:00 นาที แล้วลดลงทีละหนึ่งทุก ๆ หนึ่งวินาที
import utime as time
from machine import Pin
import tm1637
CLK, DIO = 15, 14
disp = tm1637.TM1637( clk=Pin(CLK), dio=Pin(DIO) )
disp.brightness(7) # set brightness to 7 (max.)
def show_time(sec, colon=True):
global disp
text = '{:02d}{:02d}'.format(sec//60, sec%60)
data = disp.encode_string(text)
data[1] |= int(colon)*0x80 # show a blinking colon
disp.write( data )
try:
seconds = 120 # seconds
show_time( seconds )
saved_time = time.ticks_ms()
cnt = 0;
while seconds > 0:
now = time.ticks_ms()
if time.ticks_diff( now, saved_time ) >= 500:
saved_time = now
cnt += 1
if cnt == 2:
cnt = 0
seconds -= 1
show_time( seconds, bool(cnt%2) )
except KeyboardInterrupt:
pass

รูป: การต่อวงจรทดลองใช้งานโมดูล TM1637 4-Digit 7-Segment Display
ตัวอย่างนี้สาธิตการรับส่งข้อมูลผ่านทาง UART ของ RP2040 ในลักษณะ Serial Loopback คือ ส่งข้อมูลออกไปทางขา Tx แล้วก็รับข้อมูลนั้นวกกลับเข้ามาที่ขา Rx
RP2040 มีวง จร UART จำนวน 2 ชุด คือ UART0 และ UART1 และสามารถเลือกใช้ขา GPIO ได้แตกต่างกัน (ดูได้จาก Pico PinMap) แต่ในตัวอย่างนี้ได้เลือกใช้ UART0 และขา GP0 และ GP1 สำหรับขา Tx และ Rx ตามลำดับ และตั้งค่า Baudrate ไว้เท่ากับ 115200
import utime as time
from machine import Pin, UART
# UART0_TX/RX = GPIO-0 / GPIO-1 pins
# UART1 TX/RX = GPIO-4 / GPIO-5 pins
uart = UART(0, baudrate=115200, bits=8, parity=None, stop=1,
tx=Pin(0), rx=Pin(1) )
# (115200, bits=8, parity=None, stop=1, timeout=1000 )
message = 'Hello'
uart.write( bytes( ord(ch) for ch in message) )
time.sleep_ms(1)
if uart.any():
data = uart.read( len(message) )
print( data.decode() )
โค้ดตัวอย่างนี้ สาธิตการใช้งานโมดูล Rotary Encoder Switch ซึ่งจะได้สัญญาณดิจิทัล A, B และมีการเปลี่ยนสถานะลอจิกเมื่อมีการหมุนตามเข็มหรือทวนเข็มนาฬิกา (Clockwise / Anti-Clockwise Rotation) นอกจากนั้นแล้ว ยังมีอีกหนึ่งสัญญาณ SW ให้ผลเหมือนกับวงจรกดปุ่มหรือสวิตช์ปุ่มกด
การตรวจสอบดูว่า มีการหมุนเปลี่ยนตำแหน่งหรือไม่ และเปลี่ยนไปในทิศทางใด เราจะใช้อินเทอร์รัพท์เพื่อดูการเปลี่ยนสถานะที่ขา A ทั้งขอบขาขึ้นและขาลง (Both Rising Edge & Falling Edge) ถ้าไม่มีการเปลี่ยนตำแหน่ง สถานะลอจิกของขา A และ B จะเป็น High
เมื่อเกิดเหตุการณ์ในแต่ละครั้ง ก็ทำให้ฟังก์ชันที่เป็น ISR Handler ที่เกี่ยวข้องทำงาน โดยตรวจสอบดูว่า สถานะของขา A และ B เป็นอย่างไร แล้วใช้ในการเพิ่มหรือลดค่าของตัวนับ (Incremental / Decremental Counter)
ในตัวอย่างนี้ได้เลือกใช้ขา GPIO-16, 17, 18 (GP16,GP17,GP18) ตรง กับขาสัญญาณ A, B, SW ของโมดูล Rotary Encoder Switch ที่ได้นำมาต่อวงจรร่วมกับบอร์ด RPi Pico ถ้าหมุนครบหนึ่งรอบจะได้ทั้งหมด 20 ตำแหน่ง (Positions per Revolution)
ในการต่อวงจรบนเบรดบอร์ด แนะนำให้เพิ่มวงจร R-C Filter (low-pass) ที่ขาสัญญาณ A และ B เพื่อลดปัญหาที่เกิดการกระเด้งของสวิตช์ (Switch Bouncing)
from machine import Pin
pin_sw = Pin(18, Pin.IN)
pin_a, pin_b = Pin(16, Pin.IN), Pin(17, Pin.IN)
print('AB state:', pin_a.value(), pin_b.value())
# pulse sequence on pins A and B
# clockwise: AB = 11 -> 01 -> 00 -> 10 -> 11
# anti-clockwise: AB = 11 -> 10 -> 00 -> 01 -> 11
cnt = 0 # set the rotary counter to 0
def pin_a_handler(pin):
global cnt, pin_a, pin_b
a,b = pin_a.value(), pin_b.value()
if a != b:
cnt += 1
else:
cnt -= 1
def pin_sw_handler(pin):
global cnt
cnt = 0 # reset counter
# enable IRQ pin-change on the A pin
pin_a.irq( trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING,
handler=pin_a_handler )
# enable IRQ pin-change on the SW pin
pin_sw.irq( trigger=Pin.IRQ_FALLING,
handler=pin_sw_handler)
try:
position = cnt//2
while True:
if position != cnt//2:
position = cnt//2
print('position:', position)
pass
except KeyboardInterrupt:
pass

รูป: การต่อวงจรทดลองใช้งานโมดูล Rotary Encoder Switch
ตัวอย่างนี้สาธิตการใช้คำสั่งจากโมดูล
gc
(Garbage Collector) ของไมโครไพธอน และใช้คำสั่งเพื่อตรวจสอบดูปริมาณการใช้หน่วยความจำ หรือที่ยังเหลืออยู่import gc
mem_free = gc.mem_free()
mem_alloc = gc.mem_alloc()
mem_total = mem_free + mem_alloc
gc.collect() # call garbage collector
text_fmt = 'Memory (total/alloc/free): {:,} / {:,} / {:,} bytes'
print( text_fmt.format(mem_total, mem_alloc, mem_free) )
ตัวอย่างข้อความเอาต์พุต
Memory (total/alloc/free): 192,080 / 60,368 / 131,712 bytes
ตัวอย่างนี้สาธิตการใช้โมดูล SH1106 OLED Display ที่เชื่อมต่อด้วยบัส I2C โดยเลือกใช้ I2C0 ของ RP2040 และใช้ขา GP16 / GP17 สำหรับขา SDA/SCL ตามลำดับ และนำมาแสดงข้อความเป็นตัวอย่าง
การใช้งานโมดูล SH1106 ทำได้ไม่ยากเนื่องจากมีไลบรารีไว้ให้ใช้งาน สามารถดาวน์โหลดไฟล์
sh1106.py
จาก Github แล้วนำไปใส่ลงในไดรฟ์ของไมโครไพธอนสำหรับบอร์ด RPi Picofrom machine import Pin, I2C
import utime as time
from sh1106 import *
# use I2C0 and GPIO 16,17 for SDA and SCL pins
i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=400000)
print(i2c.scan())
I2C_ADDR = 0x3C # the I2C address of SH1106 display module
W, H = 128, 64 # the screen size (128 x 64 pixels)
BLACK, WHITE = 0,1
# scan for I2C devices
if I2C_ADDR not in i2c.scan():
raise RuntimeError('OLED SH1106 not found!!!')
# create an SH1106 object
disp = SH1106_I2C( W, H, i2c, addr=I2C_ADDR )
# rotate the screen
disp.rotate( True )
# fill the entire display
disp.fill( BLACK )
# draw a white frame
disp.rect( 0, 0, W, H, WHITE )
# write some text lines (using the default font)
xpos,ypos = 0, 6
text_lines = ["Hi!", "MicroPython", "Raspberry Pi", "RP2040"]
for line in text_lines:
disp.text( '{:^16s}'.format(line), xpos, ypos )
ypos += 12
disp.show() # update the display
time.sleep_ms(1000)
#disp.poweroff()

รูป: การต่อวงจรเพื่อใช้งานโมดูล SH1106 OLED I2C Display
โค้ดนี้สาธิตการสร้าง สัญญาณเอาต์พุตแบบพัลส์ เพื่อส่งไปยังโมดูลเซนเซอร์วัดระยะห่างจากสิ่งกีดขวางด้วยคลื่นอัลตร้าโซนิก (Ultrasonic Distance Sensor Module) และวัดความกว้างของสัญญาณพัลส์ที่ได้จากโมดูลเพื่อนำมาคำนวณระยะห่าง
โมดูลเซนเซอร์มีขา Trig (Trigger) เป็นอินพุต และขา Echo เป็นเอาต์พุต (เลือกใช้รุ่นที่ทำงานได้โดยใช้แรงดันไฟเลี้ยง +3.3V) ไมโครคอนโทรลเลอร์จะต้องสร้างสัญญาณพัลส์ เช่น มีความกว้างช่วง High อย่างน้อย 10 usec ไปยังขา Trig จากนั้นให้วัดความกว้างของสัญญาณพัลส์ที่ขา Echo
การวัดความกว้างของสัญญาณพัลส์ที่ขา Echo จะต้องรอให้สัญญาณนั้น เปลี่ยนจาก Low เป็น High แล้วเปลี่ยนจาก High เป็น Low ตามลำดับ
ในตัวอย่างนี้ได้เลือกใช้ขา GP19 และ GP18 สำหรับขา Trig และ Echo ตามลำดับ
import utime as time
from machine import Pin
trig = Pin( 19, Pin.OUT )
echo = Pin( 18, Pin.IN )
saved_times = [0,0]
def pin_handler(pin):
global saved_times
# save the timestamp
saved_times[ pin.value() ] = time.ticks_us()
def measure():
# generate a short HIGH pulse on Trig pin
trig.high()
time.sleep_us(10)
trig.low()
# enable IRQ on Echo pin
echo.irq( trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING, handler=pin_handler)
# wait for 30 msec before reading timestamps
time.sleep_ms(30)
# disable IRQ on Echo pin
echo.irq(None)
dt = time.ticks_diff( saved_times[0], saved_times[1] )
distance = (340*dt/2/10000)
if distance < 0:
return -1 # invalid value
else:
return distance
try:
while True:
d = measure()
if d > 400:
print( 'Out of range (>400cm)')
elif d > 0:
print( 'Distance: {:.1f} cm.'.format(d) )
time.sleep_ms(200)
except KeyboardInterrupt:
pass
finally:
echo.irq(None)
ตัวอย่างนี้สาธิตการส่งข้อมูลไปยังไอซี 74HC595 ที่มีขาเอาต์พุต 8 ขา ต่อกับโมดูล 8x LED Bar สำหรับแสดงสถานะลอจิก และการส่งข้อมูลจะใช้วิธีเลื่อนบิตทีละบิตตามจังหวะสัญญาณ Clock
- Serial Clock (SCLK) เป็นขาอินพุต รับสัญญาณ Clock กำหนดจังหวะการเลื่อนบิต
- Serial Data (SDA) เป็นขาอินพุต รับสัญญาณข้อมูลบิตเข้ามา
- Load (LOAD) เป็นขาสัญญาณอินพุต รับสัญญาณพัลส์ เพื่อทำให้วงจรภายในไอซี นำข้อมูลที่ได้รับ ไปอัปเดตสถานะบิตที่ขาเอาต์พุต Q0..Q7 หลังจากที่ได้เลื่อนข้อมูลเข้าไปครบแล้ว
- Q0..Q7 เป็นขาสัญญาณเอาต์พุต สำหรับข้อมูล 8 บิตแบบขนาน
- Q7S เป็นขาสัญญาณเอาต์พุตสำหรับข้อมูลบิตที่ถูกเลื่อนออกมา (ใช้สำหรับการต่อไอซีแบบ Cascade หรือ Daisy Chain)
- Master Clear (/MCLR) -- Active-low (ต่อตัวต้านทาน Pullup) ใช้สำหรับรีเซตข้อมูลภายใน
- Output Enable (/OE) -- Active-low (ต่อตัวต้านทาน Pullup) เปิดหรือปิดขาเอาต์พุต Q0.. Q7
โค้ดนี้ใช้ขา GPIO ในการสร้างสัญญาณควบคุมและส่งข้อมูลไปยัง 74HC595 โดยเลือกใช้ขาดังนี้
- GP9 = LOAD
- GP10 = SCK
- GP11 = SDA
ฟังก์ชัน
send_byte()
ใช้สำหรับการส่งข้อมูลขนาดหนึ่งไบต์เท่านั้นไปยัง 74HC595 และเลือกได้ว่าจะเลื่อนข้อมูลออกแบบ MSB First หรือ LSB Firstimport utime as time
from machine import Pin
load_pin = Pin( 9, Pin.OUT ) # load pin
sda_pin = Pin(11, Pin.OUT ) # serial data
sclk_pin = Pin(10, Pin.OUT ) # serial clock
def send_byte( data, msb_first=False ):
sda_pin.low()
sclk_pin.low()
load_pin.low()
for i in range(8):
# shift-out data to the SDA pin, LSB first
if msb_first:
bit = (data & 0x80) == 0x80
data <<= 1
else:
bit = (data & 0x01) == 0x01
data >>= 1
# send data bit
if bit:
sda_pin.high()
else:
sda_pin.low()
# send a clock pulse
sclk_pin.high()
sclk_pin.low