基于STM32的轻量级Web服务器设计
文章目录
- 一、前言
- 1.1 开发背景
- 1.2 实现的功能
- 1.3 硬件模块组成
- 1.4 ENC28J60网卡介绍
- 1.5 UIP协议栈
- 【1】目标与特点
- 【2】核心组件
- 【3】应用与优势
- 1.6 添加UIP协议栈实现创建WEB服务器步骤
- 1.7 ENC28J60添加UIP协议栈实现创建WEB客户端
- 1.8 ENC28J60移植UIP协议并编写服务器测试示例
- 1.9 ENC28J60移植UIP协议并编写客户端测试示例
- 二、硬件设计
- 2.1 接线说明
- 【1】ENC28J60接线
- 【2】LED灯接线
- 【3】蜂鸣器接线
- 【4】DS18B20接线
- 2.2 代码与服务器交互的控制
- 2.3 修改网页的页面
- 2.4 设置网卡地址
- 2.5 程序运行效果
- 【1】下载程序
- 【2】串口看效果
- 【3】ping板子IP地址
- 【4】打开网页看效果
- 2.6 ENC28J60驱动代码
一、前言
1.1 开发背景
本项目的目的是构建一个基于STM32F103ZET6微控制器的嵌入式Web服务器,以满足远程监控和控制嵌入式设备的需求。随着物联网技术的快速发展,远程监控和控制嵌入式设备变得越来越重要。本项目通过选择STM32F103ZET6作为主控芯片,结合ENC28J60网卡实现网络通信,并移植UIP协议栈来构建轻量级的Web服务器。项目还集成了DS18B20温度传感器、LED灯模块和高电平触发的有源蜂鸣器,以实现远程监控和控制STM32设备端的功能,如LED灯和蜂鸣器的控制,以及设备端温度和RTC时间的显示。这种设计使得用户能够通过浏览器访问服务器,实时查看和控制嵌入式设备,为物联网应用提供了一种灵活、高效的解决方案。
1.2 实现的功能
(1)网络通信搭建:通过STM32F103ZET6微控制器与ENC28J60以太网控制器的集成,利用SPI接口实现数据传输,成功移植UIP轻量级TCP/IP协议栈,从而在嵌入式平台上搭建起一个功能完备的Web服务器。
(2)网页服务:在STM32内部存储一个简易的网页文件,该网页设计用于用户界面展示及交互。当用户使用任何标准的Web浏览器访问此服务器的IP地址时,即可加载并显示该网页内容。
(3)远程控制功能:
- LED灯控制:网页上设有控制按钮或界面元素,允许用户远程开关连接到STM32的LED灯模块,实现远程照明控制演示。
- 蜂鸣器控制:同样通过网页界面,用户能激活或关闭STM32连接的高电平触发的有源蜂鸣器,完成远程报警或信号提示功能的测试。
(4)环境监测与显示:
- 温度监控:集成DS18B20数字温度传感器,周期性采集环境温度数据,并通过Web界面实时显示给用户,提供基本的环境监测能力。
- 实时时钟显示:利用STM32内置的RTC(实时时钟)模块,获取并准确显示当前的时间信息,增强系统的实用性和用户交互体验。
项目不仅实现了从硬件选型、网络配置到软件开发的全过程,还展示了物联网技术在实际应用中的一个小而完整的案例,即通过简单的Web界面远程监控和控制物理设备,体现了STM32平台在物联网领域的灵活性与强大功能。
1.3 硬件模块组成
(1)主控制器模块:
- STM32F103ZET6微控制器:作为系统的核心处理器,负责运行控制程序、管理外设通信、处理网络数据包及执行用户指令。它拥有丰富的外设资源,如SPI、USART、I2C等,支持高速运算和低功耗操作,是实现项目功能的基础。
(2)网络通信模块:
- ENC28J60以太网控制器:通过SPI接口与STM32连接,提供物理层和数据链路层的网络功能,实现与外部网络的连接。它是项目中实现Web服务器功能的关键组件,支持以太网数据包的收发,使嵌入式设备能够接入互联网。
(3)温度监测模块:
- DS18B20数字温度传感器:通过单总线(One-Wire)接口与STM32通信,用于精确测量环境温度。该传感器具有体积小、精度高、直接数字输出等特点,非常适合嵌入式系统中的温度监控应用。
(4)输出控制模块:
- LED灯模块:作为基本的输出设备,通过GPIO(通用输入输出端口)直接由STM32控制,用于响应用户的控制命令,如点亮或熄灭,以直观展示控制效果。
- 有源蜂鸣器:通过高电平触发的方式连接至STM32的一个GPIO引脚,根据控制信号产生声音,实现报警或状态反馈功能。
(5)电源模块:为确保所有硬件组件稳定工作,需要一个合适的电源供应模块,为STM32微控制器、ENC28J60网卡、传感器及外围电路提供稳定的电压和电流。
1.4 ENC28J60网卡介绍
ENC28J60是一款集成MAC(Media Access Control,媒体访问控制)和10BASE-T PHY(物理层)的以太网控制器,特别适合于嵌入式系统和微控制器应用。
以下是ENC28J60网卡的一些关键特性与介绍:
(1)接口类型:它使用SPI(Serial Peripheral Interface,串行外设接口)作为与外部微控制器通信的主要方式,这使得它能够以较少的引脚数(通常为4或5条线)与诸如STM32、Arduino等微控制器连接,降低了硬件设计的复杂度。
(2)兼容性:ENC28J60兼容IEEE 802.3标准,这意味着它可以无缝地融入标准以太网网络环境中。它支持10Mbps的传输速率,适用于不需要高速网络连接的应用场景。
(3)集成功能:除了MAC和PHY层之外,ENC28J60还集成了其他一些功能,如缓冲区管理、DMA(Direct Memory Access,直接内存访问)支持、以及对多种网络帧类型的支持,包括广播、多播和单播。
(4)网络功能:能够实现完整的以太网数据包的发送和接收,支持IP、TCP、UDP等网络协议栈。开发者通常会结合LwIP(Lightweight IP)这样的轻量级TCP/IP协议栈来实现网络通信。
(5)物理层特性:支持10BASE-T标准,可以通过RJ45接口连接双绞线(UTP),支持自动极性和交叉检测,可工作在全双工或半双工模式下。
(6)功耗与封装:ENC28J60设计考虑到了低功耗应用的需求,适合电池供电设备。它通常采用SSOP(Shrink Small Outline Package)或QFN(Quad Flat No-Leads)等小型封装形式,便于在空间受限的设计中使用。
(7)灵活性与应用:由于其SPI接口和相对较低的成本,ENC28J60被广泛应用于各种嵌入式项目中,如物联网设备、智能家居、工业控制、远程监控系统等。
ENC28J60以其集成度高、接口灵活、成本效益好等特点,成为了许多嵌入式系统设计中实现网络连接的优选解决方案。
1.5 UIP协议栈
UIP(Micro IP)协议栈是一种专门为资源受限的嵌入式系统设计的轻量级TCP/IP协议栈。它最初由Adam Dunkels在SICS(瑞典计算机科学研究所)开发,目的是使即便是8位微控制器也能轻松实现网络通信,而不必负担全尺寸TCP/IP协议栈的开销。下面是UIP协议栈的详细介绍:
【1】目标与特点
-
轻量化设计:UIP的核心目标是在保持TCP/IP协议核心功能的同时,尽可能减小代码大小和内存占用。其代码量通常仅为几千字节,RAM消耗最低可达几百字节,这使得它非常适合于微控制器等资源有限的环境。
-
事件驱动编程:UIP采用了事件驱动的编程模型,而不是基于多线程或中断驱动的方法。这种设计减少了对内存的需求,并且简化了程序逻辑,使得协议栈更加易于理解和维护。
-
协议支持:尽管精简,UIP仍实现了网络层(IP)、网络互联层(ARP)、传输层(TCP)和部分应用层(如ICMP ping)的基本功能。它还支持UDP,尽管可能不如TCP那样全面。对于不常用的TCP/IP特性,如窗口缩放、时间戳等,则被省略以减少资源消耗。
-
无操作系统依赖:UIP设计为可以在“裸机”环境下运行,即不需要操作系统的支持,这使得它非常灵活,可以部署在各种嵌入式平台上。
【2】核心组件
- IP层:负责数据包的路由和分片重组,实现基本的网络层功能。
- ARP层:地址解析协议,用于将IP地址转换为MAC地址,确保数据包能够正确送达物理网络层。
- ICMP层:因特网控制消息协议,主要用于网络诊断,如ping命令的实现。
- TCP层:传输控制协议,用于建立可靠的、面向连接的数据传输服务,尽管在UIP中进行了大幅简化以适应嵌入式环境。
- 内存管理:由于嵌入式系统内存资源有限,UIP实现了高效的内存块管理和缓冲区分配机制。
【3】应用与优势
- 应用广泛:UIP被广泛应用于物联网设备、智能家居、传感器网络、嵌入式Web服务器等场景。
- 易于移植:由于其代码结构清晰、依赖少,UIP很容易被移植到不同的微控制器平台上。
- 资源效率:在保证基本网络功能的同时,极大地节省了系统资源,使得低成本、低功耗的设备也能实现网络连接。
1.6 添加UIP协议栈实现创建WEB服务器步骤
1.7 ENC28J60添加UIP协议栈实现创建WEB客户端
1.8 ENC28J60移植UIP协议并编写服务器测试示例
1.9 ENC28J60移植UIP协议并编写客户端测试示例
二、硬件设计
2.1 接线说明
【1】ENC28J60接线
【2】LED灯接线
【3】蜂鸣器接线
【4】DS18B20接线
2.2 代码与服务器交互的控制
这是判断网页下发的请求。
2.3 修改网页的页面
这个是写好的网页模版:
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEB服务器设计</title>
<style>.LED{margin-left: 10px;}.font{font-size:30px;width:130px;height:60px;}.LED:hover{background: red;}.LED value{background: blue;}
</style>
</head>
<body><div style="border:1px solid black;text-align: center;width:600px;height:500px;margin:auto;"><h1>基于STM32的WEB服务器设计</h1><h1>微信公众号:DS小龙哥嵌入式技术资讯</h1><h1>这是基于ENC28J60+UIP协议栈设计的WEB服务器</h1><div class="paren_LED "><button class="LED font" v="on1" num = "1">LED1</button><button class="LED font" v="on2" num = "2">LED2</button><button class="LED font" v="on3" num = "3">LED3</button><button class="LED font" v="on4" num = "4">LED4</button><button class="LED font" v="on5" num = "5" style="display: block;margin-left:23px;margin-top:5px;">蜂鸣器</button></div><div style="text-align: left;"><span class="font" style="margin-left:22px">温度:</span><input style="font-size: 30px" value="23℃" id="wendu"/></div><div style="text-align: left;"><span class="font" style="margin-left:22px">时间:</span><input style="font-size: 30px" value="1s" id="time"/></div></div><script>var LED = document.getElementsByClassName("paren_LED")[0];var wendu = document.getElementById("wendu");var time = document.getElementById("time");var xhr = new XMLHttpRequest();var xhr2 = new XMLHttpRequest();//控制灯的按钮LED.onclick = function(e){var status = e.target.getAttribute("v");var paren_LED = LED.children;xhr.open("GET","test?data="+status,true);//xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");xhr.send();xhr.onreadystatechange = function () {if(xhr.readyState==4&&xhr.status==200){//获取后台的数据var data = xhr.responseText;var sta = e.target.getAttribute("num");e.target.setAttribute("v",data+sta);for(var i=0;i<paren_LED.length;i++){var str = paren_LED[i].getAttribute("v");var st = str.substring(0,str.length-1);if("off"==st){paren_LED[i].style.background="blue";}else if("on"==st){paren_LED[i].style.background="";}else if("Beep_on"==st){paren_LED[i].style.background="";}else if("Beep_off"==st){paren_LED[i].style.background="blue";}}}}}//定时器var wd = function(){xhr2.open("GET","test?data=temp",true);xhr2.send();xhr2.onreadystatechange = function () {var data = xhr2.responseText.split("&"); wendu.value= data[0]+"℃";time.value = data[1]+"s";}};setInterval("wd()",1000);</script>
</body>
</html>
将这个文件转为C语言数组放到单片机代码工程里就行了。
如何转换? 用winhex
这个工具。
替换这个数组就行了:
2.4 设置网卡地址
因为板子是静态IP,为了方便通信,ENC28J60通过网线直接与电脑网口连接。 设置固定的IP地址。
2.5 程序运行效果
【1】下载程序
【2】串口看效果
【3】ping板子IP地址
【4】打开网页看效果
2.6 ENC28J60驱动代码
#include "delay.h"
#include <stdio.h>
#include "enc28j60.h" /*
以下是ENC28J60驱动移植接口:
MISO--->PA6----主机输入
MOSI--->PA7----主机输出
SCLK--->PA5----时钟信号
CS----->PA4----片选
RESET-->PG15---复位
*/
#define ENC28J60_CS PAout(4) //ENC28J60片选信号
#define ENC28J60_RST PGout(15) //ENC28J60复位信号
#define ENC28J60_MOSI PAout(7) //输出
#define ENC28J60_MISO PAin(6) //输入
#define ENC28J60_SCLK PAout(5) //时钟线static u8 ENC28J60BANK;
static u32 NextPacketPtr;/*
函数功能:底层SPI接口收发一个字节
说 明:模拟SPI时序,ENC28J60时钟线空闲电平为低电平,在第一个下降沿采集数据
*/
u8 ENC28J60_SPI_ReadWriteOneByte(u8 tx_data)
{u16 cnt=0; while((SPI1->SR&1<<1)==0) //等待发送区空--等待发送缓冲为空 {cnt++;if(cnt>=65530)return 0; //超时退出 u16=2个字节} SPI1->DR=tx_data; //发送一个byte cnt=0;while((SPI1->SR&1<<0)==0) //等待接收完一个byte {cnt++;if(cnt>=65530)return 0; //超时退出} return SPI1->DR; //返回收到的数据
}/*
函数功能:复位ENC28J60,包括SPI初始化/IO初始化等
MISO--->PA6----主机输入
MOSI--->PA7----主机输出
SCLK--->PA5----时钟信号
CS----->PA4----片选
RESET-->PG15---复位
*/
void ENC28J60_Reset(void)
{
/*开启时钟*/RCC->APB2ENR|=1<<12; //开启SPI1时钟RCC->APB2ENR|=1<<2; //PAGPIOA->CRL&=0X0000FFFF; //清除寄存器GPIOA->CRL|=0XB8B30000;GPIOA->ODR|=0XF<<4; // 上拉--输出高电平GPIOA->ODR&=~(1<<5);RCC->APB2ENR|=1<<8; //2 3 4 5 6 7 8GPIOG->CRH&=0x0FFFFFFF;GPIOG->CRH|=0x30000000;/*SPI2基本配置*/SPI1->CR1=0X0; //清空寄存器SPI1->CR1|=0<<15; //选择“双线双向”模式SPI1->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;SPI1->CR1|=0<<10; //全双工(发送和接收);SPI1->CR1|=1<<9; //启用软件从设备管理SPI1->CR1|=1<<8; //NSSSPI1->CR1|=0<<7; //帧格式,先发送高位SPI1->CR1|=0x1<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。SPI1->CR1|=1<<2; //配置为主设备SPI1->CR1|=1<<1; //空闲状态时, SCK保持高电平。SPI1->CR1|=1<<0; //数据采样从第二个时钟边沿开始。SPI1->CR1|=1<<6; //开启SPI设备。//针对ENC28J60的特点(SCK空闲为低电平)修改SPI的设置SPI1->CR1&=~(1<<6); //SPI设备失能SPI1->CR1&=~(1<<1); //空闲模式下SCK为0 CPOL=0SPI1->CR1&=~(1<<0); //数据采样从第1个时间边沿开始,CPHA=0 SPI1->CR1|=1<<6; //SPI设备使能ENC28J60_RST=0; //复位ENC28J60DelayMs(10); ENC28J60_RST=1; //复位结束 DelayMs(10);
}/*
函数功能:读取ENC28J60寄存器(带操作码)
参 数:op:操作码addr:寄存器地址/参数
返 回 值:读到的数据
*/
u8 ENC28J60_Read_Op(u8 op,u8 addr)
{u8 dat=0; ENC28J60_CS=0; dat=op|(addr&ADDR_MASK);ENC28J60_SPI_ReadWriteOneByte(dat);dat=ENC28J60_SPI_ReadWriteOneByte(0xFF);//如果是读取MAC/MII寄存器,则第二次读到的数据才是正确的,见手册29页if(addr&0x80)dat=ENC28J60_SPI_ReadWriteOneByte(0xFF);ENC28J60_CS=1;return dat;
}/*
函数功能:读取ENC28J60寄存器(带操作码)
参 数:op:操作码addr:寄存器地址data:参数
*/
void ENC28J60_Write_Op(u8 op,u8 addr,u8 data)
{u8 dat = 0; ENC28J60_CS=0; dat=op|(addr&ADDR_MASK);ENC28J60_SPI_ReadWriteOneByte(dat); ENC28J60_SPI_ReadWriteOneByte(data);ENC28J60_CS=1;
}/*
函数功能:读取ENC28J60接收缓存数据
参 数:len:要读取的数据长度data:输出数据缓存区(末尾自动添加结束符)
*/
void ENC28J60_Read_Buf(u32 len,u8* data)
{ENC28J60_CS=0; ENC28J60_SPI_ReadWriteOneByte(ENC28J60_READ_BUF_MEM);while(len){len--; *data=(u8)ENC28J60_SPI_ReadWriteOneByte(0);data++;}*data='\0';ENC28J60_CS=1;
}/*
函数功能:向ENC28J60写发送缓存数据
参 数:len:要写入的数据长度data:数据缓存区
*/
void ENC28J60_Write_Buf(u32 len,u8* data)
{ENC28J60_CS=0; ENC28J60_SPI_ReadWriteOneByte(ENC28J60_WRITE_BUF_MEM); while(len){len--;ENC28J60_SPI_ReadWriteOneByte(*data);data++;}ENC28J60_CS=1;
}/*
函数功能:设置ENC28J60寄存器Bank
参 数:ban:要设置的bank
*/
void ENC28J60_Set_Bank(u8 bank)
{ if((bank&BANK_MASK)!=ENC28J60BANK)//和当前bank不一致的时候,才设置{ ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ECON1,(ECON1_BSEL1|ECON1_BSEL0));ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,(bank&BANK_MASK)>>5);ENC28J60BANK=(bank&BANK_MASK);}
}/*
函数功能:读取ENC28J60指定寄存器
参 数:addr:寄存器地址
返 回 值:读到的数据
*/
u8 ENC28J60_Read(u8 addr)
{ ENC28J60_Set_Bank(addr);//设置BANK return ENC28J60_Read_Op(ENC28J60_READ_CTRL_REG,addr);
}/*
函数功能:向ENC28J60指定寄存器写数据
参 数:addr:寄存器地址data:要写入的数据
*/
void ENC28J60_Write(u8 addr,u8 data)
{ ENC28J60_Set_Bank(addr); ENC28J60_Write_Op(ENC28J60_WRITE_CTRL_REG,addr,data);
}/*
函数功能:向ENC28J60的PHY寄存器写入数据
参 数:addr:寄存器地址data:要写入的数据
*/
void ENC28J60_PHY_Write(u8 addr,u32 data)
{u16 retry=0;ENC28J60_Write(MIREGADR,addr); //设置PHY寄存器地址ENC28J60_Write(MIWRL,data); //写入数据ENC28J60_Write(MIWRH,data>>8); while((ENC28J60_Read(MISTAT)&MISTAT_BUSY)&&retry<0XFFF)retry++;//等待写入PHY结束
}/*
函数功能:初始化ENC28J60
参 数:macaddr:MAC地址
返 回 值:0,初始化成功;1,初始化失败;
*/
u8 ENC28J60_Init(u8* macaddr)
{ u16 retry=0; ENC28J60_Reset(); //复位底层引脚接口ENC28J60_Write_Op(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET);//软件复位while(!(ENC28J60_Read(ESTAT)&ESTAT_CLKRDY)&&retry<500)//等待时钟稳定{retry++;DelayMs(1);};if(retry>=500)return 1;//ENC28J60初始化失败// do bank 0 stuff// initialize receive buffer// 16-bit transfers,must write low byte first// set receive buffer start address 设置接收缓冲区地址 8K字节容量NextPacketPtr=RXSTART_INIT;// Rx start//接收缓冲器由一个硬件管理的循环FIFO 缓冲器构成。//寄存器对ERXSTH:ERXSTL 和ERXNDH:ERXNDL 作//为指针,定义缓冲器的容量和其在存储器中的位置。//ERXST和ERXND指向的字节均包含在FIFO缓冲器内。//当从以太网接口接收数据字节时,这些字节被顺序写入//接收缓冲器。 但是当写入由ERXND 指向的存储单元//后,硬件会自动将接收的下一字节写入由ERXST 指向//的存储单元。 因此接收硬件将不会写入FIFO 以外的单//元。//设置接收起始字节ENC28J60_Write(ERXSTL,RXSTART_INIT&0xFF); ENC28J60_Write(ERXSTH,RXSTART_INIT>>8); //ERXWRPTH:ERXWRPTL 寄存器定义硬件向FIFO 中//的哪个位置写入其接收到的字节。 指针是只读的,在成//功接收到一个数据包后,硬件会自动更新指针。 指针可//用于判断FIFO 内剩余空间的大小 8K-1500。 //设置接收读指针字节ENC28J60_Write(ERXRDPTL,RXSTART_INIT&0xFF);ENC28J60_Write(ERXRDPTH,RXSTART_INIT>>8);//设置接收结束字节ENC28J60_Write(ERXNDL,RXSTOP_INIT&0xFF);ENC28J60_Write(ERXNDH,RXSTOP_INIT>>8);//设置发送起始字节ENC28J60_Write(ETXSTL,TXSTART_INIT&0xFF);ENC28J60_Write(ETXSTH,TXSTART_INIT>>8);//设置发送结束字节ENC28J60_Write(ETXNDL,TXSTOP_INIT&0xFF);ENC28J60_Write(ETXNDH,TXSTOP_INIT>>8);// do bank 1 stuff,packet filter:// For broadcast packets we allow only ARP packtets// All other packets should be unicast only for our mac (MAADR)//// The pattern to match on is therefore// Type ETH.DST// ARP BROADCAST// 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9// in binary these poitions are:11 0000 0011 1111// This is hex 303F->EPMM0=0x3f,EPMM1=0x30//接收过滤器//UCEN:单播过滤器使能位//当ANDOR = 1 时://1 = 目标地址与本地MAC 地址不匹配的数据包将被丢弃//0 = 禁止过滤器//当ANDOR = 0 时://1 = 目标地址与本地MAC 地址匹配的数据包会被接受//0 = 禁止过滤器//CRCEN:后过滤器CRC 校验使能位//1 = 所有CRC 无效的数据包都将被丢弃//0 = 不考虑CRC 是否有效//PMEN:格式匹配过滤器使能位//当ANDOR = 1 时://1 = 数据包必须符合格式匹配条件,否则将被丢弃//0 = 禁止过滤器//当ANDOR = 0 时://1 = 符合格式匹配条件的数据包将被接受//0 = 禁止过滤器ENC28J60_Write(ERXFCON,ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);ENC28J60_Write(EPMM0,0x3f);ENC28J60_Write(EPMM1,0x30);ENC28J60_Write(EPMCSL,0xf9);ENC28J60_Write(EPMCSH,0xf7);// do bank 2 stuff// enable MAC receive//bit 0 MARXEN:MAC 接收使能位//1 = 允许MAC 接收数据包//0 = 禁止数据包接收//bit 3 TXPAUS:暂停控制帧发送使能位//1 = 允许MAC 发送暂停控制帧(用于全双工模式下的流量控制)//0 = 禁止暂停帧发送//bit 2 RXPAUS:暂停控制帧接收使能位//1 = 当接收到暂停控制帧时,禁止发送(正常操作)//0 = 忽略接收到的暂停控制帧ENC28J60_Write(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);// bring MAC out of reset//将MACON2 中的MARST 位清零,使MAC 退出复位状态。ENC28J60_Write(MACON2,0x00);// enable automatic padding to 60bytes and CRC operations//bit 7-5 PADCFG2:PACDFG0:自动填充和CRC 配置位//111 = 用0 填充所有短帧至64 字节长,并追加一个有效的CRC//110 = 不自动填充短帧//101 = MAC 自动检测具有8100h 类型字段的VLAN 协议帧,并自动填充到64 字节长。如果不//是VLAN 帧,则填充至60 字节长。填充后还要追加一个有效的CRC//100 = 不自动填充短帧//011 = 用0 填充所有短帧至64 字节长,并追加一个有效的CRC//010 = 不自动填充短帧//001 = 用0 填充所有短帧至60 字节长,并追加一个有效的CRC//000 = 不自动填充短帧//bit 4 TXCRCEN:发送CRC 使能位//1 = 不管PADCFG如何,MAC都会在发送帧的末尾追加一个有效的CRC。 如果PADCFG规定要//追加有效的CRC,则必须将TXCRCEN 置1。//0 = MAC不会追加CRC。 检查最后4 个字节,如果不是有效的CRC 则报告给发送状态向量。//bit 0 FULDPX:MAC 全双工使能位//1 = MAC工作在全双工模式下。 PHCON1.PDPXMD 位必须置1。//0 = MAC工作在半双工模式下。 PHCON1.PDPXMD 位必须清零。ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);// set inter-frame gap (non-back-to-back)//配置非背对背包间间隔寄存器的低字节//MAIPGL。 大多数应用使用12h 编程该寄存器。//如果使用半双工模式,应编程非背对背包间间隔//寄存器的高字节MAIPGH。 大多数应用使用0Ch//编程该寄存器。ENC28J60_Write(MAIPGL,0x12);ENC28J60_Write(MAIPGH,0x0C);// set inter-frame gap (back-to-back)//配置背对背包间间隔寄存器MABBIPG。当使用//全双工模式时,大多数应用使用15h 编程该寄存//器,而使用半双工模式时则使用12h 进行编程。ENC28J60_Write(MABBIPG,0x15);// Set the maximum packet size which the controller will accept// Do not send packets longer than MAX_FRAMELEN:// 最大帧长度 1500ENC28J60_Write(MAMXFLL,MAX_FRAMELEN&0xFF); ENC28J60_Write(MAMXFLH,MAX_FRAMELEN>>8);// do bank 3 stuff// write MAC address// NOTE: MAC address in ENC28J60 is byte-backward//设置MAC地址ENC28J60_Write(MAADR5,macaddr[0]); ENC28J60_Write(MAADR4,macaddr[1]);ENC28J60_Write(MAADR3,macaddr[2]);ENC28J60_Write(MAADR2,macaddr[3]);ENC28J60_Write(MAADR1,macaddr[4]);ENC28J60_Write(MAADR0,macaddr[5]);//配置PHY为全双工 LEDB为拉电流ENC28J60_PHY_Write(PHCON1,PHCON1_PDPXMD); // no loopback of transmitted frames 禁止环回//HDLDIS:PHY 半双工环回禁止位//当PHCON1.PDPXMD = 1 或PHCON1.PLOOPBK = 1 时://此位可被忽略。//当PHCON1.PDPXMD = 0 且PHCON1.PLOOPBK = 0 时://1 = 要发送的数据仅通过双绞线接口发出//0 = 要发送的数据会环回到MAC 并通过双绞线接口发出ENC28J60_PHY_Write(PHCON2,PHCON2_HDLDIS);// switch to bank 0//ECON1 寄存器//寄存器3-1 所示为ECON1 寄存器,它用于控制//ENC28J60 的主要功能。 ECON1 中包含接收使能、发//送请求、DMA 控制和存储区选择位。 ENC28J60_Set_Bank(ECON1);// enable interrutps//EIE: 以太网中断允许寄存器//bit 7 INTIE: 全局INT 中断允许位//1 = 允许中断事件驱动INT 引脚//0 = 禁止所有INT 引脚的活动(引脚始终被驱动为高电平)//bit 6 PKTIE: 接收数据包待处理中断允许位//1 = 允许接收数据包待处理中断//0 = 禁止接收数据包待处理中断ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE);// enable packet reception//bit 2 RXEN:接收使能位//1 = 通过当前过滤器的数据包将被写入接收缓冲器//0 = 忽略所有接收的数据包ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN);if(ENC28J60_Read(MAADR5)== macaddr[0])return 0;//初始化成功else return 1; }/*
函数功能:读取EREVID
参 数:
*/
u8 ENC28J60_Get_EREVID(void)
{//在EREVID 内也存储了版本信息。 EREVID 是一个只读控//制寄存器,包含一个5 位标识符,用来标识器件特定硅片//的版本号return ENC28J60_Read(EREVID);
}/*
函数功能:通过ENC28J60发送数据包到网络
参 数:len :数据包大小packet:数据包
*/
void ENC28J60_Packet_Send(u32 len,u8* packet)
{//设置发送缓冲区地址写指针入口ENC28J60_Write(EWRPTL,TXSTART_INIT&0xFF);ENC28J60_Write(EWRPTH,TXSTART_INIT>>8);//设置TXND指针,以对应给定的数据包大小 ENC28J60_Write(ETXNDL,(TXSTART_INIT+len)&0xFF);ENC28J60_Write(ETXNDH,(TXSTART_INIT+len)>>8);//写每包控制字节(0x00表示使用macon3的设置) ENC28J60_Write_Op(ENC28J60_WRITE_BUF_MEM,0,0x00);//复制数据包到发送缓冲区//printf("len:%d\r\n",len); //监视发送数据长度ENC28J60_Write_Buf(len,packet);//发送数据到网络ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS);//复位发送逻辑的问题。参见Rev. B4 Silicon Errata point 12.if((ENC28J60_Read(EIR)&EIR_TXERIF))ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ECON1,ECON1_TXRTS);
}/*
函数功能:从网络获取一个数据包内容
函数参数:maxlen:数据包最大允许接收长度packet:数据包缓存区
返 回 值:收到的数据包长度(字节)
*/
u32 ENC28J60_Packet_Receive(u32 maxlen,u8* packet)
{u32 rxstat;u32 len; if(ENC28J60_Read(EPKTCNT)==0)return 0; //是否收到数据包? //设置接收缓冲器读指针ENC28J60_Write(ERDPTL,(NextPacketPtr));ENC28J60_Write(ERDPTH,(NextPacketPtr)>>8); // 读下一个包的指针NextPacketPtr=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0);NextPacketPtr|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8;//读包的长度len=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0);len|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8;len-=4; //去掉CRC计数//读取接收状态rxstat=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0);rxstat|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8;//限制接收长度 if (len>maxlen-1)len=maxlen-1; //检查CRC和符号错误// ERXFCON.CRCEN为默认设置,一般我们不需要检查.if((rxstat&0x80)==0)len=0;//无效else ENC28J60_Read_Buf(len,packet);//从接收缓冲器中复制数据包 //RX读指针移动到下一个接收到的数据包的开始位置 //并释放我们刚才读出过的内存ENC28J60_Write(ERXRDPTL,(NextPacketPtr));ENC28J60_Write(ERXRDPTH,(NextPacketPtr)>>8);//递减数据包计数器标志我们已经得到了这个包 ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC);return(len);
}
相关文章:

基于STM32的轻量级Web服务器设计
文章目录 一、前言1.1 开发背景1.2 实现的功能1.3 硬件模块组成1.4 ENC28J60网卡介绍1.5 UIP协议栈【1】目标与特点【2】核心组件【3】应用与优势 1.6 添加UIP协议栈实现创建WEB服务器步骤1.7 ENC28J60添加UIP协议栈实现创建WEB客户端1.8 ENC28J60移植UIP协议并编写服务器测试示…...

用r语言处理 Excel数据当中的缺失值方法
以下是使用 R 编程语言处理 Excel 缺失数据的一些常见方法示例代码:(无需循环) 读取包含缺失数据的 Excel 文件 data <- read.csv(“your_file.csv”) 查看数据中是否有缺失值 sum(is.na(data)) 用平均值填充缺失值 data c o l u m …...

AWS 高防和阿里云高防深度对比
随着网络攻击的不断增加,企业对于网络安全的需求也越来越高。在这种情况下,高防护服务成为了企业网络安全的重要组成部分。AWS和阿里云作为全球领先的云计算服务提供商,都提供了高防护服务,但它们之间存在着一些差异。我们九河云一…...

ctfshow web 月饼杯II
web签到 <?php //Author:H3h3QAQ include "flag.php"; highlight_file(__FILE__); error_reporting(0); if (isset($_GET["YBB"])) {if (hash("md5", $_GET["YBB"]) $_GET["YBB"]) {echo "小伙子不错嘛ÿ…...

「前端+鸿蒙」核心技术HTML5+CSS3(二)
1、开发者文档 开发者文档通常由浏览器厂商或技术社区提供,包含有关Web技术(如HTML、CSS、JavaScript)的详细信息,API文档,以及最佳实践。例如,MDN Web Docs是一个广泛认可的开发者资源。 2、块级元素与行列元素 块级元素:在页面上占据整行的元素,如<div>、<…...

unity接入live2d
在bilibili上找到一个教程,首先注意一点,你直接导入那个sdk,并且打开示例,显示的模型是有问题的,你需要调整模型上脚本的一个枚举值,调整它的渲染顺序是front z to我看教程时候,很多老师都没有提…...

练习题-17
以下题目来自2024年5月清华大学“丘成桐数学科学领军计划数学水平考试”。第11题本人参考了网友Fiddie (数学兔的极大理想)的解答,原网址是 https://mp.weixin.qq.com/s/q9slRWL4iO_TcSdkmbfbbw. 第10题:在10维列向量构成的内积空间 V V V中…...

乐高小人分类项目
数据来源 LEGO Minifigures | Kaggle 建立文件目录 BASE_DIR lego/star-wars-images/ names [YODA, LUKE SKYWALKER, R2-D2, MACE WINDU, GENERAL GRIEVOUS ] tf.random.set_seed(1)# Read information about dataset if not os.path.isdir(BASE_DIR train/):for name in …...

个人关于ChatGPT的用法及建议
概述 这里只是个人常用的几个软件,做一下汇总,希望对各位有用。 如果有更高认知的朋友,请留下你的工具名称,提醒我一下,谢谢~ 常用的chatgpt模型工具: 以下是一些知名的例子: 文…...

神经网络的工程基础(二)——随机梯度下降法|文末送书
相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型:从线性回归到通用人工智能》,欢迎有兴趣的读者多多支持。 本文涉及到的代码链接如下:regression2chatgpt/ch06_optimizer/stochastic_gradient_descent.ipynb 本文将讨论利用…...

常见的几种编码方式
常见的编码方式及其特点: 编码方式的设计是为了适应不同的字符集和应用需求,因此它们在表示字符时使用的位数和字节数各不相同 常见编码方式及其位数和字节数 ASCII(American Standard Code for Information Interchange)&#x…...

ubuntu移动硬盘重命名
因为在ubuntu上移动硬盘的名字是中文的,所以想要改成英文的。 我的方法: 将移动硬盘插到windows上,直接右键重命名。再插到ubuntu上名字就改变了。 别人的方法: ubuntu下如何修改U盘名字-腾讯云开发者社区-腾讯云 在自带的软件…...

VUE框架前置知识总结
一、前言 在学习vue框架中,总是有些知识不是很熟悉,又不想系统的学习JS,因为学习成本太大了,所以用到什么知识就学习什么知识。此文档就用于记录零散的知识点。主要是还是针对与ES6规范的JS知识点。 以下实验环境都是在windows环…...

张宇1000题80%不会?别急,这个方法肯定有用!
这太正常了,1000题的难度本来就高,不要慌 我考研的时候跟的也是张宇老师,但是1000题我根本就没做几道题就给换成880题660题了,而且只是强化阶段用880题,基础阶段我用的都是汤家凤的1800题。 不要担心做的不是张宇老师…...

【python】爬虫记录每小时金价
数据来源: https://www.cngold.org/img_date/ 因为这个网站是数据随时变动的,用requests、BeautifulSoup的方式解析html的话,数据的位置显示的是“--”,并不能取到数据。 所以采用webdriver访问网站,然后从界面上获取…...

一行命令将已克隆的本地Git仓库推送到内网服务器
一、需求背景 我们公司用gitea搭建了一个git服务器,其中支持win7的最高版本是v1.20.6。 我们公司的电脑在任何时候都不能连接外网,但是希望将一些开源的仓库移植到内网的服务器来。一是有相关代码使用的需求,二是可以建设一个内网能够查阅的…...

Linux文本处理三剑客(详解)
一、文本三剑客是什么? 1. 对于接触过Linux操作系统的人来说,应该都听过说Linux中的文本三剑客吧,即awk、grep、sed,也是必须要掌握的Linux命令之一,三者都是用来处理文本的,但侧重点各不相同,a…...

AI在线UI代码生成,不需要敲一行代码,聊聊天,上传图片,就能生成前端页面的开发神器
ioDraw的在线UI代码生成器是一款开发神器,它可以让您在无需编写一行代码的情况下创建前端页面。 主要优势: 1、极简操作:只需聊天或上传图片,即可生成响应式的Tailwind CSS代码。 2、节省时间:自动生成代码可以节省大…...

go-zero整合单机版ClickHouse并实现增删改查
go-zero整合单机版ClickHouse并实现增删改查 本教程基于go-zero微服务入门教程,项目工程结构同上一个教程。 本教程主要实现go-zero框架整合单机版ClickHouse,并暴露接口实现对ClickHouse数据的增删改查。 go-zero微服务入门教程:https://b…...

行政工作如何提高效率?桌面备忘录便签软件哪个好
在行政管理工作中,效率的提高无疑是每个行政人员都追求的目标。而随着科技的发展,各种便捷的工具也应运而生,其中桌面备忘录便签软件便是其中的佼佼者。那么,这类软件又如何帮助我们提高工作效率呢? 首先,…...

利用向日葵和微信/腾讯会议实现LabVIEW远程开发
利用向日葵远程控制软件结合微信或腾讯会议的视频通话功能,可以实现LabVIEW的远程开发和调试。通过向日葵进行远程桌面访问,配合视频通话工具进行实时沟通与问题解决,不仅提高了开发效率,还减少了地域限制带来的不便。介绍这种远程…...

SpringBoot 单元测试 指定 环境
如上图所示,在配置窗口中添加--spring.profiles.activedev,就可以了。...

Flutter 中的 SliverOpacity 小部件:全面指南
Flutter 中的 SliverOpacity 小部件:全面指南 Flutter 是一个功能强大的 UI 框架,由 Google 开发,允许开发者使用 Dart 语言来构建高性能、美观的跨平台应用。在 Flutter 的滚动组件体系中,SliverOpacity 是一个用来为其子 Slive…...

源码分析の前言
源码分析路线图: 初级部分:ArrayList->LinkedList->Vector->HashMap(红黑树数据结构,如何翻转,变色,手写红黑树)->ConcurrentHashMap 中级部分:Spring->Spring MVC->Spring Boot->M…...

接口性能测试复盘:解决JMeter超时问题的实践
在优化接口并重新投入市场后,我们面临着一项关键任务:确保其在高压环境下稳定运行。于是,我们启动了一轮针对该接口的性能压力测试,利用JMeter工具模拟高负载场景。然而,在测试进行约一分钟之后,频繁出现了…...

[数据集][目标检测]猕猴桃检测数据集VOC+YOLO格式1838张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):1838 标注数量(xml文件个数):1838 标注数量(txt文件个数):1838 标注…...

摸鱼大数据——Hive函数7-9
7、日期时间函数 Hive函数链接:LanguageManual UDF - Apache Hive - Apache Software Foundation SimpleDateFormat (Java Platform SE 8 ) current_timestamp: 获取时间原点到现在的秒/毫秒,底层自动转换方便查看的日期格式 常用 to_date: 字符串格式时间…...

python连接数据库
python连接MYSQL、postgres、oracle等的基本操作 python连接mysql MySQLdb MySQLdb又叫MySQL-python ,是 Python 连接 MySQL 的一个驱动,很多框架都也是基于此库进行开发,只支持 Python2.x,而且安装的时候有很多前置条件&#…...

能不能接受这些坑?买电车前一定要看
图片来源:汽车之家 文 | Auto芯球 作者 | 雷慢 刚有个朋友告诉我,买了电车后感觉被骗了, 很多“坑”都是他买车后才知道的。 不提前研究,不做功课,放着我这个老司机不请教, 这个大冤种他不当谁当&…...

k8s中pod如何排错?
排除Kubernetes Pod故障通常涉及一系列步骤,以诊断问题并找到解决方案。以下是一些常见的故障排除方法: 检查Pod状态: 使用kubectl get pods查看Pod的状态。如果Pod没有处于Running状态,查看更详细的信息,使用kubectl describe …...