21.从0学ARM-网卡DM9000详解-基于uboot

原创 一口Linux 2021-05-11 11:40
从0学arm系列继续更新两篇,这是第一篇,下一篇是uboot中的网络协议栈详解。
之前系列请参考所有文章合集【墙裂建议收藏】:
从0学ARM

一、网卡

1. 概念

网卡是一块被设计用来允许计算机在计算机网络上进行通讯的计算机硬件。由于其拥有MAC地址,因此属于OSI模型的第2层。它使得用户可以通过电缆或无线相互连接。

每一个网卡都有一个被称为MAC地址的独一无二的48位串行号,它被写在卡上的一块ROM中。在网络上的每一个计算机都必须拥有一个独一无二的MAC地址。没有任何两块被生产出来的网卡拥有同样的地址。这是因为电气电子工程师协会(IEEE)负责为网络接口控制器(网卡)销售商分配唯一的MAC地址。

网卡上面装有处理器和存储器(包括RAM和ROM)。网卡和局域网之间的通信是通过电缆或双绞线以串行传输方式进行的。而网卡和计算机之间的通信则是通过计算机主板上的I/O总线以并行传输方式进行。

因此,网卡的一个重要功能就是要进行串行/并行转换。由于网络上的数据率和计算机总线上的数据率并不相同,因此在网卡中必须装有对数据进行缓存的存储芯片。

网卡以前是作为扩展卡插到计算机总线上的,但是由于其价格低廉而且以太网标准普遍存在,大部分新的计算机都在主板上集成了网络接口。

这些主板或是在主板芯片中集成了以太网的功能,或是使用一块通过PCI (或者更新的PCI-Express总线)连接到主板上的廉价网卡。

除非需要多接口或者使用其它种类的网络,否则不再需要一块独立的网卡。甚至更新的主板可能含有内置的双网络(以太网)接口。

2. 主要功能

1、数据的封装与解封 发送时将上一层传递来的数据加上首部和尾部,成为以太网的帧。接收时将以太网的帧剥去首部和尾部,然后送交上一层

2、链路管理 主要是通过CSMA/CD(Carrier Sense Multiple Access with Collision Detection ,带冲突检测的载波监听多路访问)协议来实现

3、数据编码与译码 即曼彻斯特编码与译码。其中曼彻斯特码,又称数字双向码、分相码或相位编码(PE),是一种常用的二元码线路编码方式之一,被物理层使用来编码一个同步位流的时钟和数据。在通信技术中,用来表示所要发送比特 流中的数据与定时信号所结合起来的代码。常用在以太网通信,列车总线控制,工业总线等领域。

3. 分类

  1. 按总线接口类型分 按网卡的总线接口类型来分一般可分bai为ISA接口网卡、PCI接口网卡以及在服务器上使用的PCI-X总线接口类型的网卡,笔记本电脑所使用的网卡是PCMCIA接口类型的。
  • (1)ISA总线网卡
  • (2)PCI总线网卡
  • (3)PCI-X总线网卡
  • (4)PCMCIA总线网卡
  • (5)USB总线接口网卡
  1. 按网络接口划分 除了可以按网卡的总线接口类型划分外,我们还可以按网卡的网络接口类型来划分。网卡最终是要与网络进行连接,所以也就必须有一个接口使网线通过它与其它计算机网络设备连接起来。不同的网络接口适用于不同的网络类型,目前常见的接口主要有以太网的RJ-45接口、细同轴电缆的BNC接口和粗同轴电AUI接口、FDDI接口、ATM接口等。而且有的网卡为了适用于更广泛的应用环境,提供了两种或多种类型的接口,如有的网卡会同时提供RJ-45、BNC接口或AUI接口。
  • (1)RJ-45接口网卡
  • (2)BNC接口网卡
  • (3)AUI接口网卡
  • (4)FDDI接口网卡
  • (5)ATM接口网卡
  1. 按带宽划分 随着网络技术的发展,网络带宽也在不断提高,但是不同带宽的网卡所应用的环境也有所不同,目前主流的网卡主要有10Mbps网卡、100Mbps以太网卡、10Mbps/100Mbps自适应网卡、1000Mbps千兆以太网卡四种。
  • (1)10Mbps网卡
  • (2)100Mbps网卡
  • (3)10Mbps/100Mbps网卡
  • (4)1000Mbps以太网卡

二、DM9000

DM9000芯片是DAVICOM公司生产,DM9000A 是一款完全集成的、性价比高、引脚数少、带有通用处理器接口的单芯片快速以太网控制器。

一个 10/100M PHY 和 4K 双字的 SRAM 。它是出于低功耗和高性能目的设计的,其 IO 端口支持 3.3V 与 5V 容限值。

DM9000A 为适应各种处理器,提供了 8 位、16 位数据接口访问内部存储器。

DM9000A物理协议层接口完全支持使用 10MBps 下 3 类、4 类、5 类非屏蔽双绞线和 100MBps 下 5类非屏蔽双绞线。这是完全遵照 IEEE 802.3u 标准。

它的自动协商功能将自动完成 DM9000AE配置以使其发挥出最佳性能。

它还支持 IEEE 802.3x 全双工流量控制。

1. 模块图

图1 DM9000内部结构框架

EEPROM Interface接口用于存放mac地址,Internal SRAM用于存放收发数据,MII部分把MAC部分与PHY部分连接起来通信,AUTO-MDIX用于自适应10/100M网络,在物理层上,MAC在PHY之下。

2. 引脚分析

(#:表示低电平有效)

开发板FS4412的网卡DM9000A连接到了SROM控制器,下面我们分析数据线、地址线和信号线连接

1) SD0~15

SD0~15: 16位数据线连接到引脚BUF_B_Xm0DATA[0:15],由CMD引脚决定访问类型。

可见数据和地址线都连接到了SOC的XM0上。

数据线和信号线对应的SROMC的引脚如上图。

2)  CMD

 dm9000       外围电路             转换电路         soc 
  CMD--------BUF_B_Xm0ADDR2--------Xm0ADDR2-----Xm0ADDR2

如下图所示:CMD:      命令线,当CMD为高,表示SD 传输的是数据,CMD为低表示传输的是地址,接在exynos4412的BUF_B_Xm0ADDR2上,可见CMD复用了地址线Xm0ADDR2引脚。

3)  IOR#、IOW#

 dm9000       外围电路         转换电路    soc 
  IOR--------BUF_Xm0OEn--------Xm0OEn-----Xm0OEn
  IOW--------BUF_Xm0WEn--------Xm0WEn-----Xm0WEn  

4) CS#

 dm9000       外围电路         转换电路    soc 
  CS--------BUF_Xm0cs1--------Xm0cs1-----Xm0CSn1

CS#:片选,放在exynos4412的Bank1的片选上面,内存基地址是0x05000000。

我们的DM9000A是放在exynos4412的Bank1(0X05000000)的片选上面。

而DM9000的CMD引脚接在Bank1的LADDR2上面

读写DM9000A的地址CMD拉低, 此时向0X05000000地址上读写的数据便是DM9000A的内部寄存器地址

读写DM9000A的数据CMD拉高,此时向0X05000000+4地址上读写的数据便是DM9000A的数据

设置exynos4412的bank1的硬件位宽,时序,因为不同的硬件,涉及的数据收发都不同。

5)  INT#

中断线DM9000_IRQ通过U8转接到引脚XEINT6由上图可知中断引脚INT,接在exynos4412的GPX0_6脚上。uboot中的DM9000A的驱动没有用到中断。

3. 复用GPIO引脚

XM0引脚复用了GPIO引脚,所以需要初始化对应的GPIO引脚来使能SROMC。

1) GPY0CON2) GPY1CON3)GPY3CON

4) GPY5CON

5) GPY6CON

三、SROM 控制器

1. 概念

SROM是高速存储器,Cache技术就是通过在DROM和CPU之间插入一小块SROM来减小CPU和存储之间的速度差异的。

本篇参考开发板FS4412,DM9000挂接到exynos 4412的SROM控制器上

EXYNOS 4412包含了SROM控制器,特性如下:

  • 外部 8/16位 NOR Flash/PROM/ SRAM memory.
  • 4组内存,每块内存最多16 MB

首先我们要初始化 exynos4412的 SROM 控制器,设置总线宽度和相关时序。

针对 SROM 控制器的每一个 bank 只有2 个寄存器:SROM_BW 和 SROM_BC

2. SROM_BW

在 SROM_BW 寄存器中,我们只关心与 bank1 相关的域。

上面分析过, DM9000A 的 16 根数据线全部接在 exynos 4412的数据线上,所以 DataWidth1 设置为 1; DM9000A 的地址是按字节存取的,所以 AddrMode1 设置为 1; 通过查看原理图,没有使用 Xm0WAITn和 Xm0BEn 引脚; 所以 WaitEnable1 和 ByteEnable1 均设置为 0。

SROM_BW[7:4]=0x3

3. SROM_BC1

SROM 控制器读时序和 DM9000A 的读时序主要通过SROM_BCn控制寄存器设置。

设置这些时序之前,首先来看DM9000A芯片手册时序图和exynos4412的时序图
详尽时序分析:,内存控制器使用HCLK作为时钟,在HCLK为100MHz时,1个clock大约为10ns。信号值的设定如下:

信号含义最低时间(ns)
Tacs地址发出后等多长时间发片选, DM9000AE 中 CS 和 CMD(地址)同时发出,所以 Tacs最低为0ns0
Tcos发出片选信号后等多长时间发出读使能信号(nOW、 IOR),在 DM9000A 的时序图上对应 T1,最小为 00
Tacc读使能信号持续时间,access cycle ,读写使能后,多久才能访问数据,在 DM9000A 的时序图上对应 T210
Tcoh当DM9000A的写信号取消后,数据线上的数据还需要至少3ns才消失(nOE读写取消后,片选需要维持多长时间)在 DM9000A 的时序图中对应 T43
Tcah片选结束后,地址保存时间, DM9000A 中CS和cmd同时结束,所以 Tcah=00
Tacp页模式,不管0
PMC页模式,不管0

从DM9000A的读写时序图中可以看出,T2+T6实际上构成了DM9000A的一个访问周期,因此还需要满足:Tacs + Tcos + Tacc + Tcoh + Tcah>= T2+T6,最终使用下面的表达式来表达:(Tacs >= 0 && Tacs <= 4) && (Tcos >= 0 && Tcos <= 4) && (Tacc >= 1 && Tacc <= 14 ) && (Tcoh >=1 && Tcoh <= 4 )

寄存器SROM_BCn (n = 0 to 3)定义如下:

故设置参考值为:

#define DM9000_Tacs     (0x1)   // address set-up
#define DM9000_Tcos     (0x1)   // chip selection set-up
#define DM9000_Tacc     (0x5)   // access cycle
#define DM9000_Tcoh     (0x1)   // chip selection hold
#define DM9000_Tah      (0xC)   // address holding time
#define DM9000_Tacp     (0x9)   // page mode access cycle
#define DM9000_PMC      (0x1)   // normal(1data)page mode configuration

4. SROM初始化

u-boot 已经自带了 DM9000系列网卡的驱动,在 u-boot 源码中的 driver/net/dm9000x.c 的有一段说明:

       06/03/2008 Remy Bohmer <linux@bohmer.net>
   - Fixed the driver to work with DM9000A.
     (check on ISR receive status bit before reading the
     FIFO as described in DM9000 programming guide and
     application notes)
   - Added autodetect of databus width.
   - Made debug code compile again.
   - Adapt eth_send such that it matches the DM9000*
     application notes. Needed to make it work properly
     for DM9000A.
   - Adapted reset procedure to match DM9000 application
     notes (i.e. double reset)
   - some minor code cleanups
   These changes are tested with DM9000
{A,EP,E} together
   with a 200MHz Atmel AT91SAM9261 core

可见,2008年Remy Bohmer已经为 DM9000A 添加了驱动,但是我们仍然需要针对板子做一些修改。

前一章我们针对参考的fs4412开发板移植了DM9000A的驱动,下面我们来详细分析DM9000A驱动程序。

分析驱动涉及到以下几个文件:

arch/arm/lib/board.c
board/samsung/origen/origen.c
drivers/net/Dm9000x.c
drivers/net/Dm9000x.h
include/config_cmd_default.h
include/configs/origen.h
include/net.h
net/eth.c

5. 宏定义

在include/configs/origen.h中需要定义DM9000A基地址和编译的宏。其中最重要的几个宏如下:

名称说明
CONFIG_DM9000_BASEDM9000A 的基地址0x05000000
DM9000_IODM9000A 的 INDEX 端口地址CONFIG_DM9000_BASE
DM9000_DATADM9000A 的 DATA 端口地址(CONFIG_DM9000_BASE + 4)
CONFIG_DRIVER_DM9000Makefile中用于控制dm9000驱动是否编译1
CONFIG_DM9000_USE_16BITDM9000A数据宽度
CONFIG_DM9000_NO_SROM表示没有使用SROM1

其中DM9000_DATA 定义为基地址+0x4,刚好把 Xm0ADDR2 拉高,即把 CMD 拉高。

查看文件drivers/net/Makefile:从 Makefile 得知,要把 DM9000A 的驱动编译进 u-boot中,需要定义 CONFIG_DRIVER_DM9000 这个宏。

宏定义如下:

#ifdef CONFIG_CMD_NET
#define CONFIG_NET_MULTI
#define CONFIG_DRIVER_DM9000      1
#define CONFIG_DM9000_BASE        0x05000000 
#define DM9000_IO           CONFIG_DM9000_BASE
#define DM9000_DATA         (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT
#define CONFIG_DM9000_NO_SROM     1
#define CONFIG_ETHADDR      11:22:33:44:55:66
#define CONFIG_IPADDR       192.168.6.187
#define CONFIG_SERVERIP           192.168.6.186
#define CONFIG_GATEWAYIP          192.168.6.1
#define CONFIG_NETMASK      255.255.255.0
#endif

除此以外我们还需要添加一些 u-boot 的命令,比如 ping 命令用来检查网络是否通畅,tftp用来下载文件。

uboot通过宏来控制是否编译这些命令,include/configs/origen.h定义了一些宏,但是有的是undefine,我们要打开它们。

/* Command definition*/
#include <config_cmd_default.h>

#define CONFIG_CMD_PING
#define CONFIG_CMD_ELF
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MMC
#define CONFIG_CMD_FAT
#define CONFIG_CMD_NET
#undef CONFIG_CMD_NFS
#define CONFIG_CMD_HELLO
#define CONFIG_CMD_LEDA

除此之外头文件:u-boot-2013.01/include/config_cmd_all.h 也列出了一些可用的命令。

#define CONFIG_CMD_BDI  /* bdinfo   */
#define CONFIG_CMD_BOOTD /* bootd   */
#define CONFIG_CMD_CONSOLE /* coninfo   */
#define CONFIG_CMD_ECHO  /* echo arguments  */
#define CONFIG_CMD_EDITENV /* editenv   */
#define CONFIG_CMD_FPGA  /* FPGA configuration Support */
#define CONFIG_CMD_IMI  /* iminfo   */
#define CONFIG_CMD_ITEST /* Integer (and string) test */
#ifndef CONFIG_SYS_NO_FLASH
#define CONFIG_CMD_FLASH /* flinfo, erase, protect */
#define CONFIG_CMD_IMLS  /* List all found images */
#endif
#define CONFIG_CMD_LOADB /* loadb   */
#define CONFIG_CMD_LOADS /* loads   */
#define CONFIG_CMD_MEMORY /* md mm nm mw cp cmp crc base loop mtest */
#define CONFIG_CMD_MISC  /* Misc functions like sleep etc*/
#define CONFIG_CMD_NET  /* bootp, tftpboot, rarpboot */
#define CONFIG_CMD_NFS  /* NFS support   */
#define CONFIG_CMD_RUN  /* run command in env variable */
#define CONFIG_CMD_SAVEENV /* saveenv   */
#define CONFIG_CMD_SETGETDCR /* DCR support on 4xx  */
#define CONFIG_CMD_SOURCE /* "source" command support */
#define CONFIG_CMD_XIMG  /* Load part of Multi Image */

6. 初始化srom

在arch/arm/lib/board.c的函数board_init_r中有如下代码:

void board_init_r(gd_t *id, ulong dest_addr)
{
 ……
 board_init(); /* Setup chipselects */
 ……
}

函数board_init()定义在board/samsung/origen/origen.c中,我们在该函数中添加了初始化srom代码:

int board_init(void)
{
 gpio1 = (struct exynos4_gpio_part1 *) EXYNOS4_GPIO_PART1_BASE;
 gpio2 = (struct exynos4_gpio_part2 *) EXYNOS4_GPIO_PART2_BASE;

 gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL);

#ifdef CONFIG_DRIVER_DM9000
 dm9000aep_pre_init();
#endif
 return 0;
}

函数dm9000aep_pre_init用来设置 SROM 控制器。

static void dm9000aep_pre_init(void)
{
 unsigned int tmp;
 unsigned char smc_bank_num = 1;
 unsigned int     smc_bw_conf=0;
 unsigned int     smc_bc_conf=0;

 /* gpio configuration */
 writel(0x002200200x11000000 + 0x120);//GPY0CON
 writel(0x000022220x11000000 + 0x140);//GPY1CON
 /* 16 Bit bus width */
 writel(0x222222220x11000000 + 0x180);//GPY3CON
 writel(0x0000FFFF0x11000000 + 0x188);//GPY3PUD
 writel(0x222222220x11000000 + 0x1C0);//GPY5CON
 writel(0x0000FFFF0x11000000 + 0x1C8);//GPY5PUD
 writel(0x222222220x11000000 + 0x1E0);//GPY6CON
 writel(0x0000FFFF0x11000000 + 0x1E8);//GPY6PUD
               
 smc_bw_conf &= ~(0xf<<4);
 smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
 smc_bc_conf = ((DM9000_Tacs << 28)
   | (DM9000_Tcos << 24)
   | (DM9000_Tacc << 16)
   | (DM9000_Tcoh << 12)
   | (DM9000_Tah << 8)
   | (DM9000_Tacp << 4)
   | (DM9000_PMC));
 exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf);
}
/*
 *  exynos_config_sromc() - select the proper SROMC Bank and configure the
 *  band width control and bank control registers
 *  srom_bank    - SROM
 *  srom_bw_conf  - SMC Band witdh reg configuration value
 *  srom_bc_conf  - SMC Bank Control reg configuration value
 */


void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
 unsigned int tmp;
 struct exynos_sromc *srom = (struct exynos_sromc *)(EXYNOS4412_SROMC_BASE);

 /* Configure SMC_BW register to handle proper SROMC
  * bank */

 tmp = srom->bw;
 tmp &= ~(0xF << (srom_bank * 4));
 tmp |= srom_bw_conf;
 srom->bw = tmp;

 /* Configure SMC_BC
  * register */

 srom->bc[srom_bank] = srom_bc_conf;
}

四、DM9000A驱动分析

DM9000A所能支持的功能非常的多,驱动的实现相对比较复杂,搞清楚裸机的网卡驱动,我们再去学习Linux内核的DM9000驱动就相对容易一些。本节将详细讲解DM9000A网卡的数据的收发操作的流程。

1. 相关结构体

struct board_info

/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
 u32 runt_length_counter; /* counter: RX length < 64byte */
 u32 long_length_counter; /* counter: RX length > 1514byte */
 u32 reset_counter; /* counter: RESET */
 u32 reset_tx_timeout; /* RESET caused by TX Timeout */
 u32 reset_rx_status; /* RESET caused by RX Statsus wrong */
 u16 tx_pkt_cnt;
 u16 queue_start_addr;
 u16 dbug_cnt;
 u8 phy_addr;
 u8 device_wait_reset; /* device state */
 unsigned char srom[128];
 void (*outblk)(volatile void *data_ptr, int count);
 void (*inblk)(void *data_ptr, int count);
 void (*rx_status)(u16 *RxStatus, u16 *RxLen);
 struct eth_device netdev;
board_info_t;

static board_info_t dm9000_info;

该结构体是用来维护DM9000系列网卡的结构体,所有和网卡DM9000A信息都保存到该结构体中。struct eth_devicestruct board_info中有一个重要的成员 netdev,该成员是uboot提供的标准的统一的网卡设备接口。

struct eth_device {
 char name[16];
 unsigned char enetaddr[6];
 int iobase;
 int state;

 int  (*init) (struct eth_device *, bd_t *);
 int  (*send) (struct eth_device *, void *packet, int length);
 int  (*recv) (struct eth_device *);
 void (*halt) (struct eth_device *);
#ifdef CONFIG_MCAST_TFTP
 int (*mcast) (struct eth_device *, u32 ip, u8 set);
#endif
 int  (*write_hwaddr) (struct eth_device *);
 struct eth_device *next;
 int index;
 void *priv;
};

该结构体维护了操作网卡的回调函数等信息,我们只需要把网口的收发数据操作封装到对应的回调函数中,然后注册到系统即可。

2. 网卡注册/注销

进入到arch/arm/lib/board.c 中的 board_init_r 函数:

665 #if defined(CONFIG_CMD_NET)
666     puts("Net:   ");
667     eth_initialize(gd->bd);
668 #if defined(CONFIG_RESET_PHY_R)
669     debug("Reset Ethernet PHY\n");
670     reset_phy();
671 #endif

如果定义了 CONFIG_CMD_NET,就调用 eth_initialize(gd->bd)进行网卡初始化。

这个宏在include/config_cmd_default.h 中定义,这个头文件又被单板配置文件 include/configs/origen.h 所包含。

eth_initialize 函数在 net/eth.c 中定义,下面是该函数部分代码:

308     /*
309      * If board-specific initialization exists, call it.
310      * If not, call a CPU-specific one
311      */

312     if (board_eth_init != __def_eth_init) {
313         if (board_eth_init(bis) < 0)
314             printf("Board Net Initialization Failed\n");
315     } else if (cpu_eth_init != __def_eth_init) {
316         if (cpu_eth_init(bis) < 0)
317             printf("CPU Net Initialization Failed\n");
318     } else
319         printf("Net Initialization Skipped\n");

这段代码功能是:如果定义了单板相关的初始化函数就调用它,否则调用 CPU 相关的初始化函数。

其中__def_eth_init 函数,同样在net/eth.c 中定义

105  * CPU and board-specific Ethernet initializations.  Aliased function
106  * signals caller to move on
107  */
108 static int __def_eth_init(bd_t *bis)
109 {
110     return -1;
111 }
112 int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
113 int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));

这里用到了 gcc 的弱符号和别名属性。如果我们没有定义自己的 board_eth_init 函数,则 board_eth_init 就和__def_eth_init 相同,调用 board_eth_init 就相当于调用__def_eth_init,现在就能明白上面的 if 判断语句了。

board_eth_init 在board/samsung/origen/origen.c 中定义

264 #ifdef CONFIG_CMD_NET
265 int board_eth_init(bd_t *bis)                                                  
266 
{      
267 
268     int rc = 0;
269 #ifdef CONFIG_DRIVER_DM9000
270     rc = dm9000_initialize(bis);                                            
271 #endif                                                                         
272     return rc;                                                              
273 }  
274 #endif

这里通过配置宏来决定调用哪个网卡初始化函数。

我们使用的是 DM9000A,我们先查看下 DM9000A 的驱动源文件drivers/net/DM9000x.c,初始化函数如下:

626 int dm9000_initialize(bd_t *bis)
627 
{
628     struct eth_device *dev = &(dm9000_info.netdev);
629 
630     /* Load MAC address from EEPROM */
631     dm9000_get_enetaddr(dev);
632 
633     dev->init = dm9000_init;
634     dev->halt = dm9000_halt;
635     dev->send = dm9000_send;
636     dev->recv = dm9000_rx;
637     sprintf(dev->name, "dm9000");
638 
639     eth_register(dev);
640 
641     return 0;
642 } 

该函数就是 DM9000A 的初始化函数。631行dm9000_get_enetaddr 从 EEPROM 加载MAC地址,

static void dm9000_get_enetaddr(struct eth_device *dev)
{
#if !defined(CONFIG_DM9000_NO_SROM)
 int i;
 for (i = 0; i < 3; i++)
  dm9000_read_srom_word(i, dev->enetaddr + (2 * i));
#endif
}

该函数根据宏CONFIG_DM9000_NO_SROM 来决定是否从EEPROM 加载MAC地址, 参考的板子上的 DM9000A 没有接 EEPROM,我们在 origen.h 中定义了这个宏,表示不从 EEPROM 加载 MAC地址。

633~636行是将网卡的初始化和收发数据的函数填充到dev中,用于注册到系统中:

639行,函数eth_register()的参数是dev,该变量地址其实是dm9000_info.netdev的地址。dm9000_info定义在同一文件下:

108  static board_info_t dm9000_info;

函数eth_register()位于net/eth.c中;

  • 功能:用于注册网卡到系统中,如果之前网卡设备链表为空,则直接复制给全局指针变量eth_devices和eth_current ,如果不为空,则把当前网卡插入到链表eth_devices中。
int eth_register(struct eth_device *dev)
{
 struct eth_device *d;
 static int index;

 assert(strlen(dev->name) < sizeof(dev->name));

 if (!eth_devices) {//网卡设备链表为空
  eth_current = eth_devices = dev;
  eth_current_changed();
 } else {//找到表尾
  for (d = eth_devices; d->next != eth_devices; d = d->next)
   ;
  d->next = dev;//插入表尾
 }

 dev->state = ETH_STATE_INIT;
 dev->next  = eth_devices;//新的设备指向网卡设备表头
 dev->index = index++;

 return 0;
}

其中

eth_devices:网卡设备的链表 eth_current:用于保存当前使用的网卡

网卡注销网卡注销函数eth_unregister() 该函数会将网卡节点dev从链表eth_devices中删除,并重新设置变量eth_current。

int eth_unregister(struct eth_device *dev)
{
 struct eth_device *cur;

 /* No device */
 if (!eth_devices)
  return -1;

 for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
      cur = cur->next)
  ;

 /* Device not found */
 if (cur->next != dev)
  return -1;

 cur->next = dev->next;

 if (eth_devices == dev)
  eth_devices = dev->next == eth_devices ? NULL : dev->next;

 if (eth_current == dev) {
  eth_current = eth_devices;
  eth_current_changed();
 }

 return 0;
}

3. 寄存器

DM9000A 拥有一系列的控制和状态寄存器,这些寄存器可以被处理器所访问,这些寄存器是按字节对齐的。

所有的 CSRs 在软件或者硬件复位后都将被置为默认值,除非他们被另外标识。

编号寄存器描述偏移地址复位后默认值
1NCR网络控制寄存器00H00H
2NSR网络状态寄存器01H00H
3TCR发送控制寄存器02H00H
4TSR I发送状态寄存器 103H00H
5TSR II发送状态寄存器 204H00H
6RCR接收控制寄存器05H00H
7RSR接收状态寄存器06H00H
8ROCR接收溢出计数寄存器07H00H
9BPTR背压阈值寄存器08H37H
10FCTR流控制阈值寄存器09H38H
11FCRTX/RX 流控制寄存器0AH00H
12EPCREEPROM&PHY 控制寄存器0BH00H
13EPAREEPROM&PHY 地址寄存器0CH40H
14EPDRLEEPROM&PHY 低字节数据寄存器0DHXXH
15EPDRHEEPROM&PHY 高字节数据寄存器0EHXXH
16WCR唤醒控制寄存器0FH00H
17PAR物理地址寄存器10H~15H由 EEPROM决定
18MAR广播地址寄存器16H~1DHXXH
19GPCR通用目的控制寄存器(8bit 模式)1EH01H
20GPR通用目的寄存器1FHXXH
21TRPALTX SRAM 读指针地址低字节22H00H
22TRPAHTX SRAM 读指针地址高字节23H00H
23RWPALRX SRAM 写指针地址低字节24H00H
24RWPAHRX SRAM 写指针地址高字节25H0CH
25VID厂家 ID28H~29H0A46H
26PID产品 ID2AH~2BH9000H
27CHIPR芯片版本2CH18H
28TCR2发送控制寄存器 22DH00H
29OCR操作控制寄存器2EH00H
30SMCR特殊模式控制寄存器2FH00H
31ETXCSR即将发送控制/状态寄存器30H00H
32TCSCR发送校验和控制寄存器31H00H
33RCSCSR接收校验和控制状态寄存器32H00H
34MRCMDX内存数据预取读命令寄存器(地址不加 1)F0HXXH
35MRCMDX1内存数据读命令寄存器(地址不加 1)F1HXXH
36MRCMD内存数据读命令寄存器(地址加 1)F2HXXH
37MRRL内存数据读地址寄存器低字节F4H00H
38MRRH内存数据读地址寄存器高字节F5H00H
39MWCMDX内存数据写命令寄存器(地址不加 1)F6HXXH
40MWCMD内存数据写命令寄存器(地址加 1)F8HXXH
41MWRL内存数据写地址寄存器低字节FAH00H
42MWRH内存数据写地址寄存器高字节FBH00H
43TXPLLTX 数据包长度低字节寄存器FCHXXH
44TXPLHTX 数据包长度高字节寄存器FDHXXH
45ISR中断状态寄存器FEH00H
46IMR中断屏蔽寄存器FFH00H

关于默认值的要点(Key to Default) 在下面寄存器描述中,默认栏采用如下形式:

<Reset Value>, <Access Type>

其中

1 该位设为逻辑 1
0 该位设为逻辑 0
X 没有默认值
P 电源复位恢复默认值
H 硬件复位恢复默认值
S 软件复位恢复默认值
E 从 EEPROM 得到默认值
T 从捆绑引脚(strap pin)得到默认值

:

RO = 只读
RW = 可读可写
R/C = 可读/擦除
RW/C1=可读可写/通过写1擦除
WO = 只写

保留位被隐藏且应写 0,在读访问时保留位没有定义。

如何读取 DM9000A 的寄存器 RSR?假设要读取 DM9000A 的寄存器 RSR(RX Status Register),需要分 2 步:

  1. 向 INDEX 端口写入 RSR 寄存器的地址(0x06) 条件:nGCS1 信号拉低、 Xm0WEn 信号拉低、 Xm0ADDR2 拉低, 或者说向下面的地址写数据 0x06
  2. 从 DATA 端口读取 RSR 寄存器的值 条件:nGCS1 信号拉低、 Xm0OEn 信号拉低、 Xm0ADDR2 拉高, 或者说从下面的地址读数据

DM9000A的寄存器很多,但是我们并需要都掌握,我们只需要掌握其中几个最重要的寄存器的使用即可。

  1. 网络控制寄存器(NCR)
  2. 网络状态寄存器(NSR)
在这里插入图片描述
ISR

DAVICOM 指定配置和状态寄存器(DSCSR)

4. 网卡的初始化

网卡的初始化函数入口位于文件net/eth.c下的函数eth_init():

404 int eth_init(bd_t *bis)
405 
{
406     struct eth_device *old_current, *dev;
  ……
425     old_current = eth_current;
426     do {
427         debug("Trying %s\n", eth_current->name);
428 
429         if (eth_current->init(eth_current, bis) >= 0) {
430             eth_current->state = ETH_STATE_ACTIVE;
431 
432             return 0;
433         }
434         debug("FAIL\n");
 ……
440 }

429行即调用我们注册的dm9000A初始化函数,从这也可以看出,整个架构是把网卡的驱动独立分隔开,与硬件操作相关的代码由用户自己填充并注册到系统中即可,便于扩展。进入dm9000_init():

290 static int dm9000_init(struct eth_device *dev, bd_t *bd)
291 
{
292     int i, oft, lnk;
293     u8 io_mode;
294     struct board_info *db = &dm9000_info;
295 
296     DM9000_DBG("%s\n", __func__);
297 
298     /* RESET device */
299     dm9000_reset();
300 
301     if (dm9000_probe() < 0)
302         return -1;
303 
304     /* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */
305     io_mode = DM9000_ior(DM9000_ISR) >> 6;
306 
307     switch (io_mode) {
308     case 0x0:  /* 16-bit mode */
309         printf("DM9000: running in 16 bit mode\n");
310         db->outblk    = dm9000_outblk_16bit;
311         db->inblk     = dm9000_inblk_16bit;
312         db->rx_status = dm9000_rx_status_16bit;
313         break;
314     case 0x01:  /* 32-bit mode */
315         printf("DM9000: running in 32 bit mode\n");
316         db->outblk    = dm9000_outblk_32bit;
317         db->inblk     = dm9000_inblk_32bit;
318         db->rx_status = dm9000_rx_status_32bit;
319         break;
320     case 0x02/* 8 bit mode */
321         printf("DM9000: running in 8 bit mode\n");
322         db->outblk    = dm9000_outblk_8bit;
323         db->inblk     = dm9000_inblk_8bit;
324         db->rx_status = dm9000_rx_status_8bit;
325         break;
326     default:
327         /* Assume 8 bit mode, will probably not work anyway */
328         printf("DM9000: Undefined IO-mode:0x%x\n", io_mode);
329         db->outblk    = dm9000_outblk_8bit;
330         db->inblk     = dm9000_inblk_8bit;
331         db->rx_status = dm9000_rx_status_8bit;
332         break;
333     } 
334 
335     /* Program operating register, only internal phy supported */
336     DM9000_iow(DM9000_NCR, 0x0);
337     /* TX Polling clear */
338     DM9000_iow(DM9000_TCR, 0);
339     /* Less 3Kb, 200us */
340     DM9000_iow(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
341     /* Flow Control : High/Low Water */
342     DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
343     /* SH FIXME: This looks strange! Flow Control */
344     DM9000_iow(DM9000_FCR, 0x0);
345     /* Special Mode */
346     DM9000_iow(DM9000_SMCR, 0);
347     /* clear TX status */
348     DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
349     /* Clear interrupt status */
350     DM9000_iow(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
351 
352     printf("MAC: %pM\n", dev->enetaddr);
353 
354     /* fill device MAC address registers */
355     for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
356         DM9000_iow(oft, dev->enetaddr[i]);
357     for (i = 0, oft = 0x16; i < 8; i++, oft++)
358         DM9000_iow(oft, 0xff);
359 
360     /* read back mac, just to be sure */
361     for (i = 0, oft = 0x10; i < 6; i++, oft++)
362         DM9000_DBG("%02x:", DM9000_ior(oft));
363     DM9000_DBG("\n");
364 
365     /* Activate DM9000 */
366     /* RX enable */
367     DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
368     /* Enable TX/RX interrupt mask */
369     DM9000_iow(DM9000_IMR, IMR_PAR); 
370 
371     i = 0;
372     while (!(dm9000_phy_read(1) & 0x20)) {  /* autonegation complete bit */
373         udelay(1000);
374         i++;
375         if (i == 10000) {
376             printf("could not establish link\n");
377             return 0;
378         }
379     }
380 
381     /* see what we've got */
382     lnk = dm9000_phy_read(17) >> 12;
383     printf("operating at ");
384     switch (lnk) {
385     case 1:
386         printf("10M half duplex ");
387         break;
388     case 2:
389         printf("10M full duplex ");
390         break;
391     case 4:
392         printf("100M half duplex ");
393         break;
394     case 8:
395         printf("100M full duplex ");
396         break;
397     default:
398         printf("unknown: %d ", lnk);
399         break;
400     }
401     printf("mode\n");
402     return 0;
403 }

299行 函数DM9000_reset()是对dm9000A重置 301行 函数dm9000_probe()分别从寄存器VID、PID读取厂家ID、产品ID 305行 读取DM9000A的 ISR寄存器,根据bite[6:7]的值来决定最终从DM9000A中读取数位数,并将对应的函数设置到db->outblk和db->inblk这两个变量,最终上层服务想收发数据就通过这两个函数,对于16位模式,就分别赋值dm9000_outblk_16bit、dm9000_inblk_16bit;db->rx_status该函数用于从DM9000A中读取网卡的状态信息和数据包的长度,对于16位模式会赋值为dm9000_rx_status_16bit 336~350行 对DM9000A进行初始化配置 355~358行 将mac地址写入到DM9000A的PAR寄存器 367行 使能数据接收 369行 使能SRAM的读/写指针在指针地址超过SRAM的大小时自动跳回起始位置 382行 读取phy寄存器DSCSR,打印当前网口的带宽

通过读 bit[15:12]来看经过自动协商后选择的是哪一种模式。网卡自动协商完成后,结果将被写到该位。若该位为 1,意味着操作 1 模式是 100M 全双工模式。

5. 数据的发送

发送流程

  1. 清中断,ISR寄存器bit[1] = 1
  2. 发送写操作,操作MWCMD
  3. 通过DM9000_DATA写入数据
  4. 设置数据帧的长度 TXPLL、TXPLH
  5. 发送发送请求,TCR
  6. 等待数据发送完毕,轮训检查NSR
  7. 清中断,ISR寄存器bit[1] = 1

网卡数据的发送函数是dm9000_send()

405 /*
406   Hardware start transmission.
407   Send a packet to media from the upper layer.
408 */

409 static int dm9000_send(struct eth_device *netdev, void *packet, int length)
410 
{
411     int tmo;
412     struct board_info *db = &dm9000_info;
413 
414     DM9000_DMP_PACKET(__func__ , packet, length);
415 
416     DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
417                                                                                                                                               
418     /* Move data to DM9000 TX RAM */
419     DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
420 
421     /* push the data to the TX-fifo */
422     (db->outblk)(packet, length);
423 
424     /* Set TX length to DM9000 */
425     DM9000_iow(DM9000_TXPLL, length & 0xff);
426     DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
427 
428     /* Issue TX polling command */
429     DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
430 
431     /* wait for end of transmission */
432     tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
433     while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
434         !(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {
435         if (get_timer(0) >= tmo) {
436             printf("transmission timeout\n");
437             break;
438         }
439     }
440     DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
441 
442     DM9000_DBG("transmit done\n\n");
443     return 0;
444 }

该函数的参数

struct eth_device *netdev:设备
void *packet   :发送数据包存放的内存的首地址
int length     :发送的数据包长度

414行 打开debug开关,该行会打印发送的数据包 416行 使能数据包发送,将寄存器ISR的bit[1]设置为1 419行 通过寄存器MWCMD写入一个地址,并向该地址对应的 SRAM 中写数据。执行写该指令之后,写指针会根据操作模式(8 位或 16 位)自动增加 1 或 2。422行 调用上一节db->outblk所赋值的函数将数据包发送的DM9000A的发送fifo中 425~426行 将发送数据包长度写入到寄存器TXPLL/TXPLH中,这两个寄存器分别对应低字节和高字节 429行 向寄存器TCR的bit[0]写入1,来请求发送数据,发送完毕该位自动清0 432~440行  通过向寄存器ISR的bit[1]写入1,来清楚发送标记位

其中发送函数dm9000_outblk_16bit() 定义如下:

159 static void dm9000_outblk_16bit(volatile void *data_ptr, int count)
160 
{
161  int i;
162  u32 tmplen = (count + 1) / 2;
163 
164  for (i = 0; i < tmplen; i++)
165   DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
166 }

164~165行 就是循环从地址DM9000_DATA读取数据并存储到data_ptr执行的内存中 此处我们看到每次都是从相同的地址读取数据,为什么不需要做地址偏移呢?答:寄存器MWCMD已经和我们说的很清楚了,写该指令之后,指写指针根据操作模式(8 位或 16 位)增 加 1 或 2。

6. 数据的接收

DM9000A的数据接收

464 static int dm9000_rx(struct eth_device *netdev)
465 
{
466     u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0];
467     u16 RxStatus, RxLen = 0;
468     struct board_info *db = &dm9000_info;
469 
470     /* Check packet ready or not, we must check
471        the ISR status first for DM9000A */

472     if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
473         return 0;
474 
475     DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
476 
477     /* There is _at least_ 1 package in the fifo, read them all */
478     for (;;) {
479         DM9000_ior(DM9000_MRCMDX);  /* Dummy read */
480 
481         /* Get most updated data,
482            only look at bits 0:1, See application notes DM9000 */

483         rxbyte = DM9000_inb(DM9000_DATA) & 0x03;
484 
485         /* Status check: this byte must be 0 or 1 */
486         if (rxbyte > DM9000_PKT_RDY) {
487             DM9000_iow(DM9000_RCR, 0x00);   /* Stop Device */
488             DM9000_iow(DM9000_ISR, 0x80);   /* Stop INT request */
489             printf("DM9000 error: status check fail: 0x%x\n",
490                 rxbyte);
491             return 0;
492         }
493 
494         if (rxbyte != DM9000_PKT_RDY)
495             return 0/* No packet received, ignore */
496 
497         DM9000_DBG("receiving packet\n");
498 
499         /* A packet ready now  & Get status/length */
500         (db->rx_status)(&RxStatus, &RxLen);
501 
502         DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen);
503 
504         /* Move data from DM9000 */
505         /* Read received packet from RX SRAM */
506         (db->inblk)(rdptr, RxLen);
507    
508         if ((RxStatus & 0xbf00) || (RxLen < 0x40)
509             || (RxLen > DM9000_PKT_MAX)) {
510             if (RxStatus & 0x100) {
511                 printf("rx fifo error\n");
512             }
513             if (RxStatus & 0x200) {
514                 printf("rx crc error\n");
515             }
516             if (RxStatus & 0x8000) {
517                 printf("rx length error\n");
518             }
519             if (RxLen > DM9000_PKT_MAX) {
520                 printf("rx length too big\n");
521                 dm9000_reset();
522             }
523         } else {
524             DM9000_DMP_PACKET(__func__ , rdptr, RxLen);
525 
526             DM9000_DBG("passing packet to upper layer\n");
527             NetReceive(NetRxPackets[0], RxLen);
528         }
529     }
530     return 0;
531 }

472行 DM9000A的寄存器ISR的bit[0]必须设置为1,否则无法接收数据 475行 将ISR的bit[0]设置为1 479行 读取寄存器MRCMDX, 以从接收 SRAM 中读数据;执行读取该指令之后,指向内部 SRAM的读指针不变。DM9000A 开始预取 SRAM 中数据到内部数据缓冲中 483~494行 从地址DM9000_DATA中读取数据,从SRAM中读取的第一个数据的bit[0]必须是1,否则出错 500行 通过函数指针db->rx_status读取网卡的状态和接收到的数据包的长度 506行 通过函数指针db->inblk从网卡中读取数据 527行 通过函数NetReceive()提交给上层协议栈

真正读取数据的函数是dm9000_inblk_16bit(); 定义如下:

static void dm9000_inblk_16bit(void *data_ptr, int count)
{
 int i;
 u32 tmplen = (count + 1) / 2;

 for (i = 0; i < tmplen; i++)
  ((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA);
}

原理类似于函数dm9000_outblk_16bit,不再重复。

由此可见,要分析DM9000A的数据收发的原理和流程,就要分析我们注册网卡的以下几个函数:

635   dev->send = dm9000_send;
636   dev->recv = dm9000_rx;

310   db->outblk    = dm9000_outblk_16bit;
311   db->inblk     = dm9000_inblk_16bit;


- END -
 精彩文章合集
所有原创
Linux驱动
粉丝问答
C语言
从0学ARM
计算机网络
一口Linux 写点代码,写点人生!
评论
  • MASSAGE GUN 筋膜枪拆解 今天给车子做保养,厂家送了一个筋膜枪。产品拿在手里还是挺有分量的。标价108元。通过海鲜市场一搜索,几十元不等,而且还是爆款。不多说,我们就来看看里面用了什么料,到底值几个钱。外观篇 首先给它来个开箱照,从外观看,确实还是很精致,一点都不逊色品牌产品。 从箱中取出筋膜枪,沉甸甸的。附上产品的各方位视角 产品的全家福 我装上球头,使用了一番,还真不赖,有不同的敲击速度和根据力度调节不同的档位。拆解篇 拿出我的螺丝套装,对产品开始进行拆解,首先
    zhusx123 2025-04-13 16:52 41浏览
  • 什么是车用高效能运算(Automotive HPC)?高温条件为何是潜在威胁?作为电动车内的关键核心组件,由于Automotive HPC(CPU)具备高频高效能运算电子组件、高速传输接口以及复杂运算处理、资源分配等诸多特性,再加上各种车辆的复杂应用情境等等条件,不难发见Automotive HPC对整个平台讯号传输实时处理、系统稳定度、耐久度、兼容性与安全性将造成多大的考验。而在各种汽车使用者情境之中,「高温条件」就是你我在日常生活中必然会面临到的一种潜在威胁。不论是长时间将车辆停放在室外的高
    百佳泰测试实验室 2025-04-10 15:09 161浏览
  •   效能评估系统:量化效能、驱动决策的关键利器   效能评估系统作为一种用于量化剖析、评判以及优化系统或组织效能的专业工具,借助数学建模、数据分析、仿真模拟等一系列技术手段,全方位地对目标系统在特定任务或环境下的表现进行评估,为科学决策提供坚实依据。下面从核心功能、技术架构、应用场景及发展趋势四个关键层面展开详细介绍。   应用案例   目前,已有多个效能评估系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润效能评估系统。这些成功案例为效能评估系统的推广和应用提供了有力支持。
    华盛恒辉l58ll334744 2025-04-10 11:35 20浏览
  •   天空卫星健康状况监测维护管理系统:全方位解析  在航天技术迅猛发展的当下,卫星在轨运行的安全与可靠至关重要。整合多种技术,实现对卫星的实时监测、故障诊断、健康评估以及维护决策,有力保障卫星长期稳定运转。  应用案例       系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合就可以找到。  一、系统架构与功能模块  数据采集层  数据处理层  智能分析层  决策支持层  二、关键技术  故障诊断技术  
    华盛恒辉l58ll334744 2025-04-10 15:46 177浏览
  • 相信很多小伙伴都用过下面这个MOS管开关电路,但是有多少小伙伴了解在MOS管开关过程中,输入电压、输出电压和MOS管上的电流都是怎么变化的?特别是输出端有大负载电容时,最大浪涌电流能到多少呢?今天小编专门写一篇文章,通过理论结合仿真的方式给大家分析下~首先建立一个电路图:假定电源电压V5=12V,内阻Rs=10毫欧;MOS管的导通与关闭由$V_6$控制;负载设定为100mF电容+$12\Omega$电阻。上升阶段当控制信号输出高电平时,$V_6$电压会逐渐上升,当电压上升到三极管$Q_3$的门槛
    龙猫讲电子 2025-04-11 23:01 48浏览
  • 迈向可持续未来的征程中,可再生能源已成为全球发展的基石。在可再生能源中,太阳能以其可及性和潜力脱颖而出。光伏(PV)逆变器是太阳能系统的核心,它严重依赖先进技术将太阳能电池板的直流电转换为可用的交流电。隔离栅极驱动器就是这样一种技术,它在提高这些系统的效率、安全性和可靠性方面发挥着至关重要的作用。了解隔离栅极驱动器隔离栅极驱动器是一种专用电路,可提供驱动功率晶体管(例如MOSFET或IGBT)所需的控制信号,同时确保控制侧和电源侧之间的电气隔离。这种隔离对于维护安全性、减少电磁干扰和防止高压环境
    腾恩科技-彭工 2025-04-11 16:16 43浏览
  • 华为Freebuds pro 耳机拆解 2020年双十一花了1000大洋买了华为的Freebuds pro,这个耳机的降噪效果真是杠杠的。完全听不到外边的噪音。几年后当我再次使用这款耳机的时候。发现左耳没带多久就自动断连了。后来查了小红书说耳机的电池没电了导致,需要重新配一只,华为售后不支持维修支持更换。而且配件的价格要好几百。真是欲哭无泪,还没用多久呢。后来百度了都说这个不是很好拆(没有好工具的前提下)。 虽然网上已经有很多拆解的视频和介绍了,今天我还是要拆解看看里面是怎么样的构造(暴力)。拿
    zhusx123 2025-04-12 23:20 35浏览
  •   海上电磁干扰训练系统:全方位解析      海上电磁干扰训练系统,作为模拟复杂海上电磁环境、锻炼人员应对电磁干扰能力的关键技术装备,在军事、科研以及民用等诸多领域广泛应用。接下来从系统构成、功能特点、技术原理及应用场景等方面展开详细解析。   应用案例   系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合就可以找到。   一、系统构成   核心组件   电磁信号模拟设备:负责生成各类复杂的电磁信号,模拟海上多样
    华盛恒辉l58ll334744 2025-04-10 16:45 276浏览
  • 文/Leon编辑/侯煜‍关税大战一触即发,当地时间4月9日起,美国开始对中国进口商品征收总计104%的关税。对此,中国外交部回应道:中方绝不接受美方极限施压霸道霸凌,将继续采取坚决有力措施,维护自身正当权益。同时,中国对原产于美国的进口商品加征关税税率,由34%提高至84%。随后,美国总统特朗普在社交媒体宣布,对中国关税立刻提高至125%,并暂缓其他75个国家对等关税90天,在此期间适用于10%的税率。特朗普政府挑起关税大战的目的,实际上是寻求制造业回流至美国。据悉,特朗普政府此次宣布对全球18
    华尔街科技眼 2025-04-10 16:39 188浏览
  • 行业变局:从机械仪表到智能交互终端的跃迁全球两轮电动车市场正经历从“功能机”向“智能机”的转型浪潮。数据显示,2024年智能电动车仪表盘渗透率已突破42%,而传统LED仪表因交互单一、扩展性差等问题,难以满足以下核心需求:适老化需求:35%中老年用户反映仪表信息辨识困难智能化缺口:78%用户期待仪表盘支持手机互联与语音交互成本敏感度:厂商需在15元以内BOM成本实现功能升级在此背景下,集成语音播报与蓝牙互联的WT2605C-32N芯片方案,以“极简设计+智能交互”重构仪表盘技术生态链。技术破局:
    广州唯创电子 2025-04-11 08:59 228浏览
  •     电气间隙是指两个带电体在空气中的最短距离。导体、电介质(空气),最短距离,就是这个术语的要素了。        (图源:TI)    电气间隙是由安装类别决定的,或者更本质地说,是瞬态过电压的最大值来决定的,而不是工作电压的高低。安装类别见协议标准第007篇,瞬态过电压另见协议标准第009篇。    实际设计中怎么确定电气间隙?可以按照CAT,工作电压和绝缘等级来定。 
    电子知识打边炉 2025-04-13 18:01 43浏览
  • 技术原理:非扫描式全局像的革新Flash激光雷达是一种纯固态激光雷达技术,其核心原理是通过面阵激光瞬时覆盖探测区域,配合高灵敏度传感器实现全局三维成像。其工作流程可分解为以下关键环节:1. 激光发射:采用二维点阵光源(如VCSEL垂直腔面发射激光器),通过光扩散器在单次脉冲中发射覆盖整个视场的面阵激光,视场角通常可达120°×75°,部分激光雷达产品可以做到120°×90°的超大视场角。不同于传统机械扫描或MEMS微振镜方案,Flash方案无需任何移动部件,直接通过电信号控制激光发射模式。2.
    robolab 2025-04-10 15:30 233浏览
  • 背景近年来,随着国家对资源、能源有效利用率的要求越来越高,对环境保护和水处理的要求也越来越严格,因此有大量的固液分离问题需要解决。真空过滤器是是由负压形成真空过滤的固液分离机械。用过滤介质把容器分为上、下两层,利用负压,悬浮液加入上腔,在压力作用下通过过滤介质进入下腔成为滤液,悬浮液中的固体颗粒吸附在过滤介质表面形成滤饼,滤液穿过过滤介质经中心轴内部排出,达到固液分离的目的。目前市面上的过滤器多分为间歇操作和连续操作两种。间歇操作的真空过滤机可过滤各种浓度的悬浮液,连续操作的真空过滤机适于过滤含
    宏集科技 2025-04-10 13:45 163浏览
  • 由西门子(Siemens)生产的SIMATIC S7 PLC在SCADA 领域发挥着至关重要的作用。在众多行业中,SCADA 应用都需要与这些 PLC 进行通信。那么,有哪些高效可行的解决方案呢?宏集为您提供多种选择。传统方案:通过OPC服务器与西门子 PLC 间接通信SIMATIC S7系列的PLC是工业可编程控制器,能够实现对生产流程的实时SCADA监控,提供关于设备和流程状态的准确、最新数据。S7Comm(全称S7 Communication),也被称为工业以太网或Profinet,是西门
    宏集科技 2025-04-10 13:44 198浏览
我要评论
7
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦