【得捷Follow me第4期】玩转W5500-EVB-Pico (全流程综合贴)
[复制链接]
2024.2.20(存个草稿先)
2024.2.21
这次给大家带来的是Follow Me第四期任务的速通教学(又鸽了很久
本次活动时间比较紧,加上放假在家也没啥动力,发帖比较晚
感谢主办方eeworld、得捷电子的支持,感谢各位提前发帖的大神坛友
于得捷下单的元件所制作的模块后续均会开源
内容一:3-5分钟短视频
内容二:本篇
内容三:本次活动的资料包(包含任务代码、官方资料、还有一些乱七八糟的东西)
附加:b站视频链接(同内容一,改完Type-C后上传)
1、Quick Start
开发环境搭建
首先我们快速搭建一下我们所需的开发环境,本次活动在Thonny中使用MicroPython编程。
(1)IDE安装
打开Thonny官网,把鼠标挪到右上角“download version...”上,再点击推荐给你的版本,就可以下载了
下载完成后打开安装包,一路“NEXT”下去就行,几十MB对C盘非常友好
安装完成后双击快捷方式打开Thonny
打开后界面如下:
(2)硬件简介与刷固件
本次活动使用到的是Wiznet的W5500-EVB-Pico,ad格式的原理图和pcb都是可以下载到的,资料已经放在了压缩包中
W5500-EVB-Pico板载了一块w5500通过spi与RP2040通信,支持python、c等语言。
整体还是非常精致的哈
当然,我们想要用micropython进行编程的话还需要将对应的固件刷到芯片内,固件同样已经打包进了资料包,或者从官网下载也是行的:地址
尽量还是选择较新的固件
固件下载完成后即可准备刷写mp的固件了,找一根micro usb-usb A的线,将micro usb一端连接开发板,按住BOOT键👇
再将usb A连接至电脑,即会弹出一个u盘,松开BOOT,将下载好的.uf2文件(即我们的固件)复制进u盘,粘贴完成后最好再按一下run键,固件就安装完成了
树莓派官网也有十分易懂的烧写过程演示,这里分享一下:
最后,如果我们在Thonny中可以在右下角的上拉菜单中找到“Micropython...Pi PICO ...COM...”,点击并且shell窗口无警告,说明成功刷写固件,我们就可以正常使用了
2、入门任务
BLINK,串口HelloWorld(附加呼吸灯)
(1)点个灯先
合乎嵌礼,点灯
python点灯的代码很简单,网上也一堆例程,随便写写
blink.py内容如下:
import machine
import time
#设置引脚
led_pin = machine.Pin(25, machine.Pin.OUT)
while True:
led_pin.value(1) # 点亮LED
time.sleep(0.5) # 等待0.5秒
led_pin.value(0) # 熄灭LED
time.sleep(0.5) # 等待0.5秒
实际效果如下:
blink
修改一下代码,调用PWM函数,即可实现呼吸灯效果
import machine
import time
#设置引脚
led_pin = machine.Pin(25, machine.Pin.OUT)
def breathe_led():
pwm = machine.PWM(led_pin)
while True:
for duty_cycle in range(0, 5000, 100): #变亮
pwm.duty_u16(duty_cycle)
time.sleep_ms(10)
for duty_cycle in range(5000, 0, -100): #变暗
pwm.duty_u16(duty_cycle)
time.sleep_ms(10)
breathe_led()
实际效果如下:
breath
使用python点灯流程十分简单,非常适合新手上手
(2)串口helloworld
这里就需要用到usb转串口的模块了,市面上常见的当然是沁恒的340x系列。
放假的时候手边只有一个DAP,有虚拟串口功能,直接拿来用
我们需要用到的是DAP的TX、RX、和GND(丝印三角):
与DAP相连的是PICO的UART0和GND,共地十分重要,后续SD卡模块和电机模块都是与PICO共地的
硬件连接:
DAP和PICO的连接参考以下表格。DAP需要使用Type-C转USB数据线与电脑连接
DAP PICO PIN
TX RX 1
RX TX 0
GND GND
使用一根跳线连接PICO至电脑网口
连好的实物图:
代码也很简单,实现串口打印Helloworld的代码如下:
from machine import Pin,SPI,UART
import time
# UART0初始化
uart = UART(0, baudrate=115200, bits=8, stop=1)
while True:
# 打印"Hello World"到串口
uart.write("Hello World\r\n")
# 等待
time.sleep(1)
查看串口打印内容需安装好串口助手,还需要打好对应驱动,网上教程很多这里就不详细说明了
使用串口助手查看我们接收到的消息,可以看到PICO正在反复发送Helloworld
3、基础任务一
完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。
(1)首先我们尝试ping通开发板。
参考wiznet在github上的,在Thonny中粘贴以下代码,开发板的IP和网关需要自己修改,直接CV一般是没法用的
from machine import Pin, SPI
import network
import time
led = Pin(25, Pin.OUT)
# 初始化 W5x00 芯片
def w5x00_init():
spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
nic = network.WIZNET5K(spi, Pin(17), Pin(20)) # 通过 SPI 连接 W5x00 芯片,设置 CS 和 RESET 引脚
nic.active(True) # 激活 W5x00 芯片
nic.ifconfig(('开发板IP', '255.255.0.0', '网关', '8.8.8.8')) # 设置网络配置信息
while not nic.isconnected(): # 等待网络连接建立
time.sleep(1)
print(nic.regs()) # 打印寄存器信息
print(nic.ifconfig()) # 打印网络配置信息
def main():
w5x00_init() # 初始化 W5x00 芯片
while True:
led.value(1) # 点亮 LED 灯
time.sleep(1)
led.value(0) # 关闭 LED 灯
time.sleep(1)
if __name__ == "__main__":
main()
确保电脑ip地址和开发板ip地址在同一网关下,不然无法ping通,会像这样👇
设置正确后再次尝试,功能实现
设置开发板IP时可以先ipconfig,查看 “以太网适配器 以太网” 所显示IP
接着我们尝试ping通互联网站点
可以直接新建一个uping.py并保存到开发板,其源代码如下,以下代码粘贴后可能会出现缩进问题,建议去原网页复制RAW代码
# μPing (MicroPing) for MicroPython
# copyright (c) 2018 Shawwwn <shawwwn1@gmail.com>
# License: MIT
# Internet Checksum Algorithm
# Author: Olav Morken
# https://github.com/olavmrk/python-ping/blob/master/ping.py
# @data: bytes
def checksum(data):
if len(data) & 0x1: # Odd number of bytes
data += b'\0'
cs = 0
for pos in range(0, len(data), 2):
b1 = data[pos]
b2 = data[pos + 1]
cs += (b1 << 8) + b2
while cs >= 0x10000:
cs = (cs & 0xffff) + (cs >> 16)
cs = ~cs & 0xffff
return cs
def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
import utime
import uselect
import uctypes
import usocket
import ustruct
import urandom
# prepare packet
assert size >= 16, "pkt size too small"
pkt = b'Q'*size
pkt_desc = {
"type": uctypes.UINT8 | 0,
"code": uctypes.UINT8 | 1,
"checksum": uctypes.UINT16 | 2,
"id": uctypes.UINT16 | 4,
"seq": uctypes.INT16 | 6,
"timestamp": uctypes.UINT64 | 8,
} # packet header descriptor
h = uctypes.struct(uctypes.addressof(pkt), pkt_desc, uctypes.BIG_ENDIAN)
h.type = 8 # ICMP_ECHO_REQUEST
h.code = 0
h.checksum = 0
h.id = urandom.getrandbits(16)
h.seq = 1
# init socket
sock = usocket.socket(usocket.AF_INET, usocket.SOCK_RAW, 1)
sock.setblocking(0)
sock.settimeout(timeout/1000)
addr = usocket.getaddrinfo(host, 1)[0][-1][0] # ip address
sock.connect((addr, 1))
not quiet and print("PING %s (%s): %u data bytes" % (host, addr, len(pkt)))
seqs = list(range(1, count+1)) # [1,2,...,count]
c = 1
t = 0
n_trans = 0
n_recv = 0
finish = False
while t < timeout:
if t==interval and c<=count:
# send packet
h.checksum = 0
h.seq = c
h.timestamp = utime.ticks_us()
h.checksum = checksum(pkt)
if sock.send(pkt) == size:
n_trans += 1
t = 0 # reset timeout
else:
seqs.remove(c)
c += 1
# recv packet
while 1:
socks, _, _ = uselect.select([sock], [], [], 0)
if socks:
resp = socks[0].recv(4096)
resp_mv = memoryview(resp)
h2 = uctypes.struct(uctypes.addressof(resp_mv[20:]), pkt_desc, uctypes.BIG_ENDIAN)
# TODO: validate checksum (optional)
seq = h2.seq
if h2.type==0 and h2.id==h.id and (seq in seqs): # 0: ICMP_ECHO_REPLY
t_elasped = (utime.ticks_us()-h2.timestamp) / 1000
ttl = ustruct.unpack('!B', resp_mv[8:9])[0] # time-to-live
n_recv += 1
not quiet and print("%u bytes from %s: icmp_seq=%u, ttl=%u, time=%f ms" % (len(resp), addr, seq, ttl, t_elasped))
seqs.remove(seq)
if len(seqs) == 0:
finish = True
break
else:
break
if finish:
break
utime.sleep_ms(1)
t += 1
# close
sock.close()
ret = (n_trans, n_recv)
not quiet and print("%u packets transmitted, %u packets received" % (n_trans, n_recv))
return (n_trans, n_recv)
功能实现代码如下:
import time
import uping
from machine import Pin, SPI
import network
# 初始化串口
uart = machine.UART(0, baudrate=9600)
# 初始化LED引脚
led = Pin(25, Pin.OUT)
# 初始化W5500芯片
def w5x00_init():
spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
nic = network.WIZNET5K(spi, Pin(17), Pin(20)) # 初始化网络接口,设置SPI总线、CS引脚、复位引脚
nic.active(True) # 激活网络接口
nic.ifconfig(('192.168.137.100','255.255.255.0','192.168.137.1','8.8.8.8')) # 设置IP地址等网络配置信息
while not nic.isconnected(): # 等待网络连接成功
time.sleep(1)
print(nic.regs()) # 打印芯片寄存器状态
print(nic.ifconfig()) # 打印配置信息
def main():
w5x00_init() # 初始化W5x00芯片
uping.ping('www.jd.com') # 发起ping请求
while True:
led.value(1) # 点亮LED
time.sleep(1)
led.value(0) # 熄灭LED
time.sleep(1)
uart.write('PING JD Success!\n') # 通过串口发送消息
if __name__ == "__main__":
main()
在shell窗口可以看到ping代码已经实现预期功能(不知道为啥显示收到的包只有一个。猜测是应答时间过快还没开始计数)
在循环中我添加了串口打印ping通成功的标志,当然看板上led也可以
(2)接下来我们通过抓包软件Wireshark(👇)抓取本地PC的ping报文,展示并分析。
安装完wireshark后打开,单击靠左上方的start键即可开始捕获。
等待ping通结束,再点击start左侧的end结束捕获,有需要的话可以将捕获的分组保存一下方便之后查看
在搜索框中输入ip.addr==你的开发板IP,即可查看ping报文。
可以看到,我们生成了8个报文(4个请求报文和4个应答报文)
我们只分析第一个请求报文,报文如下:
报文的整体描述是这样的:
我们可以读出一些基本消息,如该请求报文共98字节,世界标准送达时间等等
当然这只是以太网报文的一部分,不可能将以太网以帧格式表达出来
前1-6字节和7-12字节分别为目的地址和源地址
地址的第一字节的最后两个bit位:
IG位为0表示这是一个单播MAC地址
LG位为0表示这是一个厂家出厂默认的MAC地址,为1表示是用户设置的
再后两字节0x0800表示报文为IPV4格式👇
后20字节为ip报文,按字节解释一下:
1.第一个字节:
- Bit 0-3: 版本号(Version)- 4位,表示IP协议的版本,通常为4
- Bit 4-7: 首部长度(IHL)- 4位,表示IP首部的长度,以4字节为单位
2. 第二个字节:
- Bit 0-7: 服务类型(Type of Service)- 8位,用于标识数据包的服务质量要求
3. 第三、四字节:
- Bit 0-15: 总长度(Total Length)- 16位,表示整个IP数据包的长度,包括首部和数据部分
4. 第五、六字节:
- Bit 0-15: 标识(Identification)- 16位,用于标识数据包的唯一性
5. 第七、八字节:
- Bit 0-2: 标志位(Flags)- 3位,用于控制分片
- Bit 3-15: 片偏移(Fragment Offset)- 13位,表示数据包在原始数据流中的偏移量
6. 第九、十字节:
- Bit 0-7: 生存时间(Time to Live)- 8位,表示数据包在网络中可传递的最大跳数
7. 第十一个字节:
- Bit 0-7: 协议(Protocol)- 8位,表示数据包上层协议的类型,如TCP、UDP等
8. 第十二字节:
- Bit 0-15: 首部校验和(Header Checksum)- 16位,用于校验IP首部的完整性
9. 第十三至十六字节:
- Bit 0-31: 源IP地址(Source IP Address)- 32位,表示数据包的源IP地址。即192.158.137.100
10. 第十七至二十字节:
- Bit 0-31: 目的IP地址(Destination IP Address)- 32位,表示数据包的目的IP地址。即36.152.105.3
参考以下表格 :
再接下来就是ICMP(Internet Control Message Protocol)了,按字节进行解释:
1. 第一个字节(8 bits):类型(Type)
- 表示ICMP消息的类型,指示报文的目的和功能。常见的类型包括:
- 0x00:回显应答(Echo Reply)
- 0x03:目的地不可达(Destination Unreachable)
- 0x08:回显请求(Echo Request)👈
- 其他类型表明不同的ICMP消息功能。
2. 第二个字节(8 bits):代码(Code)
- 和类型字段一起指定特定类型的具体细分。
-
类型TYPE |
代码CODE |
用途|描述 Description |
查询类Query |
差错类Error |
0 |
0 |
Echo Reply——回显应答(Ping应答)👈 |
x |
|
3 |
0 |
Network Unreachable——网络不可达 |
|
x |
3 |
1 |
Host Unreachable——主机不可达 |
|
x |
3 |
2 |
Protocol Unreachable——协议不可达 |
|
x |
3 |
3 |
Port Unreachable——端口不可达 |
|
x |
3 |
4 |
Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特 |
|
x |
3 |
5 |
Source routing failed——源站选路失败 |
|
x |
3 |
6 |
Destination network unknown——目的网络未知 |
|
x |
3 |
7 |
Destination host unknown——目的主机未知 |
|
x |
3 |
8 |
Source host isolated (obsolete)——源主机被隔离(作废不用) |
|
x |
3 |
9 |
Destination network administratively prohibited——目的网络被强制禁止 |
|
x |
3 |
10 |
Destination host administratively prohibited——目的主机被强制禁止 |
|
x |
3 |
11 |
Network unreachable for TOS——由于服务类型TOS,网络不可达 |
|
x |
3 |
12 |
Host unreachable for TOS——由于服务类型TOS,主机不可达 |
|
x |
3 |
13 |
Communication administratively prohibited by filtering——由于过滤,通信被强制禁止 |
|
x |
3 |
14 |
Host precedence violation——主机越权 |
|
x |
3 |
15 |
Precedence cutoff in effect——优先中止生效 |
|
x |
4 |
0 |
Source quench——源端被关闭(基本流控制) |
|
|
5 |
0 |
Redirect for network——对网络重定向 |
|
|
5 |
1 |
Redirect for host——对主机重定向 |
|
|
5 |
2 |
Redirect for TOS and network——对服务类型和网络重定向 |
|
|
5 |
3 |
Redirect for TOS and host——对服务类型和主机重定向 |
|
|
8 |
0 |
Echo request——回显请求(Ping请求) |
x |
|
9 |
0 |
Router advertisement——路由器通告 |
|
|
10 |
0 |
Route solicitation——路由器请求 |
|
|
11 |
0 |
TTL equals 0 during transit——传输期间生存时间为0 |
|
x |
11 |
1 |
TTL equals 0 during reassembly——在数据报组装期间生存时间为0 |
|
x |
12 |
0 |
IP header bad (catchall error)——坏的IP首部(包括各种差错) |
|
x |
12 |
1 |
Required options missing——缺少必需的选项 |
|
x |
13 |
0 |
Timestamp request (obsolete)——时间戳请求(作废不用) |
x |
|
14 |
|
Timestamp reply (obsolete)——时间戳应答(作废不用) |
x |
|
15 |
0 |
Information request (obsolete)——信息请求(作废不用) |
x |
|
16 |
0 |
Information reply (obsolete)——信息应答(作废不用) |
x |
|
17 |
0 |
Address mask request——地址掩码请求 |
x |
|
18 |
0 |
Address mask reply——地址掩码应答 |
3. 第三和第四字节(16 bits):校验和(Checksum)
- 检验整个ICMP数据报的完整性,以确保数据在传输过程中没有出现错误。(无)
4. 后面还有四字节的标识码和序列码,参考以下描述即可解读:
- Identifier (BE): 27820 (0x6cac) 👈
- Identifier (LE): 44140 (0xac6c)
- Sequence Number (BE): 1 (0x0001) 👈
- Sequence Number (LE): 256 (0x0100)
5. 后续字段为数据字段(Data fields),具体内容取决于ICMP消息类型,共56字节,具体内容可以看下图
4、基础任务二
主控板建立TCPIP,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,通过串口打印显示;通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作)
TCP(传输控制协议)是一种面向连接的协议,提供可靠的数据传输和错误检测机制。
我们这次通过W5500芯片创建一个TCP服务器,可以接收客户端的连接并处理数据。参考代码链接:
PICO代码如下:
from usocket import socket
from machine import Pin, SPI, UART
import network
import time
uart = UART(0, baudrate=115200, bits=8, stop=1)
# W5x00 chip initialization
def w5x00_init():
spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
nic = network.WIZNET5K(spi, Pin(17), Pin(20)) # spi, cs, reset pin
nic.active(True)
# Static IP configuration
nic.ifconfig(('192.168.137.100', '255.255.255.0', '192.168.137.1', '8.8.8.8'))
# DHCP configuration
# nic.ifconfig('dhcp')
print('IP address:', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
def server_loop():
s = socket()
s.bind(('192.168.137.100', 5050)) # Source IP Address
s.listen(5)
print("TEST server")
conn, addr = s.accept()
print("Connected to:", conn, "address:", addr)
print("Loopback server Open!")
while True:
data = conn.recv(2048)
print(data.decode('utf-8'))
if data != b'NULL': # Use byte literal for comparison
uart.write(data) # Serial print
conn.send(data)
def client_loop():
s = socket()
s.connect(('192.168.137.114', 5050)) # Destination IP Address
print("Loopback client Connect!")
while True:
data = s.recv(2048)
print(data.decode('utf-8'))
if data != b'NULL': # Use byte literal for comparison
s.send(data)
def main():
w5x00_init()
### TCP SERVER ###
while True:
server_loop()
### TCP CLIENT ###
# client_loop()
if __name__ == "__main__":
main()
下载好netassist(无需安装,解压即用)并打开,远程主机地址和远程主机端口需要和代码一致:
扩展选项默认不变直接确认:
运行程序,网络助手发送消息
成功👇
报文分析:
报文获取方式同上一任务
在搜索框中输入ip.addr==你的开发板IP,即可查看TCP报文。
可以看到,我们生成了9个报文。共3组,因为是互发3次报文,每组的第3次用于确认
只挑一个,报文描述如下
在数据字段(Data fields)中我们可以找到发送的内容(右下角):welcome to netassist
(TCP好图分享,来源:文末参考4)
5、进阶任务
从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送串口显示。
ntp直接使用mp的特定库即可
基本思路:初始化W5500,连接到网络并手动设置IP地址、子网掩码、网关和DNS服务器地址。然后通过NTP服务器同步系统时间,最后不断地显示本地时间。
功能实现代码如下:
from usocket import socket
from machine import Pin, SPI, UART, RTC
import network
import time
import ntptime
uart = machine.UART(0, baudrate=9600)
#W5x00芯片初始化
def w5x00_init():
spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
nic = network.WIZNET5K(spi, Pin(17), Pin(20)) # 使用SPI总线,设置CS和RESET引脚
nic.active(True)
#手动设置IP地址,子网掩码,网关和DNS服务器地址
nic.ifconfig(('192.168.xxx.xxx', '255.255.255.0', '192.168.xxx.xxx', '8.8.8.8'))
print('IP address:', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
#print(nic.regs()) # 可以打印芯片的寄存器状态进行调试
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}')
uart.write(f'{t[0]} - {t[1]:02d}-{t[2]:02d} {t[3]:02d}:{t[4]:02d}:{t[5]:02d}\n')
def main():
w5x00_init()
#手动设置一个错误的系统时间
rtc = RTC()
rtc.datetime((2021,1,1,1,1,1,1,1)) # 年、月、日、星期、时、分、秒、亚秒
print('校时前系统时间:')
show_local_time()
#使用NTP服务器校准时间
print('开始NTP校时...')
ntptime.host = 'ntp1.aliyun.com'
ntptime.settime()
print('校时后系统时间:')
show_local_time()
while True:
show_local_time()
time.sleep(1)
if __name__ == "__main__":
main()
运行,即可看见串口每隔一秒打印当前时间
在实现这个功能时经常会出现报错,大概率是服务器访问量过大造成的。
解决方法有两个,一个是反复run,多试试总会好的,二是更换ntp服务器,并且尽量选国内的
6、终极任务二
使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件
既然要使用外部存储器,肯定需要我们自备存储模块和杜邦线,经过简易连接之后再进行操作,于是我掏出了之前在得捷下单的tf卡模块:
电路连接实物图与引脚说明如下图:
想要在micropython下驱动sd卡,我们还需要添加一个额外的库,复制RAW代码,粘贴进Thonny后并保存至我们的开发板上。
通过W5500芯片进行网络通信和SD卡存储文件管理,初始化SD卡和网络设置,以及FTP服务器的功能实现,接收FTP客户端的命令并进行对应的文件管理操作。
FTP服务器通过指定端口进行数据传输,实现列出目录、发送文件、保存文件等。
功能实现代码如下:
from usocket import socket
from machine import Pin, SPI
import network
import time
import socket
import uos
import gc
import sdcard
from time import localtime
# 初始化SD卡
spi = SPI(1, sck=Pin(10), mosi=Pin(11), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)
uos.mount(sd, "/sd") # 挂载SD卡
print(uos.listdir('/sd')) # 列出SD卡中的文件
# 初始化网络
spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
nic = network.WIZNET5K(spi, Pin(17), Pin(20))
nic.active(True)
nic.ifconfig(('192.168.137.100', '255.255.255.0', '192.168.137.1', '8.8.8.8'))
print('IP address:', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
# 初始化W5x00芯片
def w5x00_init():
spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
nic = network.WIZNET5K(spi, Pin(17), Pin(20))
nic.active(True)
nic.ifconfig(('192.168.137.100', '255.255.255.0', '192.168.137.1', '8.8.8.8'))
print('IP address:', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
return nic
month_name = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
# 发送目录列表数据
def send_list_data(path, dataclient, full):
try:
for fname in uos.listdir(path):
dataclient.sendall(make_description(path, fname, full))
except:
pattern = path.split("/")[-1]
path = path[:-(len(pattern) + 1)]
if path == "":
path = "/"
for fname in uos.listdir(path):
if fncmp(fname, pattern):
dataclient.sendall(make_description(path, fname, full))
# 生成文件描述
def make_description(path, fname, full):
# 实现文件描述的生成
# ...
# 发送文件数据
def send_file_data(path, dataclient):
# 发送文件数据
# ...
# 保存文件数据
def save_file_data(path, dataclient, mode):
# 保存文件数据
# ...
# 获取绝对路径
def get_absolute_path(cwd, payload):
# 获取绝对路径
# ...
# 比较文件名和模式
def fncmp(fname, pattern):
# 比较文件名和模式
# ...
# FTP服务器函数
def ftpserver():
DATA_PORT = 13333
ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置套接字选项
ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定FTP套接字和数据套接字
ftpsocket.bind(socket.getaddrinfo("0.0.0.0", 21)[0][4])
datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4])
ftpsocket.listen(1)
datasocket.listen(1)
datasocket.settimeout(10)
msg_250_OK = '250 OK\r\n'
msg_550_fail = '550 Failed\r\n'
try:
dataclient = None
fromname = None
while True:
cl, remote_addr = ftpsocket.accept()
cl.settimeout(300)
cwd = '/'
try:
cl.sendall("220 Hello, this is the RP2020.\r\n")
while True:
gc.collect()
data = cl.readline().decode("utf-8").rstrip("\r\n")
if len(data) <= 0:
print("Client disappeared")
break
command = data.split(" ")[0].upper()
payload = data[len(command):].lstrip()
path = get_absolute_path(cwd, payload)
# 处理FTP命令
# ...
except Exception as err:
print(err)
finally:
cl.close()
cl = None
finally:
datasocket.close()
ftpsocket.close()
if dataclient is not None:
dataclient.close()
# 主函数
def main():
ftpserver()
if __name__ == "__main__":
main()
打开文件资源管理器,搜索框输入:ftp://你的开发板id即可访问,整个PICO板上的代码都是可以随意复制和下载的
7、额外模块(电机控制)
因为在订购板子时额外购买了其他元件,应要求,额外实现TB6642模块对直流减速电机的控制
其实我这次用的电机是带编码器的,时间关系只做简单控制,不涉及编码器
原理很简单:
单路PWM调占空比控制电机转速,两路io控制电机正反转和刹车
简单写一下就是这样的:
import machine
import utime
# 设置8号脚为输出引脚,设置高电平
pin8 = machine.Pin(8, machine.Pin.OUT)
pin8.value(1)
# 设置9号脚为输出引脚,设置低电平
pin9 = machine.Pin(9, machine.Pin.OUT)
pin9.value(0)
# 设置7号脚为PWM输出
pwm = machine.PWM(machine.Pin(7))
while True:
pwm.freq(1000) #设置PWM频率为1kHz
pwm.duty_u16(32768) #50%占空比对应的duty cycle值为32768
utime.sleep(5)
pwm.freq(500) #设置PWM频率为500Hz
pwm.duty_u16(16384) #25%占空比
utime.sleep(5)
最终效果请看视频:
8、小节
在整个实践过程中我也遇到了不少困难和bug
其中最让我印象深刻的是一个让PICO跑飞的代码
报错如下
STDERR: Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __thonny_helper
KeyboardInterrupt: |
反复reset、重刷mp的固件都没用。
刷了官方的blink固件,再刷mp固件才好过来
网上找了一圈甚至没找到一样的报错
(Python粘贴错误优先查看:网址)
我觉得本次活动对我来说十分有意义,因为开发板又+1因为又新接触了一种语法,并且又增加了自己的ddl
(不是很习惯这块板子的micro口,以后有机会画一板type-c)
|