- 2024-09-23
-
回复了主题帖:
罗姆有奖直播 | 高输出功率激光二极管—助力激光雷达性能提升
已报名参加
- 2024-08-27
-
回复了主题帖:
>>征集 | 使用 MCU,哪些问题最令你头大?
下载和调试接口不够统一,就比如最近用到的DAP:项目开源、成本较低、速度较快,但就是不能给某些芯片下载程序。有的厂家喜欢jtag,有的喜欢串口,有的喜欢自己设计专用下载器。做不同的项目就要备不同的下载器,切换也不够方便
- 2024-08-22
-
回复了主题帖:
运放电源接反的影响?
电源压降这么厉害就应该检查一下负载,上电后电源异常优先查找短路。没底的话上电时用热成像看一下
- 2024-08-21
-
发表了主题帖:
【51篇】(7-1)C51单片机实现按键控制的MAX517四种波形输出并显示
本帖最后由 QuaX_Chow 于 2024-8-21 22:12 编辑
声明及本系列注意事项优先阅读:🔗
工程源码及仿真文件入口在文末,如果没显示就是还在审核
任务要求:使用C51实现四按键控制MAX517分别产生方波、三角波、锯齿波和正弦波,波形通过Proteus内的示波器显示,在P2.0~P2.3接四个按键开关,分别对应与四种波形;时钟信号由,一开始无按键按下时无输出
想要实现以上功能,我们需要将任务拆分为以下几个部分:
MAX517通信
中断按键判断
1.Proteus部分
波形显示使用的是虚拟仪器模式中的“OSCILLOSCOPE”四通道示波器:
搭建好的仿真电路如下:
如果在仿真时不小心把波形显示窗口关掉了,只需在仿真状态下点击菜单栏里的“调试”-“Digital Oscilloscope”,即可显示示波器界面
2.代码部分
(1)MAX517通信
在我们驱动MAX517之前,我们首先需要理清这颗芯片的时序
驱动MAX517需要使用两线串行接口,兼容IIC
见下图:
一条时钟线,一条数据线。声明如下:
sbit SCL = P1^0;//MAX517串行时钟
sbit SDA = P1^1;//MAX517串行数据
开始状态标志为时钟线高、数据线下降沿。结束状态标志为时钟线高、数据线上升沿。
在编程时我们就需要根据芯片时序,使用GPIO进行模拟。
以SC为示例编写函数:
void start(void)//起始条件子程序
{
SDA= 1;
SCL = 1;
_nop_();
SDA =0;
_nop_();
}
完整的传输过程如下图:
控制MAX517模拟输出需要根据上图步骤编写一个有着完整的:启动-发送地址-应答-命令-应答-数据-应答-结束,流程的函数:
void DACOut (uchar ch)//串行D/A转换子程序
{
start();//发送启动信号
send(0x58);//发送地址字节
ack();
send(0x00);//发送命令字节
ack();
send(ch);//发送数据字节
ack();
stop();//结束一次转换
}
如果我们需要输出一个指定的电压,就只需要 DACOut(需要的值);
公式如下:
但是我们需要输出的是波形,而非稳定的电平,所以我们需要一直改变输出模拟量的值ch。
以输出正弦波为例,创建数组存储ch的值,使用在输出时仅需将元素递增:
uchar sindata[64]=
{ 0x80,0x8c,0x98,0xa5,0xb0,0xbc,0xc7,0xd1,
0xda,0xe2,0xea,0xf0,0xf6,0xfa,0xfd,0xff,
0xff,0xff,0xfd,0xfa,0xf6,0xf0,0xea,0xe3,
0xda,0xd1,0xc7,0xbc,0xb1,0xa5,0x99,0x8c,
0x80,0x73,0x67,0x5b,0x4f,0x43,0x39,0x2e,
0x25,0x1d,0x15,0xf,0x9,0x5,0x2,0x0,0x0,
0x0,0x2,0x5,0x9,0xe,0x15,0x1c,0x25,0x2e,
0x38,0x43,0x4e,0x5a,0x66,0x73};
方波、锯齿、三角波的实现思路类似,循环内递增、递减、延时即可,具体方法参见文末源码
(2)中断按键判断
要实现立刻切换输出波形的功能,我们需要使用到外部中断0,中断服务函数如下:
void intp0() interrupt 0
{
if(P2_0 == 0) key = 0;//三角波
if(P2_1 == 0) key = 1;//方波
if(P2_2 == 0) key = 2;//正弦
if(P2_3 == 0) key = 3;//锯齿波
}
四种波形输出效果如下:
任务完成
2024/8/21
源码以及仿真文件
#本文首发于EEWORLD,版权归作者所有
补充内容 (2024-8-27 14:28):
勘误:任务要求部分:“时钟信号由,一开始无按键按下时无输出”改为“一开始无按键按下时无输出”
- 2024-08-20
-
上传了资料:
C51单片机实现按键控制的MAX517四种波形输出并显示
-
发表了日志:
【51篇】(1-1)片内RAM两字节的拆与拼
- 2024-08-05
-
回复了主题帖:
【大学生电子竞赛题目分析】——2024年C题《无线传输信号模拟系统》
测评老师是不知道题目的,他们只会按照测评表上的要求进行测评。测量条件没满足直接不测,该项目完全没分。并且本次电赛设计赛题及测评的人极为不专业,甚致认为信号时延仅需调整初相位即可,忽略t趋近于+0时多径信号SM(t)应该为0
- 2024-07-03
-
发表了主题帖:
【51篇】(1-0)使用Keil5和Proteus的系列实验
本帖最后由 QuaX_Chow 于 2024-7-3 21:56 编辑
新的一轮学习周期结束,有感而发,总结一下上个阶段的学习成果与心得。
虽是故知新说,但不想让炒了4个月的冷饭白炒,故开帖此系列👆
链接目录:
(1-0)本篇:系列说明
(1-1)片内RAM两字节的拆与拼
(1-2)片内RAM单字节十进制值的百十个位拆分
(2-1)片内RAM单字节十进制值的百十个位拆分并显示
(3-1)C51单片机利用定时器输出PWM
(4-1)C51单片机串行口扩展并行口
(4-2)C51单片机使用串口交换双机指定并口状态并使用LED显示
(5-1)C51单片机实现双按键控制八位数码管滚动十六进制数
(5-2)C51单片机实现有紧急时间的红绿灯控制
(6-1)C51单片机实现LCD1602两行滚动非等长含自定义字符
(7-1)C51单片机实现按键控制的MAX517四种波形输出并显示
(7-2)C51单片机实现按键控制的ADC0808八路模拟量测量并显示
(待续
1.软件说明
所有仿真使用的Proteus版本均为8.17,低版本大概率不兼容
所有代码部分使用的Keil版本均为5.39,低版本有概率不兼容
2.代码说明
所有上传的代码均进行过仿真验证,未进行实物验证
本人水平有限,不可能代码最简化且部分仿真会有小瑕疵,欢迎讨论
初始几个实验会使用汇编,后面均为C
2024/7/3
补充内容 (2024-8-21 22:14):
“按键控制的MAX517四种波形输出并显示”已更新:
https://bbs.eeworld.com.cn/thread-1291106-1-1.html
-
发表了主题帖:
【51篇】(5-1)C51单片机实现双按键控制八位数码管滚动十六进制数
本帖最后由 QuaX_Chow 于 2024-7-3 15:58 编辑
2024/5/27 新建帖子
声明及本系列注意事项优先阅读:🔗
工程源码及仿真文件入口在文末
任务要求:使用C51实现双按键控制八位数码管滚动十六进制数,反向滚动间隔时间为正向的两倍,一开始无按键按下时不显示
想要实现以上功能,我们需要将任务拆分为以下几个部分:
ms级定时器
数码管扫描函数
中断按键判断
延时函数(用于数码管余辉)
1.Proteus部分
数字显示使用的是Proteus中的八段共阴数码管,型号为:7SEG-MPX-8-CC-BLUE:
搭建好的仿真电路如下。红框为基础电路,外部中断0与按键之间使用与门且拉高:
2.代码部分
阳极连接的是P0口,需要在扫描函数中将每个十六位进制数对应的字段码
字段码如下:
smg[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
而阴极连接单片机对应的片选代码如下:
unsigned char code yang[8]={0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
声明uint类型的direct变量用于判断方向。在main函数中实现数码管扫描的仿真效果更好。
void main()
{
unsigned char i;
unsigned int k;
init();
Timer0Init();
direct = 2;//初始无滚动方向
while(1)
{
while(direct == 0)
{
flag = 0;
while(direct == 0)
{
for(i=0;i<8;i++)
{
k = flag+i;
if( flag+i >= 16) k -= 16;
choose = yang[i];
show = smg[k];
delay(1);
}
}
}
while(direct == 1)
{
flag = 16;
while(direct == 1)
{
for(i=0;i<8;i++)
{
k = flag+i;
if( flag+i < 0) k += 16;
if( flag+i >= 16) k -= 16;
choose = yang[i];
show = smg[k];
delay(1);
}
}
}
}
}
参考中断表编写中断服务函数,外部中断0的中断号为0,定时器0为1:
外部中断0的中断函数中对按键进行了判断,仿真电路无需消抖:
void change() interrupt 0// using 0
{
// do{
// key();
// }while(j == 1); //中断函数,当按键按下时中断
// if(P3_2 == 0)
// {
if(P1_6 == 0) direct = 0;
if(P1_7 == 0) direct = 1;
// }
}
定时器提供数码管刷新基准(flag),每当基准达到15则清零,反向滚动计数值为正向的两倍:
void intp1() interrupt 1
{
jm++;
if(direct == 0)
{
if(jm == 12)
{
TF0 = 0;
flag++;
if(flag==16) flag = 0;
jm = 0;
}
}
if(direct == 1)
{
if(jm == 24)
{
TF0 = 0;
flag--;
if(flag==0) flag = 16;
jm = 0;
}
}
}
最终效果如下:
任务完成
2024/7/3
源码以及仿真文件
#本文首发于EEWORLD,版权归作者所有
-
发表了日志:
【51篇】(1-0)使用Keil5和Proteus的系列实验
- 2024-05-27
-
上传了资料:
C51单片机实现双按键控制八位数码管滚动十六进制数
- 2024-03-30
-
发表了主题帖:
【得捷Follow me第4期】(补充)网络控制STM32从机、实现简易频率计功能
本帖最后由 QuaX_Chow 于 2024-3-30 21:21 编辑
2024.3.30
本篇是Follow Me第四期任务提交的补充内容
分两个部分:网络控制STM32从机、实现简易频率计功能
1.网络控制从机
电脑上由网络调试助手使用TCP向W5500-EVB-Pico发送指令,再由W5500-EVB-Pico通过串口,向从机发送我们在从机设定好的指令,这样从机即可进行对应操作
框架示意图如下:
经查阅手册,从机主控F407的USART3为PB10和PB11:
连接即可:
对应在W5500-EVB-PICO上如下所示:
这里我们需要使用stlink给从机下载代码。
最终实物连接图如下:
功能实现代码如下:
from usocket import socket
from machine import Pin, SPI, UART
import network
import time
# 初始化UART
uart = UART(0, baudrate=115200, bits=8, stop=1)
# 初始化WIZNET5K以太网模块
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)
nic.ifconfig(('192.168.137.100', '255.255.255.0', '192.168.137.1', '8.8.8.8'))
print('IP地址:', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
# 服务器循环
def server_loop():
s = socket()
s.bind(('192.168.137.100', 5050))
s.listen(5)
conn, addr = s.accept()
print("已连接到:", conn, "地址:", addr)
print("服务器已开启")
while True:
data = conn.recv(2048)
print("发送: ", data.decode('utf-8'), " 给从机")
if data != b'NULL':
uart.write(data)
# 客户端循环
def client_loop():
s = socket()
s.connect(('192.168.137.114', 5050))
print("循环客户端已连接!")
while True:
data = s.recv(2048)
print(data.decode('utf-8'))
if data != b'NULL':
s.send(data)
def main():
w5x00_init()
while True:
server_loop()
if __name__ == "__main__":
main()
通过输入1~4四个数字,即可控制对应led的状态:
2.实现简易频率计功能
模块:MCU与AK5522使用IIS通信,AK5522读取到阈值高电平,模块串口打印一次“1”
这里需要开启双核,一个核用于定时,一个计数
框架示意图如下:
W5500-EVB-Pico通过串口👇直接读取AK5522模块发送的数据
模块的部分原理图如下:
功能实现代码如下:
from usocket import socket
from machine import Pin, UART
import time
import _thread
# 初始化UART1和UART0
uart1 = UART(1, baudrate=115200, bits=8, stop=1)
uart0 = UART(0, baudrate=115200, bits=8, stop=1)
# 初始化计数器
count = 0
# Core 0的线程函数,用于接收数据并计算1的数量
def receive_and_count():
global count
while True:
if uart1.any():
data = uart1.read(64) # 读取UART1接收到的数据
count += data.count(b'1')
time.sleep(1) # 每秒计算一次1的数量
# Core 1的线程函数,用于打印1的数量
def print_count():
while True:
uart0.write('FRE: {}\n'.format(count))
time.sleep(1) # 每秒打印一次计数结果
# 启动Core 0的线程
_thread.start_new_thread(receive_and_count, ())
# 在Core 1上运行打印1的数量的代码
print_count()
,用补充1的F407开发板输出1KHz的PWM,串口助手显示如下:
可以看见开发板十分准确地每秒打印了当前频率
功能实现
(完)
- 2024-02-25
-
上传了资料:
【得捷Follow me第4期】玩转W5500-EVB-Pico 资料包
- 2024-02-23
-
回复了主题帖:
【得捷电子Follow me第4期】终极任务一:DigiKey电子元器件价格及库存监视器(上)
tql
- 2024-02-22
-
加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门
- 2024-02-20
-
发表了主题帖:
【得捷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秒
实际效果如下:
[localvideo]fa63556509e325e64f557e975c0dceed[/localvideo]
修改一下代码,调用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()
实际效果如下:
[localvideo]3010d9dc1cb982ae0eb62de6a8e95248[/localvideo]
使用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
可以直接新建一个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服务器,可以接收客户端的连接并处理数据。参考代码链接:loopback
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卡,我们还需要添加一个额外的库sdcard.py,复制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)
最终效果请看视频:
[localvideo]4a3f7f2409bab3b98cd0dcdc8689bce7[/localvideo]
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
- 2024-02-13
-
加入了学习《【得捷电子Follow me第4期】使用micropython完成全部任务》,观看 【得捷电子Follow me第4期】使用micropython完成全部任务
- 2023-11-22
-
回复了主题帖:
任务审核通过名单&返现通知|得捷电子 Follow me 第2期活动第一批(正常收货者)
第三期申请了没通过