【得捷电子Follow me第1期】+ 任务2 驱动外设
[复制链接]
一、硬件搭建
找了1*40pin 2.54mm排针,一分为二,焊接到开发板上,以便插入扩展地板上,如图1所示。
图1
这样我们就可以将LED、OLED显示屏、蜂鸣器等外设接入对应端口进行测试了。
二、驱动RGB LED Stick(10-WS2813 Mini)
视频3
Grove-RGB LED Stick(10-WS2813 Mini)可在3.3V至5V的电压范围内工作,支持信号断点连续传输,当一个LED损坏,其他LED可正常工作,具有265个灰度级、3535个元件的内置控制电路和双信号导线。
主要特性:
- 控制电路和RGB芯片集成在一个3535组件中,形成一个外部控制像素。
- 智能反向连接保护。
- 采用内置的信号整形电路实现信号波形整形,且波形不失真信号发生。
- 每个像素的灰度级为256级,实现“256*256*256=16777216”全彩显示刷新频率达到2KHz。
- 串行级联接口,数据接收和解码仅依赖于一条信号线。
- 双信号线版本,信号断点连续传输。
- 任意两点距离大于5M的传输信号无任何增加电路。
- 当刷新率为30fps时,级联数至少为1024像素。
- 数据传输速度最高可达800Kbps。
- 色彩一致性可靠性好,性价比高。
灯棒上只要是不是连续2个灯坏掉,其他灯就可以正常亮,如图2所示。
图2
根据WS2812 LEDs的demo 修改来编写10个灯珠的灯棒,修改代码如下所示:
# 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)
连接设备到电脑,在Mu软件中运行neopixel_ring_10.py,将灯板连接到D16口,你会看到灯棒如视频3所示。
Mu端口输出如图3所示。
图3
图4
三、OLED显示屏
Grove - OLED Display 0.96" (SSD1315)Grove-OLED显示器0.96英寸(SSD1315)是带有Grove I2C接口的单色(白色)128×64像素无源显示矩阵模块Grove-OLED显示屏0.96英寸,性能更好,价格为四分之一。
特征
- 单色清晰显示:在继承了高对比度、高亮度和低功耗的特点的同时,显示效果得到了提升
- 工业级工作温度:工作温度范围宽:-40℃~+85℃
- 高性价比:显示器采用升级的SSD1315芯片,性能更强,价格更低。
- Grove统一套接字:该产品确保“即插即用”,可用于各种应用。
在demo中进行修改代码,Mu不知道如何导入ssd1306.py驱动,出现图下图5所示问题。(直播后我也没整明白,坛友分享补充瞬间明白了)
图5
然后直接将OLED驱动加到了文件头部,如下:
# 在这里写上你的代码 :-)
# 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()
运行后,如图所示结果。如何加入到Mu环境中需要再查找相关资料,网友的后续补充。将ssd1306部分单独拷贝一个文件,通过Mu上的文件进行操作即可。
四、蜂鸣器
蜂鸣器的操作,根据直播课老师的蜂鸣器测试代码来测试打开/关闭蜂鸣器,我的蜂鸣器连接到D18上,代码如下:
# 在这里写上你的代码 :-)
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)
首先在Mu软件上点击文件,将ssd1306.py托到PICO上,再次点击文件,运行可用,编辑代码,直接运行即可听到蜂鸣器响,OLED上显示蜂鸣器的状态。
视频4
五、总结
通过这个活动,学到了很多知识,通过实践知道哪里不懂不会,再由老师讲解,坛友点拨,真是事半功倍,非常感谢EEWORLD,感谢老师们。
|