2440+dm9000A裸机以太网通讯

发布者:RadiantBreeze最新更新时间:2021-11-04 来源: eefocus关键字:以太网通讯 手机看文章 扫描二维码
随时随地手机看文章

开头的话,好吧,我只能说写驱动程序的人都是疯子,疯子才能进入这个领域,或者一开始你没疯,后来你疯了。真的,当问题出现的时候,有可能你无数次相信硬件没问题,是自己的程序有问题,也可能最后真的是硬件本来就是坏的。或者你到最后都没搞清楚到底是硬件的问题还是软件的问题。(按理说现在程序界这么火,说明底层支持着的硬件还是很可靠的,去找自己的问题吧!我也不知道我在说什么。


进入正题。


第一部分,如果你不知道单片机和DM9000网卡的以太网通讯思路,请赶紧百度上查阅DM9000的以太网设计,如果直接看DM9000数据手册,真的会看死人。建议先看看整体思路,再然后有时间最好通看一下DM9000的数据手册。


第二部分,2440单片机的知识至少要掌握中断和串口(当然越多越好)。搞清楚中断入口程序的地址和中断函数的联系。


第三部分,DM9000这部分程序的编写。主要部分分三块。


第一块,DM9000的初始化


void DM9000_init(void)

{

uint32 i;

Test_DM9000AE();

IOSetInit();

dm9000_reg_write(DM9000_IMR, 0x80); //中断关闭

//初始化设置步骤: 1

dm9000_reg_write(DM9000_GPCR, 0x01); //设置 GPCR(1EH) bit[0]=1,使DM9000的GPIO3为输出。

dm9000_reg_write(DM9000_GPR,  0x00); //GPR bit[0]=0 使DM9000的GPIO3输出为低以激活内部PHY。

udelay(500); //延时2ms以上等待PHY上电。

//初始化设置步骤: 2

dm9000_reg_write(DM9000_NCR,  0x03); //软件复位

udelay(300); //延时20us以上等待软件复位完成

dm9000_reg_write(DM9000_NCR,  0x00); //复位完成,设置正常工作模式。

dm9000_reg_write(DM9000_NCR,  0x03); //第二次软件复位,为了确保软件复位完全成功。此步骤是必要的。

udelay(300);

dm9000_reg_write(DM9000_NCR,  0x00);

//初始化设置步骤: 3

dm9000_reg_write(DM9000_NSR,  0x2c); //清除各种状态标志位

dm9000_reg_write(DM9000_ISR,  0xbf); //清除所有中断标志位

//初始化设置步骤: 4

   

//初始化设置步骤: 5

for(i=0; i<6; i++)

dm9000_reg_write(DM9000_PAR + i, mac_addr[i]);//mac_addr[]自己定义一下吧,6个字节的MAC地址

//初始化设置步骤: 6

dm9000_reg_write(DM9000_NSR,  0x2c); //清除各种状态标志位

dm9000_reg_write(DM9000_ISR,  0x3f); //清除所有中断标志位

//初始化设置步骤: 7

dm9000_reg_write(DM9000_IMR, 0x81); //中断使能}


第二块,发送程序的编写

void DM9000_sendPcket(uint8 *datas, uint32 length)

{

uint32 len,i;

//uint8 tmp;

//Printf("发送数据rn");

dm9000_reg_write(DM9000_IMR,0x80); //先禁止网卡中断,防止在发送数据时被中断干扰

len = length; //把发送长度写入

    dm9000_reg_write(DM9000_TXPLH, (len>>8) & 0x0ff);

    dm9000_reg_write(DM9000_TXPLL, len & 0x0ff);

DM_ADD = DM9000_MWCMD;

    for(i=0; i    {

        udelay(2);

        DM_CMD = datas[i] | (datas[i+1]<<8);

    }

dm9000_reg_write(DM9000_TCR, 0x01); //发送数据到以太网上

    

    while(1)//等待数据发送完成

    {

    uint8 data;

    data = dm9000_reg_read(DM9000_TCR);//DM9000_NSR

    if((data&0x01) == 0x00) break;

    }

    /* tmp = dm9000_reg_read(DM9000_NSR);

   

    if((tmp & 0x01) == 0x04)

    {

    if((dm9000_reg_read(DM9000_TSR1)&0xfc) == 0x00)

    Printf("TSR1成功rn");

    else

    Printf("TSR1失败rn");   

    }

    else

    {

    if((dm9000_reg_read(DM9000_TSR2)&0xfc) == 0x00)

    Printf("TSR2成功rn");

    else

    Printf("TSR2失败rn");

    }

    */

    dm9000_reg_write(DM9000_NSR, 0x2c); //清除状态寄存器,由于发送数据没有设置中断,因此不必处理中断标志位

    dm9000_reg_write(DM9000_IMR, 0x81); //DM9000网卡的接收中断使能

//Printf("发送数据完成rn");

}


第三块,接受程序的编写,最难的就是接收程序了,因为这里涉及到中断,自己之前配置好外部中断,中断程序中接收DM9000的接收包。然后坑爹的是以太网帧开始位置的确定,到底从DM9000接收缓存的那个地方把数据复制到单片机内存中。手册上说的是(0x01,状态字节,长度低字节,长度高字节)这四个字节作为识别区,先用预读取指令读取缓冲区的字节,判断是0x00还是0x01,否则就复位DM9000网卡,尼玛坑爹就在这里啊,复位还是不好使啊。然后预读取完只能直接读取下面的字节了,但这样会导致读取的数据不对,可能读到的数据并不是以真正的目的物理地址开头的数据,可能错位,前移或者后移(我不知道是不是网卡初始化对不对)。所以我只能这么做了,直接检测0x01字节,加上判断目的物理地址是否正确来确定以太网帧的位置,以及这个帧是否是传给自己的。理论上这是可靠的。以下为代码。

void IOSetInit(void)

{

rGPFCON = (rGPFCON & (~(0x03<<14))) | (0x02<<14); //GPF7设置为EINT7

rEXTINT0 = (rEXTINT0 & (~(0x07<<28))) | (0x01<<28);

rEINTMASK = rEINTMASK & (~(0x01<<7));

ClearPending(BIT_EINT4_7);

pISR_EINT4_7 = (U32)Eint7_ISR;

rINTMSK = rINTMSK & (~(BIT_EINT4_7));

}

static void __irq Eint7_ISR(void)

{

uint32 i;

uint16 type,m,n;

//Printf("Eint7中断服务rn");

//VAR_RETURN VARRETURN;

//VAR_RETURN *var_re=&VARRETURN;

Buffer[12]=0,Buffer[13]=0;

len = receivepacket(Buffer);

//后面的可以自己添加,这里省略

}




uint32 receivepacket(uint8 *datas)

{

uint16 i,tmp,len=0,status=0;

uint8 ready=0;

 

//ready = 0; //希望读取到"01H"

//status = 0; //数据包状态

//len = 0;     //数据包长度

    if(dm9000_reg_read(DM9000_ISR) & 0x01)

    {

        dm9000_reg_write(DM9000_ISR, 0x01); //清除接收中断标志位

    }

    else return 0;

//else { Printf("你好rn");return 0;}

//DM_ADD=DM9000_MRCMDX;

//ready = DM_CMD; // 第一次读取,一般读取到的是 00H

    //Printf("预读取第一次:%xrn",ready);

//ready = DM_CMD ; // 第一次读取,一般读取到的是 01H

//Printf("预读取第二次:%xrn",ready);

    

    

    

    DM_ADD = DM9000_MRCMD;

   

    status=DM_CMD ;//读状态字节

    while((status&0xff)!=0x01)

    {

    status=DM_CMD ;

    

    }

    //status=(status&0xff00)>>8;

    

    len = DM_CMD; //读数据包长度

    

   

    //Printf("st=%x status=%x  len= %xrn",st,status,len);

    if( (len < 1522))//!(status & 0xbf) &&

    {

   

        for(i=0; i<6; i+=2)// 这个FOR语句是为了判断数据包是否传给自己,即检查MAC地址

        {

            //udelay(20);

            tmp = DM_CMD;

            datas[i] = tmp & 0x0ff;

            if((datas[i]!=0xff)&&(datas[i]!=mac_addr[i])) return 0;

            datas[i + 1] = (tmp >> 8) & 0x0ff;

            if((datas[i+1]!=0xff)&&(datas[i+1]!=mac_addr[i+1])) return 0;

        }

        //Printf("状态字:%xrn",status);

       

       

        for(i=6; i        {

            //udelay(20);

            tmp = DM_CMD;

            datas[i] = tmp & 0x0ff;

            datas[i + 1] = (tmp >> 8) & 0x0ff;

            

        }

  

        

    }

    else return 0;

// if(len > 1000) return 0;

//  if( (HON( ETHBUF->type ) != ETHTYPE_ARP) && (HON( ETHBUF->type ) != ETHTYPE_IP) )

// return 0;

 

   

return len;

}


第四部分:TCP/IP的知识(需要自己查资料看)。这里就是对接收到的以太网帧进行解封,和在发送数据之前对数据包进行封装。鉴于TCP协议稍复杂,我暂时实现UDP协议。UDP数据包发送之前,需要先检查本地是否有接收方IP地址所对应的物理地址,如果没有,需要先发送ARP请求包(广播),得到应答包以后解析应答包得到物理地址。然后在加上接收方目的物理地址发送加上数据的UDP包(非广播)。所以这里至少要写ARP请求包和发送ARP应答包的程序,UDP的封装和解封程序(如果用UDP协议的话),当然也可以用TCP协议。

首先是TCP/IP一些报头的结构体:


typedef struct eth_hdr //以太网头部结构,为了以后使用方便

{

uint8 d_mac[6];    //目的地址

uint8 s_mac[6];    //源地址

uint16 type;    //协议类型

}ETH_HDR;

 

typedef struct arp        //ARP首部结构

{

uint16 hwtype;      //硬件类型(1表示传输的是以太网MAC地址)

uint16 protocol; //协议类型(0x0800表示传输的是IP地址)

uint8 hwlen; //硬件地址长度(6)

uint8 protolen; //协议地址长度(4)

uint16 opcode; //操作(1表示ARP请求,2表示ARP应答)

uint8 smac[6]; //发送端MAC地址

uint8 sipaddr[4]; //发送端IP地址

uint8 dmac[6]; //目的端MAC地址

uint8 dipaddr[4]; //目的端IP地址

}ARP;

 

typedef struct ip     //IP首部结构

{

uint8 vhl;      //4位版本号4位首部长度(0x45)

uint8 tos; //服务类型(0)

uint16 len; //整个IP数据报总字节长度

uint16 ipid;            //IP标识

uint16 ipoffset;      //3位标识13位偏移

uint8 ttl;              //生存时间(32或64)

uint8 proto;          //协议(1表示ICMP,2表示IGMP,6表示TCP,17表示UDP)

uint16 ipchksum;    //首部校验和

uint8 srcipaddr[4];    //源IP

uint8 destipaddr[4];    //目的IP

}IP;

 

typedef struct tcp     //IP首部结构

{

uint16  sport;              //源端口号

uint16  dport; //目的端口号

uint32  sequencenum; //顺序号

uint32  acknowledgenum; //确认号

uint8   tcplength;          //低四位为TCP报头字(32位)的个数,高四位必须为0,是保留位

uint8   flags;    //低两位为保留位,高6位为标志位[2:7]依次为

                            //URG:紧急指针。用到的时候值为1,用来处理避免TCP数据流中断

                            //ACK:置1时表示确认号为合法,为0的时候表示数据段不包含确认信息,确认号被忽略。  

//PSH:置1时请求的数据段在接收方得到后就可直接送到应用程序,而不必等到缓冲区满时才传送。

//RST:用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。

//SYN:在连接请求中,SYN=1,ACK=0,连接响应时,SYN=1,ACK=1。

//FIN:用来释放连接,表明发送方已经没有数据发送了。

uint16  window; //指定关于发送端能传输的下一段的大小的指令,表示想收到的每个TCP数据段的大小。

uint16  tcpchksum; //TCP校验和

uint16  urgentpoint;  //紧急指针16位,紧急指针指出在本报文段中的紧急数据的位置,在URG标志设置了时才有效。

}TCP;

 

typedef struct udp              //UDP首部结构

{

uint16  sport;              //源端口号                        (34 35)

uint16 dport; //目的端口号                       (36 37)

uint16  length; //UDP数据包报总长度                (38 39)

uint16  udpchksum; //UDP校验和(可选项)                (40 41)

}UDP;


各层封装函数(解封函数就不贴了,比较灵活,不必要的情况下,可以简单判断一下就能知道各层是什么协议):

//这是一个物理层的程序,微数据加上MAC报头

//by hongfangyu 2016/1/9

 

#include "dm9000.h"

 

void MAC_pack(uint8 *datas,uint16 length,uint16 type)

{

ETH_HDR *MACBUF;

uint8 packhead[14];

MACBUF=(ETH_HDR *)&packhead;

memcpy(MACBUF->d_mac, host_mac_addr, 6);

[1] [2]
关键字:以太网通讯 引用地址:2440+dm9000A裸机以太网通讯

上一篇:mini2440 裸机编程 -led
下一篇:一起学mini2440裸机开发(五)--定时器0的基础实验

推荐阅读最新更新时间:2024-11-12 22:46

micro2440 按键驱动程序
my_buttons.c文件: #include linux/fs.h #include linux/poll.h #include linux/irq.h #include linux/interrupt.h #include mach/regs-gpio.h #include mach/hardware.h #include linux/miscdevice.h #define DEVICE_NAME mybuttons struct button_irq_desc { int irq; int pin; int pin_setting; int number; char *na
[单片机]
字符驱动编写小结(基于mini2440,LED驱动)
编程:需要什么功能(机制)、如何使用这些功能(策略) 作为驱动程序编写者,我们需要在所需的编程时间以及驱动程序的灵活性之间选择一个可接受的折中。读者可能奇怪于说驱动程序“灵活”,我们用这个词实际上是强调设备驱动程序的作用在于提供机制,而不是提供策略。 机制mechanism,策略policy。如果你看过《linux device drivers》,里面给出了大概的介绍。机制提供了干什么(do what),策略提供如何做(how to do)。驱动程序完成机制的功能,把策略的实现留给用户的应用程序。通常在机制中,驱动程序要完成打开,关闭,读写,控制等功能。这些都是设备使用时最基本的操作。而策略中就要实现一些高级的数据处
[单片机]
arm 2440中nandflash特定存储单元写入一个字节
目标描述: 需要构建一个nandflash_write()函数,入口参数有两个,一个是unsigned int 变量,一个是 unsigned long addr_for_write,然后nandflash_write()函数完成将这个变量写入到addr_for_write中。 解决办法: 第一步:判断所给的这个地址是否是字对齐,否则返回,因为不对齐的话操作起来很麻烦 第二步:选中nandflash芯片 第三步: 复制现成的nand.c文件到我的工程目录下,编译出现如此多的错误,如图 全是尼玛缺少identifier,什么情况啊?在nand.c本身所在工程目录下编译一个
[单片机]
micro2440利用LEDS与BUTTONS驱动实现按键控制led灯【开发总结】
最近玩友善之臂的micro2440,实现了按键控制led灯亮灭的功能。在这里总结一下,有什么错误希望高手们能指点一下,同时也希望能够帮到刚学驱动的新手。 首先贴出leds驱动程序: #include linux/miscdevice.h #include linux/delay.h #include asm/irq.h #include mach/regs-gpio.h #include mach/hardware.h #include linux/kernel.h #include linux/module.h #include linux/init.h #include lin
[单片机]
micro<font color='red'>2440</font>利用LEDS与BUTTONS驱动实现按键控制led灯【开发总结】
S3C2440 interrupt 从2440init.s到main分析
  这个问题困扰了我很久,2440中断到底是怎样一个怎样的机制? 自己花了很大的力气终于弄明白了,在这里和大家交流一下.   中断的实现是由硬件和软件机制结合工作的,把它们抽象出来 :由中断异常作为一个源点,在一定机制下,从表一跳至表二,再跳至表三, 表一: 表二: ^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00 HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4 表三:
[单片机]
S3C<font color='red'>2440</font> interrupt 从<font color='red'>2440</font>init.s到main分析
ARM-Linux s3c2440 之中断分析(二)
软件篇: 上一篇文章回顾了s3c2440的中断控制器原理的相关硬件知识,有了这个基础再来分析Linux中的软件分析方式,心里就有底了。面对浩瀚如海的Linux源代码,s3c2440的中断到底是怎样呢,如何处理,如何实现的呢?一步一步来揭开它神秘的面纱吧,当然需要从Linux内核源码入手! 在Linux中start_kernel()时会进行体系结构的初始化:init_IRQ(), 故名思议,其源代码如下: view plain copy void __init init_IRQ(void) { intirq; for(irq = 0; irq NR_IRQS; i
[单片机]
S3C2440 Linux驱动移植——AT24C02(EEPROM)驱动
开发板:TQ2440 内核:Linux 2.6.32 PC OS:Ubuntu 11.04 1.配置内核 打开I2C功能: 打开杂项设备,该选项打开后,EEPROM也就打开了。 2. 修改代码 修改文件: linux/arch/arm/mach-s3c2440/mach-smdk2440.c 增加如下代码片段: #include linux/i2c/at24.h static struct at24_platform_data at24c02 = { .byte_len = SZ_2K / 8, .page_size = 8, .flags = 0, };
[单片机]
S3C<font color='red'>2440</font> Linux驱动移植——AT24C02(EEPROM)驱动
mini2440的nor flash与nand flash启动过程区别
简介:一、调试经验;二、问答;三、ARM的nor flash与nand flash启动过程区别。 -------------------------------------------------------- 目标:祥读mini2440说明书 -------------------------------------------------------- 一、调试经验 1.一位老电脑科学家的提示:当你遇到怪问题时,重启是一种最简单的解决办法之一。因为操作系统本身有不完善之处,不管是微软视窗还是苹果电脑。 2.USB转口线出现乱码问题:说明串口线的功能和性能不稳定。可购买性能好的代替之。 二、问答 1.
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved