854|0

18

帖子

6

TA的资源

一粒金砂(中级)

楼主
 

【得捷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通互联网站点
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卡模块
 

 

电路连接实物图与引脚说明如下图:
 

 

tf卡模块使用DAP的3V3供电,三个板均共地

 

 

想要在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)

 

最终效果请看视频:

VID_20240223_233531
 
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
 
如有错误希望指正(
 
参考资料:1
                 2
                 3
                 4
(完)2024/2/25

 

点赞 关注
个人签名

github.com/Amanitaphalloide

 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表