commit 2a6cfbb1d99edc3d6e9d29b0ff65d70d1e6ab37a Author: graham sanderson Date: Wed Jan 20 10:52:33 2021 -0600 Initial Release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..aefcd6c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# MicroPython Examples + +Examples to accompany the "Pico Python SDK" book. diff --git a/adc/temperature.py b/adc/temperature.py new file mode 100644 index 0000000..a62a23a --- /dev/null +++ b/adc/temperature.py @@ -0,0 +1,12 @@ +import machine +import utime + +sensor_temp = machine.ADC(4) +conversion_factor = 3.3 / (65535) + +while True: + reading = sensor_temp.read_u16() * conversion_factor + + temperature = 27 - (reading - 0.706)/0.001721 + print(temperature) + utime.sleep(2) diff --git a/blink/blink.py b/blink/blink.py new file mode 100644 index 0000000..3fdb124 --- /dev/null +++ b/blink/blink.py @@ -0,0 +1,9 @@ +from machine import Pin, Timer + +led = Pin(25, Pin.OUT) +tim = Timer() +def tick(timer): + global led + led.toggle() + +tim.init(freq=2.5, mode=Timer.PERIODIC, callback=tick) diff --git a/i2c/1106oled/README.adoc b/i2c/1106oled/README.adoc new file mode 100644 index 0000000..e79915e --- /dev/null +++ b/i2c/1106oled/README.adoc @@ -0,0 +1,35 @@ += Using a SH1106-based OLED graphics display +:xrefstyle: short + +Display an image and text on I2C driven SH1106-based OLED graphics display +such as the Pimoroni Breakout Garden 1.12" Mono OLED https://shop.pimoroni.com/products/1-12-oled-breakout?variant=29421050757203 +. + +== Wiring information + +See <> for wiring instructions. + +[[oled-wiring-diagram]] +[pdfwidth=75%] +.Wiring the OLED to Pico using I2C +image::pico-and-oled.png[] + +== List of Files + +A list of files with descriptions of their function; + +i2c_1106oled_using_defaults.py:: The example code. +i2c_1106oled_with_freq.py:: The example code, explicitly sets a frequency. +sh1106.py:: SH1106 Driver Obtained from https://github.com/robert-hh/SH1106 + +== Bill of Materials + +.A list of materials required for the example +[[oled-bom-table]] +[cols=3] +|=== +| *Item* | *Quantity* | Details +| Breadboard | 1 | generic part +| Raspberry Pi Pico | 1 | http://raspberrypi.org/ +| Monochrome 128x128 I2C OLED Display | 1 | https://shop.pimoroni.com/products/1-12-oled-breakout?variant=29421050757203 +|=== diff --git a/i2c/1106oled/i2c_1106oled_using_defaults.py b/i2c/1106oled/i2c_1106oled_using_defaults.py new file mode 100644 index 0000000..53df68a --- /dev/null +++ b/i2c/1106oled/i2c_1106oled_using_defaults.py @@ -0,0 +1,34 @@ + # Display Image & text on I2C driven SH1106 OLED display +from machine import I2C, ADC +from sh1106 import SH1106_I2C +import framebuf + + +WIDTH = 128 # oled display width +HEIGHT = 128 # oled display height + +i2c = I2C(0) # Init I2C using I2C0 defaults, SCL=Pin(GP9), SDA=Pin(GP8), freq=400000 +print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address +print("I2C Configuration: "+str(i2c)) # Display I2C config + + +oled = SH1106_I2C(WIDTH, HEIGHT, i2c) # Init oled display + +# Raspberry Pi logo as 32x32 bytearray +buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + +# Load the raspberry pi logo into the framebuffer (the image is 32x32) +fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB) + +# Clear the oled display in case it has junk on it. +oled.fill(0) + +# Blit the image from the framebuffer to the oled display +oled.blit(fb, 96, 0) + +# Add some text +oled.text("Raspberry Pi",5,5) +oled.text("Pico",5,15) + +# Finally update the oled display so the image & text is displayed +oled.show() \ No newline at end of file diff --git a/i2c/1106oled/i2c_1106oled_with_freq.py b/i2c/1106oled/i2c_1106oled_with_freq.py new file mode 100644 index 0000000..1347206 --- /dev/null +++ b/i2c/1106oled/i2c_1106oled_with_freq.py @@ -0,0 +1,33 @@ +# Display Image & text on I2C driven ssd1306 OLED display +from machine import Pin, I2C +from sh1106 import SH1106_I2C +import framebuf + +WIDTH = 128 # oled display width +HEIGHT = 32 # oled display height + +i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=200000) # Init I2C using pins GP8 & GP9 (default I2C0 pins) +print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address +print("I2C Configuration: "+str(i2c)) # Display I2C config + + +oled = SH1106_I2C(WIDTH, HEIGHT, i2c) # Init oled display + +# Raspberry Pi logo as 32x32 bytearray +buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + +# Load the raspberry pi logo into the framebuffer (the image is 32x32) +fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB) + +# Clear the oled display in case it has junk on it. +oled.fill(0) + +# Blit the image from the framebuffer to the oled display +oled.blit(fb, 96, 0) + +# Add some text +oled.text("Raspberry Pi",5,5) +oled.text("Pico",5,15) + +# Finally update the oled display so the image & text is displayed +oled.show() diff --git a/i2c/1106oled/pico-and-oled.fzz b/i2c/1106oled/pico-and-oled.fzz new file mode 100644 index 0000000..2761ffa Binary files /dev/null and b/i2c/1106oled/pico-and-oled.fzz differ diff --git a/i2c/1106oled/pico-and-oled.png b/i2c/1106oled/pico-and-oled.png new file mode 100644 index 0000000..7f08cab Binary files /dev/null and b/i2c/1106oled/pico-and-oled.png differ diff --git a/i2c/1106oled/sh1106.py b/i2c/1106oled/sh1106.py new file mode 100644 index 0000000..82e8e7a --- /dev/null +++ b/i2c/1106oled/sh1106.py @@ -0,0 +1,227 @@ +# +# MicroPython SH1106 OLED driver, I2C and SPI interfaces +# +# The MIT License (MIT) +# +# Copyright (c) 2016 Radomir Dopieralski (@deshipu), +# 2017 Robert Hammelrath (@robert-hh) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Sample code sections +# ------------ SPI ------------------ +# Pin Map SPI +# - 3v - xxxxxx - Vcc +# - G - xxxxxx - Gnd +# - D7 - GPIO 13 - Din / MOSI fixed +# - D5 - GPIO 14 - Clk / Sck fixed +# - D8 - GPIO 4 - CS (optional, if the only connected device) +# - D2 - GPIO 5 - D/C +# - D1 - GPIO 2 - Res +# +# for CS, D/C and Res other ports may be chosen. +# +# from machine import Pin, SPI +# import sh1106 + +# spi = SPI(1, baudrate=1000000) +# display = sh1106.SH1106_SPI(128, 64, spi, Pin(5), Pin(2), Pin(4)) +# display.sleep(False) +# display.fill(0) +# display.text('Testing 1', 0, 0, 1) +# display.show() +# +# --------------- I2C ------------------ +# +# Pin Map I2C +# - 3v - xxxxxx - Vcc +# - G - xxxxxx - Gnd +# - D2 - GPIO 5 - SCK / SCL +# - D1 - GPIO 4 - DIN / SDA +# - D0 - GPIO 16 - Res +# - G - xxxxxx CS +# - G - xxxxxx D/C +# +# Pin's for I2C can be set almost arbitrary +# +# from machine import Pin, I2C +# import sh1106 +# +# i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000) +# display = sh1106.SH1106_I2C(128, 64, i2c, Pin(16), 0x3c) +# display.sleep(False) +# display.fill(0) +# display.text('Testing 1', 0, 0, 1) +# display.show() + +from micropython import const +import utime as time +import framebuf + + +# a few register definitions +_SET_CONTRAST = const(0x81) +_SET_NORM_INV = const(0xa6) +_SET_DISP = const(0xae) +_SET_SCAN_DIR = const(0xc0) +_SET_SEG_REMAP = const(0xa0) +_LOW_COLUMN_ADDRESS = const(0x00) +_HIGH_COLUMN_ADDRESS = const(0x10) +_SET_PAGE_ADDRESS = const(0xB0) + + +class SH1106: + def __init__(self, width, height, external_vcc): + self.width = width + self.height = height + 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 +# set shortcuts for the methods of framebuf + self.fill = fb.fill + self.fill_rect = fb.fill_rect + self.hline = fb.hline + self.vline = fb.vline + self.line = fb.line + self.rect = fb.rect + self.pixel = fb.pixel + self.scroll = fb.scroll + self.text = fb.text + self.blit = fb.blit + + self.init_display() + + def init_display(self): + self.reset() + self.fill(0) + self.poweron() + self.show() + + def poweroff(self): + self.write_cmd(_SET_DISP | 0x00) + + def poweron(self): + self.write_cmd(_SET_DISP | 0x01) + + def rotate(self, flag, update=True): + if flag: + self.write_cmd(_SET_SEG_REMAP | 0x01) # mirror display vertically + self.write_cmd(_SET_SCAN_DIR | 0x08) # mirror display hor. + else: + self.write_cmd(_SET_SEG_REMAP | 0x00) + self.write_cmd(_SET_SCAN_DIR | 0x00) + if update: + self.show() + + def sleep(self, value): + self.write_cmd(_SET_DISP | (not value)) + + 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 show(self): + for page in range(self.height // 8): + 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 + self.width + ]) + + def reset(self, res): + if res is not None: + res(1) + time.sleep_ms(1) + res(0) + time.sleep_ms(20) + res(1) + time.sleep_ms(20) + + +class SH1106_I2C(SH1106): + def __init__(self, width, height, i2c, res=None, addr=0x3c, + external_vcc=False): + self.i2c = i2c + self.addr = addr + self.res = res + self.temp = bytearray(2) + if res is not None: + res.init(res.OUT, value=1) + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.temp[0] = 0x80 # Co=1, D/C#=0 + self.temp[1] = cmd + self.i2c.writeto(self.addr, self.temp) + + def write_data(self, buf): + self.i2c.writeto(self.addr, b'\x40'+buf) + + def reset(self): + super().reset(self.res) + + +class SH1106_SPI(SH1106): + def __init__(self, width, height, spi, dc, res=None, cs=None, + external_vcc=False): + self.rate = 10 * 1000 * 1000 + dc.init(dc.OUT, value=0) + if res is not None: + res.init(res.OUT, value=0) + if cs is not None: + cs.init(cs.OUT, value=1) + self.spi = spi + self.dc = dc + self.res = res + self.cs = cs + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + if self.cs is not None: + self.cs(1) + self.dc(0) + self.cs(0) + self.spi.write(bytearray([cmd])) + self.cs(1) + else: + self.dc(0) + self.spi.write(bytearray([cmd])) + + def write_data(self, buf): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + if self.cs is not None: + self.cs(1) + self.dc(1) + self.cs(0) + self.spi.write(buf) + self.cs(1) + else: + self.dc(1) + self.spi.write(buf) + + def reset(self): + super().reset(self.res) \ No newline at end of file diff --git a/i2c/1306oled/README.adoc b/i2c/1306oled/README.adoc new file mode 100644 index 0000000..4edaa4f --- /dev/null +++ b/i2c/1306oled/README.adoc @@ -0,0 +1,32 @@ += Using a SSD1306-based OLED graphics display +:xrefstyle: short + +Display an image and text on I2C driven SSD1306-based OLED graphics display. + +== Wiring information + +See <> for wiring instructions. + +[[oled-wiring-diagram]] +[pdfwidth=75%] +.Wiring the OLED to Pico using I2C +image::pico-and-oled.png[] + +== List of Files + +A list of files with descriptions of their function; + +i2c_1306oled_using_defaults.py:: The example code. +i2c_1306oled_with_freq.py:: The example code, explicitly sets a frequency. + +== Bill of Materials + +.A list of materials required for the example +[[oled-bom-table]] +[cols=3] +|=== +| *Item* | *Quantity* | Details +| Breadboard | 1 | generic part +| Raspberry Pi Pico | 1 | http://raspberrypi.org/ +| Monochrome 128x32 I2C OLED Display | 1 | https://www.adafruit.com/product/931 +|=== diff --git a/i2c/1306oled/i2c_1306oled_using_defaults.py b/i2c/1306oled/i2c_1306oled_using_defaults.py new file mode 100644 index 0000000..2c87376 --- /dev/null +++ b/i2c/1306oled/i2c_1306oled_using_defaults.py @@ -0,0 +1,33 @@ + # Display Image & text on I2C driven ssd1306 OLED display +from machine import Pin, I2C +from ssd1306 import SSD1306_I2C +import framebuf + +WIDTH = 128 # oled display width +HEIGHT = 32 # oled display height + +i2c = I2C(0) # Init I2C using I2C0 defaults, SCL=Pin(GP9), SDA=Pin(GP8), freq=400000 +print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address +print("I2C Configuration: "+str(i2c)) # Display I2C config + + +oled = SSD1306_I2C(WIDTH, HEIGHT, i2c) # Init oled display + +# Raspberry Pi logo as 32x32 bytearray +buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + +# Load the raspberry pi logo into the framebuffer (the image is 32x32) +fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB) + +# Clear the oled display in case it has junk on it. +oled.fill(0) + +# Blit the image from the framebuffer to the oled display +oled.blit(fb, 96, 0) + +# Add some text +oled.text("Raspberry Pi",5,5) +oled.text("Pico",5,15) + +# Finally update the oled display so the image & text is displayed +oled.show() diff --git a/i2c/1306oled/i2c_1306oled_with_freq.py b/i2c/1306oled/i2c_1306oled_with_freq.py new file mode 100644 index 0000000..76133d7 --- /dev/null +++ b/i2c/1306oled/i2c_1306oled_with_freq.py @@ -0,0 +1,33 @@ +# Display Image & text on I2C driven ssd1306 OLED display +from machine import Pin, I2C +from ssd1306 import SSD1306_I2C +import framebuf + +WIDTH = 128 # oled display width +HEIGHT = 32 # oled display height + +i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=200000) # Init I2C using pins GP8 & GP9 (default I2C0 pins) +print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address +print("I2C Configuration: "+str(i2c)) # Display I2C config + + +oled = SSD1306_I2C(WIDTH, HEIGHT, i2c) # Init oled display + +# Raspberry Pi logo as 32x32 bytearray +buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + +# Load the raspberry pi logo into the framebuffer (the image is 32x32) +fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB) + +# Clear the oled display in case it has junk on it. +oled.fill(0) + +# Blit the image from the framebuffer to the oled display +oled.blit(fb, 96, 0) + +# Add some text +oled.text("Raspberry Pi",5,5) +oled.text("Pico",5,15) + +# Finally update the oled display so the image & text is displayed +oled.show() diff --git a/i2c/1306oled/pico-and-oled.fzz b/i2c/1306oled/pico-and-oled.fzz new file mode 100644 index 0000000..2761ffa Binary files /dev/null and b/i2c/1306oled/pico-and-oled.fzz differ diff --git a/i2c/1306oled/pico-and-oled.png b/i2c/1306oled/pico-and-oled.png new file mode 100644 index 0000000..7f08cab Binary files /dev/null and b/i2c/1306oled/pico-and-oled.png differ diff --git a/i2c/i2c.py b/i2c/i2c.py new file mode 100644 index 0000000..f59281a --- /dev/null +++ b/i2c/i2c.py @@ -0,0 +1,11 @@ +from machine import Pin, I2C + +i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=100000) +i2c.scan() +i2c.writeto(76, b'123') +i2c.readfrom(76, 4) + +i2c = I2C(1, scl=Pin(7), sda=Pin(6), freq=100000) +i2c.scan() +i2c.writeto_mem(76, 6, b'456') +i2c.readfrom_mem(76, 6, 4) diff --git a/i2c/i2c_without_freq.py b/i2c/i2c_without_freq.py new file mode 100644 index 0000000..9bd7c7e --- /dev/null +++ b/i2c/i2c_without_freq.py @@ -0,0 +1,3 @@ +from machine import I2C + +i2c = I2C(0) # defaults to SCL=Pin(9), SDA=Pin(8), freq=400000 diff --git a/irq/irq.py b/irq/irq.py new file mode 100644 index 0000000..a36fa72 --- /dev/null +++ b/irq/irq.py @@ -0,0 +1,5 @@ +from machine import Pin + +p2 = Pin(2, Pin.IN, Pin.PULL_UP) +p2.irq(lambda pin: print("IRQ with flags:", pin.irq().flags()), +Pin.IRQ_FALLING) diff --git a/multicore/multicore.py b/multicore/multicore.py new file mode 100644 index 0000000..bcacef7 --- /dev/null +++ b/multicore/multicore.py @@ -0,0 +1,12 @@ +import time, _thread, machine + +def task(n, delay): + led = machine.Pin(25, machine.Pin.OUT) + for i in range(n): + led.high() + time.sleep(delay) + led.low() + time.sleep(delay) + print('done') + +_thread.start_new_thread(task, (10, 0.5)) diff --git a/pio/neopixel_ring/README.adoc b/pio/neopixel_ring/README.adoc new file mode 100644 index 0000000..b51520e --- /dev/null +++ b/pio/neopixel_ring/README.adoc @@ -0,0 +1,31 @@ += Using PIO to drive a set of NeoPixel Ring (WS2812 LEDs) +:xrefstyle: short + +Combination of the PIO WS2812 demo with the Adafruit 'essential' NeoPixel example code to show off color fills, chases and of course a rainbow swirl on a 16-LED ring. + +== Wiring information + +See <> for wiring instructions. + +[[neopixel-wiring-diagram]] +[pdfwidth=75%] +.Wiring the 16-LED NeoPixel Ring to Pico +image::neopixel_ring.png[] + +== List of Files + +A list of files with descriptions of their function; + +neopixel_ring.py:: The example code. + +== Bill of Materials + +.A list of materials required for the example +[[ring-bom-table]] +[cols=3] +|=== +| *Item* | *Quantity* | Details +| Breadboard | 1 | generic part +| Raspberry Pi Pico | 1 | http://raspberrypi.org/ +| NeoPixel Ring | 1 | https://www.adafruit.com/product/1463 +|=== diff --git a/pio/neopixel_ring/neopixel_ring.fzz b/pio/neopixel_ring/neopixel_ring.fzz new file mode 100644 index 0000000..7be640b Binary files /dev/null and b/pio/neopixel_ring/neopixel_ring.fzz differ diff --git a/pio/neopixel_ring/neopixel_ring.png b/pio/neopixel_ring/neopixel_ring.png new file mode 100644 index 0000000..17e4396 Binary files /dev/null and b/pio/neopixel_ring/neopixel_ring.png differ diff --git a/pio/neopixel_ring/neopixel_ring.py b/pio/neopixel_ring/neopixel_ring.py new file mode 100644 index 0000000..0a8c407 --- /dev/null +++ b/pio/neopixel_ring/neopixel_ring.py @@ -0,0 +1,104 @@ +# Example using PIO to drive a set of WS2812 LEDs. + +import array, time +from machine import Pin +import rp2 + +# Configure the number of WS2812 LEDs. +NUM_LEDS = 16 +PIN_NUM = 6 +brightness = 0.2 + +@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24) +def ws2812(): + T1 = 2 + T2 = 5 + T3 = 3 + wrap_target() + label("bitloop") + out(x, 1) .side(0) [T3 - 1] + jmp(not_x, "do_zero") .side(1) [T1 - 1] + jmp("bitloop") .side(1) [T2 - 1] + label("do_zero") + nop() .side(0) [T2 - 1] + wrap() + + +# Create the StateMachine with the ws2812 program, outputting on pin +sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(PIN_NUM)) + +# Start the StateMachine, it will wait for data on its FIFO. +sm.active(1) + +# Display a pattern on the LEDs via an array of LED RGB values. +ar = array.array("I", [0 for _ in range(NUM_LEDS)]) + +########################################################################## +def pixels_show(): + dimmer_ar = array.array("I", [0 for _ in range(NUM_LEDS)]) + for i,c in enumerate(ar): + r = int(((c >> 8) & 0xFF) * brightness) + g = int(((c >> 16) & 0xFF) * brightness) + b = int((c & 0xFF) * brightness) + dimmer_ar[i] = (g<<16) + (r<<8) + b + sm.put(dimmer_ar, 8) + time.sleep_ms(10) + +def pixels_set(i, color): + ar[i] = (color[1]<<16) + (color[0]<<8) + color[2] + +def pixels_fill(color): + for i in range(len(ar)): + pixels_set(i, color) + +def color_chase(color, wait): + for i in range(NUM_LEDS): + pixels_set(i, color) + time.sleep(wait) + pixels_show() + time.sleep(0.2) + +def wheel(pos): + # Input a value 0 to 255 to get a color value. + # The colours are a transition r - g - b - back to r. + if pos < 0 or pos > 255: + return (0, 0, 0) + if pos < 85: + return (255 - pos * 3, pos * 3, 0) + if pos < 170: + pos -= 85 + return (0, 255 - pos * 3, pos * 3) + pos -= 170 + return (pos * 3, 0, 255 - pos * 3) + + +def rainbow_cycle(wait): + for j in range(255): + for i in range(NUM_LEDS): + rc_index = (i * 256 // NUM_LEDS) + j + pixels_set(i, wheel(rc_index & 255)) + pixels_show() + time.sleep(wait) + +BLACK = (0, 0, 0) +RED = (255, 0, 0) +YELLOW = (255, 150, 0) +GREEN = (0, 255, 0) +CYAN = (0, 255, 255) +BLUE = (0, 0, 255) +PURPLE = (180, 0, 255) +WHITE = (255, 255, 255) +COLORS = (BLACK, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE) + +print("fills") +for color in COLORS: + pixels_fill(color) + pixels_show() + time.sleep(0.2) + +print("chases") +for color in COLORS: + color_chase(color, 0.01) + +print("rainbow") +rainbow_cycle(0) \ No newline at end of file diff --git a/pio/pio_1hz.py b/pio/pio_1hz.py new file mode 100644 index 0000000..f891a25 --- /dev/null +++ b/pio/pio_1hz.py @@ -0,0 +1,33 @@ +# Example using PIO to blink an LED and raise an IRQ at 1Hz. + +import time +from machine import Pin +import rp2 + + +@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) +def blink_1hz(): + # Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000 + irq(rel(0)) + set(pins, 1) + set(x, 31) [5] + label("delay_high") + nop() [29] + jmp(x_dec, "delay_high") + + # Cycles: 1 + 7 + 32 * (30 + 1) = 1000 + set(pins, 0) + set(x, 31) [6] + label("delay_low") + nop() [29] + jmp(x_dec, "delay_low") + + +# Create the StateMachine with the blink_1hz program, outputting on Pin(25). +sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25)) + +# Set the IRQ handler to print the millisecond timestamp. +sm.irq(lambda p: print(time.ticks_ms())) + +# Start the StateMachine. +sm.active(1) diff --git a/pio/pio_blink.py b/pio/pio_blink.py new file mode 100644 index 0000000..566a3cf --- /dev/null +++ b/pio/pio_blink.py @@ -0,0 +1,28 @@ +import time +from rp2 import PIO, asm_pio +from machine import Pin + +# Define the blink program. It has one GPIO to bind to on the set instruction, which is an output pin. +# Use lots of delays to make the blinking visible by eye. +@asm_pio(set_init=rp2.PIO.OUT_LOW) +def blink(): + wrap_target() + set(pins, 1) [31] + nop() [31] + nop() [31] + nop() [31] + nop() [31] + set(pins, 0) [31] + nop() [31] + nop() [31] + nop() [31] + nop() [31] + wrap() + +# Instantiate a state machine with the blink program, at 1000Hz, with set bound to Pin(25) (LED on the rp2 board) +sm = rp2.StateMachine(0, blink, freq=1000, set_base=Pin(25)) + +# Run the state machine for 3 seconds. The LED should blink. +sm.active(1) +time.sleep(3) +sm.active(0) \ No newline at end of file diff --git a/pio/pio_exec.py b/pio/pio_exec.py new file mode 100644 index 0000000..d8cbc33 --- /dev/null +++ b/pio/pio_exec.py @@ -0,0 +1,27 @@ +# Example using PIO to turn on an LED via an explicit exec. +# +# Demonstrates: +# - using set_init and set_base +# - using StateMachine.exec + +import time +from machine import Pin +import rp2 + +# Define an empty program that uses a single set pin. +@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) +def prog(): + pass + + +# Construct the StateMachine, binding Pin(25) to the set pin. +sm = rp2.StateMachine(0, prog, set_base=Pin(25)) + +# Turn on the set pin via an exec instruction. +sm.exec("set(pins, 1)") + +# Sleep for 500ms. +time.sleep(0.5) + +# Turn off the set pin via an exec instruction. +sm.exec("set(pins, 0)") diff --git a/pio/pio_irq.py b/pio/pio_irq.py new file mode 100644 index 0000000..1080ae0 --- /dev/null +++ b/pio/pio_irq.py @@ -0,0 +1,25 @@ +import time +import rp2 + +@rp2.asm_pio() +def irq_test(): + wrap_target() + nop() [31] + nop() [31] + nop() [31] + nop() [31] + irq(0) + nop() [31] + nop() [31] + nop() [31] + nop() [31] + irq(1) + wrap() + + +rp2.PIO(0).irq(lambda pio: print(pio.irq().flags())) + +sm = rp2.StateMachine(0, irq_test, freq=1000) +sm.active(1) +time.sleep(1) +sm.active(0) \ No newline at end of file diff --git a/pio/pio_pinchange.py b/pio/pio_pinchange.py new file mode 100644 index 0000000..767c8e7 --- /dev/null +++ b/pio/pio_pinchange.py @@ -0,0 +1,46 @@ +# Example using PIO to wait for a pin change and raise an IRQ. +# +# Demonstrates: +# - PIO wrapping +# - PIO wait instruction, waiting on an input pin +# - PIO irq instruction, in blocking mode with relative IRQ number +# - setting the in_base pin for a StateMachine +# - setting an irq handler for a StateMachine +# - instantiating 2x StateMachine's with the same program and different pins + +import time +from machine import Pin +import rp2 + + +@rp2.asm_pio() +def wait_pin_low(): + wrap_target() + + wait(0, pin, 0) + irq(block, rel(0)) + wait(1, pin, 0) + + wrap() + + +def handler(sm): + # Print a (wrapping) timestamp, and the state machine object. + print(time.ticks_ms(), sm) + + +# Instantiate StateMachine(0) with wait_pin_low program on Pin(16). +pin16 = Pin(16, Pin.IN, Pin.PULL_UP) +sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16) +sm0.irq(handler) + +# Instantiate StateMachine(1) with wait_pin_low program on Pin(17). +pin17 = Pin(17, Pin.IN, Pin.PULL_UP) +sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17) +sm1.irq(handler) + +# Start the StateMachine's running. +sm0.active(1) +sm1.active(1) + +# Now, when Pin(16) or Pin(17) is pulled low a message will be printed to the REPL. diff --git a/pio/pio_pwm.py b/pio/pio_pwm.py new file mode 100644 index 0000000..b4c50b0 --- /dev/null +++ b/pio/pio_pwm.py @@ -0,0 +1,43 @@ +# Example of using PIO for PWM, and fading the brightness of an LED + +from machine import Pin +from rp2 import PIO, StateMachine, asm_pio +from time import sleep + + +@asm_pio(sideset_init=PIO.OUT_LOW) +def pwm_prog(): + pull(noblock) .side(0) + mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock + mov(y, isr) # ISR must be preloaded with PWM count max + label("pwmloop") + jmp(x_not_y, "skip") + nop() .side(1) + label("skip") + jmp(y_dec, "pwmloop") + + +class PIOPWM: + def __init__(self, sm_id, pin, max_count, count_freq): + self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin(pin)) + # Use exec() to load max count into ISR + self._sm.put(max_count) + self._sm.exec("pull()") + self._sm.exec("mov(isr, osr)") + self._sm.active(1) + self._max_count = max_count + + def set(self, value): + # Minimum value is -1 (completely turn off), 0 actually still produces narrow pulse + value = max(value, -1) + value = min(value, self._max_count) + self._sm.put(value) + + +# Pin 25 is LED on Pico boards +pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000) + +while True: + for i in range(256): + pwm.set(i ** 2) + sleep(0.01) diff --git a/pio/pio_spi.py b/pio/pio_spi.py new file mode 100644 index 0000000..eb5490a --- /dev/null +++ b/pio/pio_spi.py @@ -0,0 +1,48 @@ +from machine import Pin + +@rp2.asm_pio(out_shiftdir=0, autopull=True, pull_thresh=8, autopush=True, push_thresh=8, sideset_init=(rp2.PIO.OUT_LOW, rp2.PIO.OUT_HIGH), out_init=rp2.PIO.OUT_LOW) +def spi_cpha0(): + # Note X must be preinitialised by setup code before first byte, we reload after sending each byte + # Would normally do this via exec() but in this case it's in the instruction memory and is only run once + set(x, 6) + # Actual program body follows + wrap_target() + pull(ifempty) .side(0x2) [1] + label("bitloop") + out(pins, 1) .side(0x0) [1] + in_(pins, 1) .side(0x1) + jmp(x_dec, "bitloop") .side(0x1) + + out(pins, 1) .side(0x0) + set(x, 6) .side(0x0) # Note this could be replaced with mov x, y for programmable frame size + in_(pins, 1) .side(0x1) + jmp(not_osre, "bitloop") .side(0x1) # Fallthru if TXF empties + + nop() .side(0x0) [1] # CSn back porch + wrap() + + +class PIOSPI: + + def __init__(self, sm_id, pin_mosi, pin_miso, pin_sck, cpha=False, cpol=False, freq=1000000): + assert(not(cpol or cpha)) + self._sm = rp2.StateMachine(sm_id, spi_cpha0, freq=4*freq, sideset_base=Pin(pin_sck), out_base=Pin(pin_mosi), in_base=Pin(pin_sck)) + self._sm.active(1) + + # Note this code will die spectacularly cause we're not draining the RX FIFO + def write_blocking(wdata): + for b in wdata: + self._sm.put(b << 24) + + def read_blocking(n): + data = [] + for i in range(n): + data.append(self._sm.get() & 0xff) + return data + + def write_read_blocking(wdata): + rdata = [] + for b in wdata: + self._sm.put(b << 24) + rdata.append(self._sm.get() & 0xff) + return rdata \ No newline at end of file diff --git a/pio/pio_uart_tx.py b/pio/pio_uart_tx.py new file mode 100644 index 0000000..e2afb1a --- /dev/null +++ b/pio/pio_uart_tx.py @@ -0,0 +1,42 @@ +# Example using PIO to create a UART TX interface + +from machine import Pin +from rp2 import PIO, StateMachine, asm_pio + +UART_BAUD = 115200 +PIN_BASE = 10 +NUM_UARTS = 8 + + +@asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT) +def uart_tx(): + # Block with TX deasserted until data available + pull() + # Initialise bit counter, assert start bit for 8 cycles + set(x, 7) .side(0) [7] + # Shift out 8 data bits, 8 execution cycles per bit + label("bitloop") + out(pins, 1) [6] + jmp(x_dec, "bitloop") + # Assert stop bit for 8 cycles total (incl 1 for pull()) + nop() .side(1) [6] + + +# Now we add 8 UART TXs, on pins 10 to 17. Use the same baud rate for all of them. +uarts = [] +for i in range(NUM_UARTS): + sm = StateMachine( + i, uart_tx, freq=8 * UART_BAUD, sideset_base=Pin(PIN_BASE + i), out_base=Pin(PIN_BASE + i) + ) + sm.active(1) + uarts.append(sm) + +# We can print characters from each UART by pushing them to the TX FIFO +def pio_uart_print(sm, s): + for c in s: + sm.put(ord(c)) + + +# Print a different message from each UART +for i, u in enumerate(uarts): + pio_uart_print(u, "Hello from UART {}!\n".format(i)) diff --git a/pio/pio_ws2812.py b/pio/pio_ws2812.py new file mode 100644 index 0000000..4442337 --- /dev/null +++ b/pio/pio_ws2812.py @@ -0,0 +1,52 @@ +# Example using PIO to drive a set of WS2812 LEDs. + +import array, time +from machine import Pin +import rp2 + +# Configure the number of WS2812 LEDs. +NUM_LEDS = 8 + + +@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24) +def ws2812(): + T1 = 2 + T2 = 5 + T3 = 3 + wrap_target() + label("bitloop") + out(x, 1) .side(0) [T3 - 1] + jmp(not_x, "do_zero") .side(1) [T1 - 1] + jmp("bitloop") .side(1) [T2 - 1] + label("do_zero") + nop() .side(0) [T2 - 1] + wrap() + + +# Create the StateMachine with the ws2812 program, outputting on Pin(22). +sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22)) + +# Start the StateMachine, it will wait for data on its FIFO. +sm.active(1) + +# Display a pattern on the LEDs via an array of LED RGB values. +ar = array.array("I", [0 for _ in range(NUM_LEDS)]) + +# Cycle colours. +for i in range(4 * NUM_LEDS): + for j in range(NUM_LEDS): + r = j * 100 // (NUM_LEDS - 1) + b = 100 - j * 100 // (NUM_LEDS - 1) + if j != i % NUM_LEDS: + r >>= 3 + b >>= 3 + ar[j] = r << 16 | b + sm.put(ar, 8) + time.sleep_ms(50) + +# Fade out. +for i in range(24): + for j in range(NUM_LEDS): + ar[j] >>= 1 + sm.put(ar, 8) + time.sleep_ms(50) diff --git a/pwm/pwm_fade.py b/pwm/pwm_fade.py new file mode 100644 index 0000000..7264eda --- /dev/null +++ b/pwm/pwm_fade.py @@ -0,0 +1,25 @@ +# Example using PWM to fade an LED. + +import time +from machine import Pin, PWM + + +# Construct PWM object, with LED on Pin(25). +pwm = PWM(Pin(25)) + +# Set the PWM frequency. +pwm.freq(1000) + +# Fade the LED in and out a few times. +duty = 0 +direction = 1 +for _ in range(8 * 256): + duty += direction + if duty > 255: + duty = 255 + direction = -1 + elif duty < 0: + duty = 0 + direction = 1 + pwm.duty_u16(duty * duty) + time.sleep(0.001) diff --git a/sqi/sqi.py b/sqi/sqi.py new file mode 100644 index 0000000..5af006d --- /dev/null +++ b/sqi/sqi.py @@ -0,0 +1,11 @@ +from machine import SPI + +spi = SPI(0) +spi = SPI(0, 100_000) +spi = SPI(0, 100_000, polarity=1, phase=1) + +spi.write('test') +spi.read(5) + +buf = bytearray(3) +spi.write_readinto('out', buf)