บอร์ด Raspberry Pi Pico
บอร์ด Raspberry Pi Pico ใช้ตัวประมวลผลแบบ 32 บิต ภายในมีซีพียู Arm Cortex-M0+ Dual-Core และทีมพัฒนาได้มีการเปิดตัวบอร์ดดังกล่าวในเดือนมกราคม พ.ศ. 2564 ผู้ใช้สามารถเขียนโปรแกรมในเบื้องต้น โดยใช้ภาษา C และ MicroPython
บริษัท Adafruit ได้พัฒนาบอร์ดไมโครคอนโทรลเลอร์ที่ใช้ชิป RP2040 SoC เช่นเดียวกัน และได้พัฒนา CircuirPython v6.x ให้รองรับการใช้งานตัวประมวลผลดังกล่าวด้วย และผู้ที่สนใจสามารถดาวน์โหลดไฟล์ .UF2 จากเว็บไซต์ต่อไปนี้เพื่อนำมาติดตั้ง CircuitPython สำหรับ Raspberry Pi Pico
ตัวอย่างไฟล์ที่ได้นำมาทดลองใช้งานจากเว็บ https://circuitpython.org/board/raspberry_pi_pico/
adafruit-circuitpython-raspberry_pi_pico-en_US-6.2.0-rc.0.uf2
การติดตั้งก็ทำได้ง่าย เริ่มต้นโดยการกดปุ่ม BOOTSEL บนบอร์ดค้างไว้ แล้วเสียบสาย USB เชื่อมต่อกับคอมพิวเตอร์ จากนั้นปล่อยปุ่มดังกล่าว เพื่อให้อุปกรณ์เริ่มทำงานและเข้าสู่โหมด USB UF2 Bootloader จากนั้นจะมองเห็นไดรฟ์ใหม่ปรากฏขึ้น (RPI-RP2 ) จากนั้นให้คลิกเลือกและลากไฟล์ .UF2 ไปยังไดรฟ์ดังกล่าว
เมื่อได้ติดตั้งไฟล์เฟิร์มแวร์สำหรับ CircuitPython ได้สำเร็จแล้ว บอร์ด Pico จะเริ่มต้นทำงานใหม่อีกครั้งและปรากฏไดรฟ์ใหม่ชื่อว่า CIRCUITPY และพร้อมใช้งาน ผู้ใช้สามารถเปิดใช้งานโปรแกรมอย่างเช่น Thonny IDE เชื่อมต่อกับ CircuitPython ผ่านทาง Serial
เมื่อเริ่มต้นทำงาน CircuitPython จะมองเห็นไฟล์ code.py
ใน Flash File Storage ของบอร์ด Pico หรือไม่ ถ้ามีไฟล์ดังกล่าว CircuitPython ก็จะทำคำสั่งของโค้ดไพธอนภายในไฟล์ดังกล่าว
ในส่วนของ Shell จะมีข้อความเริ่มต้นซึ่งแสดงข้อมูลเกี่ยวกับเวอร์ชันของ CircuitPython ดังนี้
Adafruit CircuitPython 6.2.0-rc.0 on 2021-04-01; Raspberry Pi Pico with rp2040
โค้ดตัวอย่างที่ 1: Onboard LED Blink
ลองแก้ไขโค้ดในไฟล์ code.py
ตามตัวอย่างต่อไปนี้
Copy import board
import digitalio
import time
# use the onboard LED pin as output
LED_PIN = board . GP25 # the same as board.LED
led = digitalio . DigitalInOut (LED_PIN)
led . direction = digitalio . Direction . OUTPUT
while True :
led . value = not led . value # toggle the LED output
time . sleep ( 0.5 ) # sleep for 0.5 seconds
led . deinit () # turn off and release the LED pin
การทำงานของโค้ดนี้สาธิตการทำให้ LED ที่ขา GP25 กระพริบได้ โดยการเปิดใช้งานขา GPIO ดังกล่าวในทิศทางเอาต์พุต และใช้คำสั่งจากคลาส DigitalInOut
จากโมดูล digitalio
ของ CircuitPython
ให้บันทึกการแก้ไขโค้ดลงไฟล์ดังกล่าวโดยกดปุ่ม Ctrl+S จากนั้นกดปุ่ม Ctrl+D เพื่อให้โค้ดดังกล่าวเริ่มทำงานใหม่ ซึ่งจะทำให้ LED (ขา GP25) บนบอร์ดไมโครคอนโทรลเลอร์กระพริบต่อเนื่องไป ถ้าต้องการหยุดการทำงานของโค้ดดังกล่าว ให้กดปุ่ม Ctrl+C (สองครั้ง) ในหน้าต่าง Shell
โค้ดตัวอย่างที่ 2: Push Button
ถ้ดไปเป็นการลองอ่านสถานะอินพุตจากขา GP15 ที่มีการต่อวงจรปุ่มกดภายนอกแบบ Active-Low และมีการทำคำสั่งซ้ำ เพื่อตรวจสอบสถานะของอินพุตจากปุ่มกด ถ้ามีการกดปุ่ม จะได้สถานะเป็น Low หรือ 0 และทำให้ LED หยุดกระพริบ และจบการทำงานของโค้ด
Copy import board
import digitalio
import time
led = digitalio . DigitalInOut (board.GP25)
led . direction = digitalio . Direction . OUTPUT
button = digitalio . DigitalInOut (board.GP15)
button . switch_to_input (pull = digitalio.Pull.UP)
while True :
if button . value == 0 :
print ( 'Button pressed...' )
break
led . value = not led . value
time . sleep ( 0.2 )
led . deinit ()
button . deinit ()
โค้ดตัวอย่างที่ 3: PWM-based LED Dimming
ลองเปลี่ยนมาสร้างสัญญาณแบบ PWM (Pulse Width Modulation) โดยใช้คลาส PWMOut
ของโมดูล pwmio
ตามตัวอย่างต่อไปนี้
Copy import board
import digitalio
import pwmio
import time
import math
pwm_led = pwmio . PWMOut (board.GP25, frequency = 500 ,
duty_cycle = 0 , variable_frequency = False )
button = digitalio . DigitalInOut (board.GP15)
button . switch_to_input (pull = digitalio.Pull.UP)
index = 0
N = 32
k = ( 2 * math . pi / N)
while True :
if button . value == 0 :
print ( 'Button pressed...' )
break
value = ( 1 + math . sin (k * index) ) / 2
pwm_led . duty_cycle = ( int ) (( 2 ** 16 - 1 ) * value)
index = (index + 1 ) % N
time . sleep ( 0.05 )
pwm_led . deinit ()
button . deinit ()
เมื่อโค้ดทำงาน สามารถสังเกตเห็นได้ว่า LED จะค่อย ๆ สว่างขึ้นแล้วดับลงอย่างช้า ๆ (LED Dimming ) แล้วเกิดซ้ำไปเรื่อย ๆ
ความสว่าง (LED Brightness ) เปลี่ยนแปลงได้ (ตามฟังก์ชันรูปไซน์) และเกิดจากการปรับค่าความกว้างหรือ Duty Cycle ของสัญญาณพัลส์แบบ PWM ที่มีค่าอยู่ในช่วง 0..65535 หรือ 16 บิต และตั้งค่าความถี่ไว้ที่ 500Hz
โค้ดตัวอย่างที่ 4 : Rotary Encoder Inputs
ตัวอย่างถัดไปเป็นการใช้งานโมดูล Rotary Encoder ที่ให้สัญญาณดิจิทัลแบบพัลส์จำนวน 2 ช่อง (A และ B ) เมื่อมีการหมุนเกิดขึ้น จะทำให้เกิดสัญญาณพัลส์ทั้งสองช่อง และสามารถตรวจสอบทิศทางการหมุนและการเปลี่ยนแปลงตำแหน่งตามจำนวนสเต็ป (Steps ) ที่เกิดขึ้นได้
ผู้พัฒนา CircuitPython ได้สร้างคลาส IncrementalEncoder
ของโมดูล rotaryio
ไว้สำหรับการใช้งานในลักษณะนี้แล้ว มาดูตัวอย่างการเขียนโค้ดดังนี้
Copy import time
import board
import rotaryio
print ( 'Rotary Encoder demo...' )
rotary_pins = (board . GP16 , board . GP17)
encoder = rotaryio . IncrementalEncoder ( * rotary_pins )
last_position = None
try :
while True :
if encoder . position < 0 :
encoder . position = 0
elif encoder . position > 100 :
encoder . position = 100
position = encoder . position
if last_position is None or position != last_position :
print (position)
last_position = position
time . sleep ( 0.1 )
except KeyboardInterrupt :
print ( 'Terminated...' )
encoder . deinit ()
del encoder
ในโค้ดตัวอย่างนี้ มีการเลือกใช้ขา GP16 และ GP17 สำหรับสัญญาณอินพุตของ Incremental Rotary Encoder ซึ่งใช้แรงดันไฟเลี้ยง 3.3V
และได้มีการสร้างอ็อปเจกต์จากคลาส rotaryio.IncrementalEncoder
และใช้ตัวแปร encoder
ในการอ้างอิง ถ้าต้องการอ่านค่าของตัวนับที่ระบุตำแหน่งในขณะนั้น ก็ให้อ่านค่าจาก encoder.position
หรือจะเขียนค่าลงในตัวแปรดังกล่าวก็ได้ ในตัวอย่างนี้มีการตรวจสอบค่าโดยการอ่านค่าซ้ำไปเรื่อย ๆ เว้นระยะเวลาประมาณ 0.1 วินาที และมีการจำกัดค่าให้อยู่ในช่วง 0..100
โค้ดตัวอย่างที่ 5: NeoPixel RGB LED - Adjust Brightness
ตัวอย่างถัดไปเป็นการนำโมดูล NeoPixel / WS2812B RGB LEDs (มีจำนวน 8 ตำแหน่ง หรือ จำนวนพิกเซล NUM_PIXELS
เท่ากับ 8) มาต่อเพิ่ม ที่ขา GP18 ของบอร์ด Pico โดยใช้โมดูล Rotary Encoder เป็นอุปกรณ์อินพุตเพื่อใช้ในการปรับระดับความสว่างของ LEDs (เลือกแสงสีแดง)
Copy import time
import board
import rotaryio
import neopixel # requires the CircuitPython Neopixel library
print ( 'Rotary Encoder + NeoPixel demo...' )
pixel_pin = board . GP18
rotary_pins = (board . GP16 , board . GP17)
NUM_PIXELS = 8
color = ( 255 , 0 , 0 ) # use red color
pixels = neopixel . NeoPixel (pixel_pin,
NUM_PIXELS, brightness = 0.0 , auto_write = False )
encoder = rotaryio . IncrementalEncoder ( * rotary_pins )
last_position = None
try :
pixels . fill ( color ) # use red color
pixels . show ()
while True :
if encoder . position < 0 :
encoder . position = 0
elif encoder . position > 100 :
encoder . position = 100
position = encoder . position
if last_position is None or position != last_position :
pixels . brightness = position / 100.0
pixels . show ()
print (position)
last_position = position
time . sleep ( 0.1 )
except KeyboardInterrupt :
print ( 'Terminated...' )
pixels . deinit ()
encoder . deinit ()
del encoder , pixels
ค่าของตัวนับที่อ่านได้อยู่ในช่วง 0..100 จะถูกนำมาหารด้วย 100.0 เพื่อให้ได้ตัวเลขในช่วง 0.0 ถึง 1.0 และใช้กำหนดระดับความสว่างของโมดูล NeoPixel
สำหรับการใช้งานโมดูล NeoPixel จะต้องติดตั้งไลบรารีเพิ่ม โดยใช้ไฟล์ที่มีชื่อว่า neopixel.mpy
ซึ่งสามารถดาวน์โหลดได้จาก https://circuitpython.org/libraries (อยู่ในไฟล์ .zip
รวมกับไฟล์ต่าง ๆ ที่เป็นไลบรารีของ CircuitPython ) แล้วนำไปใส่ลงในไดเรกทอรี lib
ภายในไดรฟ์ CIRCUITPY (Flash Storage ) ของบอร์ด Pico
ข้อสังเกต : โมดูล NeoPixel / WS2812B โดยทั่วไปแล้ว จะใช้แรงดันไฟเลี้ยง +5V แต่ก็สามารถใช้ +3.3V ได้เช่นกัน
โค้ดตัวอย่างที่ 6: NeoPixel RGB LEDs - Turn On/Off Pixels
ตัวอย่างถัดไปสาธิตการใช้งานโมดูล Rotary Encoder กับโมดูล NeoPixel ในลักษณะที่แตกต่างจากตัวอย่างที่แล้ว โดยใช้ตำแหน่งที่ได้จาก Rotary Encoder มากำหนดจำนวนของ RGB LED ของโมดูล NeoPixel ที่ให้แสงสีแดง ซึ่งจะอยู่ระหว่าง 0 ถึง 8 (RGB LED ในทุกตำแหน่ง ให้แสงสีแดง)
Copy import time
import board
import rotaryio
import neopixel # requires the CircuitPython Neopixel library
print ( 'Rotary Encoder + NeoPixel demo...' )
pixel_pin = board . GP18
rotary_pins = (board . GP16 , board . GP17)
NUM_PIXELS = 8
color = ( 255 , 0 , 0 ) # use red color
pixels = neopixel . NeoPixel (pixel_pin,
NUM_PIXELS, brightness = 1.0 , auto_write = False )
encoder = rotaryio . IncrementalEncoder ( * rotary_pins )
last_position = None
def set_color ( level ):
global pixels
pixels [ 0 : NUM_PIXELS ] = level * [color] + (NUM_PIXELS - level) * [( 0 , 0 , 0 )]
pixels . show ()
try :
set_color ( 0 )
while True :
if encoder . position < 0 :
encoder . position = 0
elif encoder . position > NUM_PIXELS :
encoder . position = NUM_PIXELS
position = encoder . position
if last_position is None or position != last_position :
set_color (position)
print (position)
last_position = position
time . sleep ( 0.1 )
except KeyboardInterrupt :
print ( 'Terminated...' )
pixels . deinit ()
encoder . deinit ()
del encoder , pixels
โค้ดตัวอย่างที่ 7: SH1106 I2C OLED Display
ตัวอย่างถัดไปเป็นการสาธิตการใช้งานโมดูล SH1106 I2C OLED ขนาด 128 x 64 พิกเซล โดยเชื่อมต่อผ่านบัส I2C ความเร็ว 400kHz และเลือกใช้ขา GP18 และ GP19 ของบอร์ด Pico สำหรับขาสัญญาณ SDA และ SCL ของบัส I2C
Copy import time
import board
import busio
from sh1106 import SH1106_I2C
i2c_pins = (board . GP19 , board . GP18)
i2c = busio . I2C ( scl = i2c_pins[ 0 ], sda = i2c_pins[ 1 ], frequency = 400000 )
while not i2c . try_lock ():
pass
# scan I2C devices
print ( [ hex (x) for x in i2c. scan ()] )
WIDTH = 128
HEIGHT = 64
disp = SH1106_I2C ( WIDTH, HEIGHT, i2c, 0x 3c )
disp . poweron ()
disp . framebuf . rect ( 0 , 0 , WIDTH, HEIGHT // 2 , 1 ) # draw a frame
m = 4
disp . framebuf . fill_rect (m, m, WIDTH - 2 * m, HEIGHT // 2 - 2 * m, 1 ) # fill area
disp . framebuf . text ( "CircuitPython" , 24 , 12 , 0 ) # show text
disp . framebuf . text ( " Pico RP2040 " , 24 , 42 , 1 ) # show text
disp . framebuf . text ( "SH1106 I2C LCD" , 20 , 54 , 1 ) # show text
disp . show ()
invert = 0
for i in range ( 5 ):
disp . invert ( invert ) # invert display
invert = not invert
time . sleep ( 1.0 )
disp . poweroff ()
i2c . unlock ()
i2c . deinit ()
print ( 'done' )
นอกจากนั้นได้สร้างไฟล์ sh1106.py
เพื่อใช้งานเป็นไลบรารีสำหรับการใช้งานโมดูล SH1106 ในเบื้องต้น การทำงานของคลาส SH1106
ต้องใช้งานร่วมกับ FrameBuffer
จากโมดูล adafruit_framebuf
ดังนั้นจึงต้องติดตั้งไฟล์ adafruit_framebuf.mpy
ลงใน/lib
และไฟล์ font5x8.bin
ด้วย
ไฟล์ sh1106.py
Copy # This is a modified version of the SH1106 MicroPython library
# https://github.com/robert-hh/SH1106/blob/master/sh1106.py
from micropython import const
import adafruit_framebuf as framebuf
# dependencies:
# https://github.com/adafruit/Adafruit_CircuitPython_framebuf
# https://github.com/adafruit/Adafruit_CircuitPython_framebuf/blob/master/examples/font5x8.bin
_SET_CONTRAST = const ( 0x 81 )
_SET_NORM_INV = const ( 0x a6 )
_SET_DISP = const ( 0x ae )
_SET_SCAN_DIR = const ( 0x c0 )
_SET_SEG_REMAP = const ( 0x a0 )
_LOW_COLUMN_ADDRESS = const ( 0x 00 )
_HIGH_COLUMN_ADDRESS = const ( 0x 10 )
_SET_PAGE_ADDRESS = const ( 0x B0 )
class SH1106_I2C :
def __init__ ( self , width , height , i2c , addr =0x 3c , external_vcc = False ):
self . width = width
self . height = height
self . i2c = i2c
self . addr = addr
self . external_vcc = external_vcc
self . pages = self . height // 8
self . buffer = bytearray (self.pages * self.width)
fb = framebuf . FrameBuffer (self.buffer, self.width, self.height,
framebuf.MVLSB)
self . framebuf = fb
self . framebuf . fill ( 0 ) # fill with black color
self . show ()
def write_cmd ( self , cmd ):
buf = bytearray ( 2 )
buf [ 0 ] = 0x 80 # Co=1, D/C#=0
buf [ 1 ] = cmd
self . i2c . writeto (self.addr, buf)
def write_data ( self , buf ):
self . i2c . writeto (self.addr, b '\x40' + buf)
def contrast ( self , contrast ):
self . write_cmd (_SET_CONTRAST)
self . write_cmd (contrast)
def invert ( self , invert ):
self . write_cmd (_SET_NORM_INV | (invert & 1 ))
def rotate ( self , flag , update = True ):
if flag :
self . write_cmd (_SET_SEG_REMAP | 0x 01 ) # mirror display vertically
self . write_cmd (_SET_SCAN_DIR | 0x 08 ) # mirror display horizontally
else :
self . write_cmd (_SET_SEG_REMAP | 0x 00 )
self . write_cmd (_SET_SCAN_DIR | 0x 00 )
if update :
self . show ()
def poweroff ( self ):
self . write_cmd (_SET_DISP | 0x 00 )
def poweron ( self ):
self . write_cmd (_SET_DISP | 0x 01 )
def show ( self ):
for page in range (self.pages):
self . write_cmd (_SET_PAGE_ADDRESS | page)
self . write_cmd (_LOW_COLUMN_ADDRESS | 2 )
self . write_cmd (_HIGH_COLUMN_ADDRESS | 0 )
self . write_data (
self.buffer[ self.width * page : self.width * (page + 1 )] )
######################################################################
โค้ดตัวอย่างที่ 8: DHT22 Temperature & Humidity Sensor
ถัดไปเป็นตัวอย่างการอ่านค่าอุณหภูมิและความชื้นสัมพัทธ์จากโมดูล DHT22 และนำมาแสดงเป็นข้อความบนโมดูล SH1106 I2C OLED
โมดูล DHT22 ใช้แรงดันไฟเลี้ยง +3.3V และในการต่อวงจร ขา DATA ของโมดูลนี้ ต่อเข้ากับขา GP16 ของบอร์ด Pico
การเขียนโค้ด CircuitPython เพื่อใช้งานโมดูล DHT22 ก็ทำได้ไม่ยาก เนื่องจากมีไลบรารีไว้ให้ใช้งาน แต่จะต้องเพิ่มไฟล์ adafruit_dht.mpy
(หาได้จาก CircuitPython Library Bundle )
Copy import time
import board
import busio
from sh1106 import SH1106_I2C
from adafruit_dht import DHT22
dht = DHT22 (board.GP16)
i2c_pins = (board . GP19 , board . GP18)
i2c = busio . I2C ( scl = i2c_pins[ 0 ], sda = i2c_pins[ 1 ], frequency = 400000 )
while not i2c . try_lock ():
pass
# scan I2C devices
print ( [ hex (x) for x in i2c. scan ()] )
WIDTH = 128
HEIGHT = 64
disp = SH1106_I2C ( WIDTH, HEIGHT, i2c, 0x 3c )
disp . poweron ()
disp . framebuf . rect ( 0 , 0 , WIDTH, HEIGHT, 1 ) # draw a frame
while True :
try :
dht . measure ()
disp . framebuf . fill_rect ( 1 , 1 , WIDTH - 2 , HEIGHT - 2 , 0 )
text = " Temperature: { :.1f } " . format (dht.temperature)
print (text)
disp . framebuf . text (text, 4 , 20 , 1 ) # show text
text = " Rel.Humdity: { :.1f } " . format (dht.humidity)
print (text)
disp . framebuf . text (text, 4 , 40 , 1 ) # show text
disp . show ()
time . sleep ( 2.0 )
except RuntimeError as ex :
pass # DHT checksum error
except KeyboardInterrupt :
break
dht . exit ()
i2c . unlock ()
i2c . deinit ()
print ( 'done' )
กล่าวสรุป
เนื้อหาในส่วนนี้แนะนำการใช้งาน CircuitPython สำหรับบอร์ด Pico RP2040 ในเบื้องต้น พร้อมโค้ดตัวอย่างเพื่อทดสอบการทำงานร่วมกับโมดูลหรือวงจรอื่น
เผยแพร่ภายใต้ลิขสิทธิ์
Attribution-ShareAlike 4.0 International ( CC BY-SA 4.0 )