ENC28J60+STM32F103在STM32CubeIDE上移植lwIP2.1.2

article/2025/11/5 15:19:22
ENC28J60+STM32F103在STM32CubeIDE上移植lwIP2.1.2

  为了学习lwIP,网购了一块正点原子的Mini STM32开发板和一个ENC28J60以太网模块,发现正点原子所给的示例代码是基于lwIP1.4.1的,有点偏老,最新版本的lwIP是2.1.2,使用的开发平台是Keil uVison5,而自己习惯了在STM32CubeIDE进行编程。STM32CubeIDE是免费的,其图形界面配置生成代码的方式非常便捷,而且其代码编辑能力丝毫不比Keil差,这是我选择STM32CubeIDE进行开发的原因。于是就产生了在STM32CubeIDE上移植lwIP的想法,经过几天的摸索,踩了不少坑,终于移植成功了。现在把移植步骤和要点作简要记录。 
​  一开始采用从http://download.savannah.nongnu.org/releases/lwip/上下载lwip-2.1.2.zip和contrib-2.1.0.zip,参照网上移植步骤一步一步来,发现比较麻烦,且容易出错。后来发现STM32CubeIDE(1.5.0版本)自己带有lwIP库,是最新的2.1.2版本,而且可以通过图形界面配置lwIP的参数,真是非常的方便。只是因为STM32F103芯片不带以太网控制器,所以无法在IDE的图形配置中调出lwIP的配置选项。​  
​  要想在STM32F103中使用STM32CubeIDE自带的lwIP包,首先必须选择一款带有以太网控制器的芯片(本例中选择STM32F407VE), 来建立一个临时工程,在临时工程中配置好lwIP,然后把配置好的lwIP包拷贝到F103的项目工程中,再修改lwIP的底层接口函数和ENC28J60的驱动函数。具体步骤实现如下:​
​  首先,使用STM32F407VE建立临时工程,在Connectivity中选择以太网控制器“ETH”,在Mode中选“RMII”(选择其他也可,只要Mode不为“Disable”就行,主要目的是使能“ETH”后,才能配置lwIP),如下图所示:
 

在这里插入图片描述

图1 以太网控制器的配置
  ​  “ETH”中其他的配置选项(Configuration)不需要更改,因为这是配置STM32F407VE自带的以太网控制器硬件参数,在STM32F103中不需要,后面要手工修改ENC28J60的驱动。

​  在配置“ETH”后,可使能lwIP,并进行相应的配置了:
 

在这里插入图片描述

图2: lwIP使能
  ​  在使能lwIP后,需要简单的配置下基本的网络参数,为简单起见,在移植时关闭DHCP(在测试时,直连PC,不通过路由器,无DHCP功能),采用输入静态IP地址的方式,在本例中,IP地址设置为192.168.1.100,操作如下:  

在这里插入图片描述

图3: 设置IP地址
​  关闭STM32F407的硬件Checksum,在发送和接收网络包时,为保障数据的正确性,会对包的头部进行校验,在F407中,校验是由以太网控制器的硬件完成的,所以默认配置是开启硬件校验的,但在ENC28J60+STM32F103环境中,是没有硬件校验功能的,只能采用软件生成Checksum,所以必须关闭硬件校验,否自会出现ping不通、发送的报文被接收方认为是错误的数据而丢弃的现象。  

在这里插入图片描述

图4: Checksum配置

​  下面接着对Key Options中内存堆的大小进行调整,原来大小为1600字节,调整为2048字节(ENC28J60驱动在接收数据包时申请的内存大约1600字节),其他的保持默认配置不用改动。本例为无操作系统移植,所以没有使能Middleware中的FREERTOS,如果想要RTOS的支持,可采用类似的方式进行配置。因为我们只要临时项目生成配置好的lwIP包,其他的项目配置内容,如系统时钟、中断、GPIO口等,都不用去管。至此可以点击“Generate Code”生成代码了,在生成代码的过程中,如果有警告信息,请忽视,这是警告项目中还有很多配置没设置好的原因。代码生成完后,项目根目录下多出两个文件夹“LWIP”和“Middllewares”:
 
在这里插入图片描述

图5: 系统生成的lwIP文件夹
  ​  “LWIP”文件夹中包含了lwIP的基本配置文件和硬件驱动接口文件,​“Middllewares”中包含的“src”和“arch”为lwIP的核心文件和平台移植相关代码,所有的代码STM32CubeIDE开发环境都帮我们弄好了,基本不需要修改,唯一需要修改的是ethernetif.c文件,其实开发环境也帮我们自动生成了,只不过驱动是针对STM32F407内部的以太网控制器的,和我们实际使用的ENC28J60不符合而已。

​  下一步是生成STM32F103的项目(MiniSTM32使用的是STM32F103RCT6),并把F407临时项目生成的“LWIP”和“Middllewares”文件夹拷贝到F103项目的根目录下,并按照下图在项目的“Properties”->“C/C++ General”->"Paths and Symbols"中添加头文件、代码的包含路径。
 

在这里插入图片描述

图6: 添加头文件、代码路径
 

​  Include包含的头文件比较多,如果一个一个添加嫌麻烦的话,可用下面的“Export Settings…”在F407临时项目中导出xml配置文件,直接导入移植目标项目中,然后删除包含“STM32F4xx”的条目即可。至此,lwIP包的核心部分移植基本完成,下面进行lwIP底层和ENC28J60驱动接口的更改、SPI相关GPIO口的设置。
​  因为本移植是在正点原子的STM32 Mini开发板上进行的,所以ENC28J60的驱动也是在正点原子提供的驱动基础上进行修改的,具体下载地址为http://www.openedv.com/docs/book-videos/zdyzshipin/4free/Lwip.html,包含两个文件:enc28j60.c和enc28j60.h,把这两个文件拷贝到移植项目的“Core/Src”文件下,注意,这两个文件的编码方式为GB2312,需要使用notepad++或sublime等软件转换为UTF-8编码,不然在STM32CubeIDE中打开时,中文注释会出现乱码。

​  正点原子ENC28J60模块有8个引脚,分别是: GND、 RST、MISO、SCK、MOSI、INT、CS 和 V3.3。其中GND和 V3.3用于给模块供电,MISO/MOSI/SCK用于SPI通信,CS是片选信号,INT为中断输出引脚,RST为模块复位信号。网上有很多ENC28J60模块是10引脚的,不能和Mini开发板直接连接,这点请注意。除了电源和地,其他6个引脚和STM32F103的GPIO的对应关系如下:
 
在这里插入图片描述

图7: 引脚对应关系
 

​  在本项目中,ENC28J60模块是和STM32F103的SPI1连接通信的,所以接下来是对SPI1端口以及相关的片选、中断、复位的GPIO端口设置(STM32F103的RCC基本时钟、JTAG调试的配置请参阅相关资料,这里就不赘述了)。首先设置SPI1模块,Mode设置为“Full-Duplex Master”,波特率的分频系数设为8,其他的参数保持默认即可:
 

在这里插入图片描述

图8: SPI1设置
 

​  其他GPIO端口设置:PA1(INT)设置为中断模式,其“GPIO mode”配置为下降沿触发方式(External Interrupt Mode with Falling edge trigger detection),“GPIO Pull-up/Pull-down”设置为上拉模式(Pull-up),PA4(RST)和PC4(CS)都设置成:GPIO mode为“Output Push Pull”;GPIO output level为“High”,具体见图9。
 
在这里插入图片描述

图9: 中断、片选、复位GPIO设置

 
​  接下来在NVIC中配置ENC28J60模块的中断引脚,中断连接STM32F103的PA1口,使能EXTI line1 interrupt,并设置中断优先级为2(具体优先级的设置可由自己决定,但需掌握STM32的中断机制):
 
在这里插入图片描述

图10: ENC28J60引脚中断设置
 

​  至此,图形界面的软件、硬件配置基本完成,进入软件修改部分,主要修改的内容分三块:ENC28J60驱动、lwIP底层接口和main函数。下面依次来说明软件的修改过程。
  在修改ENC28J60驱动前,需要在“Core/Src”目录下新建一个头文件“”enc28j60_sys.h",该文件定义了一些驱动需要的数据类型、GPIO端口位操作宏等(参考正点原子驱动中的sys.h),其内容如下:

#ifndef SRC_ENC28J60_SYS_H_
#define SRC_ENC28J60_SYS_H_#include "stm32f1xx_hal.h"
#include "stm32f103xe.h"
#include "ethernetif.h"typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef __IO uint32_t  vu32;#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出
#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入#define delay_ms(x) HAL_Delay(x)		//用HAL库的延时函数替代  delay_ms()
#define INTX_DISABLE() __disable_irq() 	//关中断
#define INTX_ENABLE() __enable_irq()	//开中断
#define printf(x,...)	//因为没有使能串口,未重定义printf()函数,用宏定义去掉原来驱动的打印函数
#endif /* SRC_ENC28J60_SYS_H_ */

 
​  在前面的步骤中,已经把ENC28J60的驱动拷贝到“Core/Src”目录下,对enc28j60.h的修改比较简单,只需要把原来的#include "sys.h"替换为#include "enc28j60_sys.h"即可。
​  enc28j60.c的修改相对来说麻烦点,需要声明两个外部变量:“hspi1”为IDE自动生成的SPI1句柄,在main.c中定义,netif结构体gnetif,在lwip.c中定义,增加三个函数,其中两个SPI1_ReadWriteByte()、SPI1_SetSpeed()为SPI1的读写和速度调整函数,另一个为ENC28J60接收到数据而触发的中断回调函数HAL_GPIO_EXTI_Callback(),原来的中断处理函数“void EXTI1_IRQHandler(void)”和“void ENC28J60_ISRHandler(void)”都注释掉,enc28j60.c中变动部分的代码如下

extern SPI_HandleTypeDef hspi1; //IDE自动生成,在main.c文件中定义
extern struct netif gnetif;		//lwIP netif结构体,在lwip.c文件中定义void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性__HAL_SPI_DISABLE(&hspi1);            //关闭SPIhspi1.Instance->CR1&=0XFFC7;          //位3-5清零,用来设置波特率hspi1.Instance->CR1|=SPI_BaudRatePrescaler;//设置SPI速度__HAL_SPI_ENABLE(&hspi1);             //使能SPI}u8 SPI1_ReadWriteByte(u8 TxData)
{u8 Rxdata;HAL_SPI_TransmitReceive(&hspi1,&TxData,&Rxdata,1, 1000);return Rxdata;          		    //返回收到的数据
}//初始化ENC28J60
//macaddr:MAC地址
//返回值:0,初始化成功;
//       1,初始化失败;
u8 ENC28J60_Init(void)
{u8 version;u16 retry=0;u32 temp;__HAL_SPI_ENABLE(&hspi1); //使能SPI外设//初始化MAC地址temp=*(vu32*)(0x1FFFF7E8);	//获取STM32的唯一ID的前24位作为MAC地址后三字节enc28j60_dev.macaddr[0]=2;enc28j60_dev.macaddr[1]=0;enc28j60_dev.macaddr[2]=0;enc28j60_dev.macaddr[3]=(temp>>16)&0XFF;	//低三字节用STM32的唯一IDenc28j60_dev.macaddr[4]=(temp>>8)&0XFFF;enc28j60_dev.macaddr[5]=temp&0XFF;ENC28J60_RST=0;			//复位ENC28J60delay_ms(10);	 ENC28J60_RST=1;			//复位结束				    delay_ms(10);	ENC28J60_Write_Op(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET);	//软件复位while(!(ENC28J60_Read(ESTAT)&ESTAT_CLKRDY)&&retry<250)	//等待时钟稳定{retry++;delay_ms(1);}	if(retry>=250)return 1; //ENC28J60初始化失败version=ENC28J60_Get_EREVID();			//获取ENC28J60的版本号printf("ENC28J60 Version:%d\r\n",version);	enc28j60_dev.NextPacketPtr=RXSTART_INIT;ENC28J60_Write(ERXSTL,RXSTART_INIT&0XFF);	//设置接收缓冲区起始地址低8位ENC28J60_Write(ERXSTH,RXSTART_INIT>>8);		//设置接收缓冲区起始地址高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);//ERXWRPTH:ERXWRPTL 寄存器定义硬件向FIFO 中//的哪个位置写入其接收到的字节。 指针是只读的,在成//功接收到一个数据包后,硬件会自动更新指针。 指针可//用于判断FIFO 内剩余空间的大小  8K-1500。 //设置接收读指针字节ENC28J60_Write(ERXRDPTL,RXSTART_INIT&0XFF);ENC28J60_Write(ERXRDPTH,RXSTART_INIT>>8);//接收过滤器ENC28J60_Write(ERXFCON,0);ENC28J60_Write(EPMM0,0X3F);ENC28J60_Write(EPMM1,0X30);ENC28J60_Write(EPMCSL,0Xf9);ENC28J60_Write(EPMCSH,0Xf7);ENC28J60_Write(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);//将MACON2 中的MARST 位清零,使MAC 退出复位状态。ENC28J60_Write(MACON2,0x00);ENC28J60_Write(MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);// 最大帧长度 1518ENC28J60_Write(MAMXFLL,MAX_FRAMELEN&0XFF);ENC28J60_Write(MAMXFLH,MAX_FRAMELEN>>8);ENC28J60_Write(MABBIPG,0x15);ENC28J60_Write(MAIPGL,0x12);ENC28J60_Write(MAIPGH,0x0C);//设置MAC地址ENC28J60_Write(MAADR5,enc28j60_dev.macaddr[0]);ENC28J60_Write(MAADR4,enc28j60_dev.macaddr[1]);ENC28J60_Write(MAADR3,enc28j60_dev.macaddr[2]);ENC28J60_Write(MAADR2,enc28j60_dev.macaddr[3]);ENC28J60_Write(MAADR1,enc28j60_dev.macaddr[4]);ENC28J60_Write(MAADR0,enc28j60_dev.macaddr[5]);//配置PHY为全双工  LEDB为拉电流ENC28J60_PHY_Write(PHCON1,PHCON1_PDPXMD);	ENC28J60_PHY_Write(PHCON2,PHCON2_HDLDIS);ENC28J60_Set_Bank(ECON1);ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_TXERIE|EIE_RXERIE);ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN);printf("ENC28J60 Duplex:%s\r\n",ENC28J60_Get_Duplex()?"Full Duplex":"Half Duplex");	//获取双工方式return 0;
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{while(ENC28J60_INT == 0){u8 status;u8 packetnum;u16 temp;ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,EIE,EIE_INTIE);	//关闭ENC28J60的全局中断status=ENC28J60_Read(EIR);	//读取以太网中断标志寄存器if(status&EIR_PKTIF)		//接收到数据,处理数据{ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,EIR,EIR_PKTIF);	//清除ENC28J60的接收中断标志位ethernetif_input(&gnetif);}if(status&EIR_TXIF)			//以太网发送中断{ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,EIR,EIR_TXIF);	//清除ENC28J60的接收中断标志位}if(status&EIR_RXERIF)		//接收错误中断标志位{ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,EIR,EIR_RXERIF);packetnum=ENC28J60_Read(EPKTCNT);temp=ENC28J60_Read(ERXRDPTH)<<8;		//读取高字节temp|=ENC28J60_Read(ERXRDPTL);			//读取低字节temp++;ENC28J60_Write(ERXRDPTL,temp&0XFF);		//先写入低字节ENC28J60_Write(ERXRDPTH,temp>>8);		//先写入低字节ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC);printf("接收错误!接收到数据包个数:%d\r\n",packetnum);}if(status&EIR_TXERIF)		//发送错误中断标志位{ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,EIR,EIR_TXERIF);ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ESTAT,ESTAT_LATECOL|ESTAT_TXABRT);printf("发送错误!\r\n");}ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE);	//打开ENC28J60的全局中断}
}

 
  lwIP底层接口代码的修改。在文件中增加extern dev_strucrt enc28j60_dev变量的声明,主要修改的函数是low_level_init()、low_level_output()、low_level_input()、ethernetif_input()和ethernetif_init()五个函数,保留原来的sys_jiffies()和sys_now()函数,其余的函数都删除掉,修改后的ethernet.c驱动文件如下:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip/opt.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "lwip/ethip6.h"
#include "ethernetif.h"
#include <string.h>
#include "../../Core/Src/enc28j60.h"
/* Network interface name */
#define IFNAME0 's'
#define IFNAME1 't'
extern dev_strucrt enc28j60_dev;static void low_level_init(struct netif *netif)
{netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置MAC地址长度,为6个字节//初始化MAC地址,和ENC28J60驱动中的MAC地址一致netif->hwaddr[0] = enc28j60_dev.macaddr[0];netif->hwaddr[1] = enc28j60_dev.macaddr[1];netif->hwaddr[2] = enc28j60_dev.macaddr[2];netif->hwaddr[3] = enc28j60_dev.macaddr[3];netif->hwaddr[4] = enc28j60_dev.macaddr[4];netif->hwaddr[5] = enc28j60_dev.macaddr[5];netif->mtu=1500; //最大允许传输单元,允许该网卡广播和ARP功能netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;}static err_t low_level_output(struct netif *netif, struct pbuf *p)
{struct pbuf *q;int l=0;u8 *buffer;buffer=mem_malloc(p->tot_len);	//申请内存if(buffer==NULL)printf("发送数据缓冲区内存申请失败\r\n");for(q=p;q!=NULL;q=q->next){memcpy((u8_t*)&buffer[l], q->payload, q->len);l=l+q->len;}ENC28J60_Packet_Send(p->tot_len,buffer);mem_free(buffer);				//释放内存return ERR_OK;
}static struct pbuf * low_level_input(struct netif *netif)
{struct pbuf *p,*q;u32 len;u8 *buffer;int l=0;p=NULL;buffer=mem_malloc(1600);			//申请内存if(buffer!=NULL)len=ENC28J60_Packet_Receive(MAX_FRAMELEN,buffer);	//接收数据else{printf("接收数据缓冲区内存申请失败\r\n");return p;}p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL);	//pbufs内存池分配pbufif(p!=NULL){for(q=p;q!=NULL;q=q->next){memcpy((u8_t*)q->payload,(u8_t*)&buffer[l], q->len);l=l+q->len;}}mem_free(buffer);					//释放内存return p;
}void ethernetif_input(struct netif *netif)
{err_t err;struct pbuf *p;p=low_level_input(netif);   //调用low_level_input函数接收数据if(p==NULL) return;err=netif->input(p, netif); //调用netif结构体中的input字段(一个函数)来处理数据包if(err!=ERR_OK){LWIP_DEBUGF(NETIF_DEBUG,("ethernetif_input: IP input error\n"));pbuf_free(p);p = NULL;}return;
}err_t ethernetif_init(struct netif *netif)
{LWIP_ASSERT("netif!=NULL",(netif!=NULL));
#if LWIP_NETIF_HOSTNAME			//LWIP_NETIF_HOSTNAMEnetif->hostname="lwip";  	//初始化名称
#endifnetif->name[0]=IFNAME0; 	//初始化变量netif的name字段netif->name[1]=IFNAME1; 	//在文件外定义这里不用关心具体值netif->output=etharp_output;//IP层发送数据包函数netif->linkoutput=low_level_output;//ARP模块发送数据包函数low_level_init(netif); 		//底层硬件初始化函数return ERR_OK;
}
void ethernetif_update_config(struct netif *netif)
{//定义的空函数,lwip.c中有对该函数的调用,避免编译报错
}
u32_t sys_jiffies(void)
{return HAL_GetTick();
}u32_t sys_now(void)
{return HAL_GetTick();
}

​  最后是对main.c文件以及一些相关变量的调整,当ENC28J60模块接收到报文时,可采用两种方式读取数据:轮询方式和中断方式,在本移植中采用中断方式来接收报文。首先在main.c中声明四个外部函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern void MX_LWIP_Init(void);
extern void MX_LWIP_Process(void);
extern unsigned char  ENC28J60_Init(void);
extern void sys_check_timeouts(void);
/* USER CODE END 0 */

再在main()函数while循环的前面添加ENC28J60_Init()和MX_LWIP_Init()两个初始化函数,在while循环内添加sys_check_timeouts()函数:

  /* USER CODE BEGIN WHILE */ENC28J60_Init();MX_LWIP_Init();while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */sys_check_timeouts();}/* USER CODE END 3 */

另外需要在lwip.h头文件注释掉“extern ETH_HandleTypeDef heth;”,已防编译报错,这个变量是在临时项目中IDE自动生成的以太网控制器的类型声明,在本项目中不需要。
​  至此,全部的代码修改已完成,IP地址的定义在lwip.c文件中的MX_LWIP_Init()函数内,如需修改IP地址,请在此处进行。编译下载后就可进行验证测试了,用网线连接开发板和PC机网口,注意首先要关闭windows的防火墙,不然会出现ping不通的现象,配置PC上网卡的IP地址为:192.168.1.20,掩码:255.255.255.0,网关:192.168.1.1,完成后从电脑上ping开发板地址192.168.1.100,结果如下:
 
在这里插入图片描述

图11: ping测试结果
 

​  能ping通,则说明lwIP内部的协议栈运行正常。本次lwIP移植为无操作系统实验性质的移植,如需要RTOS的支持,可采用类似的方式进行,在有以太网控制器的芯片上建立临时项目,配置好lwIP和RTOS选项后,拷贝相关的内容至STM32F103项目下进行开发。


http://chatgpt.dhexx.cn/article/nJtaTQ16.shtml

相关文章

RT-Thread—STM32—enc28j60

!!! 本文已同步到码云 - 点击此链接获取最新 - 可进入码云提交修改 !!! 概述 本教程主要是讲解如何使用ENC28J60模块介入以太网 本例程的模板使用通用模板环境搭建里面的模板RT-Thread—STM32—环境搭建 文末也有我移植好的例程&#xff0c;不过建议大家从头开始移植&#xf…

GD32F4xx 以太网芯片(enc28j60)驱动移植

1、enc28j60 简介 ENC28J60 是带有行业标准串行外设接口(SPI)的独立以太网控制器。 主要特性: (1)SPI最高通信速率:10Mb/s。只支持SPI的模式0,0,且SPI端口要求SCK在空闲状态时为低电平。 (2)支持全双工和半双工模式。 (3)8KB 发送/接收数据包双端口RAM.,可配置发送…

enc28j60 linux 驱动_linux enc28j60网卡驱动移植(硬件spi和模拟spi)

本来想移植DM9000网卡的驱动&#xff0c;无奈硬件出了点问题&#xff0c;通过杜邦线链接开发板和DM9000网卡模块&#xff0c;系统上电&#xff0c;还没加载网卡驱动就直接崩溃了&#xff0c;找不到原因。。。刚好手上有一个enc28j60的网卡模块&#xff0c;于是就着手移植enc28j…

lwip-2.1.3在STM32F103ZE+ENC28J60有线网卡上无操作系统移植(使用STM32 HAL库)

程序下载链接&#xff1a;百度网盘 请输入提取码&#xff08;提取码&#xff1a;k6tz&#xff09; 【重要说明】 连接方式一&#xff08;推荐&#xff09;&#xff1a; 电脑有线网卡断开&#xff0c;无线网卡连无线路由器&#xff0c;无线网卡配置成自动获取IP地址。 板子的E…

enc28j60是带SPI接口的独立以太网控制器(即网卡),兼容IEEE 802.3,集成MAC和10 BASE-T PHY.而KSZ8081只是PHY芯片和网口扫盲三:以太网芯片MAC和PHY的关系

百度百科中介绍以太网控制器也称以太网适配器&#xff0c;就是我们通常称的“网卡”。电脑中网卡通过PCI和CPU相连&#xff0c;网卡上RJ45插网线水晶头。教程中的ENC28J60通过SPI和单片机相连&#xff0c;ENC28J60模块的RJ45插网线的水晶头 以太网控制器_百度百科 1.概述 enc2…

基于enc28j60的学习心得

1.概述 enc28j60是带SPI接口的独立以太网控制器&#xff0c;兼容IEEE 802.3&#xff0c;集成MAC和10 BASE-T PHY&#xff0c;最高速度可达10Mb/s。基于enc28j60控制器的理解可阅读文章&#xff1a; ENC28J60学习笔记&#xff0c;在该文章内详细介绍控制器的使用方法&#xff0c…

单片机学习:手把手教你移植LWIP(ENC28J60)

这里只是移植&#xff0c;所以LWIP那么多的协议都不需要管&#xff0c;只要知道哪里需要我们修改&#xff0c;为什么修改就可以了。 上图就是整个移植的基本思路&#xff0c;非常清晰的三个层次。其实想想&#xff0c;本质上就是收发数据&#xff0c;只是LWIP协议通过对数据的…

单片机 STM32 HAL 网络模块 ENC28J60

文章目录 一、 简介二、特性三、示例代码 一、 简介 ENC28J60 是带有行业标准串行外设接口&#xff08;Serial Peripheral Interface&#xff0c;SPI&#xff09;的独立以太网控制器。它可作为任何配备有 SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3的全部规范&#x…

ENC28J60 简介

单片机以太网方案 单片机想要使用以太网的话&#xff0c;通常有以下几种方案&#xff1a; 如果 MCU 内部集成 MAC 控制器&#xff0c;则只需外接一个 PHY 芯片就可以了如果 MCU 内部没有 MAC 控制器&#xff0c;需要外接 MAC 芯片和 PHY 芯片&#xff0c;这两颗芯片可以分立也…

c语言程序设计 国外教材,标准C程序设计(第7版国外计算机科学经典教材)

导语 内容提要 E.巴拉古路萨米著李周芳译的《标准C程序设计(第7版国外计算机科学经典教材)》专门用于满足渴望成为程序员的学生&#xff0c;最新版按照Bloom分类法所定的学习目标来呈现主题&#xff0c;支持基于学习的成果。本书解释了基本概念和高级内容&#xff0c;且主要关注…

学习C语言的教材

作者&#xff1a; 阮一峰 日期&#xff1a; 2011年9月18日 我的C语言是自学的&#xff0c;这些年看过不少教材。 下面&#xff0c;我对其中一些教材做个点评。 1. How to Think Like a Computer Scientist: C version 这是我读过最易懂的C语言教材。 虽然它只讲解最基本的语法&…

新概念c语言周二强07答案,新概念C语言能力教程(普通高等教育十二五规划教材)...

导语 内容提要 周二强编写的《新概念C语言能力教程(普通高等教育十二五规划教材)》以先进的教学理念为指导&#xff0c;以培养编程能力与学习能力为目标&#xff0c;从全新的角度解析了C语言&#xff0c;高屋建瓴地阐释了C语言学习中的诸多难点&#xff0c;对序列点、指针等概念…

计算机程序c语言教材,全国计算机等级考试二级C语言程序设计教材(2018年版)...

2018年计算机二级教材&#xff1a;C语言程序设计 简介 书名&#xff1a;全国计算机等级考试二级教程——C语言程序设计(2018年版) 作者&#xff1a;教育部考试中心 出版社&#xff1a;高等教育出版社 出版时间&#xff1a;2017年11月 ISBN&#xff1a;9787040488524 定价&#…

树莓派(0)C语言教材学习

学习日记的功能主要是记录学习C语言的知识还有难上手的地方&#xff0c;之后会记录配置树莓派环境还有设计linux环境下C语言的实验题目 目前在K&R的《C语言程序设计》教材学习&#xff0c;前面的内容没有难度&#xff0c;基本上和高级程序设计语言的学习思路一样&#xff0…

国内C语言教材中几种值得商榷的说法

作者&#xff1a;巨同升 “C语言程序设计”这门课程在国内高校普遍开设已有近三十年&#xff0c;课程的建设和研究取得了长足的进步&#xff0c;涌现出了数量众多、各具特色的C语言教材。尽管如此&#xff0c;在许多C语言教材中还或多或少地存在着一些不准确甚至是值得商榷的说…

既然谭浩强的C语言教材不好,那应该选什么书作C语言教材?

易道云学院C语言/C语法学习不在于你看了多少书&#xff0c;而在于你实实在在写了多少有效代码。易道云学院 回到这个问题&#xff0c;其实我个人认为&#xff0c;看什么样的书&#xff0c;也是需要应对不一样的场景易道云学院去有目的地涉猎。我暂时想到了几种情况&#xff0c;…

c语言课本答案解析宋士银,c语言教材

22.40定价&#xff1a;28.00(8折) /2007-02-01 根据教育部高等学校计算机科学与技术教学指导委员会提出的《关于进一步加强高等学校计算机基础教学的意见暨计算机基础课程教学基本要求》的有关要求&#xff0c;编者组织了一批多年工作在教学一线且有丰富教学经验的教师编写了《…

C语言的环境变量配置

一、编辑器选择与安装 这边选用的编辑器是比较常见的devc&#xff0c;当然vscode和vc也都可以用来学习C语言&#xff0c;要软件和C语言环境变量的话可以加QQ群&#xff1a;373270625 第一步全部默认勾选就行无脑下一步 第二步选择一个放软件的文件夹&#xff0c;我这边选择的…

Jmeter环境变量配置

解压后 电脑桌面----》“计算机”图标----》鼠标右键选择“属性”----》点击高级系统设置----》高级---》环境变量页面 1.在系统变量框&#xff0c;点击“新建”&#xff0c;建立一个变量&#xff1a;JMETER_HOME,值为你解压的jmeter安装路径 2.配置classpath变量&#xff0c…

java设置环境变量jre_JRE环境变量配置图解

JRE(Java Runtime Environment,Java运行环境),运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。如果大家需要查看JRE环境变量配置图解过程,看完本文你的问题也就迎刃而解了。 我们这里使用jre-7u67-windows-i586的32位JRE安装包,大小只有20多M,比JDK7小了…