6月30日
总结
此次项目根据需要完成的四个基础任务可分为四个部分,分别是熟悉mircopython的基本语法、驱动外设、同步网络时间和实现定位功能。其中,熟悉mircopython的基本语法这一部分又可分为环境搭建、固件下载和点灯,驱动外设这一部分则包括了驱动蜂鸣器和驱动OLED。根据OLED上显示的结果不同,驱动OLED部分又可分为显示“Hello World"、显示M蛇图形、显示树莓图形和驱动RP2040内的温度传感器并显示实时结果。同步网络时间这一部分可分为连接网络和同步网络时间。实现定位功能这一部分则是主要阐述如何使用GPS模块实现定位。
首先是固件下载。在官方网站下载好uf2文件 rp2-pico-w-20230619-unstable-v1.20.0-226-gca79b4961.uf2 (1.53 MB, 下载次数: 0)
下好固件之后,先按住开发板上的BOOTSEL按键,再给板子上电,此时电脑会将Pico识别为U盘,出现一个名为RPI-RP2的盘,将下载好的UF2文件复制到此盘中就行。
其次是环境搭建。先下载下载Mu Editor软件,这个软件好像有人上传了,大家可以找找。或者可以到https://codewith.mu/进行下载。下载好之后进行安装,Mu Editor的安装还挺简单的,这里就不赘述了。安装完成之后运行Mu,在选择模式处选择RP2040,一个紫色的树莓标识,很明显。
安装完成之后,如果板子已上电,要注意Mu右下角处的图标是否出现红叉。如果出现红叉,可以拔了再插试试。如果没有出现,我们就可以进行调试了。
print("Hello World")
在Mu的输入界面输入上面的代码,若下方打印出同样的语句,则环境搭建成功。如下图所示
最后是点灯。点灯属于驱动LED,本应放在驱动外设这部分,但因为点灯作为搭建环境成功之后迈入门槛的第一步,成功点灯能为持有者带来极大的成就感,又有助于熟悉mircopython的基本语法,为后续打下良好的基础,所以就放在了熟悉mircopython的基本语法这一部分。代码如下:
import machine
import time
led = machine.Pin('LED',machine.Pin.OUT)
while(True):
led.on()
time.sleep(.5)
led.off()
time.sleep(.5)
运行此代码,即可看到pico w上的LED有规律地闪烁。点灯成功!
首先是驱动蜂鸣器。将Buzzer V1.2的扩展模块通过4pin连接线接入Grove的A0接口,黑线对准GND,其他口也一一对齐,可选择高低电平反转驱动蜂鸣器发声。代码如下:
import machine
from machine import Pin
import utime
buzzer = machine.Pin("GP26", machine.Pin.OUT)
while True:
buzzer.on()
utime.sleep(1)
buzzer.off()
utime.sleep(1)
运行此代码后,蜂鸣器发声,它发出的声音较为尖锐,具体的声音是怎样的,可以在上面的视频中了解到。
然后是驱动OLED。将OLED模块接入I2C0接口。想要驱动OLED,要加载一个OLED的驱动包才能完成驱动这一过程。此OLED的型号为SSD1315,是以SSD1306为驱动芯片的,所以可以使用SSD1306的驱动包。先下载好SSD1306的驱动包,具体代码放在下面:
from micropython import const
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)
将它加载到pico w里,可以在Mu Editor中加载。具体为先复制以上代码,粘贴到Mu Editor中并生成.py文件,在此软件中的工具栏点击“文件”(若文件选项为灰色,先点击REPL选项,此时文件选项为黄色,点击文件选项)下方会出现如图
左边是pico w上的.py文件,右边是电脑上的.py文件,将右边的SSD1306.py拖动到左边,则此时SSD1306驱动包已加载成功。如果在Mu Editor上加载的包检测不到或是时灵时不灵,也可以在Thonny上加载驱动包。安装Thonny并打开的最后一步会让使用者选择语言,下方就有一个不同版本的选择,可以选择标准还是树莓派。此时选择树莓派,打开后的界面和Mu Editor很像。在这里加载代码,会让使用者选择加载到电脑还是pico w。选择加载到pico w,此时驱动包应该就已经加载完成了,可以到Mu Editor中运行主程序了。
首先实现一个比较简单的显示功能,让OLED显示”Hello World"。代码如下:
import time
from machine import Pin, I2C
from SSD1306 import SSD1306_I2C
# Grove Shield For Pi Pico I2C0
i2c = I2C(0, sda=Pin(8), scl=Pin(9), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
oled.text("Hello World!", 0, 0, 1)
oled.show()
运行此代码后,OLED上会显示Hello World字样。如上所示。
对驱动OLED有了一个基本的认识之后,可以驱动OLED显示M蛇图形。具体代码如下:
from machine import Pin, I2C
from SSD1306 import SSD1306_I2C
#Grove Shield for Pi Pico I2C0
i2c = I2C(0, sda = Pin(8), scl = Pin(9), freq = 400000)
oled = SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.fill_rect(0, 0, 32, 32, 1)
oled.fill_rect(2, 2, 28, 28, 0)
oled.vline(9, 8, 22, 1)
oled.vline(16, 2, 22, 1)
oled.vline(23, 8, 22, 1)
oled.fill_rect(26, 24, 2, 4, 1)
oled.text('MicroPython', 40, 0, 1)
oled.text('SSD1306', 40, 12 ,1)
oled.text('OLED 128x64', 40, 24, 1)
oled.show()
运行此代码,OLED上会显示M蛇图形。如下:
也可以驱动OLED显示树莓图形。具体代码如下:
# 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()
运行此代码,OLED上会显示树莓图形。如下:
也可以驱动OLED显示驱动PR2040内置温度传感器的实时示数。具体代码如下:
import time
from machine import Pin, I2C
from SSD1306 import SSD1306_I2C
#Grove Shield for Pi Pico I2C0
i2c = I2C(0, sda = Pin(8), scl = Pin(9), freq = 400000)
oled = SSD1306_I2C(128, 64, i2c)
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65536)
while True:
reading = sensor_temp.read_u16() * conversion_factor
temp = 27 - (reading - 0.706) / 0.001721
oled.fill(0)
oled.text(f'temp is {temp:.1f}', 0, 12, 1)
oled.show()
time.sleep(0.5)
运行此代码,OLED上会显示温度传感器的实时示数。如下:
驱动外设到这里就告一段落了。
首先是连接网络。有一点要注意:pico w只能连接2.4GHz频段的网络。代码如下:
import network
import time
from machine import RTC
import ntptime
import urequests
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('wifi/热点名称', 'wifi/热点密码')
max_wait = 10
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('等待连接')
time.sleep(1)
if wlan.status() != 3:
raise RuntimeError('联网失败')
else:
print('网络已连接')
status = wlan.ifconfig()
print( 'ip = ' + status[0] )
r = urequests.get("https://www.digikey.cn/zh")
print(r.status_code)
print(r.headers)
r.close()
如果连接成功会打印出”网络已连接“字样,并返回200的数值,具体如下:
此时便已成功连接上网络。
然后是同步网络时间。具体代码如下:
import network
import time
from machine import RTC
import ntptime
import urequests
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('wifi/热点名称', 'wifi/热点密码')
max_wait = 10
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('等待连接')
time.sleep(1)
if wlan.status() != 3:
raise RuntimeError('联网失败')
else:
print('网络已连接')
status = wlan.ifconfig()
print( 'ip = ' + status[0] )
def show_local_time(timezone=8):
rtc = RTC()
now = time.time()
now += timezone * 3600
t = time.localtime(now)
print(f'{t[0]}-{t[1]:02d}-{t[2]:02d} {t[3]:02d}:{t[4]:02d}:{t[5]:02d}')
rtc = RTC()
rtc.datetime((2020, 1, 1, 3, 9, 0, 0, 0))
print('校时前系统时间:')
show_local_time()
print('开始NTP校时. . .')
ntptime.host = 'cn.pool.ntp.org'
ntptime.settime()
print(f'校时后系统时间:')
show_local_time()
运行此代码,会出现如下
此时便已成功同步网络时间。
实现定位功能需要使用GNSS模块进行定位。先把micropyGPS库导入到pico w里,这是micropyGPS库的地址 ,可在里面下载好,以便之后导入。具体导入过程在上面导入SSD1306驱动包时已经有详细描述,此处不再赘述。首先确保GNSS模块处于露天环境下,露天环境有利于GNSS模块进行定位功能。此时小蓝灯一闪一闪,可运行如下代码
from machine import UART, Pin
import time
from micropyGPS import MicropyGPS
uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
time.sleep(0.1)
rxData = bytes()
my_gps = MicropyGPS()
while True:
if uart0.any():
stat = my_gps.update(uart0.read(1).decode("ascii"))
# Note the conversion to to chr, UART outputs ints normally
if stat:
print("Latitude:", my_gps.latitude_string())
print("Longitude:", my_gps.longitude_string())
print(
"Speed:",
my_gps.speed_string("kph"),
"or",
my_gps.speed_string("mph"),
"or",
my_gps.speed_string("knot"),
)
print("Date (Long Format):", my_gps.date_string("long"))
print("Date (Short D/M/Y Format):", my_gps.date_string("s_dmy"))
print("timestamp (Short [H,M,S] Format):", my_gps.timestamp)
stat = None
等待片刻,运行结果如下
如上图所示,已成功使用GNSS模块完成定位功能。
心得体会
感谢Digi-Key联合EEWORLD举办的这个"Follow me活动",让我有机会可以接触到Raspberry Pi Pico W这么一款有趣好玩的开发板,在完成这四个基础任务的过程中得以逐渐熟悉mircopython这门高级语言的基本语法和Mu Editor这款软件的基本使用方法,同时也在这期间熟悉了OLED、蜂鸣器和GNSS模块的基本使用方法。在发现问题中不断解决问题,翻阅大佬前辈们的帖子、上网搜索解决方法,经过不断的摸索,成功解决了完成这四个基础任务中途遇到的困难,学会了稍有妥协地将驱动包导入pico w中。此次活动令我受益匪浅,既获得了一套包括外设和主板的开发板套件,又收获了满满的学习经验。在不断摸索中学习成长,在继续探索中不断进步。
此次活动对小白非常友好,必须完成的四个基础任务并不难,有大佬前辈的帖子可以翻阅借鉴,还能在微信群与大佬前辈们交流,就是直播教学活动有点少,希望这样的活动能够多多举办,也希望以后的直播教学能够多一点或者详细一点,比如导入驱动包那一部分。
代码
-
led.py
(182 Bytes, 下载次数: 0)
-
Buzzer.py
(216 Bytes, 下载次数: 0)
-
SSD1306.py
(4.46 KB, 下载次数: 0)
-
HelloWorldOLED.py
(271 Bytes, 下载次数: 0)
-
oledhelloworld.py
(4.92 KB, 下载次数: 0)
-
drawMsnakegraph.py
(519 Bytes, 下载次数: 0)
-
drawRaspberrygraph.py
(1.61 KB, 下载次数: 0)
-
drivetheTemperaturesensor.py
(503 Bytes, 下载次数: 0)
-
network.py
(626 Bytes, 下载次数: 0)
-
networkandsynchronizetime.py
(979 Bytes, 下载次数: 0)
-
position.py
(998 Bytes, 下载次数: 1)
|