基于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时间的显示。这种设计使得用户能够通过浏览器访问服务器,实时查看和控制嵌入式设备,为物联网应用提供了一种灵活、高效的解决方案。

image-20240530155757078

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服务器步骤

img

img

img

img

img

img

img

1.7 ENC28J60添加UIP协议栈实现创建WEB客户端

img

img

img

img

img

1.8 ENC28J60移植UIP协议并编写服务器测试示例

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

1.9 ENC28J60移植UIP协议并编写客户端测试示例

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

二、硬件设计

2.1 接线说明

【1】ENC28J60接线

image-20240415233409988

【2】LED灯接线

image-20240415233516888

【3】蜂鸣器接线

image-20240415233530012

【4】DS18B20接线

image-20240415233547945

2.2 代码与服务器交互的控制

这是判断网页下发的请求。

image-20240415233906046

image-20240415233845331

2.3 修改网页的页面

这个是写好的网页模版:

image-20240415233956201

代码如下:

<!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 这个工具。

image-20240415234141048

替换这个数组就行了:

image-20240415234221592

2.4 设置网卡地址

因为板子是静态IP,为了方便通信,ENC28J60通过网线直接与电脑网口连接。 设置固定的IP地址。

image-20240415234301652

2.5 程序运行效果

【1】下载程序

image-20240415234504402

【2】串口看效果

image-20240415234520817

【3】ping板子IP地址

image-20240415234420460

【4】打开网页看效果

image-20240415234546440

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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/20233.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NetMizer 日志管理系统前台RCE漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、产品介绍 NetMizer日志管理系统是一个与NetMizer流量管理设备配合…

【开源】Wordpress自定义鼠标样式插件

插件简介 使用此插件可一键自定义Wordpress前端鼠标指针样式。利用该插件&#xff0c;站长可以快速实现替换多种鼠标指针样式于网站前端。 鼠标图案均来自于网络&#xff0c;插件仅作收集整理&#xff0c;插件完全开源无任何商业性质。 插件截图 使用教程 下载插件文件 下载…

文件系统小册(FusePosixK8s csi)【2 Posix标准】

文件系统小册&#xff08;Fuse&Posix&K8s csi&#xff09;【2 Posix】 往期文章&#xff1a;文件系统小册&#xff08;Fuse&Posix&K8s csi&#xff09;【1 Fuse】 POSIX&#xff1a;可移植操作系统接口&#xff08;标准&#xff09; 1 概念 POSIX&#xff1a;…

前端Vue自定义支付密码输入框键盘与设置弹框组件的设计与实现

摘要 随着信息技术的不断发展&#xff0c;前端开发的复杂性日益加剧。传统的开发方式&#xff0c;即将整个系统构建为一个庞大的整体应用&#xff0c;往往会导致开发效率低下和维护成本高昂。任何微小的改动或新功能的增加都可能引发对整个应用逻辑的广泛影响&#xff0c;这种…

【原创】springboot+mysql医院预约挂号管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

【Redis】List源码剖析

大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#xff0c;但是也想日更的人。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4aa;&#x1f4aa;&#x1f4aa…

游戏交易平台源码游戏帐号交易平台系统源码

功能介绍 1&#xff1a;后台可以添加删除游戏分类 2&#xff1a;会员中心可以出售游戏币,账号&#xff0c;装备 3&#xff1a;后台可以对会员和商品进行管理 4&#xff1a;多商家入驻,商家发布信息 5&#xff1a;手机版功能可以生成APP 6&#xff1a;在线支付可支持微信和支…

VQAScore开启文本到视觉生成评估新篇章

随着生成式人工智能技术的飞速发展&#xff0c;如何全面评估生成内容的质量和与输入提示的一致性成为了一个挑战。在图像-文本对齐领域&#xff0c;传统的评估方法如CLIPScore存在局限性&#xff0c;尤其是在处理涉及多个对象、属性和关系的复杂提示时。它们通常基于简单的词袋…

MES系统的功能、架构及应用价值

MES系统生产过程控制的主要方面涵盖了生产计划与控制、生产调度与排程、数据采集与监控、质量控制与管理、物料管理与控制以及设备管理与维护等多个方面。这些功能共同构成了MES系统的核心价值&#xff0c;帮助企业实现生产过程的数字化、智能化和精细化管理。 一、工厂使用MES…

Nginx 1.26.0 爆 HTTP/3 QUIC 漏洞,建议升级更新到 1.27.0

据悉&#xff0c;Nginx 1.25.0-1.26.0 主线版本中涉及四个与 NGINX HTTP/3 QUIC 模块相关的中级数据面 CVE 漏洞&#xff0c;其中三个为 DoS 攻击类型风险&#xff0c;一个为随机信息泄漏风险&#xff0c;影响皆为允许未经身份认证的用户通过构造请求实施攻击。目前已经紧急发布…

密码与网络安全(一):专栏导读

1.专栏目的 这个专栏的核心目的是提升博主自己的密码与网络安全知识&#xff0c;其次也想将相关的学习收获分享给感兴趣的小伙伴。博主自己的工作主要量子技术相关&#xff0c;身边的同事基本上也是物理专业出身&#xff0c;最近和单位密码领域同事聊天时他提到一个思路很好的启…

【Linux 网络】网络基础(三)(其他重要协议或技术:DNS、ICMP、NAT)

一、DNS&#xff08;Domain Name System&#xff09; DNS 是一整套从域名映射到 IP 的系统。 1、DNS 背景 TCP/IP 中使用 IP 地址和端口号来确定网络上的一台主机的一个程序&#xff0c;但是 IP 地址不方便记忆。于是人们发明了一种叫主机名的东西&#xff0c;是一个字符串&…

学习笔记——网络参考模型——TCP/IP模型

二、TCP/IP模型 TCP/IP模型(TCP/IP协议栈)&#xff1a;很多个互联网协议的集合&#xff0c;其中以TCP和IP为主&#xff0c;将这些协议的集合称为TCP/IP协议栈。目前使用最多的协议模型。 因为OSI协议栈比较复杂&#xff0c;且TCP和IP两大协议在业界被广泛使用&#xff0c;所以…

JavaScript 动态网页实例 —— 窗口控制

除了打开和关闭窗口之外,还有很多其他控制窗口的方法。例如,可以使用 window.focus()方法使窗口获得焦点,也可以利用与其相对的window.blur 方法使窗口失去焦点。本节介绍移动窗口、改变窗口大小、窗口滚动、窗口超时操作、常用窗口事件、常用窗口扩展等窗口控制的方法和手段。…

[每周一更]-(第99期):MySQL的索引为什么用B+树?

文章目录 B树与B树的基本概念B树&#xff08;Balanced Tree&#xff09;B树&#xff08;B-Plus Tree&#xff09;对比 为什么MySQL选择B树1. **磁盘I/O效率**2. **更稳定的查询性能**3. **更高的空间利用率**4. **并发控制** 其他树结构的比较参考 索引是一种 数据结构&#x…

LeeCode热题100(两数之和)

本文纯干货&#xff0c;看不懂来打我&#xff01; 自己先去看一下第一题的题目两数之和&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 简单来说就是让你在一个数组里面找两个数&#xff0c;这两个数的和必须满足等于目标值target才行。 我认为你要是没有思路的话&a…

营造科技展厅主题氛围,多媒体应用有哪些新策略?

长久以来&#xff0c;展厅作为线下向公众传递信息的窗口&#xff0c;其设计风格与内容主题紧密相连&#xff0c;展现出千姿百态的面貌。然而&#xff0c;随着数字多媒体技术的日新月异&#xff0c;展厅不再仅仅是传统的信息展示平台&#xff0c;而是成为了引领内容展示潮流的风…

【Git】git合并分支指定内容到主分支

git合并分支指定内容到主分支 在现实开发中&#xff0c;往往需要合并分支内容&#xff0c;如下图&#xff1a; 我们平时在其他分支修改了部分代码&#xff0c;如何将分支部分代码合并到主分支上面呢&#xff1f; 合并步骤&#xff1a; 1、切换当前到主分支 git checkout m…

大型制造业集团IT信息化总体规划方案(65页PPT)

方案介绍&#xff1a; 本大型制造业集团IT信息化总体规划方案旨在通过构建先进、高效、稳定的IT信息化系统&#xff0c;支撑集团各业务领域的运营和管理需求&#xff0c;促进集团整体运营效率和竞争力的提升。通过实施本项目&#xff0c;集团将能够更好地应对市场变化和客户需…

python绘制piper三线图

piper三线图 Piper三线图是一种常用于水化学分析的图表&#xff0c;它能够帮助我们理解和比较水样的化学成分。该图表由三个部分组成&#xff1a;两个三角形和一个菱形。两个三角形分别用于显示阳离子和阴离子的相对比例&#xff0c;而菱形部分则综合显示了这些离子比例在水样…