单片机驱动DM9000网卡芯片(详细调试过程)【上】
DM9000网卡驱动分析

DM9000网卡驱动分析#include <linux/module.h>#include <linux/ioport.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <linux/crc32.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/dm9000.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/irq.h>#include <asm/delay.h>#include <asm/irq.h>#include <asm/io.h>#if defined(CONFIG_ARCH_S3C2410)#include <mach/regs-mem.h>#endif#include "dm9000.h"/* Board/System/Debug information/definition ---------------- *///在EEPROM或PHY地址寄存器中要选择内部PHY,那么7-6位强制为01#define DM9000_PHY 0x40 /* PHY address 0x01 */#define CARDNAME "dm9000"#define DRV_VERSION "1.31"/** Transmit timeout, default 5 seconds.*///传输超时时间设定,当传输超时时调用函数dm9000_timeout(struct net_device *dev) static int watchdog = 5000;module_param(watchdog, int, 0400);//在驱动程序加载时可以重新设定watchdog MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");/* DM9000 register address locking.** The DM9000 uses an address register to control where data written* to the data register goes. This means that the address register* must be preserved over interrupts or similar calls.** During interrupt and other critical calls, a spinlock is used to* protect the system, but the calls themselves save the address* in the address register in case they are interrupting another* access to the device.** For general accesses a lock is provided so that calls which are* allowed to sleep are serialised so that the address register does* not need to be saved. This lock also serves to serialise access* to the EEPROM and PHY access registers which are shared between* these two devices.*//* The driver supports the original DM9000E, and now the two newer* devices, DM9000A and DM9000B.*/enum dm9000_type {TYPE_DM9000E, /* original DM9000 */TYPE_DM9000A,TYPE_DM9000B};/* Structure/enum declaration ------------------------------- */typedef struct board_info {//cmd脚决定了数据数据口还是地址索引void __iomem *io_addr; /* Register I/O base address */void __iomem *io_data; /* Data I/O address */u16 irq; /* IRQ */u16 tx_pkt_cnt;//当前待传输的数据包的数量,最多两个//第二个数据包长度存于此处,第二个数据包写入网卡SRAM中后要释放skb u16 queue_pkt_len;u16 queue_start_addr;u16 dbug_cnt;u8 io_mode; /* 0:word, 2:byte */u8 phy_addr;u8 imr_all;//在probe()函数中有db->flags = pdata->flags;unsigned int flags;unsigned int in_suspend :1;int debug_level;enum dm9000_type type;void (*inblk)(void __iomem *port, void *data, int length);void (*outblk)(void __iomem *port, void *data, int length);void (*dumpblk)(void __iomem *port, int length);struct device *dev; /* parent device */struct resource *addr_res; /* resources found */struct resource *data_res;//物理地址struct resource *addr_req; /* resources requested */struct resource *data_req;//I/O映射后的虚拟地址struct resource *irq_res;struct mutex addr_lock; /* phy and eeprom access lock *///在probe函数中初始化,处理函数dm9000_poll_work,链路连接状态改变 struct delayed_work phy_poll;struct net_device *ndev;spinlock_t lock;struct mii_if_info mii;u32 msg_enable;//网络入口信息} board_info_t;/* debug code */#define dm9000_dbg(db, lev, msg...) do { \if ((lev) < CONFIG_DM9000_DEBUGLEVEL && \(lev) < db->debug_level) { \dev_dbg(db->dev, msg); \} \} while (0)static inline board_info_t *to_dm9000_board(struct net_device *dev){//存取私有数据指针专用函数return netdev_priv(dev);}/* DM9000 network board routine ---------------------------- */static voiddm9000_reset(board_info_t * db){dev_dbg(db->dev, "resetting device\n");//NCR(00H):网络控制寄存器/* RESET device */writeb(DM9000_NCR, db->io_addr);udelay(200);writeb(NCR_RST, db->io_data);udelay(200);}/** Read a byte from I/O port*/static u8ior(board_info_t * db, int reg){//对网卡寄存器进行操作要先写该寄存器的偏移地址writeb(reg, db->io_addr);return readb(db->io_data);}/** Write a byte to I/O port*/static voidiow(board_info_t * db, int reg, int value){writeb(reg, db->io_addr);writeb(value, db->io_data);}/* routines for sending block to chip */static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count) {writesb(reg, data, count);}static void dm9000_outblk_16bit(void __iomem *reg, void *data, int count) {writesw(reg, data, (count+1) >> 1);}static void dm9000_outblk_32bit(void __iomem *reg, void *data, int count) {writesl(reg, data, (count+3) >> 2);}/* input block from chip to memory */static void dm9000_inblk_8bit(void __iomem *reg, void *data, int count) {readsb(reg, data, count);}static void dm9000_inblk_16bit(void __iomem *reg, void *data, int count) {readsw(reg, data, (count+1) >> 1);}static void dm9000_inblk_32bit(void __iomem *reg, void *data, int count) {readsl(reg, data, (count+3) >> 2);}/* dump block from chip to null *///读出没用的数据,为了改变网卡SRAM FIFO的数据指针,static void dm9000_dumpblk_8bit(void __iomem *reg, int count){int i;int tmp;for (i = 0; i < count; i++)tmp = readb(reg);}static void dm9000_dumpblk_16bit(void __iomem *reg, int count){int i;int tmp;count = (count + 1) >> 1;for (i = 0; i < count; i++)tmp = readw(reg);}static void dm9000_dumpblk_32bit(void __iomem *reg, int count){int i;int tmp;count = (count + 3) >> 2;for (i = 0; i < count; i++)tmp = readl(reg);}/* dm9000_set_io** select the specified set of io routines to use with the* device*///设定I/O线宽static void dm9000_set_io(struct board_info *db, int byte_width) {/* use the size of the data resource to work out what IO* routines we want to use*/switch (byte_width) {case 1:db->dumpblk = dm9000_dumpblk_8bit;db->outblk = dm9000_outblk_8bit;db->inblk = dm9000_inblk_8bit;break;case 3:dev_dbg(db->dev, ": 3 byte IO, falling back to 16bit\n");case 2:db->dumpblk = dm9000_dumpblk_16bit;db->outblk = dm9000_outblk_16bit;db->inblk = dm9000_inblk_16bit;break;case 4:default:db->dumpblk = dm9000_dumpblk_32bit;db->outblk = dm9000_outblk_32bit;db->inblk = dm9000_inblk_32bit;break;}}/*延时调度。
DM9000驱动移植详解及问题点

OK6410、2.6.36内核移植,dm9000 驱动移植,详细!分类:嵌入式学习Linux学习2012-04-27 00:54 3004人阅读评论(7) 收藏举报interfaceccompressionresourcesstructtable还是先来吐槽:本来我是在上一个星期的周末已经把Linux2.6.34.11 的驱动已经成功的移植到,OK6410 的开发板上的,并且能够启动主机上的NFS 根文件系统,可是我在周一的时候,开始学习LCD 的驱动程序,在修改内核文件的时候,有几处错误修改,将原来自己做的2.6.34.11 的内核源码搞的乱七八糟的,在这里还是自己在修改内核的时候没有提注重注释,并且没有记录下来自己的操作步骤,以至于我没办法,恢复2.6.34 的内核,所以也就只能重新先开始最基础的内核移植了。
这次我选择的是2.6.36.2 的内核,谁知到一开始移植就出现一大堆问题。
在这里我不得不说,飞凌开发人员对内核修改的代码,管理真的是太扯了,自己在注销任何一个设备是没有一点点注释,就把这个设备原有的线性地址分配给其它设备了,让我让我们这些菜鸟干看着一大堆的报错信息顶个什么用,真的是伤不起。
好了不乱扯了,现在开始记录。
我的开发环境是:VMware Ubuntu 10.10 。
OK6410 A版256M+2G 的开发板。
主机系统:XP。
Uboot:飞凌提供的Uboot。
参考内核:飞凌提供的Forlinx 的2.6.36.2 内核操作步骤以下./ 均代表你的内核根目录1、修改./Makefile191 ARCH ?=arm // 指定cpu类型,arm后面不要有空格,要不然编译是会提醒ARCH 不能为一个目录192 CROSS_COMPILE ?=/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux- // 指定交叉编译器的路径,按照你自己的进行指定路径2、先来说说nand flash 的驱动涉及到的文件:MTD 通用nand flash 驱动程序位置:./drivers/mtd/nand/.nand_base.cNAND Flash 的platform 设备信息: ./drivers/mtd/nand/s3c_nand.c有了上面的依赖驱动依赖程序、接下来修改./arch/arm/mach-s3c64xx/mach-smdk6410.c 1) nandflash 驱动,修改方法加载头文件[cpp]view plaincopyprint?1.#include <linux/mtd/mtd.h>2.#include <linux/mtd/partitions.h>3.#include <plat/nand.h> //这些头文件放在./arch/arm/plat-samsung/include/ 下面添加nand 结构体[cpp]view plaincopyprint?1.// add by acanoe first2.extern void s3c64xx_reserve_bootmem(void); //add by acanoe3.4.5.struct mtd_partition ok6410_nand_part[] = {6. {7. .name = "Bootloader",8. .offset = 0,9. .size = (1 * SZ_1M),10. .mask_flags = MTD_CAP_NANDFLASH,11. },12. {13. .name = "Kernel",14. .offset = (1 * SZ_1M),15. .size = (5*SZ_1M) ,16. .mask_flags = MTD_CAP_NANDFLASH,17. },18. {19. .name = "User",20. .offset = (6 * SZ_1M),21. .size = (120*SZ_1M) ,22. },23. {24. .name = "File System",25. .offset = MTDPART_OFS_APPEND,26. .size = MTDPART_SIZ_FULL,27. }28.};29.30.31.static struct s3c2410_nand_set ok6410_nand_sets[] = {32. [0] = {33. .name = "nand",34. .nr_chips = 1,35. .nr_partitions = ARRAY_SIZE(ok6410_nand_part),36. .partitions = ok6410_nand_part,37. },38.};39.40.41.static struct s3c2410_platform_nand ok6410_nand_info = {42. .tacls = 25,43. .twrph0 = 55,44. .twrph1 = 40,45. .nr_sets = ARRAY_SIZE(ok6410_nand_sets),46. .sets = ok6410_nand_sets,47.};48.//add by acanoe first修改 smdk6410_devices[] __initdata = {对照这个结构体将那些进行修改,注意 by acanoe 的语句为修改重点。
fpga驱动dm9000

DM90001.总体介绍该DM9000 是一款完全集成的和符合成本效益单芯片快速以太网MAC 控制器与一般处理接口,一个10/100M 自适应的PHY 和4K DWORD 值的SRAM 。
它的目的是在低功耗和高性能进程的3.3V 与5V 的支持宽容。
DM9000 还提供了介质无关的接口,来连接所有提供支持介质无关接口功能的家用电话线网络设备或其他收发器。
该DM9000 支持8 位,16 位和32 -位接口访问内部存储器,以支持不同的处理器。
DM9000 物理协议层接口完全支持使用10MBps 下3 类、4 类、5 类非屏蔽双绞线和100MBps 下5 类非屏蔽双绞线。
这是完全符合IEEE 802.3u 规格。
它的自动协调功能将自动完成配置以最大限度地适合其线路带宽。
还支持IEEE 802.3x 全双工流量控制。
这个工作里面DM9000 是非常简单的,所以用户可以容易的移植任何系统下的端口驱动程序。
2.内部寄存器DM9000(A)包含一系列可被访问的控制状态寄存器,这些寄存器是字节对齐的,他们在硬件或软件复位时被设置成初始值。
以下为DM9000 的寄存器功能详解:NCR(00H):网络控制寄存器(Network Control Register )7:EXT_PHY:1 选择外部PHY,0 选择内部PHY,不受软件复位影响。
6:WAKEEN:事件唤醒使能,1 使能,0 禁止并清除事件唤醒状态,不受软件复位影响。
5:保留。
4:FCOL:1 强制冲突模式,用于用户测试。
3:FDX:全双工模式。
内部PHY 模式下只读,外部PHY 下可读写。
2-1:LBK:回环模式(Loopback)00 通常,01MAC 内部回环,10 内部PHY 100M 模式数字回环,11 保留。
0:RST:1 软件复位,10us 后自动清零。
NSR (01H):网络状态寄存器(Network Status Register )7:SPEED:媒介速度,在内部PHY 模式下,0 为100Mbps,1 为10Mbps。
DM9000网卡驱动

DM9000网卡驱动笔记网卡芯片为DM9000,ARM开发板用的是S3C6410,PC机装的是RED HAT ENTERPRISE LINUX 5.5.网卡接入soc上并没有接入到专门的驱动ic上,而是接入到了SROM的控制器上,所以需要配置的寄存器就需要配置SROM的寄存器。
S3C6410支持6页的SROM,DM9000的片选连接到了SROM控制的bank1,地址线连接的是addr2,也就是以后地址线上发出去的数据,第2位为0,数据为地址,第2位为1,数据为数据(这比较难理解)。
根据这样的地址数据的读写,操作DM9000中的寄存器,配置好网卡芯片,arm端封装一个arp请求包,在PC机上用tcpdump命令看能不能接收到如果能接受,网卡配置就算比较成功了附上源码:#include "up.h"#define INDEX (0x18000000)#define DATA (0x18000004)int (*printf)(char *, ...) = 0x57e11d4c;void (*udelay)(int ) = 0x57e00a98;struct ethhdr{unsigned char dest[6];unsigned char source[6];unsigned short type;};struct arphdr{unsigned short hw_type;unsigned short pro_type;unsigned char hw_len;unsigned char pro_len;unsigned short op;unsigned char src_mac[6];unsigned char src_ip[4];unsigned char dest_mac[6];unsigned char dest_ip[4];};void write_dm9000(unsigned char reg, unsigned short val); unsigned short read_dm9000(unsigned char reg);void init_dm9000();void send_data(unsigned char *buff, int len);int create_arp(unsigned char *buff);void *memset(void *s, int c, int len);unsigned short htons(unsigned short);int main(){/*set XM0bank1*/SROM_BW = (1 << 4);SROM_BC1 = (4 << 4) | (5 << 16);unsigned char buff[1024] = {0};int len = create_arp(buff);init_dm9000();send_data(buff, len);return 0;}void write_dm9000(unsigned char reg, unsigned short val) {*(unsigned char *)INDEX = reg;*(unsigned short *)DATA = val;}unsigned short read_dm9000(unsigned char reg) {*(unsigned char *)INDEX = reg;unsigned char c = *(unsigned char *)DATA;return c;}void init_dm9000(){//application notes//step 1: p22write_dm9000(0x1f, 0x00);//step 2:write_dm9000(0x00, 0x01);udelay(10);write_dm9000(0x00, 0x00);//step 3:write_dm9000(0x00, (1 << 3));//step 4:write_dm9000(0xff, 0xff);//step 5://step 6:write_dm9000(0x10, 0x11);write_dm9000(0x11, 0x22);write_dm9000(0x12, 0x33);write_dm9000(0x13, 0x44);write_dm9000(0x14, 0x55);write_dm9000(0x15, 0x66);//step 7://step 8:write_dm9000(0x01, (1 << 2) | (1 << 3) | (1 << 5));write_dm9000(0xfe, 0x3f);printf("3\n");//step 9://step 10:write_dm9000(0xff, 0xff);//step 11:write_dm9000(0x05, 0x01);//step 12:}void send_data(unsigned char *buff, int len){//p28://step 1:unsigned char c = read_dm9000(0xfe);if(c >> 7)printf("8 bit mode\n");elseprintf("16 bit mode\n");//step 2*(unsigned char*)INDEX = 0xf8;int tmp = (len + 1) >> 1;int i = 0;for(i=0; i<tmp; i++){*(unsigned short *)DATA = ((unsigned short*)buff)[i];}//step 3write_dm9000(0xfd, (len >> 8) & 0xff);write_dm9000(0xfc, len & 0xff);//step 4write_dm9000(0x02, 0x01);}int create_arp(unsigned char *buff){int len = 0;struct ethhdr *eth = (struct ethhdr *)buff;eth->dest[0] = 0xff;eth->dest[1] = 0xff;eth->dest[2] = 0xff;eth->dest[3] = 0xff;eth->dest[4] = 0xff;eth->dest[5] = 0xff;eth->source[0] = 0x11;eth->source[1] = 0x22;eth->source[2] = 0x33;eth->source[3] = 0x44;eth->source[4] = 0x55;eth->source[5] = 0x66;eth->type = htons(0x0806);struct arphdr *arp = (struct arphdr *)(buff + sizeof(struct ethhdr));arp->hw_type = htons(1);arp->pro_type = htons(0x0800); arp->hw_len = 6;arp->pro_len = 4;arp->op = htons(1);arp->src_mac[0] = 0x11;arp->src_mac[1] = 0x22;arp->src_mac[2] = 0x33;arp->src_mac[3] = 0x44;arp->src_mac[4] = 0x55;arp->src_mac[5] = 0x66;arp->src_ip[0] = 192;arp->src_ip[1] = 168;arp->src_ip[2] = 1;arp->src_ip[3] = 2;arp->dest_mac[0] = 0xff;arp->dest_mac[1] = 0xff;arp->dest_mac[2] = 0xff;arp->dest_mac[3] = 0xff;arp->dest_mac[4] = 0xff;arp->dest_mac[5] = 0xff;arp->dest_ip[0] = 192;arp->dest_ip[1] = 168;arp->dest_ip[2] = 1;arp->dest_ip[3] = 1;len = sizeof(struct ethhdr) + sizeof(struct arphdr);return len;}void *memset(void *s, int c, int len){int i = 0;for(i=0; i<len; i++)((unsigned char *)s)[i] = c;}unsigned short htons(unsigned short data){unsigned short ret = 0;ret = ((data >> 8) & 0xff) | ((data << 8) & 0xff00);return ret;。
dm9000a嵌入式网卡芯片故障调试技术相关

DM9000AEP调试的时候注意事项:1.首先要判断芯片的真假。
常见的方法是读取芯片的ID号。
当然有些工程师朋友的寄存器设置,以及时序设置有问题,读取芯片的ID号就会错。
读取ID是最基本的操作。
2.其次确认芯片无质量问题后,再确定是硬件,还是软件方面的问题。
一般软件方面,问题很容易解决。
硬件问题,需要注意:网络变压器的CT端,一般要接DM9000AEP的管脚2,9脚输出的2.5V,输出的时候一般要加一个稳压电容以及一个滤波电容,稳压电容至少200uf。
另外可以通过10M下是否有连接,来判断网络变压器是否连接正确。
不同的网络变压器,连接电路一般有差异,要注意兼容性。
3.其次发送和接收的几个50欧姆的电阻,电阻值一定要正确,如果不正确,会有丢包现象。
或者网络时断时续。
4.DM9000AEP的datasheet,必须要看,不看的工程师,自己瞎琢磨,一般会耽误时间的。
5.在驱动程序里最好要添加打印信息,打印信息可以反馈硬件的故障,对分析问题,解决问题很有帮助。
6.一般晶振,网络变压器,最好要买真货,如果买到假的,通常很难从电路上分析出问题,通常要经过很长时间的测试,才发现。
最好不要在电子市场买东西,假的东西很多。
亲身经历。
特别是Pluse的H1102很多假冒的。
7.网络变压器的49.9的电阻,不用接任何电源。
8.PWRST复位引脚,如果是通过GPIO控制,应该是下拉电阻,不是上拉电阻。
Davicom高速以太网路晶片1、10/100/1000M 超高速以太网络芯片DM9702 - 超高速以太网络三合一单芯片. (Coming Soon)2、10/100M 高速以太网络芯片DM9000A (DM9000AE/DM9000AEP) - DM9000A Ethernet Controller With General Processor Interface DM9000E - (SMSC LAN91C111 Equivalent)ISA接口高速以太网络三合一单芯片(含 MII / RMII 接口).DM9000K - 研发套件DM9601 - USB接口高速以太网络三合一单芯片.DM9102D - PCI 接口高速以太网络三合一单芯片(Auto-MDIX). 0.25umDM9102A - PCI 接口高速以太网络三合一单芯片. 0.35um3、10/100M 高速以太网络实体层收发器DM9161 - 低功率, 高速以太网络实体层收发器. 48-pin 0.35um.DM9161A - 低功率, 高速以太网络实体层收发器. Auto-MDIX. 48-pin 0.25um.DM9331 - 100M高速以太网络光纤实体层媒体转换器. 48-pin.DM9301 - 100M高速以太网络光纤实体层媒体转换器.4、10M 以太网络芯片DM9008 - ISA接口以太网络三合一单芯片(性价比优于RTL8019AS,CS8900A )DM9009 - 最经济, 最有效率PCI 接口以太网络三合一单芯片 (含MII/RMII/7-wired GPSI 接口).DM9081 - 最经济, 最有效率以太网络 (8+2) 集线器芯片.。
u_boot移植(七)之支持dm9000网卡

u_boot移植(七)之支持dm9000网卡上一节我们已经让uboot支持了Nand Flash操作,调试的时候我们用的串口下载数据到内存,大家会发现下载的速度很慢,这一节我们就让我们的uboot支持网络功能,通过网线来下载数据内存,速度会快很多。
我们先来看看uboot中网卡的初始化代码。
一、uboot中网卡初始化代码分析打开lib_arm/board.c文件,可以看到如下代码:可以看到,我们必须定义宏CONFIG_CMD_NET ,网卡初始化相关的代码才会编译进uboot。
记录一下,需要在include/configs/fsc100.h文件中定义CONFIG_CMD_NET。
接下来,我们来看看eth_initialize()这个函数是如何实现的。
可以看到,网卡的初始化最终调用了board_eth_init()函数,我们来看看board_eth_init()函数时如何实现的。
看到上面代码,我想很多人都很奇怪,这是什么写法?board_eth_init(bd_t *bis) __attribute__((weak,alias("__def_eth_init"))) ;这是GNU的对C语言的扩展语法:(1)alias 表示给board_eth_init()函数取了一个别名叫做__def_eth_init()函数,这样如果board_eth_init函数没有实现,编译器就会使用它的别名__def_eth_init()函数。
(2)weak表示board_eth_init()函数是一个弱符号。
在gcc的世界里,如果一个函数没有用__attribute__((weak))进行修饰,就表示这个符号是一个强符号。
当强符号和弱符号同时存在的时候,编译器就会使用弱符号。
也就是说当我们重新实现了board_eth_init()函数,编译器就会使用我们实现的函数。
现在的问题来了,该怎么实现这个函数呢?我们先来看看别人是怎么实现board_eth_init()这个函数的,然后我们依葫芦画瓢,就能实现自己的board_eth_init()函数了。
成功移植U-BOOT之---- DM9000网卡驱动

5、修改完成,开始编译
make TQ2440_config
make CROSS_COMPILE=arm-linux-
把编译后在顶层目录生成的 u-boot.bin 通过 SAMBA 拷出来,通过 JTAG 或 JLINK 烧到开发 板!
-------- -------------- -------
…… /*修改环境变量*/ #define CONFIG_ETHADDDR 00:01:02:03:04:05 #define CONFIG_NETMASK 255.255.255.0 #define CONFIG_IPADDR 192.168.0.61 #define CONFIG_SERVERIP 192.168.0.60
case 2: printf("10M case 4: printf("100M half duplex ");
break; case 8:
printf("100M full duplex "); break; default: printf("unknown: %d ", lnk); break; } p rintf("mo d e \n");
#endif
return 0; }
重新编译,再烧写,问题解除。
今天成功的在天嵌 TQ2440 的板子上完成了 U-BOOT -DM9000 网卡驱动的移植!
实验使用的是国嵌实验 4-2-4 修改过的源码(u-boot-basic.tar.gz) 废话不说,记笔记:
mkdir 4-2-4 cp u-boot-basic.tar.gz 4-2-4/ cd 4-2-4 tar zxvf u-boot-basic.tar.gz
DM9000网卡的基本工作原理

DM9000⽹卡的基本⼯作原理MAC:主要负责数据帧的创建,数据差错,检查,传送控制等。
PHY:物理接⼝收发器,当收到MAC过来的数据时,它会加上校验码,然后按照物理层的规则进⾏数据编码,再发送到传输介质上,接收过程则相反。
MII:媒体独⽴接⼝,“媒体独⽴”表明MAC⼀定情况下,任何类型的PHY设备都可以正常⼯作。
DM9000⽹卡部分函数实现:/*//实验步骤//初始化dm900//数据包发送//数据包接收*/#include "dm9000.h"#include "arp.h"#define DM_ADD (*((volatile unsigned short *)0x18000000))#define DM_DAT (*((volatile unsigned short *)0x18000004))/*Register*/#define MEM_SYS_CFG (*(volatile unsigned *)0x7E00F120)#define SROM_BW (*(volatile unsigned *)0x70000000)#define SROM_BC1 (*(volatile unsigned *)0x70000008)#define GPNCON (*(volatile unsigned *)0x7F008830) /* 中断相关寄存器 */#define EINT0CON0 (*(volatile unsigned *)0x7F008900)#define EINT0MASK (*(volatile unsigned *)0x7F008920)#define EINT0PEND (*(volatile unsigned *)0x7F008924)#define VIC0INTENABLE (*(volatile unsigned *)0x71200010)#define EINT7_VECTADDR (*(volatile unsigned *)0x71200104)#define VIC0ADDRESS *((volatile unsigned int *)0x71200f00)#define VIC1ADDRESS *((volatile unsigned int *)0x71300f00)u8 *buffer = &arpbuf;u8 host_mac_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};u8 mac_addr[6] = {9,8,7,6,5,4};u8 ip_addr[4] = {192,168,1,113};u8 host_ip_addr[4] = {192,168,1,101};u16 packet_len;void cs_init(){// MEM_SYS_CFGSROM_BW &= (~(1<<4)); //设置位宽度SROM_BW |= (1<<4);SROM_BC1 = (0x0<<28)|(0x0<<24)|(0x7<<16)|(0x0<<12)|(0x0<<8)|(0x0<<4)|(0x0<<0); //设置时序参考uboot ok6410的⽹卡⽚选位于bank1 }void int_init() //中断初始化{GPNCON &= (~(0x3<<14));GPNCON |= (0x2<<14);// EINT0PEND &= (~(0x1<<7));// EINT0PEND |= (0x1<<7);}void dm9000_reg_write(u16 reg,u16 data){DM_ADD = reg;DM_DAT = data;}u8 dm9000_reg_read(u16 reg){DM_ADD = reg;return DM_DAT;}void dm9000_reset(){dm9000_reg_write(DM9000_GPCR, GPCR_GPIO0_OUT);dm9000_reg_write(DM9000_GPR, 0);dm9000_reg_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));dm9000_reg_write(DM9000_NCR, 0);dm9000_reg_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));dm9000_reg_write(DM9000_NCR, 0);}void dm9000_probe(void){u32 id_val;id_val = dm9000_reg_read(DM9000_VIDL);id_val |= dm9000_reg_read(DM9000_VIDH) << 8;id_val |= dm9000_reg_read(DM9000_PIDL) << 16;id_val |= dm9000_reg_read(DM9000_PIDH) << 24;if (id_val == DM9000_ID) {printf("dm9000 is found !\n");return ;} else {printf("dm9000 is not found !\n");return ;}}void dm9000_init(){u32 i;/*⽚选(独⽴芯⽚)*/cs_init();/*中断初始化*/int_init();/*设备复位操作*/dm9000_reset();/*捕获dm9000*/dm9000_probe();/*MAC初始化*//* Program operating register, only internal phy supported */dm9000_reg_write(DM9000_NCR, 0x0);/* TX Polling clear */dm9000_reg_write(DM9000_TCR, 0);/* Less 3Kb, 200us */dm9000_reg_write(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);/* Flow Control : High/Low Water */dm9000_reg_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));/* SH FIXME: This looks strange! Flow Control */dm9000_reg_write(DM9000_FCR, 0x0);/* Special Mode */dm9000_reg_write(DM9000_SMCR, 0);/* clear TX status */dm9000_reg_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);/* Clear interrupt status */dm9000_reg_write(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);/*填充MAC地址*/for (i = 0; i < 6; i++)dm9000_reg_write(DM9000_PAR+i, mac_addr[i]);/*激活DM9000*/dm9000_reg_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);/* Enable TX/RX interrupt mask */dm9000_reg_write(DM9000_IMR, IMR_PAR);}void dm9000_tx(u8 *data,u32 length){u32 i;/*禁⽌中断*/dm9000_reg_write(DM9000_IMR,0x80);/*写⼊发送数据的长度*/dm9000_reg_write(DM9000_TXPLL, length & 0xff);dm9000_reg_write(DM9000_TXPLH, (length >> 8) & 0xff);/*写⼊待发送的数据*/DM_ADD = DM9000_MWCMD; // MWCMD是DM9000内部SRAM的DMA指针,根据处理器模式,写后⾃动增加for(i=0;i<length;i+=2){DM_DAT = data[i] | (data[i+1]<<8); //低8 ⾼8}/*启动发送*/dm9000_reg_write(DM9000_TCR, TCR_TXREQ);/*等待发送结束*/while(1){u8 status;status = dm9000_reg_read(DM9000_TCR);if((status&0x01)==0x00)break;}/*清除发送状态*/dm9000_reg_write(DM9000_NSR,0x2c);/*恢复中断使能*/dm9000_reg_write(DM9000_IMR,0x81);// printf("dm9000_tx");}#define PTK_MAX_LEN 1522u32 dm9000_rx(u8 *data){u8 status,len;u16 tmp;u32 i;/*判断是否产⽣中断,且清除*/if(dm9000_reg_read(DM9000_ISR) & 0x01)dm9000_reg_write(DM9000_ISR,0x01);elsereturn0;/*空读*/dm9000_reg_read(DM9000_MRCMDX);/*读取状态*/status = dm9000_reg_read(DM9000_MRCMD);/*读取包长度*/len = DM_DAT;/*读取包数据*/if(len<PTK_MAX_LEN){for(i=0;i<len;i+=2){tmp = DM_DAT;data[i] = tmp & 0x0ff;data[i+1] = (tmp>>8)&0x0ff; }}return len;}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
单片机驱动DM9000网卡芯片(详细调试过程)【上】2008年05月19日星期一 18:02和其它网卡芯片不同,DM9000系列网卡芯片在嵌入式开发板上很常见,尤其是有关ARM-Linux的开发板上的网络连接部分几乎都是采用该芯片完成的。
当然,其它网卡芯片,如RTL8019的应用也很常见,在很多开发板上得到应用然而RTL8019的介绍在网上可以找到非常详细的介绍,尤其是用单片机对其做底层驱动的介绍非常丰富。
下面的网站就介绍了用AVR驱动RTL8019网卡芯片的非常详细的过程,有兴趣的朋友可以参考一下。
http://members.home.nl/bzijlstra/software/examples/RTL8019as.htm AVR驱动RTL8019网卡芯片的详细介绍。
言归正传。
在网上也能找到许多关于DM9000网卡芯片的介绍,然而这些介绍大多是关于Linux或WinCE下的驱动程序或移植,很少有介绍单片机驱动DM9000的例子。
因此我在这里把我调试DM9000E的过程详细说明一下,仅供参考。
本文主要介绍单片机驱动DM9000E网卡芯片的详细过程。
从网卡电路的连接,到网卡初始化相关程序调试,再到ARP协议的实现,一步一步详细介绍调试过程。
如果有时间也会把UDP和TCP通讯实验过程写出来。
当然,会用单片机编写DM9000的驱动,再想编写ARM下的Linux的驱动就容易的多了。
在调试之前,应该先参考两份技术文档,可以从下面网站中下载。
DM9000E.pdf(芯片数据资料)和 DM9000 Application Notes Ver 1_22 061104.pdf(应用手册)或者DM9000 Datasheet VF03:/userfile/24247/DM9000-DS-F03-041906_1.pdfDM9000A Datasheet:/userfile/24247/DM9000A-DS-F01-101906.pdfDM9000 Application Notes V1.22/big5/download/Data%20Sheet/DM9000_Application_Notes_Ver_1 _22%20061104.pdf一、电路连接DM9000E网卡芯片支持8位、16位、32位模式的处理器,通过芯片引脚EEDO(65脚)和WAKEUP(79脚)的复位值设置支持的处理器类型,如16位处理器只需将这两个引脚接低电平即可,其中WAKEUP内部有60K下拉电阻,因此可悬空该引脚,或作为网卡芯片唤醒输出用。
其它型号请参考相应的数据手册。
图1 DM9000引脚如图所示,对处理器驱动网卡芯片来说,我们比较关心的有以下几个引脚:IOR、IOW、AEN、CMD(SA2)、INT、RST,以及数据引脚SD0-SD15-SD31和地址引脚SA4-SA9。
其中,地址引脚配合AEN引脚来选通该网卡芯片,对于大多数的应用来说没有意义,因为在我们的应用中一般只用一个网卡芯片,而这些地址引脚主要用于在多网卡芯片环境下选择其中之一。
DM9000工作的默认基地址为0x300,这里我们按照默认地址选择,将SA9、SA8接高电平,SA7-DA4接低电平。
多网卡环境可以根据TXD0-TXD3配置SA4-SA7来选择不同的网卡,这里不做介绍,有兴趣的朋友请参考应用手册和数据手册。
数据引脚SD0-SD31则根据前面所讲的配置处理器模式与处理器的数据总线进行选择连接即可,没用到的引脚悬空。
那么,除了地址、数据引脚外,剩下的与处理器有关引脚对我们来说及其重要了,而与处理器无关的引脚,只需按照应用手册连接即可。
IOR和IOW是DM9000的读写选择引脚,低电平有效,即低电平时进行读(IOR)写(IOW)操作;AEN是芯片选通引脚,低电平有效,该引脚为低时才能进行读写操作;CMD的命令/数据切换引脚,低电平时读写命令操作,高电平时读写数据操作。
图2 读时序图3 写时序这些引脚接口和其它单片机外围器件的引脚接口基本相同,其使用也一样。
对于有总线接口的单片机来说,如51系列,ARM等直接连接即可。
对于没有总线接口的来说,如AVR mega32等可以直接用I/O引脚模拟总线时序进行连接。
连接时要参考读写时序,如上图所示。
具体连接电路,有时间我再画出来,暂时先略了。
二、编写驱动程序在这,我使用C语言编写驱动程序,这需要非常注意一点,即处理器所用的C编译器使用“大端格式”还是“小端格式”,这可以在相应处理器的C编译器说明上找到。
一般比较常见的是小端格式。
而对于8位处理器来说,在编写驱动程序时,可以不考虑,但是在编写网络协议的时候,一定好考虑,因为网络协议的格式是大端格式,而大部分编译器或者我们习惯的是小端格式,这一点需要注意。
在DM9000中,只有两个可以直接被处理器访问的寄存器,这里命名为CMD端口和DATA端口。
事实上,DM9000中有许多控制和状态寄存器(这些寄存器在上一篇文章中有详细的使用说明),但它们都不能直接被处理器访问,访问这些控制、状态寄存器的方法是:(1)、将寄存器的地址写到CMD端口;(2)、从DATA端口读写寄存器中的数据;1、读、写寄存器其实,INDEX端口和DATA端口的就是由芯片上的CMD引脚来区分的。
低电平为INDEX端口,高电平为DATA端口。
所以,要想实现读写寄存器,就必须先控制好CMD引脚。
若使用总线接口连接DM9000的话,假设总线连接后芯片的基地址为0x800300(24根地址总线),只需如下方法:#define DM_ADD (*((volatile unsigned int *) 0x8000300))#define DM_CMD (*((volatile unsigned int *) 0x8000304))//向DM9000寄存器写数据void dm9000_reg_write(unsigned char reg, unsigned char data){udelay(20);//之前定义的微妙级延时函数,这里延时20usDM_ADD = reg;//将寄存器地址写到INDEX端口udelay(20);DM_CMD = data;//将数据写到DATA端口,即写进寄存器}//从DM9000寄存器读数据unsigned int dm9000_reg_read(unsigned char reg){udelay(20);DM_ADD = reg;udelay(20);return DM_CMD;//将数据从寄存器中读出}只得注意的是前面的两个宏定义DM_ADD和DM_CMD,定义的内容表示指向无符号整形变量的指针,在这里0x800300是DM9000命令端口的地址,对它的赋值操作就相当于把数据写到该地址中,即把数据写到DM9000的命令端口中。
读的道理也一样。
这是一种很常见的宏定义,一般在处理器中定义通用寄存器也是这样定义的。
若没有总线接口的话,可以使用IO口模拟总线时序的方法实现寄存器的读写。
这里只说明实现步骤。
首先将处理器的I/O端口与DM9000的IOR等引脚直接相连(电平匹配的情况下),又假设已经有宏定义“IOR”I/O端口控制DM9000的IOR引脚,其它端口控制DM9000引脚的命名相同,“PIO1”(根据处理器情况,可以是8位、16位或32位的I/O端口组成)控制数据端口。
这样宏命名更直观些。
写寄存器的函数如下:void dm9000_reg_write(unsigned char reg, unsigned char data){PIO1 = reg;AEN = 0;CMD = 0;IOR = 1;IOW = 0;udelay(1);AEN = 1;IOW = 1;udelay(20);PIO1 = data;AEN = 0;CMD = 0;IOR = 1;IOW = 0;udelay(1);AEN = 1;IOW = 1;}读寄存器的写法类似,这里就略一下了。
这一过程看上去有些复杂,呵呵,其实执行起来也蛮有效率的,执行时间差不多。
这种模拟总线时序的方式实际并不复杂,只是把总线方式下自动执行的过程手动的执行了一遍而已。
在DM9000中,还有一些PHY寄存器,也称之为介质无关接口MII(Media Independent Interface)寄存器。
对这些寄存器的操作会影响网卡芯片的初始化和网络连接,这里不对其进行操作,所以对这些寄存器的访问方法这里也略了(在上篇文章中有介绍)。
操作不当反而使网卡不能连接到网络。
至此,我们已经写好了两个最基本的函数:dm9000_reg_write()和dm9000_reg_read(),以及前面的宏定义DM_ADD和DM_CMD。
下面将一直用到。
2、初始化DM9000网卡芯片。
初始化DM9000网卡芯片的过程,实质上就是填写、设置DM9000的控制寄存器的过程,这里以程序为例进行说明。
其中寄存器的名称宏定义在DM9000.H中已定义好。
注:一下函数中unsigned char为一个字节unsigned int为两个字节//DM9000初始化void DM9000_init(void){unsigned int i;IO0DIR |= 1 << 8;IO1CLR |= 1 << 8;udelay(500000);IO2SET |= 1 << 8;udelay(500000);IO1CLR |= 1 << 8;udelay(500000);/*以上部分是利用一个IO口控制DM9000的RST引脚,使其复位。
这一步可以省略,可以用下面的软件复位代替*/dm9000_reg_write(GPCR, 0x01);//设置 GPCR(1EH) bit[0]=1,使DM9000的GPIO3为输出。
dm9000_reg_write(GPR, 0x00);//GPR bit[0]=0 使DM9000的GPIO3输出为低以激活内部PHY。
udelay(5000);//延时2ms以上等待PHY上电。
dm9000_reg_write(NCR, 0x03);//软件复位udelay(30);//延时20us以上等待软件复位完成dm9000_reg_write(NCR, 0x00);//复位完成,设置正常工作模式。
dm9000_reg_write(NCR, 0x03);//第二次软件复位,为了确保软件复位完全成功。
此步骤是必要的。
udelay(30);dm9000_reg_write(NCR, 0x00);/*以上完成了DM9000的复位操作*/dm9000_reg_write(NSR, 0x2c);//清除各种状态标志位dm9000_reg_write(ISR, 0x3f);//清除所有中断标志位/*以上清除标志位*/dm9000_reg_write(RCR, 0x39);//接收控制dm9000_reg_write(TCR, 0x00);//发送控制dm9000_reg_write(BPTR, 0x3f);dm9000_reg_write(FCTR, 0x3a);dm9000_reg_write(RTFCR, 0xff);dm9000_reg_write(SMCR, 0x00);/*以上是功能控制,具体功能参考上一篇文章中的说明,或参考数据手册的介绍*/for(i=0; i<6; i++)dm9000_reg_write(PAR + i, mac_addr[i]);//mac_addr[]自己定义一下吧,6个字节的MAC地址/*以上存储MAC地址(网卡物理地址)到芯片中去,这里没有用EEPROM,所以需要自己写进去*//*关于MAC地址的说明,要参考网络相关书籍或资料*/dm9000_reg_write(NSR, 0x2c);dm9000_reg_write(ISR, 0x3f);/*为了保险,上面有清除了一次标志位*/dm9000_reg_write(IMR, 0x81);/*中断使能(或者说中断屏蔽),即开启我们想要的中断,关闭不想要的,这里只开启的一个接收中断*//*以上所有寄存器的具体含义参考上一篇文章,或参考数据手册*/}这样就对DM9000初始化完成了,怎么样,挺简单的吧。