- 2024-08-12
-
加入了学习《【Follow me第二季第1期】+ 小白带你使用circuitpython开发CircuitPlaygroundExpress》,观看 【Follow me第二季第1期】+ 小白带你使用circuitpython开发CircuitPlaygroundExpress
-
发表了主题帖:
【Follow me第二季第1期】+ 小白带你使用circuitpython开发CircuitPlaygroundExpress
本帖最后由 lihuahua 于 2024-8-12 12:22 编辑
视频介绍
【Follow me第二季第1期】+ 小白带你使用circuitpython开发CircuitPlaygroundExpress-EEWORLD大学堂
代码链接
【Follow me第二季第1期】+ 小白带你使用circuitpython开发CircuitPlaygroundExpress-嵌入式开发相关资料下载-EEWORLD下载中心
一、入门任务(必做):开发环境搭建,板载LED点亮
1、从CircuitPython官网下载UF2文件:https://circuitpython.org/board/circuitplayground_express/
2、单击开发板中间“Reset”按钮,开发板环形灯变为“绿色”,我的电脑出现开发板图标,并将下载的“UF2”文件拖入其中。
3、从 https://codewith.mu 下载 Mu,然后安装软件
4、打开软件Mu,选择模式“CircuitPython”
5、从 CIRCUITPY 驱动器加载 code.py 文件
6、编写“闪灯”代码:
#use the Circuit Playground library
from adafruit_circuitplayground import cp
import time
#不断循环
while True:
cp.red_led = True #led引脚为高电平
time.sleep(0.5) #0.5s
cp.red_led = False #led引脚为高电平
time.sleep(0.5) #0.5s
print("Hello World!") #通过串口输出
“闪灯”软件流程图
7、然后将将 code.py 文件保存在 CIRCUITPY 驱动器上,发现LED闪烁。
点击下载程序按钮
实物效果
MU软件串口界面
二、基础任务一(必做):控制板载炫彩LED,跑马灯点亮和颜色变换
使用到板载NeoPixel, 每个NeoPixel LED由Red、Green、Blue三种颜色组成,每种颜色控制数值在0~255间变化,最终可以合成不同颜色。
板载NeoPixel编号
软件流程图
“跑马灯+颜色变换”实物图片
#use the Circuit Playground library
from adafruit_circuitplayground import cp
import time
cp.pixels.brightness = 0.1 #亮度
i = 0 #当前显示索引
color_index = 0 #颜色索引
R = 25
G = 25
#跑马灯颜色显示缓冲区
color = [(255, 0, 0),(0, 255, 0),(0, 0, 255),(255, 255, 0),(255, 0, 255),(0, 255, 255),(255, 255, 255)]
#不断循环
while True:
cp.pixels.brightness = 0.1 + 0.05 * i #亮度逐渐增加
cp.pixels[i] = color[color_index] #点亮i号
if i == 0:
j = 9
else:
j = i - 1
cp.pixels[j] = (0, 0, 0) #关闭上一次点亮的灯
i = (i + 1)%10 #i 的值会从 0 开始递增,当它达到 10 时,再次回到 0 并继续递增
color_index = (color_index + 1)%7
time.sleep(0.1) #延迟0.1s
三、基础任务二(必做):监测环境温度和光线,通过板载LED展示舒适程度
1、本实验使用到板载温度、光强传感器
2、使用CircuitPython库读取板载温度的数值,通过序号为9~5的NeoPixel显示舒适程度,温度越高亮的NeoPixel(蓝灯)的数目越多;
3、读取光强传感器的数值,通过序号为0~4的NeoPixel显示舒适程度,光照越强亮的NeoPixel(绿灯)的数目越多。
软件流程图
(初始状态)
(光照变强,绿灯数量变多)
(温度变高,蓝灯数目变多)
实物图片
#use the Circuit Playground library
import time
from adafruit_circuitplayground import cp
cp.pixels.auto_write = False #需要调用cp.pixels.show()显示函数
cp.pixels.brightness = 0.3 #亮度
#最低、最高温度
minimum_temp = 24
maximum_temp = 30
while True:
light_now = cp.light #获取当前亮度
light_peak = round(light_now / 320 * 4)#光线传感器输出值0~320,输出亮度等级
for i in range(5):
if i <= light_peak:
cp.pixels[i] = (0, 255, 0)
else:
cp.pixels[i] = (0, 0, 0)
Temperature_now = cp.temperature #获取当前温度值
#获得温度等级
Temperature_peak = int((Temperature_now - minimum_temp) / (maximum_temp - minimum_temp) * 5)
print((light_now,Temperature_now)) #绘图
print("亮度等级",int(light_peak)) #串口输出
print("温度等级",int(Temperature_peak))
for i in range(9,4,-1): #循环变量从9递减到5
if i >= 10 - Temperature_peak:
cp.pixels[i] = (0, 0, 255)
else:
cp.pixels[i] = (0, 0, 0)
cp.pixels.show() #pixels显示使能
time.sleep(0.1) #延迟0.1s
四、基础任务三(必做):接近检测——设定安全距离并通过板载LED展示,检测到入侵时,发起声音报警
使用到板载红外发射管、接收管,红外发射管发射38khz的红外线,通过检测IR_PROXIMITY引脚的电压来获得距离信息。
电路原理图
软件流程图
眼镜盒靠近电路板前
眼镜盒不断靠近,绿灯数目增加,同时报警
实物图片
import time
import board
import analogio
import digitalio
from adafruit_circuitplayground import cp
# 红外线输出控制引脚
IR_Tx_pin = digitalio.DigitalInOut(board.REMOTEOUT)
IR_Tx_pin.switch_to_output()
# 红外接近传感器模拟量读取
analogin = analogio.AnalogIn(board.IR_PROXIMITY)
# 红外接近传感器模拟量读取的最大、小值
minimum_temp = 39500
maximum_temp = 43000
cp.pixels.auto_write = False #需要调用cp.pixels.show()显示函数
cp.pixels.brightness = 0.3 #亮度
while True:
#输出38khz,50%占空比的红外线
for i in range(32):
IR_Tx_pin.value = True
time.sleep(0.000013)
IR_Tx_pin.value = False
time.sleep(0.000013)
analog_now = analogin.value #获取当前红外线强度对应模拟量
analog_peak = int((analog_now - minimum_temp) / (maximum_temp - minimum_temp) * 10) #将模拟量转化为强度等级
print((analog_now,analog_peak)) #绘图
for i in range(10):
if i <= analog_peak:
cp.pixels[i] = (0, 255, 0)
else:
cp.pixels[i] = (0, 0, 0)
cp.pixels.show() #pixels显示使能
if analog_peak >= 8:
cp.play_tone(300, 0.1) #频率hz,持续时间s
time.sleep(0.1)
五、进阶任务(必做):制作不倒翁——展示不倒翁运动过程中的不同灯光效果
使用到板载3轴加速度传感器,获得加速度值,作为pixels颜色显示分量。
软件流程图
水平放置
竖直放置
侧置
实物图片
#use the Circuit Playground library
import time
from adafruit_circuitplayground import cp
#基础颜色
R = 0
G = 0
B = 0
while True:
#如果还未打开拨码开关
if not cp.switch:
# If the switch is to the right, it returns False!
print("Slide switch off!")
cp.pixels.fill((0, 0, 0))
continue #跳过本次循环
x, y, z = cp.acceleration #获取3轴加速度值,单位m/s2
print((x, y, z))
# int处理浮点数,会自动截断小数部分
cp.pixels.fill(((R + abs(int(x))), (G + abs(int(y))), (B + abs(int(z)))))
time.sleep(0.05) #50ms运行周期
六、创意任务二:章鱼哥——章鱼哥的触角根据环境声音的大小,章鱼哥的触角可舒展或者收缩
使用到板载mic、外接舵机
舵机连接方法
本实验需要安装库:/CircuitPython_Servo/,然后单击与正在使用的 CircuitPython 版本匹配的目录,并将该目录的内容复制到 CIRCUITPY 驱动器。
软件流程图
舵机基础偏转角
声音增加后,舵机角度偏转
实物图片
import array
import math
import audiobusio
import board
import neopixel
import time
import pwmio
from adafruit_motor import servo
PEAK_COLOR = (100, 0, 255)# Color of the peak pixel.
NUM_PIXELS = 90 # Number of total pixels - 10 build into Circuit Playground
# Exponential scaling factor.
# Should probably be in range -10 .. 10 to be reasonable.
CURVE = 2 #对声音敏感程度
SCALE_EXPONENT = math.pow(10, CURVE * -0.1)
NUM_SAMPLES = 160 # Number of samples to read at once.
#限幅
def constrain(value, floor, ceiling):
return max(floor, min(value, ceiling))
# 在output_min和output_max之间按指数缩放input_value.
def log_scale(input_value, input_min, input_max, output_min, output_max):
normalized_input_value = (input_value - input_min) / (input_max - input_min) #输入缩放
return output_min + math.pow(normalized_input_value, SCALE_EXPONENT) * (output_max - output_min)
#计算一组值的标准化均方根值,在计算RMS之前消除直流偏压
def normalized_rms(values): #该函数接受一个参数values,用于存储要进行处理的值的列表
minbuf = int(mean(values)) #计算给定值列表 values 的平均值,将结果转换为整数
#计算了一组已经减去平均值的样本值的平方和。
#使用列表解析来计算每个值减去平均值后的平方,并将这些平方值相加得到 samples_sum。
samples_sum = sum(float(sample - minbuf) * (sample - minbuf) for sample in values)
#计算标准化均方根值。首先将平方和除以值的数量得到平均平方值,
#然后使用 math.sqrt 函数计算其平方根。最后函数返回这个标准化均方根值
return math.sqrt(samples_sum / len(values))
#求平均值
def mean(values):
return sum(values) / len(values)
def volume_color(volume):
return 200, volume * (255 // NUM_PIXELS), 0
# Main program
#################################舵机初始化####################################
# create a PWMOut object on Pin A2.
pwm = pwmio.PWMOut(board.A2, duty_cycle=2 ** 15, frequency=50)
# Create a servo object, my_servo.
my_servo = servo.Servo(pwm)
#################################mic初始化####################################
mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16)
#创建名为samples的数组,类型为"unsigned short"("H")
#初始化NUM_SAMPLES个元素的数组,每个元素的初始值为0。
samples = array.array("H", [0] * NUM_SAMPLES)
##记录初始样品以进行校准。假设开始的时候很安静
mic.record(samples, len(samples))
# Set lowest level to expect, plus a little.
input_floor = normalized_rms(samples) + 10
# OR: used a fixed floor
# input_floor = 50
# You might want to print the input_floor to help adjust other values.
print(input_floor)
# Corresponds to sensitivity: lower means more pixels light up with lower sound
# Adjust this as you see fit.
input_ceiling = input_floor + 500
angle_now = 0
systick = 0
while True:
if systick % 4 == 0:
mic.record(samples, len(samples)) #对短时间的音频进行采样
magnitude = normalized_rms(samples) #使用均方根计算 (RMS) 计算样本中的能量(对应于音量)
# 计算0到NUM_PIXELS范围内的缩放对数读数
c = log_scale(
constrain(magnitude, input_floor, input_ceiling),
input_floor,
input_ceiling,
0,
NUM_PIXELS,
)
#舵机角度控制周期
if systick % 2 == 0:
if angle_now < c:
angle_now = angle_now + 2
if angle_now > c:
angle_now = angle_now - 2
#输出角度限幅
if angle_now > 90:
angle_now = 90
elif angle_now < 0:
angle_now = 0
my_servo.angle = angle_now #舵机角度输出
print((c,angle_now)) #串口打印输出
time.sleep(0.01) #延时10ms
systick = systick + 1 #时钟增加
七、心得体会
1、在使用MicroPython(或 CircuitPython)开发时,通常需要将特定的固件(通常是.uf2文件)烧录到开发板中,用于:初始化文件系统、包含驱动程序和功能、启动MicroPython解释器
2、MicroPython 没有将 Python 代码编译为本机机器码的步骤。相比之下,传统的 C 代码需要经过编译以生成可在特定硬件上运行的二进制文件。
3、.uf2 文件通常用于存储 MicroPython 或 CircuitPython 解释器以及用户的 Python 代码,而引导加载程序是负责将这些内容加载到设备中并启动它们的关键组件。
- 2024-08-10
-
加入了学习《【Follow me第二季第1期】在arduino环境下多app调度全部任务》,观看 【Follow me第二季第1期】在arduino环境下多app调度全部任务
-
上传了资料:
【Follow me第二季第1期】+ 小白带你使用circuitpython开发CircuitPlaygroundExpress
- 2024-08-02
-
加入了学习《【Follow me第二季第1期】全部任务演示》,观看 全部任务演示2.0
- 2024-07-22
-
加入了学习《【得捷Follow me第4期】+小白手把手带你学习rp2040+w5500(所有任务)》,观看 【得捷Follow me第4期】+小白手把手带你学习rp2040+w5500(所有任务)
-
加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门
- 2024-04-07
-
加入了学习《《对话通信原理视频》》,观看 0103
- 2024-03-08
-
加入了学习《Underactuated Robotics MIT 2019 spring 6.382》,观看 Underactuated Robotics
- 2024-02-28
-
加入了学习《Follow me第4期 总结视频》,观看 Follow me第4期-总结视频
- 2024-02-20
-
加入了学习《平衡小车与电机PID系列视频教程》,观看 平衡小车直立控制调试教程
-
发表了主题帖:
【得捷Follow me第4期】+小白手把手带你学习rp2040+w5500(所有任务)
总结视频:
【得捷Follow me第4期】+小白手把手带你学习rp2040+w5500(所有任务)
可编译下载的代码:
download.eeworld.com.cn/detail/lihuahua/631259
一、入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld)
0.1 开发环境搭建(在ubuntu环境下使用clion进行C开发)
1、更新软件包列表,不进行实际升级操作:
book@100ask:~$ sudo apt-get update
2、安装git工具:
book@100ask:~$ sudo apt-get install git
3、安装python:
book@100ask:~$ sudo apt-get install python-tk python3-tk
4、拉取树莓派sdk文件:
book@100ask:~$ git clone https://github.com/raspberrypi/pico-setup/
5、进入pico-setup路径,并修改文件“pico-setup.sh”,需要注释一些代码:
book@100ask:~/rp2040/pico-setup$ vi pico-setup.sh
6、安装“pico_setup.sh “:
book@100ask:~/rp2040/pico-setup$ ./pico_setup.sh
7、安装clion-IDE:
book@100ask:~/rp2040/pico-setup$ sudo snap install clion –classic
8、拉取树莓派工程生成软件包:
book@100ask:~/rp2040/pico-setup$ cd ..
book@100ask:~/rp2040$ git clone https://github.com/raspberrypi/pico-project-generator.git
9、重新打开终端,修改sdk的路径:
book@100ask:~/rp2040/pico-setup$ ls
pico pico-project-generator pico_setup.sh
book@100ask:~/rp2040/pico-setup$ pwd
/home/book/rp2040/pico-setup
book@100ask:~/rp2040/pico-setup$ echo "export PICO_SDK_PATH=/home/book/rp2040/pico-setup" >> .bashrc
10、安装树莓派工程生成软件:
book@100ask:~/rp2040/pico-setup$ cd pico-project-generator
11、打开树莓派工程生成软件:
book@100ask:~/rp2040/pico-setup/pico-project-generator$ ./pico_project.py --gui
12、通过clion打开刚刚生成的工程:
12.1将工程路径中的头文件所在文件夹告诉编译器:
12.2将工程路径中的源文件告诉cmake:被编译的c文件左上角有蓝色方块标记
12.3工程编译:
13、程序烧录
按下BOOT和RESET,然板子复位
松开RESET(BOOT仍按下),然板子进入BOOT
等电脑上弹出RPI-RP2的盘符(文件夹)后,可松开BOOT
然后把编译后的uf2固件拖拽到这个盘中即可
拖拽成功后,盘符会自动消失,此时固件烧录工作即完成
Pico会重新启动,开始运行程序
14、安装网络数据检测软件:
book@100ask:~$ sudo add-apt-repository ppa:wireshark-dev/stable
book@100ask:~$ sudo apt update
book@100ask:~$ sudo apt install
book@100ask:~$ sudo usermod -aG wireshark $USER
0.2 BLINK:GPIO25
0.3 串口HelloWorld:
将rp2040开发板的GP0引脚连接到串口调试助手的RXD引脚
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
int main()
{
stdio_init_all();
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while(1){
gpio_put(LED_PIN, 1);
sleep_ms(500);
gpio_put(LED_PIN, 0);
sleep_ms(250);
printf("hello world!\n")
}
}
二、基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。
将笔记本wifi的因特网共享给有线网卡:
通过网线连接W5100和PC的网口。
Rp2040的ip设置为:192.168.137.110
虚拟机中的Ubuntu ping树莓派rp2040结果:
主机PC ping树莓派:
使用抓包软件Wireshark抓取ubuntu的ping报文:
使用过滤器找到需要的数据包:
输入icmp协议找到ping包:
通过ip地址筛选:ip.addr==192.168.137.110
第一行:物理层
第二行:数据链路层,数据来源于什么设备,发往什么设备
第三行:网络层ipv4协议
第四行:icmp应用层协议
根据type和code获得当前数据包的类型,下图为ping请求数据包
树莓派dns转换:
树莓派ping百度:
本任务需要官方sdk包的支持:W5100S_W5500+PR2040 C_SDK_Examples: Example routine for WIZnet W5100S/W5500 Ethernet chip + Raspberry PI Pico (gitee.com)
将sdk包中的以下文件放入工程文件中:
主函数:
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"
#include "hardware/i2c.h"
#include "inc/raspberry26x32.h"
#include "inc/ssd1306_font.h"
#include "inc/ssd1306_i2c.h"
#include "inc/wizchip_conf.h"
#include "inc/w5500.h"
#include "inc/w5500_spi.h"
#include "inc/dns.h"
#include "inc/loopback.h"
#include "inc/ping.h"
//定义了一个全局变量net_info来存储网卡地址信息
static wiz_NetInfo net_info = {
.mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x2e},//mac地址
.ip = {192, 168, 137, 110}, //IP地址
.sn = {255, 255, 255, 0}, //子网掩码
.gw = {192, 168, 137, 1}, //默认网关
.dns = {8, 8, 8, 8}, //dns信息
.dhcp = NETINFO_STATIC}; //DHCP模式为静态地址模式
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
#define SOCKET_ID 0
int dns_test(void);
bool repeating_timer_callback(struct repeating_timer *t);
void do_ping(void);
wiz_NetInfo get_info;
static uint8_t ethernet_buf_dns[ETHERNET_BUF_MAX_SIZE] = {0,};
uint8_t DNS_2nd[4] = {114,114,114,114};
uint8_t Domain_name[] = "www.baidu.com"; //需要的解析域名
uint8_t Domain_IP[4] = {0,}; //域名IP地址
uint8_t g_msec_cnt = 0;
static uint8_t ethernet_buf_ping[ETHERNET_BUF_MAX_SIZE] = {0,};
static uint8_t destip[4]={192, 168, 137, 1};
static uint16_t destport = 8080;
static uint16_t local_port =8000;
int main()
{
/*---------------------------------LED-GPIO-CONFIGURATION---------------------------------------*/
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
/*---------------------------------W5500-CONFIGURATION---------------------------------------*/
struct repeating_timer timer;
stdio_init_all(); //串口重定义到printf函数
sleep_ms(2000);
wizchip_initialize(); //初始化
sleep_ms(5000);
printf("W5500 network install example.\r\n");
wizchip_setnetinfo(&net_info); //设置网卡信息
print_network_information(get_info);
printf("W5500 dns test example.\r\n");
DNS_init(0,ethernet_buf_dns); //传入一个socket端口号和dns信息的接收缓存buff地址
add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer);
while (true) {
if (dns_test()){
while(true){
do_ping();
if(req>=4)
{
while (true) {
gpio_put(LED_PIN, 1);
sleep_ms(500);
gpio_put(LED_PIN, 0);
sleep_ms(250);
}
}
}
}
sleep_ms(2000);
}
}
int dns_test(void)
{
int ret;
printf("\r\n===== DNS Client Example =====\r\n");
printf("> DNS 1st: %d.%d.%d.%d\r\n",net_info.dns[0],net_info.dns[1],net_info.dns[2],net_info.dns[3]);
printf("> DNS 2nd: %d.%d.%d.%d\r\n",DNS_2nd[0],DNS_2nd[1],DNS_2nd[2],DNS_2nd[3]);
printf("==============================\r\n");
printf("> Example Domain Name : %s\r\n",Domain_name);
//dns服务器ip,请求解析的域名,以及解析后ip存放的地址
if((ret = DNS_run(net_info.dns, Domain_name, Domain_IP)) > 0)
{
printf("> 1st DNS Reponsed\r\n");
}
else if((ret != -1) && ((ret = DNS_run(DNS_2nd, Domain_name, Domain_IP)) > 0))
{
printf("> 2nd DNS Responsed\r\n");
}
else if(ret == -1)
{
printf("> MAX_DOMAIN_NAME is too small. Should be redefined it. \r\n");
}
else
{
printf("> DNS Failed\r\n");
}
if(ret > 0)
{
printf("> Translated %s to %d.%d.%d.%d\r\n",Domain_name,Domain_IP[0],Domain_IP[1],Domain_IP[2],Domain_IP[3]);
return 1;
}
return 0;
}
/* Timer */
bool repeating_timer_callback(struct repeating_timer *t)
{
DNS_time_handler();
return true;
}
void do_ping(void)
{
printf("------------PING_TEST_START-----------------------\r\n");
sleep_ms(1000); // wait 1000ms
//ping_auto(0,destip); //传入一个socket端口号和需要Ping的对象IP信息,成功则返回Ping的信息,Ping失败则打印失败的信息
ping_auto(0,Domain_IP);
}
三、基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作)
更改笔记本电脑有线网口ip为:192.168.0.18
rp2040+w5500的ip设置为:192.168.0.110
报文分析:
主函数:
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/i2c.h"
#include "inc/wizchip_conf.h"
#include "inc/w5500_spi.h"
#include "inc/loopback.h"
//定义了一个全局变量net_info来存储网卡地址信息
static wiz_NetInfo net_info = {
.mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x2e},//mac地址
.ip = {192, 168, 0, 110}, //IP地址
.sn = {255, 255, 255, 0}, //子网掩码
.gw = {192, 168, 0, 1}, //默认网关
.dns = {8, 8, 8, 8}, //dns信息
.dhcp = NETINFO_STATIC}; //DHCP模式为静态地址模式
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
#define SOCKET_ID 0
wiz_NetInfo get_info;
static uint8_t ethernet_buf_tcpclient[ETHERNET_BUF_MAX_SIZE] = {0,};
static uint16_t local_port_tcpclient = 8080;
int main()
{
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
struct repeating_timer timer;
stdio_init_all(); //串口重定义到printf函数
wizchip_initialize(); //初始化
printf("W5500 network install example.\r\n");
wizchip_setnetinfo(&net_info); //设置网卡信息
print_network_information(get_info);
sleep_ms(200);
printf("W5500 tcp server example.\r\n");
while(true)
{
//tcp服务端的回环测试函数:socket端口号、收发数据的缓存和端口
loopback_tcps(SOCKET_ID, ethernet_buf_tcpclient, local_port_tcpclient);
gpio_put(LED_PIN, 1);
sleep_ms(250);
gpio_put(LED_PIN, 0);
sleep_ms(250);
}
}
三、进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示。
NTP报文格式
NTP报文格式如上图所示,它的字段含义参考如下:
LI 闰秒标识器,占用2个bit
VN 版本号,占用3个bits,表示NTP的版本号,现在为3
Mode 模式,占用3个bits,表示模式
stratum(层),占用8个bits
Poll 测试间隔,占用8个bits,表示连续信息之间的最大间隔
Precision 精度,占用8个bits,,表示本地时钟精度
Root Delay根时延,占用8个bits,表示在主参考源之间往返的总共时延
Root Dispersion根离散,占用8个bits,表示在主参考源有关的名义错误
Reference Identifier参考时钟标识符,占用8个bits,用来标识特殊的参考源
参考时间戳,64bits时间戳,本地时钟被修改的最新时间。
原始时间戳,客户端发送的时间,64bits。
接受时间戳,服务端接受到的时间,64bits。
传送时间戳,服务端送出应答的时间,64bits。
认证符(可选项)
主函数:
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "inc/wizchip_conf.h"
#include "inc/w5500_spi.h"
#include "inc/sntp.h"
//定义了一个全局变量net_info来存储网卡地址信息
static wiz_NetInfo net_info = {
.mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x2e},//mac地址
.ip = {192, 168, 137, 110}, //IP地址
.sn = {255, 255, 255, 0}, //子网掩码
.gw = {192, 168, 137, 1}, //默认网关
.dns = {8, 8, 8, 8}, //dns信息
.dhcp = NETINFO_STATIC}; //DHCP模式为静态地址模式
wiz_NetInfo get_info;
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
#define SOCKET_SNTP 0
#define TIMEZONE 39 // China
static uint8_t ethernet_buf_ntp[ETHERNET_BUF_MAX_SIZE] = {0,};
static uint8_t g_sntp_server_ip[4] = {203, 107, 6, 88};
datetime time={0};
int main()
{
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
struct repeating_timer timer;
stdio_init_all(); //串口重定义到printf函数
sleep_ms(2000);
wizchip_initialize(); //初始化
sleep_ms(5000);
printf("W5500 network install example.\r\n");
wizchip_setnetinfo(&net_info); //设置网卡信息
print_network_information(get_info);
sleep_ms(200);
printf("W5500 ntp example.\r\n");
SNTP_init(SOCKET_SNTP,g_sntp_server_ip,TIMEZONE,ethernet_buf_ntp);
while(true)
{
//tcp服务端的回环测试函数:socket端口号、收发数据的缓存和端口
SNTP_run(&time);
printf(" %d-%d-%d, %d:%d:%d\n", time.yy, time.mo, time.dd, time.hh, time.mm, time.ss);
gpio_put(LED_PIN, 1);
sleep_ms(200);
gpio_put(LED_PIN, 0);
sleep_ms(800);
}
}
四、终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。
硬件连线:
软件架构:
终极任务在thonny环境下使用python完成:
1、首先安装thonny开发环境(建议下载最新版本,否则有些插件搜不到):https://thonny.org/
2、然后下载固件:https://micropython.org/download/W5500_EVB_PICO/
3、线路连接:将tf卡模块的3v3、GND、cs、MOSI、CLK、MISO引脚分别连接到rp2040开发板的3v3、GND、GP13、GP11、GP10、GP12引脚。
4、Thonny安装sdcard库:工具-管理包
5、参考链接GitHub - robert-hh/FTP-Server-for-ESP8266-ESP32-and-PYBD: Small FTP server for ESP8266/ESP32/PYBD on the MicroPython platform,使用库中的uftpd.py文件,更改标签为“start”的代码:
# start listening for ftp connections on port 21
def start(host='0.0.0.0',port=21, verbose=0, splash=True):
global ftpsockets, datasocket
global verbose_l
global client_list
global client_busy
alloc_emergency_exception_buf(100)
verbose_l = verbose
client_list = []
client_busy = False
addr = socket.getaddrinfo(host, port)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr[0][4])
sock.listen(1)
sock.setsockopt(socket.SOL_SOCKET,
_SO_REGISTER_HANDLER,
lambda s : accept_ftp_connect(s,host))
ftpsockets.append(sock)
if splash:
print("FTP server started on {}:{}".format(host, port))
datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
datasocket.bind(('0.0.0.0', _DATA_PORT))
datasocket.listen(1)
datasocket.settimeout(10)
6、主函数代码为:
import network,time
from machine import Pin,SPI
#w5500 初始化
def w5x00_init():
spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))#初始化SPI外设接口,用于wiznet芯片通信与控制
nic = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin
nic.active(True)#网络激活
#配置静态网络信息
nic.ifconfig(('192.168.137.110','255.255.255.0','192.168.137.1','8.8.8.8'))
while not nic.isconnected():#当网络未配置成功时
time.sleep(1)
print(nic.ifconfig())#打印IP、子网掩码、网关、DNS
def sdcard_init():
import os
from sdcard import SDCard
from machine import Pin,SPI
#sd卡模块连接到rp2040的spi1外设
spi = SPI(1,2_000_000,sck=Pin(10), mosi=Pin(11), miso=Pin(12))
#spi,cs
sd = SDCard(spi, Pin(13))
# 挂载SD卡到/sd目录
os.mount(sd, '/sd')
print(f'success mounted sdcard in /sd')
if __name__ == "__main__":
# 初始化网络
w5x00_init()
# 初始化并挂在sd卡
sdcard_init()
import uftpd
uftpd.start(host = '192.168.137.110',port = 21,verbose = 0)
7、实验结果
五、对本次活动的心得体会:
本人接触嵌入式有5年时间,大多偏向于单片机底层,对网络协议一知半解,故很高兴能有机会参加本次活动。通过本次活动,我学习了rp2040在linux环境下的开发、windows-thonny环境下的开发,学习了ubuntu的基本命令、python的基本语法、基本网络拓扑结构、ping操作、dns的功能、tcp服务器的原理、从ntp服务器同步时间、基于tf卡建立ftp服务器。
刚接触任务时,我采用thonny开发,但我不太熟悉python,故有些吃力,看到w5500官方有专门的c-sdk,所以我最后采用了linux环境下的clion开发,这对熟悉c语言的同学很友好。前几个任务w5500官方基本都有sdk例程参考,所以我很轻松的完成了任务。终极任务因为python环境下有github参考例程,故终极任务采用python语言开发。
六、参考链接:
eeworld follow me4教程:W5500-EVB-Pico 使用入门-直播回放: FollowMe 4 W5500-EVB-Pico 使用入门-EEWORLD大学堂
w5500官方csdn教程:WIZnet W5500-EVB-Pico C/C++教程_WIZnet的博客-CSDN博客
w5500官方c-sdk:WIZnet HK/W5100S_W5500+PR2040 C_SDK_Examples - 码云 - 开源中国 (gitee.com)
-
上传了资料:
【得捷Follow me第4期】+小白手把手带你学习rp2040+w5500(所有任务)
- 2024-02-18
-
加入了学习《【得捷电子Follow me第2期】任务提交》,观看 【得捷电子Follow me第2期】任务提交
-
回复了主题帖:
【得捷电子Follow me第4期】终极任务2:SD 卡搭建 FTP 服务器
哥 有源码吗 小白想参考一下
-
加入了学习《【得捷电子Follow me第4期】上手W5500-EVB-Pico》,观看 【得捷电子Follow me第4期】上手W5500-EVB-Pico
- 2024-02-12
-
加入了学习《【得捷电子Follow me第4期】全部任务合集》,观看 【得捷电子Follow me第4期】全部任务合集
- 2024-01-24
-
加入了学习《Digi-Key: Follow Me 系列(1) 直播回放及答疑记录》,观看 现场答疑记录
-
加入了学习《Digi-Key: Follow Me 系列(1) 直播回放及答疑记录》,观看 Raspberry Pi Pico W 使用入门
- 2024-01-10
-
加入了学习《平衡小车与电机PID系列视频教程》,观看 离散化PID入门