> For the complete documentation index, see [llms.txt](https://think-embedded.gitbook.io/micropython/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://think-embedded.gitbook.io/micropython/micropython-for-rp2040-pico/rpi-pico-rp2040-code-examples.md).

# 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 %}
