ENC28J60 简介

Posted Li-Yongjun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ENC28J60 简介相关的知识,希望对你有一定的参考价值。

单片机以太网方案

单片机想要使用以太网的话,通常有以下几种方案:

  1. 如果 MCU 内部集成 MAC 控制器,则只需外接一个 PHY 芯片就可以了
  2. 如果 MCU 内部没有 MAC 控制器,需要外接 MAC 芯片和 PHY 芯片,这两颗芯片可以分立也可以集成在一颗芯片中

以上两种方案,MAC + PHY 完成了 TCP/IP 五层模型的最低两层,即物理层和数据链路层。上面几层,如网络层、传输层、应用层则需要在 MCU 中实现。当然,普通程序员要想实现 IP 层、TCP/UDP 层还是有一定难度的。不过不用担心,我们可以使用成熟的开源 TCP/IP 协议栈,如 uIP、LwIP。它们都是轻量级的 TCP/IP 协议栈,适用于资源受限的单片机。

  1. 使用硬件 TCP/IP 协议栈,MCU 只需要实现应用层代码就可以了

例如,使用 W5500 芯片,传输层及其以下全部由外部这颗芯片完成,MCU 只需要配置 W5500,从 W5500 收发数据,完成应用层逻辑就可以了。还有一种方案是 MCU 外接一个串口转以太网模块,原理和使用 W5500 方案类似。

ENC28J60

ENC28J60 属于上面介绍的方案 2,ENC28J60 单颗芯片集成了 MAC 和 PHY,提供 SPI 接口用来和 MCU 通信。

寄存器 —— 硬件的灵活性体现

在方案 2 中,网络层、传输层、应用层 是以软件的形式在 CPU 中实现的,而物理层、数据链路层则是以硬件的形式在 ENC28J60 中实现的,虽然 ENC28J60 是一个硬件,我们无法对其进行编程修改功能,但其仍然留有一定的灵活性,那就是可以配置的寄存器。我们可以设置寄存器以达到控制最低两层的目的,也可以读寄存器以达到监控最低两层的目的。
比如我们可以设置 ECON1 寄存器,来控制接收使能、发送请求、DMA、存储区选择等。
还可以设置 PHCON1 寄存器,来控制 PHY 模块复位等功能。

缓冲器 —— 硬件的价值体现

说到底 ENC28J60 的功能是从 IP 层拿数据并发送出去,或者将收到的数据送给 IP 层。这两个方向的操作都需要用到缓冲器,用来暂存数据。

ENC28J60 读写特性

ENC28J60 中有三种类型的存储器:
• 控制寄存器
• 以太网缓冲器
• PHY 寄存器
其中 MCU 可以通过 SPI 接口,使用指令来直接读写 控制寄存器以太网缓冲器。但是不可以通过 SPI 接口直接访问 PHY 寄存器,只可以通过 MAC 中的 MII 访问这些寄存器。
可以理解为 SPI 是和 MAC 相连的,可以直接访问 MAC 的寄存器,但是 PHY 只与 MAC 通过 RMII、MIIM 接口相连,所以想要读取 PHY 寄存器的值,必须要通过 MAC,进一步讲是通过 MAC 的 MIIM 接口,或者说是通过 MAC 的 MII 寄存器。

ENC28J60 中所有的存取器都是以静态 RAM 的方式实现的。
控制寄存器存储空间分为四个存储区,可用 ECON1 寄存器中的存储区选择位 BSEL1:BSEL0 进行选择。 每个存储区都是 32 字节长,可以用 5 位地址值进行寻址。
所有存储区的最后五个单元 (1Bh 到 1Fh)都指向同一组寄存器:EIE、EIR、ESTAT、ECON2 和 ECON1。 它们是控制和监视器件工作的关键寄存器,由于被映射到同一存储空间,因此可以在不切换存储区的情况下很方便地访问它们。
下图是所有的控制寄存器:

能够从任意 Bank 访问 ECON1 是有必要的,因为 ECON1 中存有选择 Bank 的两个 bit,倘若 ECON1 只能在 Bank 0 中访问,那么,一旦将访问权切换到 Bank 2,而 Bank 2 又无法访问 ECON1 来选择 Bank,那不就悲催了嘛,只能停留在 Bank 2 了。。😂

读取 PHY 寄存器实例

下面粗略地写一段代码来读取 PHY 的 PHSTAT1 寄存器,此寄存器的 bit2 表示链路状态

我们就按照步骤一步步写

代码

int main(void)


  /* USER CODE BEGIN 1 */
	uint8_t tx_data = 0x00;
	uint8_t rx_data = 0x00;
	uint16_t pyh_data;
  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
/* 1111111111111111111. */
/* select bank 2 begin */
	tx_data = 0xA0 | 0x1F;	// BFC | ECONT1	// BF
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x03;	// 清除最低两位					// 03
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);		// 拉高 CS 引脚可结束 BFC 命令
	
	tx_data = 0x80 | 0x1F;	// BFS | ECONT1	// 9F
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x02;	// 10h 表示选中 bank2		// 02
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);		// 拉高 CS 引脚可结束 BFS 命令
/* select bank 2 end */



/* MIREGADR(14h) <-- PHSTAT1(01h) begin */
	tx_data = 0x40 | 0x14;	// WCR | MIREGADR(14h) // 地址	// 54
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x01;	// PHSTAT1(01h) // 值										// 01
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
/* MIREGADR(14h) <-- PHSTAT1(01h) end */


/* 2222222222222222222 */
/* MICMD(12h) <-- MIIRD(01h) begin */			// 92
	tx_data = 0x80 | 0x12;	// BFS | MICMD(12h) // 地址
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x01;	// MIIRD(01h) // 值			// 01
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
/* MICMD(12h) <-- MIIRD(01h) end */


/* select bank 3 begin */
	tx_data = 0x80 | 0x1F;	// BFS | ECONT1	// 9F
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x03;	// 11h 表示选中 bank2		// 03
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);		// À­¸ß CS Òý½Å¿É½áÊø BFS ÃüÁî
/* select bank 3 end */


/* MISTAT(0Ah) <-- BUSY(01h) begin */
	tx_data = 0x80 | 0x0A;	// BFS | MICMD(0Ah) // 地址	// 8A
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x01;	// BUSY(01h) // 值									// 01
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
/* MISTAT(0Ah) <-- BUSY(01h) end */

/* 333333333333333333. */
/* 等待 10.24us */
//	HAL_Delay(1);

/*  MISTAT(0Ah) */
//	tx_data = 0x00 | 0x0A;	// RCR | MISTAT(0Ah) // µØÖ·
//	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
//	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
//	
//	HAL_SPI_Transmit(&hspi1, &rx_data, 1, 10); // ¶ÁÖµ
//	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
///* MIREGADR(14h) <-- PHSTAT1(01h) end */
//	printf("rx_data = 0x%x\\r\\n", rx_data);

/* 4 */
/* select bank 2 begin */
	tx_data = 0xA0 | 0x1F;	// BFC | ECONT1
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x03;	// Çå³ý×îµÍÁ½Î»
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);		// À­¸ß CS Òý½Å¿É½áÊø BFC ÃüÁî
	
	tx_data = 0x80 | 0x1F;	// BFS | ECONT1
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x02;	// 10h ±íʾѡÖÐ bank2
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);		// À­¸ß CS Òý½Å¿É½áÊø BFS ÃüÁî
/* select bank 2 end */

/* MICMD(12h) <-- MIIRD(01h) begin */
	tx_data = 0xA0 | 0x12;	// BFC | MICMD(12h) // µØÖ·
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	tx_data = 0x01;	// MIIRD(01h) // Öµ
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
/* MICMD(12h) <-- MIIRD(01h) end */

/* 5 */
/* read MIRDL(18h) begin */
	tx_data = 0x00 | 0x18;	// RCR | MIRDL(18h) // µØÖ·
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	
	HAL_SPI_Receive(&hspi1, &rx_data, 1, 10); // ¶ÁÖµ
//	printf("rx_data = 0x%x\\r\\n", rx_data);
	
	HAL_SPI_Receive(&hspi1, &rx_data, 1, 10); // ¶ÁÖµ
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
//	printf("rx_data = 0x%x\\r\\n", rx_data);

	pyh_data = rx_data;

/* read MIRDH(19h) begin */
	tx_data = 0x00 | 0x19;	// RCR | MIRDH(19h) // µØÖ·
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1, &tx_data, 1, 10);
	HAL_SPI_Receive(&hspi1, &rx_data, 1, 10);
	
	HAL_SPI_Receive(&hspi1, &rx_data, 1, 10); // ¶ÁÖµ
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
/* read MIRDH(19h) end */
//	printf("rx_data = 0x%x\\r\\n", rx_data);

	pyh_data = pyh_data + (rx_data << 8);
	printf("pyh_data = 0x%x\\r\\n", pyh_data);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
//		printf("helo\\r\\n");
		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
		HAL_Delay(1000);



		
//		printf("0x%X: 0x%x\\n", tx_data, rx_data);
//		cr_addr++;
  
  /* USER CODE END 3 */


插拔网线
      
读取到的 PHSTAT1 寄存器值

插上网线,值为 0x1804,即 bit2 = 1;
拔掉网线,值为 0x1800,即 bit2 = 0。

读到的寄存器状态和网线连接状态一致,成功!

以上是关于ENC28J60 简介的主要内容,如果未能解决你的问题,请参考以下文章

ENC28J60 简介

ENC28J60基于AVRNET修改ENC28J60驱动过程(STM32+ CubeMx + ENC28J60)

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

新型以太网控制器ENC28J60及其接口技术

新型以太网控制器ENC28J60及其接口技术

enc28j60网卡驱动模块添加进linux内核,Kconfig,Makefile配置过程