[Digi-Key Follow me Issue 1] + Task 2: Driving peripherals
[Copy link]
1. Hardware Construction
I found a 1*40pin 2.54mm pin header, split it in two, and soldered it to the development board so that it can be inserted into the expansion board, as shown in Figure 1.
Figure 1
In this way, we can connect peripherals such as LED, OLED display, buzzer, etc. to the corresponding ports for testing.
2. Driving RGB LED Stick (10-WS2813 Mini)
视频3
The Grove-RGB LED Stick (10-WS2813 Mini) can operate in a voltage range of 3.3V to 5V, supports continuous transmission of signal breakpoints , and when one LED is damaged, the other LEDs can work normally. It has a built-in control circuit with 265 gray levels, 3535 components, and dual signal wires.
Key Features:
- The control circuit and RGB chip are integrated into a 3535 component to form an externally controlled pixel.
- Intelligent reverse connection protection.
- The built-in signal shaping circuit is used to achieve signal waveform shaping, and the waveform is not distorted.
- The grayscale level of each pixel is 256, realizing "256*256*256=16777216" full-color display with a refresh rate of 2KHz.
- Serial cascade interface, data reception and decoding rely on only one signal line.
- Dual signal line version, signal breakpoint continuous transmission.
- There is no additional circuit for the transmission signal between any two points with a distance greater than 5M.
- When the refresh rate is 30fps, the number of cascades is at least 1024 pixels.
- The data transmission speed can reach up to 800Kbps.
- Good color consistency and reliability, high cost performance.
As long as two consecutive lights on the light stick are not broken, the other lights can light up normally, as shown in Figure 2.
Figure 2
of WS2812 LEDs, we can modify it to write a light bar with 10 lamp beads. The modified code is as follows:
# Example using PIO to drive a set of 10-WS2813 LEDs.
import array, time
from machine import Pin
import rp2
# Configure the number of WS2813 LEDs.
NUM_LEDS = 10
PIN_NUM = 16
brightness = 0.2
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2813():
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 ws2813 program, outputting on pin
sm = rp2.StateMachine(0, ws2813, 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)
Connect the device to the computer, run neopixel_ring_10.py in the Mu software, connect the light board to the D16 port, and you will see the light bar as shown in Video 3.
The Mu port output is shown in Figure 3.
Figure 3
Figure 4
3. OLED display
Grove - OLED Display 0.96" (SSD1315) The Grove-OLED Display 0.96" (SSD1315) is a monochrome (white) 128×64 pixel passive display matrix module with Grove I2C interface. The Grove-OLED Display 0.96" has better performance and is a quarter of the price.
feature
- Monochrome clear display: While inheriting the characteristics of high contrast, high brightness and low power consumption, the display effect has been improved
- Industrial-grade operating temperature: wide operating temperature range: -40°C~+85°C
- High cost performance: The monitor adopts the upgraded SSD1315 chip, which has stronger performance and lower price.
- Grove Unified Socket: This product ensures "plug and play" and can be used in various applications.
When modifying the code in the demo, Mu does not know how to import the ssd1306.py driver, resulting in the problem shown in Figure 5 below. (I still did not understand it after the live broadcast, but the forum friends shared and supplemented it and I understood it instantly)
Figure 5
Then add the OLED driver directly to the file header, as follows:
# 在这里写上你的代码 :-)
# Display Image & text on I2C driven ssd1315 OLED display
from machine import Pin, I2C
from micropython import const
import framebuf
#from ssd1306 import SSD1306_I2C
#import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer):
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)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO,
self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV,
0x80,
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
# display
SET_CONTRAST,
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01,
): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
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):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=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.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)
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()
After running, the result is as shown in the figure. How to add it to the Mu environment requires further information and subsequent supplements from netizens . Copy the ssd1306 part into a separate file and operate it through the file on Mu.
4. Buzzer
The operation of the buzzer is to test turning on/off the buzzer according to the buzzer test code of the live class teacher. My buzzer is connected to D18, and the code is as follows:
# 在这里写上你的代码 :-)
import time,machine
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
i2c=I2C(0,sda=Pin(8),scl=Pin(9),freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
buzzer_pin = machine.Pin(18,machine.Pin.OUT)
while True:
buzzer_pin.value(1)
oled.fill(0)
oled.text('buzzer on',0,12,1)
oled.show()
time.sleep(0.3)
buzzer_pin.value(0)
oled.fill(0)
oled.text('buzzer off',0,12,1)
oled.show()
time.sleep(0.7)
First, click the file on the Mu software, drag ssd1306.py to the PICO, click the file again, run it, edit the code, and run it directly to hear the buzzer sound. The status of the buzzer is displayed on the OLED.
视频4
V. Summary
Through this activity, I learned a lot of knowledge. Through practice, I figured out what I didn't understand, and then with the explanations from the teachers and guidance from the forum friends, I got twice the result with half the effort. I am very grateful to EEWORLD and the teachers.
|