一、前言(含视频)
本次Follow Me 4共分为4个任务:入门任务,基础任务,进阶任务和终极任务,其中,基础任务分为两个小任务,所以共六个任务,都顺利完成。
并且已经将内容一的视频上传至网络大学堂,视频地址为:https://training.eeworld.com.cn/video/39296 ;视频里包含了各任务的现场烧录演示及说明。
任务完成所用开发环境为Paspberry Pi的Pico SDK v1.5.1+VSCode;其中VSCode不多赘述,而Paspberry Pi的Pico SDK v1.5.1的下载地址为 ,安装过程可参考 WIZnet W5500-EVB-Pico树莓派入门教程(一)-CSDN博客;
任务完成所用硬件如下几个:W5500-EVB-Pico(必购);Pico-LCD 1.14(用于大部分的显示任务);AtomS3(W5500-EVB-Pico在做FTP服务器时,读取SD卡需要用的SPI,导致无法使用Pico-LCD做显示,通过UDP传输显示数据给AtomS3,用AtomS3做显示);SEEED XIAO ESP32 C3(测试W5500-EVB-Pico的UDP Server功能)、DAPLink一个(自备,用于在线仿真,方便调试网络)以及自己绘制的LCD转接板一个,方便给将W5500-EVB-Pico主板与Pico-LCD组合起来,转接板如下:
图1-1
下面将各任务逐一介绍并展示(内容二)。
二、入门任务
2.1 任务介绍
搭建开发环境,点亮小灯让其闪烁,并在显示屏上显示内容。
2.2 主要代码片段
LCD的显示主要靠移植Pico-LCD的官方驱动,其下载地址为https://www.waveshare.net/w/upload/2/28/Pico_code.7z; 将其C驱动部分复制至工程目录下,并改名为LCD,在工程目录下的CMake中添加如下命令:
add_subdirectory(DISPLAY/Config)
add_subdirectory(DISPLAY/LCD)
add_subdirectory(DISPLAY/Fonts)
add_subdirectory(DISPLAY/GUI)
并在项目目录下的CMake中添加如下命令:
include_directories(../DISPLAY/Config)
include_directories(../DISPLAY/GUI)
include_directories(../DISPLAY/LCD)
这样即包含了Pico-LCD的驱动库,初始化后就可直接显示输入内容,初始化过程如下:
DEV_Module_Init();
DEV_SET_PWM(30);
LCD_1IN14_V2_Init(HORIZONTAL);
LCD_1IN14_V2_Clear(BLACK);
UDOUBLE Imagesize = LCD_1IN14_V2_HEIGHT*LCD_1IN14_V2_WIDTH*2;
BlackImage = (UWORD *)malloc(Imagesize);
Paint_NewImage((UBYTE *)BlackImage,LCD_1IN14_V2.WIDTH,LCD_1IN14_V2.HEIGHT, 0, BLACK);
Paint_SetScale(65);
Paint_SetRotate(ROTATE);
Paint_Clear(BLACK);
LCD_1IN14_V2_Display(BlackImage);
之后在Paint_DrawString_EN函数中输入字符串数组,显示坐标即可显示相应内容。
小灯的闪烁代码如下:
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while (true) {
gpio_put(LED_PIN, 1);
sleep_ms(250);
gpio_put(LED_PIN, 0);
sleep_ms(250);
}
2.3 功能展示
图2-1 驱动显示屏(显示屏通过转接板叠加在主控板上)
三、基础任务一
3.1 任务介绍
主板W5500-EVB-Pico获取静态IP,并且PC能够ping通,同时主板可以ping通外网;PCping通结果抓包显示。
3.2 主要代码片段
需要用到W5500的官方库,其地址为https://gitee.com/wiznet-hk/w5500-evb-pico-routine.git
随后如同上述LCD添加入库,即可调用相关函数即可实现静态IP:只要初始化W5500完成,再设置网络信息,标记为静态获取即可。
// 网络信息 默认静态
wiz_NetInfo net_info = {
.mac = {0x00, 0x08, 0xdc, 0x1e, 0xed, 0x2e}, // Configured MAC address
.ip = {192, 168, 1, 31}, // Configured IP address
.sn = {255, 255, 255, 0}, // Configured subnet mask
.gw = {192, 168, 1, 1}, // Configured gateway
.dns = {8, 8, 8, 8}, // Configured domain address
};
// 初始化
wizchip_initialize();
sleep_ms(50);
wizchip_setnetinfo(&net_info);
print_network_information(get_info);
想要ping通互联网,则是向特定的目的主机发送 ICMP(Internet Control Message Protocol 因特网报文控制协议)Echo 请求报文,核心代码如下:
void ping_auto(uint8_t s, uint8_t *addr)
{
uint8_t i;
int32_t len = 0;
uint8_t cnt=0;
for(i = 0; i<=3;i++)
{
sleep_ms(10);
switch(getSn_SR(s))
{
case SOCK_CLOSED:
close(s);
IINCHIP_WRITE(Sn_PROTO(s), IPPROTO_ICMP);
if(socket(s,Sn_MR_IPRAW,3000,0)!=0)
{ }
while(getSn_SR(s)!=SOCK_IPRAW);
sleep_ms(2000);
break;
case SOCK_IPRAW:
ping_request(s, addr);
req++;
sleep_ms(50);
while(1)
{
if ( (len = getSn_RX_RSR(s) ) > 0)
{
ping_reply(s, addr, len);
sleep_ms(50);
rep++;
break;
}
else if(cnt > 200)
{
printf( "Request Time out. \r\n");
cnt = 0;
break;
}
else
{
cnt++;
sleep_ms(50); /*wait 50ms*/
}
// wait_time for 2 seconds, Break on fail
}
break;
default:
break;
}
#ifdef PING_DEBUG
if(rep!=0)
{
printf("Ping Request = %d, PING_Reply = %d\r\n",req,rep);
if(rep == req)
printf( "PING SUCCESS\r\n " );
else
printf( "REPLY_ERROR\r\n " );
}
else{}
#endif
}
}
3.3 功能展示
图3-1 PC端Ping主板成功图
图3-2 主控板Ping互联网成功串口输出图
四、基础任务二
4.1 任务介绍
主板W5500-EVB-Pico做UDP服务器,并能将PC端的UDP客户端发送内容显示在显示屏上,再抓包对比报文。同时使用SEEED XIAO ESP32 C3做UDP客户端进行测试。
4.2 核心代码片段
正常初始化网络即可,UDP主要涉及Socket,包含头文件 #include "socket.h" 即可,然后轮询状态(判断是否有数据传输等)即可,代码如下:
int32_t udps_2(uint8_t sn, uint8_t* buf, uint16_t port)
{
int32_t ret;
uint16_t size, sentsize;
uint8_t destip[4];
uint16_t destport;
switch(getSn_SR(sn))
{
case SOCK_UDP :
if((size = getSn_RX_RSR(sn)) > 0)
{
if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;
ret = recvfrom(sn, buf, size, destip, (uint16_t*)&destport);
buf[ret]=0x00;
// printf("recv from[%d.%d.%d.%d][%d]: %s\n", destip[0],destip[1],destip[2],destip[3],destport,buf);
sprintf(RecvIP[RecvCnt], "> %d.%d.%d.%d:%d", destip[0],destip[1],destip[2],destip[3],destport);
sprintf(RecvBuf[RecvCnt], "> %s", buf);
ddebug(RecvIP[RecvCnt]);
ddebug(RecvBuf[RecvCnt]);
if(++RecvCnt >= 10) RecvCnt = 0;
if(ret <= 0)
{
return ret;
}
size = (uint16_t) ret;
sentsize = 0;
while(sentsize != size)
{
ret = sendto(sn, buf+sentsize, size-sentsize, destip, destport);
if(ret < 0)
{
return ret;
}
sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
}
}
break;
case SOCK_CLOSED:
if((ret = socket(sn, Sn_MR_UDP, port, 0x00)) != sn)
return ret;
printf("local port:%d\n",port);
break;
default :
break;
}
return 1;
}
4.3 成果展示
先是用PC端的UDP(192.168.1.22:3333)发送Follow Me 4!到到主控板(192.168.1.31:8000),并用WireShark抓包(过滤条件为<ip.addr == 192.168.1.31 and udp>),显示如下:
图4-1 PC和主控板UDP通信抓包显示
再用SEED XIAO ESP32C3做UDP的客户端(192.168.1.45:8000)去测试主控板的UDP服务器功能,每3秒发送一次Follow Me 4!;同时通过显示屏展示收到的内容,完成情况如下:
图4-2 主控板显示屏显示UDP接收内容(包含主机和XIAOESP32C3)
五、进阶任务
5.1 任务内容
从NTP服务器同步时间,获取时间送显示屏显示。
5.2 核心代码片段
本质上还是UDP,这里选择用tx的NTP服务器,测试期间其地址为106.55.184.199;NTP及格式解析核心代码如下:
// NTP
int8_t SNTP_run(datetime *time)
{
uint16_t RSR_len;
uint32_t destip = 0;
uint16_t destport;
uint16_t startindex = 40;
switch(getSn_SR(NTP_SOCKET))
{
case SOCK_UDP:
if ((RSR_len = getSn_RX_RSR(NTP_SOCKET)) > 0)
{
if (RSR_len > MAX_SNTP_BUF_SIZE) RSR_len = MAX_SNTP_BUF_SIZE;
recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport);
get_seconds_from_ntp_server(data_buf,startindex);
time->yy = Nowdatetime.yy;
time->mo = Nowdatetime.mo;
time->dd = Nowdatetime.dd;
time->hh = Nowdatetime.hh;
time->mm = Nowdatetime.mm;
time->ss = Nowdatetime.ss;
ntp_retry_cnt=0;
close(NTP_SOCKET);
return 1;
}
if(ntp_retry_cnt<0xFFFF)
{
if(ntp_retry_cnt==0)//first send request, no need to wait
{
sendto(NTP_SOCKET,ntpmessage,sizeof(ntpmessage),NTPformat.dstaddr,ntp_port);
ntp_retry_cnt++;
}
else // send request again? it should wait for a while
{
if((ntp_retry_cnt % 0xFFF) == 0) //wait time
{
sendto(NTP_SOCKET,ntpmessage,sizeof(ntpmessage),NTPformat.dstaddr,ntp_port);
ntp_retry_cnt++;
}
}
}
else //ntp retry fail
{
ntp_retry_cnt=0;
close(NTP_SOCKET);
}
break;
case SOCK_CLOSED:
socket(NTP_SOCKET,Sn_MR_UDP,ntp_port,0);
break;
}
return 0;
}
// 送显示
sprintf(time_str[count], "> %d-%02d-%02d %02d:%02d:%02d", time.yy, time.mo, time.dd, time.hh, time.mm, time.ss);
ddebug(time_str[count]);
5.3 成果展示
NTP实验内容较好展示,直接百度上搜索北京时间,然后对比即可,显示每1s才刷新一次,会存在误差,对比如下:
图5-1 左为浏览器显示北京时间 右为主控板每秒输出时间
六、最终任务
6.1 任务内容
使用外部SD卡存储器,组建简易FTP文件服务器,并能正常上传下载文件。同时因SD卡需占用一路SPI,故舍弃LCD,采用UDP传输给AtomS3用于显示FTP服务器相关内容。
6.2 核心代码片段
主要是SD卡和FatFs的移植,有现成的库,地址为 ;
设置静态IP,并修改并记住FTP的ID和密码,再开启Fatfs功能,需要修改部分代码,主要添加两个接口函数,scan_files和get_filesize,分别是扫描和获取大小,内容如下
long get_filesize(const char *dirPath, const char *filename) {
char fullPath[256]; // 假设路径长度不超过255个字符
FILINFO fno;
FRESULT fr;
// 构造完整路径
snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, filename);
// 使用 f_stat 获取文件信息
fr = f_stat(fullPath, &fno);
if (fr == FR_OK) {
return (long)fno.fsize; // 返回文件大小
} else {
return -1; // 发生错误,返回 -1
}
}
FRESULT scan_files (
char *path, /* Start node to be scanned (***also used as work area***) */
char *files,
uint16_t *num)
{
FRESULT res;
DIR dir;
UINT i;
static FILINFO fno;
uint16_t _num = 0;
*num = 0;
res = f_opendir(&dir, path); /* Open the directory */
if (res == FR_OK) {
for (;;) {
res = f_readdir(&dir, &fno); /* Read a directory item */
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
i = strlen(path);
uint8_t isdir = (fno.fattrib & AM_DIR);
char size[32];
sprintf(size, "%ld", (fno.fattrib & AM_DIR) ? 0 : fno.fsize);
files += sprintf(files, "%crwxr-xr-x 1 pomin pomin %s Jan 19 2024 %s\r\n", (fno.fattrib & AM_DIR) ? 'd' : '-', size, fno.fname);
_num++;
}
f_closedir(&dir);
}
*num = _num;
return res;
}
6.3 成果展示
PC端使用Filezilla做FTP客户端,W5500烧录代码后,使用Filezilla连接,地址为192.168.1.31,ID为DIgikey,密码为Followme4,端口不用选择,默认为21,连接成功,并能上传下载文件,上传过程截图如下:
图6-1 PC向W5500主控板传输图片(速度200K/s)
虽说无法使用LCD了,但是怎么能没有显示屏呢?所以还用了AtomS3做UDP服务器,将W5500主控板通过UDP发送来的内容进行显示,当作W5500的显示屏,展示如下:
图6-2 AtomS3做主控板的显示器(演示中,上传了两张图片,下载了文本)
七、心得体会
首先是学到了不少东西,接触了RP2040、W5500、ESP32,对Cmake、Ethernet、WIFI等都有了更深的理解,然后最大的感悟还是,没想到嵌入式开发还能如此简单,很多库都是别人写好的,而rp2040引脚又少,调用起来非常简单,觉得嵌入式就该往这个方向发展,更多耗费时间的还得是逻辑与算法。再者就是群里的大佬太多了,见识到了很多,自己仍需努力,向大佬看齐,最后当然是感谢得捷,感谢EEWORLD,谢谢你们提供了这个宝贵的机会,也希望今后多多出这些活动,也祝你们越来越好!
八、附件
这里就是内容三了,已将全部源码打包并上传至:https://download.eeworld.com.cn/detail/Alohaq/631392 ;如果你也安装了Pico-sdk,那么只需要添加到pcio的vscode环境里,即可编译下载全部内容。
|