# RPi Pico RP2040 Code Examples

## คำแนะนำ

ในการต่อวงจรเพื่อทดลองการทำงานของโค้ดตัวอย่างโดยใช้บอร์ดไมโครคอนโทรลเลอร์ **RPi Pico** ร่วมกับวงจรหรือโมดูลต่าง ๆ ควรมีความรู้พื้นฐานในการต่อวงจรและระมัดระวังเพื่อมิให้เกิดความเสียหายกับอุปกรณ์

{% hint style="info" %}
โดยทั่วไปแล้วบอร์ด **RPi Pico** ใช้แรงดันไฟเลี้ยงจากพอร์ต **USB** ซึ่งจะได้ประมาณ **5V** แต่ไม่เกิน **5.5V** แต่ระดับแรงดันไฟฟ้าที่ขา **I/O** จะอยู่ที่ **3.3V** เท่านั้น และไม่สามารถใช้งานได้กับวงจรที่มีระดับแรงดันไฟฟ้าสูงกว่า เช่น **5V** (**Not 5V tolerant**)
{% endhint %}

การต่อวงจรที่ขา **I/O** ของบอร์ด ควรตรวจสอบจาก **Pinout Diagram** ของบอร์ดทุกครั้ง

![รูป: RPi Pico - Pin Reference (Source: Adafruit)](/files/-MTIvJPTu1Rh8cIJColu)

![รูป: RPi Pico PinMap](/files/-MTcMzPmVgQ-7nPasD11)

## โค้ดตัวอย่างที่ 1: LED Blink

โค้ดตัวอย่างแรก สาธิตการทำให้ **LED** กระพริบได้ โดยเลือกใช้ **LED** บนบอร์ด **Pico** ซึ่งต่อวงจรอยู่กับขา **GPIO-25** และสาธิตการใช้คลาส `machine.Pin` ที่เกี่ยวข้องกับขา **GPIO**&#x20;

ตัวแปร `led` อ้างอิงขา **GPIO-25 Pin** ที่เปิดใช้งานในทิศทางเอาต์พุต และใช้คำสั่ง `led.value()` สำหรับอ่านสถานะลอจิกในขณะนั้น และกำหนดสถานะใหม่ได้เช่นกัน โดยทำคำสั่ง เช่น `led.value(0)` หรือ `led.value(1)`

ถ้าต้องการจบการทำงานของโค้ดนี้เมื่อรันผ่านทาง **MicroPython REPL** ให้กดคีย์ **Ctrl+C**&#x20;

```python
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
```

## โค้ดตัวอย่างที่ 2:  Input Button

โค้ดตัวอย่างนี้ สาธิตการอ่านค่าจากขา **GPIO** ที่ต่อรับสัญญาณอินพุตจากวงจรปุ่มกด (**Active-Low Push Button**) โดยกำหนดให้ขา **GPIO-16** เป็นขาอินพุตและเปิดใช้งาน **Internal Pull-Up** สำหรับขาดังกล่าว และใช้ตัวแปร `button` ในการอ้างอิงขาดังกล่าว ในตัวอย่างนี้ เมื่อกดปุ่มจะอ่านค่าโดยใช้คำสั่ง `button.value()` ได้เป็น **0** หรือลอจิก **LOW** และจบการทำงานของโปรแกรม

```python
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**)

```python
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)
```

## โค้ดตัวอย่างที่ 3: Push Button + Toggle LED

ตัวอย่างนี้สาธิตการเปิดใช้งานอินเทอร์รัพท์ที่ขา **GPIO-14 (GP14)** ที่ต่อกับวงจรปุ่มกดบนเบรดบอร์ด เมื่อมีการกดปุ่มแล้วปล่อยแต่ละครั้ง จะทำให้ **LED** บนบอร์ดสลับสถานะหนึ่งครั้ง

```python
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
```

## โค้ดตัวอย่างที่ 4: PWM-based LED Fading

ตัวอย่างนี้สาธิตการสร้างสัญญาณแบบ **PWM (Pulse Width Modulation)** และให้เป็นเอาต์พุตที่ขา **GPIO-25** กำหนดความถี่ให้เท่ากับ **1000 Hz (1 kHz)** และเริ่มต้นให้มีค่า **Duty Cycle** เป็น **0** การกำหนดค่า **Duty Cycle** สำหรับ **PWM** นั้น จะต้องเป็นเลขจำนวนเต็มขนาด 16 บิต อยู่ในช่วง **0..65535**&#x20;

โค้ดตัวอย่างนี้ จะเพิ่มค่า **Duty Cycle** จาก **0** และไปจนถึงค่าเท่ากับ **255\*256** จากนั้นจะลดลงไปจนถึง **0** อีกครั้ง แล้วจะวนซ้ำในลักษณะนี้ไปเรื่อย ๆ&#x20;

```python
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** บนบอร์ดค่อย ๆ สว่างขึ้นและดับลงซ้ำไปเรื่อย ๆ&#x20;

ถ้าจะลองใช้โมดูล **RGB LED** ที่ใช้ขาสัญญาณควบคุมแบบ 3 ขา ก็สามารถทำได้เช่นกัน โค้ดตัวอย่างนี้สาธิตการใช้ขา **GPIO-11, 12, 13** **(GP11,GP12,GP13)** เป็นขาสัญญาณเอาต์พุต นำไปต่อกับขาสัญญาณของโมดูล **RGB LED (Active-High)** เพื่อเปลี่ยนค่า **Duty Cycle** ของสัญญาณแต่ละช่องไปตามลำดับ&#x20;

```python
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](/files/-MTDmOIfFeiAj6894Y19)

## โค้ดตัวอย่างที่ 5: Timer-based LED Blink

ตัวอย่างนี้สาธิตการใช้งาน **Timer** (ตอนนี้ใช้ได้เฉพาะ **Software Timer** และใช้ **Timer  id** เท่ากับ **-1)** เพื่อทำคำสั่งหรือเรียกใช้ฟังก์ชันตามคาบเวลาหรือความถี่ที่ได้กำหนดไว้ ซึ่งเป็นเหตุการณ์ที่เกิดขึ้นซ้ำ (**Periodic**) เช่น การสลับสถานะเอาต์พุตของ **LED** ที่อัตรา **10 Hz**

```python
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()
```

## โค้ดตัวอย่างที่ 6: LED Blink Using a Thread

ตัวอย่างนี้สาธิตการใช้คำสั่ง `_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**&#x20;

```python
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()
```

## โค้ดตัวอย่างที่ 7: OLED I2C Display

โค้ดตัวอย่างนี้สาธิตการใช้งานบัส **I2C** และเชื่อมต่อกับโมดูล **OLED I2C Display (SSD1306 Driver)** โดยใช้งานร่วมกับไลบรารี `ssd1306` ซึ่งสามารถดาวน์โหลดไฟล์ [`ssd1306.py`](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py) ได้จาก **Github** แล้วนำไปใส่ลงในไดรฟ์ของไมโครไพธอน

เนื่องจาก **RP2040** มีบัส **I2C** ให้ใช้งาน 2 ชุด ในตัวอย่างนี้ได้เลือกใช้ **I2C1** และใช้ขา **GPIO-14** และ **GPIO 15** ซึ่งสามารถใช้เป็นขา **I2C1\_SDA** และ **I2C1\_SCL** ได้

ขนาดของโมดูล **OLED I2C** ได้เลือกมาลองใช้งาน มีขนาด **128 x 32** พิกเซล และมีแอดเดรสตรงกับ `0x3C`&#x20;

```python
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()
```

## โค้ดตัวอย่างที่ 8: SSD1306 OLED + BH1750 Light Sensor

ตัวอย่างนี้เป็นการใช้งานบัส **I2C** โดยนำบอร์ด **RP2040** ไปเชื่อมต่อกับโมดูล **SSD1306 OLED Display** และโมดูลเซ็นเซอร์แสง **BH1750** ทั้งสองโมดูลนี้ใช้บัส **I2C1** ร่วมกัน และใช้แรงดันไฟเลี้ยง **+3.3V** จากบอร์ด **RP2040**&#x20;

แอดเดรสของโมดูล **SSD1306** และ **BH1750** ตรงกับ `0x3C` และ `0x23` ตามลำดับ

```python
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 ที่ได้นำมาทดลองใช้งาน](/files/-MTDmh3rHml81Se8CjxG)

ตัวอย่างการสร้างคลาสในภาษาไมโครไพธอนสำหรับ **BH1750** แล้วนำมาใช้งาน โดยบันทึกลงในไฟล์ `bh1750.py`

```python
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`

```python
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
```

## โค้ดตัวอย่างที่ 9: WDT (Watchdog Timer)

โค้ดตัวอย่างนี้สาธิตการเปิดใช้งาน **Watchdog Timer (WDT)** ซึ่งเป็นวงจรภายใน **RP2040** และเมื่อเปิดการทำงานของ **WDT** แล้ว จะต้องมีการป้อนค่าให้ **WDT (WDT feeding)** ภายในระยะเวลาที่กำหนด มิฉะนั้นแล้ว วงจร **WDT** จะรีเซตการทำงานของไมโครไพธอน

ในตัวอย่างนี้ จะต้องมีการต่อวงจรปุ่มกดภายนอกที่ขา **GPIO-16** ด้วย และเมื่อโค้ดนี้เริ่มทำงาน จะต้องมีการกดปุ่มค้างไว้ในช่วงเวลาดังกล่าว จึงจะเปิดการทำงานของ **WDT**&#x20;

```python
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
```

## โค้ดตัวอย่างที่ 10: System Info

โค้ดตัวอย่างนี้สาธิตการใช้คำสั่งของโมดูล `uos` เพื่อสอบถามข้อมูลเกี่ยวกับระบบและการใช้งานระบบไฟล์ในหน่วยความจำแฟลช (**Flash File System**) ของไมโครไพธอน

```python
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
```

## โค้ดตัวอย่างที่ 11: On-Chip Temperature Reading with ADC

ตัวอย่างนี้สาธิตการใช้งานวงจร **ADC (Analog-to-Digital Converter)** ภายใน **RP2040** เพื่ออ่านค่าจากเซ็นเซอร์วัดอุหภูมิที่อยู่ภายในชิป และต่อสัญญาณแบบแอนะล็อกเข้าช่องหมายเลข **4** ของวงจร **ADC**

```python
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** ตามลำดับ

```python
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
```

## โค้ดตัวอย่างที่ 12: TM1637 4-Digit 7-Segment Display

ตัวอย่างนี้สาธิตการใช้งานไลบรารีสำหรับ **TM1637** ซึ่งเป็นไอซีควบคุมการทำงานของโมดูล **4-Digit 7-Segment Display**&#x20;

ไฟล์ [`tm637.py`](https://github.com/mcauser/micropython-tm1637/blob/master/tm1637.py) ซึ่งเป็นไลไบรารีและมีโค้ดที่เขียนด้วยภาษาไมโครไพธอนสำหรับ **TM1637** สามารถดาวน์โหลดได้จาก **Github** ดังนั้นต้องดาวน์โหลดไฟล์นี้แล้วนำไปใส่ไว้ใน **File System** ของบอร์ด **RP2040**

การทำงานของโค้ดนี้ได้เลือกใช้ขา **GP15** และ **GP14** สำหรับขาสัญญาณ **CLK** แลพ **DIO** ของโมดูล **TM1637** ตามลำดับ (ใช้แรงดันไฟเลี้ยง **3.3V**) และจะแสดงผลตัวเลขเป็นนาฬิกาที่นับถอยหลัง เริ่มต้นที่ **2:00** นาที แล้วลดลงทีละหนึ่งทุก ๆ หนึ่งวินาที

```python
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 ](/files/-MTHBh8iITNgjyxLJazL)

## โค้ดตัวอย่างที่ 13: UART Loopback Test

ตัวอย่างนี้สาธิตการรับส่งข้อมูลผ่านทาง **UART** ของ **RP2040** ในลักษณะ **Serial Loopback** คือ ส่งข้อมูลออกไปทางขา **Tx** แล้วก็รับข้อมูลนั้นวกกลับเข้ามาที่ขา **Rx**&#x20;

**RP2040** มีวงจร **UART** จำนวน 2 ชุด คือ **UART0** และ **UART1** และสามารถเลือกใช้ขา **GPIO** ได้แตกต่างกัน (ดูได้จาก **Pico PinMap**) แต่ในตัวอย่างนี้ได้เลือกใช้ **UART0** และขา **GP0** และ **GP1** สำหรับขา **Tx** และ **Rx** ตามลำดับ และตั้งค่า **Baudrate** ไว้เท่ากับ **115200**

```python
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() )
```

## โค้ดตัวอย่างที่ 14: Rotary Encoder Input

โค้ดตัวอย่างนี้ สาธิตการใช้งานโมดูล **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**)

```python
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](/files/-MTJ-zHG9eDiCpPT-FNZ)

## โค้ดตัวอย่างที่ 15: Memory Info

ตัวอย่างนี้สาธิตการใช้คำสั่งจากโมดูล `gc` **(Garbage Collector)** ของไมโครไพธอน และใช้คำสั่งเพื่อตรวจสอบดูปริมาณการใช้หน่วยความจำ หรือที่ยังเหลืออยู่

```python
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`        &#x20;

## โค้ดตัวอย่างที่ 16: SH1106 OLED I2D Display

ตัวอย่างนี้สาธิตการใช้โมดูล **SH1106 OLED Display** ที่เชื่อมต่อด้วยบัส **I2C** โดยเลือกใช้ **I2C0** ของ **RP2040** และใช้ขา **GP16 / GP17** สำหรับขา **SDA/SCL** ตามลำดับ และนำมาแสดงข้อความเป็นตัวอย่าง

การใช้งานโมดูล **SH1106** ทำได้ไม่ยากเนื่องจากมีไลบรารีไว้ให้ใช้งาน สามารถดาวน์โหลดไฟล์ [`sh1106.py`](https://github.com/robert-hh/SH1106/blob/master/sh1106.py) จาก **Github** แล้วนำไปใส่ลงในไดรฟ์ของไมโครไพธอนสำหรับบอร์ด **RPi Pico**

```python
from 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](/files/-MTTcw4wWE5fbkdOn-A0)

## โค้ดตัวอย่างที่ 17: Ultrasonic Distance Sensor

โค้ดนี้สาธิตการสร้างสัญญาณเอาต์พุตแบบพัลส์ เพื่อส่งไปยังโมดูลเซนเซอร์วัดระยะห่างจากสิ่งกีดขวางด้วยคลื่นอัลตร้าโซนิก (**Ultrasonic Distance Sensor Module**) และวัดความกว้างของสัญญาณพัลส์ที่ได้จากโมดูลเพื่อนำมาคำนวณระยะห่าง

โมดูลเซนเซอร์มีขา **Trig (Trigger)** เป็นอินพุต และขา **Echo** เป็นเอาต์พุต (เลือกใช้รุ่นที่ทำงานได้โดยใช้แรงดันไฟเลี้ยง **+3.3V**) ไมโครคอนโทรลเลอร์จะต้องสร้างสัญญาณพัลส์ เช่น มีความกว้างช่วง **High** อย่างน้อย **10 usec** ไปยังขา **Trig** จากนั้นให้วัดความกว้างของสัญญาณพัลส์ที่ขา **Echo**&#x20;

การวัดความกว้างของสัญญาณพัลส์ที่ขา **Echo** จะต้องรอให้สัญญาณนั้น เปลี่ยนจาก **Low** เป็น **High** แล้วเปลี่ยนจาก **High** เป็น **Low** ตามลำดับ

ในตัวอย่างนี้ได้เลือกใช้ขา **GP19** และ **GP18** สำหรับขา **Trig** และ **Echo** ตามลำดับ&#x20;

```python
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) 
```

## โค้ดตัวอย่างที่ 18:  74HC595 Bit Shifting with SPI&#x20;

ตัวอย่างนี้สาธิตการส่งข้อมูลไปยังไอซี **74HC595** ที่มีขาเอาต์พุต 8 ขา ต่อกับโมดูล **8x LED Bar** สำหรับแสดงสถานะลอจิก และการส่งข้อมูลจะใช้วิธีเลื่อนบิตทีละบิตตามจังหวะสัญญาณ **Clock**&#x20;

การทำงานของ **74HC595** จะใช้ขาสัญญาณดังนี้ (ศึกษาเพิ่มเติมได้จาก **Datasheet** \[[**1**](https://www.ti.com/lit/ds/scls041i/scls041i.pdf)]\[[**2**](https://assets.nexperia.com/documents/data-sheet/74HC_HCT595.pdf)])

* **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**&#x20;

โค้ดนี้ใช้ขา **GPIO** ในการสร้างสัญญาณควบคุมและส่งข้อมูลไปยัง **74HC595** โดยเลือกใช้ขาดังนี้

* **GP9 = LOAD**&#x20;
* **GP10 = SCK**
* **GP11 = SDA**&#x20;

ฟังก์ชัน `send_byte()` ใช้สำหรับการส่งข้อมูลขนาดหนึ่งไบต์เท่านั้นไปยัง **74HC595** และเลือกได้ว่าจะเลื่อนข้อมูลออกแบบ **MSB First** หรือ **LSB First**

```python
import 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()
    # send a pulse to load pin
    load_pin.high()
    load_pin.low()

for data in [0x00,0x01,0x81,0x42,0x24,0x18,0x55,0xaa,0xff]:
    send_byte( data ^ 0xff, True ) # MSB first, use inverted bits
    time.sleep(0.5)
```

อีกแนวทางหนึ่งคือ เปลี่ยนมาใช้ **Hardware SPI** ของ **RP2040** ก็มีโค้ดตัวอย่างดังนี้ แต่มีข้อจำกัดของไมโครไพธอน **"rp2**" ในเวอร์ชันที่ใช้งานคือ สามารถใช้ได้เพียงโหมด **MSB First** เท่านั้น&#x20;

โค้ดนี้ใช้ขา **GPIO** เหมือนเดิม เมื่อเปลี่ยนมาใช้ **SPI** ก็จะตรงกับ **SPI1** ของ **RP2040** ( ขา **GP10**  ตรงกับ **SPI1\_SCK** และ ขา **GP11** ตรงกับ **SPI1\_MOSI** )

```python
from machine import Pin, SPI
import utime as time

# SDA=11 (SPI1_MOSI), SCK=10 (SPI1_SCK), LOAD=9
# use SPI bus id=1, SPI mode (0,0), clock frequency = 1 MHz
spi = SPI(1, baudrate=1_000_000, polarity=0, phase=0, bits=8,
          firstbit=SPI.MSB,
          sck=Pin(10), mosi=Pin(11), miso=None )
load = Pin(9,Pin.OUT)

def send_byte( spi, load, data ):
    load.low()
    # send byte, MSB first
    spi.write( bytes([data ^ 0xff]) ) # use inverted bits 
    load.high()
    load.low()

for data in [0x00,0x01,0x81,0x42,0x24,0x18,0x55,0xaa,0xff]:
    send_byte(spi, load, data )
    time.sleep(0.5)
data = 0x01
try:
    while True:
        send_byte( spi, load, data )
        data = ((data << 1) | (data >> 7)) & 0xff
        time.sleep(0.2)
except KeyboardInterrupt:
    pass
finally:
    send_byte( spi, load, 0x00 )
    spi.deinit()
```

![รูป: การต่อวงจรไอซี 74HC595 และโมดูล 8x LED Bar](/files/-MTTg-3iKp-Ncfc26b46)

## กล่าวสรุป

เนื้อหาในส่วนนี้ ได้นำเสนอตัวอย่างโค้ดไมโครไพธอนที่สามารถนำไปทดสอบการทำงาน โดยใช้บอร์ดไมโครคอนโทรลเลอร์ **RPi Pico (RP2040)** และจะเห็นได้ว่า มีการนำไปใช้งานร่วมกับโมดูลอิเล็กทรอนิกส์แบบต่าง ๆ ตัวอย่างเหล่านี้ จะช่วยให้ผู้เริ่มศึกษา เห็นรูปแบบการใช้คำสั่งต่าง ๆ ของไมโครไพธอน และเป็นแนวทางในการนำไปประยุกต์ใช้งานต่อไป

{% hint style="info" %}
**เผยแพร่ภายใต้ลิขสิทธิ์**\
**Attribution-ShareAlike 4.0 International (**[**CC BY-SA 4.0**](https://creativecommons.org/licenses/by-sa/4.0/)**)**
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://think-embedded.gitbook.io/micropython/micropython-for-rp2040-pico/rpi-pico-rp2040-code-examples.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
