- 2024-11-01
-
加入了学习《 得捷电子Follow Me第2季第一期任务汇报》,观看 得捷电子Follow Me第4期任务汇报
- 2024-10-23
-
加入了学习《得捷Follow me第二季第1期视频》,观看 得捷Follow me第二季第1期视频
- 2024-09-02
-
加入了学习《Follow me 第二季第1期演示视频》,观看 Follow me 第二季第1期演示视频
- 2024-09-01
-
上传了资料:
Follw Me第二季第一期全部任务源码
- 2024-08-31
-
发表了主题帖:
【Follow me第二季第1期】任务提交贴
本帖最后由 Alohaq 于 2024-11-4 13:04 编辑
一、前言
Follow me第二季第1期的用的主板为Adafruit Circuit Playground Express,其为一块一体化设计板,具有处理器、传感器、LED、USB等,非常适合用于电子产品和编程,能够让创意充分发挥。这期共有四个任务,分别为入门任务,基础任务,进阶任务和创意任务,创意任务选择了其中之一——《章鱼哥》,所有任务均已完成,视频链接为:https://training.eeworld.com.cn/video/40998。
本人这期任务所用器件如下:
Adafruit Circuit Playground Express,为本期任务主板,任务必购硬件;
Round Display for Xiao(Seeed Studio),在我看来,没有显示的主板是不完整的,加上本期任务主板为圆形主板,遂配备圆形显示器,其驱动芯片为GC9A01,任务选购硬件;
自制转接板,用于将任务主板与圆形显示器组合,自制PCB打印;
SG90S,180°舵机,为创意任务《章鱼哥》配备,并配备机械手模拟触角,个人自备;
鲸鱼不倒翁一只,为进阶任务配备,个人自备;
机械手一个,模拟章鱼哥的触角,为创意任务《章鱼哥》配备,个人自备。
本人这期任务所用环境为:VScode+PlatformIO,软件开发框架为Arduino,主要依赖的驱动库为Adafruit Circuit Playground、GFX Library for Arduino,其中Adafruit Circuit Playground为官方Arduino驱动库,可以控制Adafruit Circuit Playground Express上的所有外设;GFX Library for Arduino是一个 Arduino 图形库,支持具有各种数据总线接口的各种显示器,本次用于驱动Round Display for Xiao显示板。
二、任务实现详情
2.1 入门任务
任务介绍:开发环境搭建,板载LED点亮(个人增加显示屏点亮)。
涉及器件:Adafruit Circuit Playground Express;Round Display for Xiao(Seeed Studio)。
涉及库:Adafruit Circuit Playground、GFX Library for Arduino。
具体内容:
开发环境采用VSCode+PlatformIO,其中PlatformIO为VSCode的插件,VSCode的安装不再赘述,安装完成后在扩展中搜索PaltformIO进行安装,随后新建项目,以本项目为例:Board选择Adafruit Circuit Playground Express,Framework选择Arduino,Name我命名为01_Primer_Led,可以个人选择保存路径,详情可见图2-1,随后创建即可,开发环境初步搭建完成。
图2-1 首次创建项目
接着我们再添加官方为准备的驱动库Adafruit Circuit Playground,在VSCode的标签页中选择PIO Home,然后点击侧边工具栏中的Libraries,搜索Adafruit Circuit Playground,找到后选择打开,并在进入后选择添加至工程,详情如图2-2所示。
图2-2 添加官方驱动库
添加完官方驱动库后,在源文件中包含头文件<Adafruit_CircuitPlayground.h>即可,随后进行编译,此时会发现编译不通过,如图2-3所示,有errors产出:#error TinyUsB is not selected, please select it in "Tools->Menu->USB Stack“,同时也能看出是源文件Adafruit_TinyUSB_API.cpp的29行报出。
图2-3 errors
追根溯源,打开Adafruit_TinyUSB_API.cpp,找到29行,如图2-4所示,可发现是在没有USE_TINYUSB宏定义的情况下,又宏定义了ARDUINO_ARCH_SAMD(Board选择Adafruit Circuit Playground Express时会定义该宏定义)的时候,会报出上述错误。我们可以在预编译中增加USE_TINYUSB来解决报错,也可以选择忽略Tiny USB的驱动库,两者均可在platformio.ini中实现,分别为build_flags = -D USE TINYUSB和lib_ignore = Adafruit TinyUSB Library,具体如图2-4所示,具体使用哪一个,取决于您是否需要TinyUSB,二者选其一即可,可用‘;‘进行注释。随后便可正常编译烧录。
图2-4 platformio.ini添加内容
由于该任务本人还需驱动圆形显示器,需要额外添加库,我选择的驱动库为GFX Library for Arduino,操作步骤同库Adafruit Circuit Playground,详情如图2-2所示。
图2-3 添加显示驱动库
驱动库添加完成后,在需要使用的源文件中加上头文件<Arduino_GFX_Library.h>,然后选择合适的驱动函数即可,具体见代码:
#include <Adafruit_CircuitPlayground.h>
#include <Arduino_GFX_Library.h>
// #define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin
Arduino_DataBus *bus = new Arduino_SWSPI(3 /* DC */, 2 /*CS*/, 10 /* SCK*/, 9 /* MOSI*/, 6 /*MISO*/); //CS A5(2) SCK A3(10) MOSI A2(9) MISO A1(6) DC A4(3)
Arduino_GFX *gfx = new Arduino_GC9A01(bus, DF_GFX_RST, 0 /* rotation */, true /* IPS */);
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin 13 as an output.
pinMode(LED_BUILTIN, OUTPUT);
gfx -> begin();
gfx->fillScreen(BLACK);
gfx->setTextSize(3);
gfx->setTextColor(RED);
gfx->setCursor(15, 90);
gfx->println("Hello World!");
gfx->println("Follow me 2-1");
delay(1000); // 5 seconds
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
效果图:
图2-4 LED闪烁
图2-5 显示屏显示内容
2.2 基础任务一
任务介绍:控制板载炫彩LED,跑马灯点亮和颜色变换。
涉及器件:Adafruit Circuit Playground Express。
涉及库:Adafruit Circuit Playground。
具体内容:代码先是跑马灯,RGB逐一点亮,这个很好实现,for函数即可;随后是颜色变化,这里用上SIN函数加上时间,对r、g、b分别加上相位差赋值,进而实现炫彩呼吸渐变,详情见代码,代码如下:
#include <Adafruit_CircuitPlayground.h>
#include <math.h>
// do NOT include the standard NeoPixel library
void setup() {
// initialize the Circuit Playground as usual
// this will initialize the onboard NeoPixels as well
CircuitPlayground.begin();
delay(100);
// 跑马灯
for (uint8_t i=0; i<10; i++) {
CircuitPlayground.setPixelColor(i, 255, 255, 255);
delay(200);
}
}
void loop() {
static int time = 0; // 用于控制颜色渐变的时间变量
CircuitPlayground.clearPixels();
// 呼吸灯
for(int i=0; i<10; i++) {
float angle = (float)(i + time) / 5.0 * M_PI;
// 使用 sin 函数计算 RGB 颜色值
uint8_t r = (uint8_t)((sin(angle) + 1) * 127.5);
uint8_t g = (uint8_t)((sin(angle + 2.0 * M_PI / 3.0) + 1) * 127.5);
uint8_t b = (uint8_t)((sin(angle + 4.0 * M_PI / 3.0) + 1) * 127.5);
CircuitPlayground.setPixelColor(i, r, g, b); // 设置第 i 个 LED 的颜色
}
delay(50); // 延迟,用于控制动画的速度
time++; // 递增时间变量,以生成渐变效果
}
效果图:
图2-6 炫彩RGB
2.3 基础任务二
任务介绍:监测环境温度和光线,通过板载LED展示舒适程度。
涉及器件:Adafruit Circuit Playground Express。
涉及库:Adafruit Circuit Playground。
具体内容:主要利用光传感器,毕竟温度的升降较难控制且周期很长,简易利用读数,对应上0~1,该部分用于红光显示,1剩下部分分给蓝光,进而展示舒适度,这里红光表示环境很不舒适,反之越蓝越舒适。详情见代码,代码如下:
#include <Adafruit_CircuitPlayground.h>
void setup() {
CircuitPlayground.begin();
// Serial.begin(9600);
// while (!Serial); // Wait until serial console is opened
// Serial.println("Ready to detection environment");
}
float temp_range = 42-30;
float r, b = 0;
void loop() {
// Serial.print("Light sensor: ");
// Serial.println(CircuitPlayground.lightSensor());
// Serial.print("Temperature: ");
// Serial.println(CircuitPlayground.temperature());
r = (CircuitPlayground.temperature() - 30) / temp_range;
// Serial.println(r);
r += (300 - CircuitPlayground.lightSensor() ) / 300.0;
// Serial.println(r);
if (r > 1) r = 1;
b = 1 - r;
CircuitPlayground.clearPixels();
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 255*r, 0, 255*b);
}
delay(500);
}
效果图:
图2-7 黑暗环境下,红灯表明不舒适
2.4 基础任务三
任务介绍:接近检测——设定安全距离并通过板载LED展示,检测到入侵时,发起声音报警。
涉及器件:Adafruit Circuit Playground Express;Round Display for Xiao(Seeed Studio)。
涉及库:Adafruit Circuit Playground、GFX Library for Arduino。
具体内容:
图2-8 RX对应引脚
利用红外的TX发出38KHz的信号,模拟超声波测距,然后直接读取RX处的ADC值,码值越大,说明接收的信号越强,即物体越接近,注意不能通过RX接收处的解码芯片,否则无法完成,遂涉及引脚的寄存器操作,对应工程图如图2-8所示,同时距离会在显示屏上显示,越近数字越小,当数字为1时,发出“danger”声报警,同时rgb颜色随距离越近,由绿变红,详情见代码,代码如下:
#include <Adafruit_CircuitPlayground.h>
#include <Arduino_GFX_Library.h>
// #define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin
Arduino_DataBus *bus = new Arduino_SWSPI(3 /* DC */, 2 /*CS*/, 10 /* SCK*/, 9 /* MOSI*/, 6 /*MISO*/); //CS A5(2) SCK A3(10) MOSI A2(9) MISO A1(6) DC A4(3)
Arduino_GFX *gfx = new Arduino_GC9A01(bus, DF_GFX_RST, 0 /* rotation */, true /* IPS */);
void setup() {
CircuitPlayground.begin();
// PA4做ADC输入
PORT->Group[PORTA].DIRCLR.reg = (1 << 4); // Set pin A4 as input
PORT->Group[PORTA].PINCFG[4].bit.PMUXEN = 1; // Enable peripheral multiplexer on pin A4
PORT->Group[PORTA].PMUX[4 >> 1].reg |= PORT_PMUX_PMUXE_B; // Set pin A4 to peripheral function B (ADC)
// 配置 ADC
ADC->CTRLA.bit.ENABLE = 0; // 先禁用 ADC 以进行配置
ADC->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_PIN4; // 选择 PA4 作为 ADC 输入通道
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV64 | ADC_CTRLB_RESSEL_12BIT; // 设置预分频器和分辨率
ADC->SAMPCTRL.reg = 0x3F; // 设置采样时间
ADC->CTRLA.bit.ENABLE = 1; // 启用 ADC
// PORT->Group[PORTA].DIRSET.reg = (1 << 17); // 将 PA17 配置为输出
// PORT->Group[PORTA].OUTSET.reg = (1 << 17); // 将 PA17 设置为高电平
pinMode(CPLAY_IR_EMITTER, OUTPUT);
pinMode(CPLAY_BUZZER, OUTPUT);
// pinMode(CPLAY_SPEAKER_SHUTDOWN, OUTPUT);
// digitalWrite(CPLAY_SPEAKER_SHUTDOWN, HIGH);
gfx -> begin();
gfx->fillScreen(BLACK);
gfx->setTextSize(4);
gfx->setTextColor(RED);
// Serial.begin(9600);
// while (!Serial); // Wait until serial console is opened
// Serial.println("Ready to receive IR signals");
}
const uint8_t spDANGER[] PROGMEM = {0x2D,0xBF,0x21,0x92,0x59,0xB4,0x9F,0xA2,0x87,0x10,0x8E,0xDC,0x72,0xAB,0x5B,0x9D,0x62,0xA6,0x42,0x9E,0x9C,0xB8,0xB3,0x95,0x0D,0xAF,0x14,0x15,0xA5,0x47,0xDE,0x1D,0x7A,0x78,0x3A,0x49,0x65,0x55,0xD0,0x5E,0xAE,0x3A,0xB5,0x53,0x93,0x88,0x65,0xE2,0x00,0xEC,0x9A,0xEA,0x80,0x65,0x82,0xC7,0xD8,0x63,0x0A,0x9A,0x65,0x5D,0x53,0xC9,0x49,0x5C,0xE1,0x7D,0x2F,0x73,0x2F,0x47,0x59,0xC2,0xDE,0x9A,0x27,0x5F,0xF1,0x8B,0xDF,0xFF,0x03};
void loop() {
// CircuitPlayground.irSend.send(7, 0x5555, 16);
// delay(1);
for (int i = 0; i < 200; i++) { // 38kHz PWM信号
digitalWrite(CPLAY_IR_EMITTER, HIGH);
delayMicroseconds(13);
digitalWrite(CPLAY_IR_EMITTER, LOW);
delayMicroseconds(13);
}
// digitalWrite(CPLAY_BUZZER, LOW);
//Continue looping until you get a complete signal received
// 开始转换
ADC->SWTRIG.bit.START = 1; // 启动 ADC 转换
// 等待转换完成
while (ADC->INTFLAG.bit.RESRDY == 0);
// digitalWrite(CPLAY_IR_EMITTER, LOW);
// 读取结果
uint16_t result = ADC->RESULT.reg;
uint8_t dis = (result - 2300) / 60;
// if (result > 1500)
// Serial.println(result);
gfx->setCursor(110, 110);
gfx->fillScreen(BLACK);
gfx->println(7-dis);
if (dis >= 6) {
CircuitPlayground.speaker.say(spDANGER);
CircuitPlayground.speaker.end();
}
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 255 * dis / 7.0, 255 * (7 - dis) / 7.0, 0);
}
delay(300);
// delay(100);
}
效果图:
图2-9 物体距离过近,红光警示
2.5进阶任务
任务介绍:制作不倒翁——展示不倒翁运动过程中的不同灯光效果。
涉及器件:Adafruit Circuit Playground Express。
涉及库:Adafruit Circuit Playground。
具体内容:首先利用LIS3DH得到X、Y、Z,其中LIS3DH设置加速度为±2g,采样为50Hz,故需对其处理(乘上4),处理完后,进而算出横滚角和俯仰角,计算如下:
再通过俯仰角和横滚角计算出对应方位及深度,利用方向角索引至对应RGB,再利用深度,决定点亮几个RGB,达到RGB随不倒翁运动而变化的效果。详情见代码,代码如下:
#include <Adafruit_CircuitPlayground.h>
void setup() {
CircuitPlayground.begin();
CircuitPlayground.lis.setDataRate(LIS3DH_DATARATE_50_HZ);
CircuitPlayground.lis.setRange(LIS3DH_RANGE_2_G);
// Serial.begin(9600);
// while (!Serial); // Wait until serial console is opened
// Serial.println("Ready to read LIS3DH");
}
float X, Y, Z;
short pitch ,roll;
void loop() {
CircuitPlayground.lis.read();
X = (float)CircuitPlayground.lis.x*4/65536*1000; // 还可以试试看motionX
Y = (float)CircuitPlayground.lis.y*4/65536*1000;
Z = (float)CircuitPlayground.lis.z*4/65536*1000;
pitch = (short)(atan2((float)(0-Y), Z) * 180 / 3.14159); //转换为度数
roll = (short)(atan2((float)(X), Z) * 180 / 3.14159); //转换为度数
float direction = atan2(roll, pitch); // 计算方向角
int ledIndex;
// Serial.println(direction); // direction范围是[-PI, PI]
if (direction >= 0) {
ledIndex = round((direction / PI) * 6); // 映射到LED索引
}
else {
ledIndex = 11 + round((direction / PI) * 6); // 映射到LED索引
}
// int ledIndex = round((direction / PI) * 6) % 12; // 映射到LED索引
// Serial.println(ledIndex);
float tiltMagnitude = sqrt(pitch * pitch + roll * roll); // 计算倾斜幅度
int ledCount = round((tiltMagnitude / 90.0) * 6) * 2; // 映射点亮数量
static int time = 0; // 用于控制颜色渐变的时间变量
CircuitPlayground.clearPixels();
// float angle = (float)(time) / 10 * 2.0 * M_PI;
float angle = (float)(time) / 5.0 * M_PI;
// 使用 sin 函数计算 RGB 颜色值
uint8_t r = (uint8_t)((sin(angle) + 1) * 127.5);
uint8_t g = (uint8_t)((sin(angle + 2.0 * M_PI / 3.0) + 1) * 127.5);
uint8_t b = (uint8_t)((sin(angle + 4.0 * M_PI / 3.0) + 1) * 127.5);
for (int i = -ledCount/2; i < ledCount/2; i++) {
int indexToLight = ledIndex + i;
// CircuitPlayground.setPixelColor(indexToLight, 0, 64, 64);
CircuitPlayground.setPixelColor(indexToLight, r, g, b);
}
// Serial.println(ledIndex);
time++; // 递增时间变量,以生成渐变效果
delay(100);
}
效果图:
图2-10 不倒翁倾斜,rgb随倾斜方向亮灯
2.6 创意任务
任务介绍:章鱼哥——章鱼哥的触角根据环境声音的大小,章鱼哥的触角可舒展或者收缩。
涉及器件:Adafruit Circuit Playground Express。
涉及库:Adafruit Circuit Playground。
具体内容:章鱼哥听到环境噪音(利用板载的麦克风噪音检测实现),认为敌人来临,遂伸张触角游泳离开,即环境音变大则伸张触角,环境正常则收缩触角。代码如下:
#include <Adafruit_CircuitPlayground.h>
#include <Servo.h>
Servo myservo; // create Servo object to control a servo
void setup() {
CircuitPlayground.begin();
myservo.attach(A5); // attaches the servo on pin 9 to the Servo object
// Serial.begin(9600);
// myservo.writeMicroseconds(1450); // 舵机顺时针旋转
}
void loop() {
// for (int pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
// // in steps of 1 degree
// myservo.write(pos); // tell servo to go to position in variable 'pos'
// delay(15); // waits 15ms for the servo to reach the position
// }
// CircuitPlayground.soundSensor();
// Serial.println(CircuitPlayground.soundSensor());
// Serial.println( CircuitPlayground.mic.soundPressureLevel(500) );
// myservo.writeMicroseconds(1300); // 舵机顺时针旋转 收缩
// delay(1000);
// myservo.writeMicroseconds(1600);// 舵机逆时针旋转 伸张
// myservo.writeMicroseconds(2000);// 舵机逆时针旋转
// delay(2000);
if ( CircuitPlayground.mic.soundPressureLevel(300) > 75)// 有噪声则伸张
{
myservo.writeMicroseconds(1800);// 舵机逆时针旋转 伸张
}
else
{
myservo.writeMicroseconds(1200); // 舵机顺时针旋转 收缩
}
}
效果图:
图2-11 章鱼听到噪音,准备伸张触角
三、活动心得
收获满满!动手能力进一步得到加强,思路进一步得以展开!祝越办越好!
最后附上源码:Follw Me第二季第一期全部任务源码-嵌入式开发相关资料下载-EEWORLD下载中心
- 2024-02-26
-
回复了主题帖:
【得捷Follow me第4期】项目总结
秦天qintian0303 发表于 2024-2-26 09:12
Pico-LCD 1.14(用于大部分的显示任务),还是的带显示才能更高完成任务
就最终任务无法使用Pico-LCD(基础任务静态IP那无需显示),因为SPI分给W5500和SD卡,没硬件SPI支持显示了,又不想软模拟,就用UDP传给AtomS3当显示了。
-
加入了学习《得捷电子Follow Me第4期任务汇报》,观看 得捷电子Follow Me第4期任务汇报
-
加入了学习《Follow me 第4期任务视频》,观看 Follow me 第4期任务视频
- 2024-02-25
-
上传了资料:
Follow me 第4期全部任务实现源码
-
发表了主题帖:
【得捷Follow me第4期】项目总结
一、前言(含视频)
本次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的下载地址为 https://github.com/raspberrypi/pico-setup-windows,安装过程可参考 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 驱动显示屏(显示屏通过转接板叠加在主控板上)
图2-2 BLink
三、基础任务一
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的移植,有现成的库,地址为 https://github.com/elehobica/pico_fatfs.git ;
设置静态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环境里,即可编译下载全部内容。
- 2024-01-25
-
加入了学习《直播回放: FollowMe 3 与得捷一起解锁开发板的超能力》,观看 FollowMe 3 与得捷一起解锁开发板的超能力