⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 arm.c

📁 2812 TCP/IP协议的实现
💻 C
📖 第 1 页 / 共 2 页
字号:
//--------------------------------------以太网驱动程序--------------------
#define HARDWARE_GLOBALS
#include "config.h"
#include "../include/cfg_net.h"


#define     NET_RST       0X00000040//0x20000000//------复位控制信号的管脚名称为P0.6[0x40=(1000000)B]
//*-------------------------------------------------------复位脚的管脚号,它与硬件有关在EASY2200板上是P0.6脚,所以 NET_RST=0X00000040

#define     NET_BASE_ADDR      0X83400000//0x20000000//------网卡基地址,特指实验室的开发板,教材P395有相关描述
//--------------------------------------------------------*它与硬件有关在EASY2200板上是0X83400000

//#include "rtl8019as.h"
EX_RAM uint16 iic=0;//------------------------------------????

union    REC_BUFF_UNION REC_BUFF[MAX_REC_BUFF];//---------定义接受缓冲区数组
/****************************************************************************
* 名称:WriteToNet()
* 功能:把数据写入RTL8019AS
* 入口参数: ADDR    写入地址//------------------8位地址
      WRITEDATA     写入数据//------------------16位数据
* 出口参数:无
****************************************************************************/

void    WriteToNet(uint8 ADDR_16,uint16 WRITEDATA)//--------把数据写入ARM的NET_BASE_ADDR+ADDR_1的单元中,即RTL8019AS的的ADDR_1寄存器中  
{
(*((volatile unsigned short *) NET_BASE_ADDR+ADDR_16))=WRITEDATA;//0x83400000//-----网卡寄存器是8位,为何用uint16的数据写入,尽管这样并不会产生问题,但这样是不是不严谨??????
//----------------------------------------------------------------------------------答:使用RTL8019AS的16位模式,故数据传送用16位更稳妥,不会因编译器不同,而使结果不同!
}

/****************************************************************************
* 名称:ReadFromNet()
* 功能:从RTL8019AS把数据读出
* 入口参数: ADDR    读出地址//------------------8位地址
* 出口参数: READDATA      读出数据//------------------16位数据
****************************************************************************/
uint16    ReadFromNet(uint8 ADDR_16)////-----网卡寄存器是8位,为何用uint16的数据写入,这样是不是不严谨??????
{
uint16 temp;
temp=(*((volatile unsigned short *) NET_BASE_ADDR+ADDR_16));//--------NET_BASE_ADDR=0x83400000//----unsigned short:16位
return (temp);
}
/**********************************************************************
**函数原型:      void    page(uchar pagenumber)
**入口参数:?uchar pagenumber: 要切换的页
**出口参数:    无
**返 回 值: 无      
**说      明: 选择页,可选择0,1,2三页,第四页ne000兼容芯片保留 
************************************************************************/
void page(uint8 pagenumber)//----此函数通过设置CR寄存器的Bit7~Bit6位选择pagenumber指定的页(即:页切换函数)  
{
uint8 temp;
temp=ReadFromNet(0);//command register
//temp=temp&0x3f;-----------------------------若这样改会产问题,任何对TXP的置位都会引起数据位重发
temp=temp&0x3B ;//注意txp位不能要-------------原因见上,详细原因见相关文档使temp(=CR)的Bit7~Bit6位清零
pagenumber=pagenumber <<6;//------------------将pagenumber的值左移至Bit7~Bit6位
temp=temp | pagenumber;//---------------------将temp(=CR)的Bit7~Bit6位设置成指定的页
WriteToNet(0,temp);//-------------------------将CR的Bit7~Bit6位设置成指定的页
//-------------------一个疑问:WriteToNet(uint8,unit16)函数要求第二个参数是16位数据,但temp是unit8类型?????????
//----------------------------这样是不是不严谨,我认为应为
}

/**********************************************************************
**函数原型:      void    SetMacID()
**入口参数:    *mac_ptr
**出口参数:    无
**返 回 值: 无            
**说      明: 设置芯片物理地址,物理地址已经存储在程序空间内 
************************************************************************/
void SetMacID(uint8 * mac_ptr)   
{
//把MAC地址写入MY——MAC——ID中
page(1);
WriteToNet(1 , *mac_ptr);
mac_ptr++;
WriteToNet(2 , *mac_ptr);
mac_ptr++;
WriteToNet(3 , *mac_ptr);
mac_ptr++;
WriteToNet(4 , *mac_ptr);
mac_ptr++;
WriteToNet(5 , *mac_ptr);
mac_ptr++;
WriteToNet(6 , *mac_ptr);
page(0);//---------------------------------切换到0页
}
/**********************************************************************
**函数原型:      void     Send_Packet(struct _pkst *TxdData)
**入口参数:    struct _pkst *TxdData :指向要发送数据的结构指针
**              
**出口参数:    无
**返 回 值: 无              
**说      明: 发送数据包,以太网底层驱动程序,所有的数据发送都要通过该程序
************************************************************************/
void Send_Packet(struct _pkst *TxdData)//------_pkst是一个结构体类型,其定义在cfg_net.h文件中  
{
static uint8 Tx_Buff_Sel=0;//-----------------缓冲区编号,static类型,第一次运行时初始化为0,但以后每次调用均是上一次调用后的值
//--------------------------------------------此变量实现的功能是:记住网卡正在发送的页.

struct _pkst *ExPtr;
//uint16 i;
uint8 *TEPTR;
union send_temp{
         uint16 words;
         uint8 bytes[2];
         }send_buff;
uint16 ii,length=0;//------------------ii用于记录要发送数据的总长度

//i=0;//记录发送是否小于60个字节
page(0);//切换至第0页
length=length+TxdData->length;//--------------------------------这两句可以合并成:
ExPtr=TxdData->STPTR;//-----------------TxdData的一个成员-------ExPtr=TxdData
while(ExPtr!=NULL)//计算出要发送的数据的总长度
    { 
    length=length+ExPtr->length;
    ExPtr=ExPtr->STPTR;
    }
ii=length;
Tx_Buff_Sel=Tx_Buff_Sel^1;//发送缓冲区的切换--------------------发送缓冲送共有两个,第个六页.故缓冲区编号范围是:0~1
//--------------------------------------------------------------0^1=1;1^1=0.通过异或可实现缓冲区切换

if(Tx_Buff_Sel)//------------------//Tx_Buff_Sel==1时
    {
    WriteToNet(0x09,0x40);     //设置发送页地址//------第一页:字节偏移量为:40~45
    }   
else//-----------------------------//Tx_Buff_Sel==0时
    {
    WriteToNet(0x09,0x46);     //设置发送页地址//------第二页:字节偏移量为:46~51
       }

WriteToNet(0x08,0x00);      //写入RSAR0 DMA起始地址低位read page address low
WriteToNet(0x0b,ii>>8);     //写入RSCR1 DMA 计数器高位read count high
WriteToNet(0x0a,ii&0x00ff);    //写入RSCR0 DMA 计数器低位read count low;
WriteToNet(0,0x12);      //启动DMA写write dma, page0
TEPTR=TxdData->DAPTR;//--------------------------字节类型指针TEPTR指向以太网头
for(ii=0;ii<(((TxdData->length)+1)/2);ii++)//----将以太网头写入网卡缓冲区
//-----------------------------------------------16位总线,一次要写一个16位字,故只要写(((TxdData->length)+1)/2)次
    {
    send_buff.bytes[0]=*TEPTR;//-----------------send_buff是send_temp类型(联合体),成员为两个:bytes,words
    TEPTR++;
    send_buff.bytes[1]=*TEPTR;
    TEPTR++;
    WriteToNet(0x10,send_buff.words);//---16位总线,所以一次要写一个16位字
    }
ExPtr=TxdData->STPTR;
while(ExPtr!=NULL)//----------------------这样处理的原因是:以太网帧的数据并不是存放在一个数组中,而是存放在一个链表中
    {//---------------------------------------一个疑问:如果长度是奇数,将多发送一个字节数据,如何处理这个问题????????
    TEPTR=ExPtr->DAPTR;//------------------------字节类型指针TEPTR指向以太网数据
    for(ii=0;ii<((ExPtr->length+1)/2);ii++)//----将以太网数据写入网卡缓冲区
     {
     send_buff.bytes[0]=*TEPTR;
     TEPTR++;
     send_buff.bytes[1]=*TEPTR;
     TEPTR++;
     WriteToNet(0x10,send_buff.words); 
     }
    ExPtr=ExPtr->STPTR;
    }
//如果少于60

/***************************************/
//以下为终止DMA操作
WriteToNet(0x0b,0x00); 
WriteToNet(0x0a,0x00);
WriteToNet(0x00,0x22);     //结束或放弃DMA操作

WriteToNet(0x07,0xff);//-----------------------------------清除中断标志
if(Tx_Buff_Sel)//------------------//Tx_Buff_Sel==1时//----这是设置网卡发送数据起始地址的命令,与DMA传送命令不同
    {
    WriteToNet(0x04,0x40);     //txd packet start;//---------第一页:字节偏移量为:40~45
    }
else//-----------------------------//Tx_Buff_Sel==0时//----第二页:字节偏移量为:46~51
    {
    WriteToNet(0x04,0x46);    //txd packet start;//----------这是设置网卡发送数据起始地址的命令,与DMA传送命令不同
       }
ii=length;
if(length<60)
    {
    //如果数据长度<60字节,设置长度为60字节
    ii=60;//-----------------------------------------------填充的内容是不确定的,在没有发生复位、溢出等特殊情况下,
    //-----------------------------------------------------填充的内容是前两个数据包对应位置的数据
    }

WriteToNet(0x06,ii>>8); //high byte counter
WriteToNet(0x05,ii&0x00ff);//low byte counter
WriteToNet(0x07,0xff);//-----------------------------------清除中断标志
WriteToNet(0x00,0x3e);         //to sendpacket;

/***************************************/
//重发数据的处理
for(length=0;length<6;length++) //最多重发6次
    {
    for(ii=0;ii<1000;ii++)
     {//检查CR寄存器的txp位是否为低,为1说明正在发送,为0说明发完或出错放弃
     if((ReadFromNet(0X00)&0x04)==0)  
      { break; }
           }
    if((ReadFromNet(0X04)&0x01)!=0)//表示发送成功,判断发送状态寄存器TSR,决定是否出错
    //-------------------------------ReadFromNet(0X04)&0x01)=1则发送成功,跳出循环
     {break;};
    WriteToNet(0x00,0x3e);         //to sendpacket;
    }

/**************************************/
//OS_EXIT_CRITICAL();
}

/**********************************************************************
**函数原型:      unsigned char * Rec_Packet()
**入口参数:?无
**出口参数:    返回数据指针     unsigned char * 
**返 回 值: NULL               没有新数据包
**                unsigned char *           接收到新数据包
**说      明: 查询是否有新数据包并接收进缓冲区
************************************************************************/
uint8 Rec_Packet() //-------------------通过RTL8019AS接收以太网包,调用Rec_Ethernet_Packed()处理以太网包
{
static uint8 REC_BUFF_NUM=0;
static uint8 bnry,curr;    //?可否只做局部?
static uint16 tmp[2];
//static uint16 crt=0;
uint16 * REC_BUFF_PTR_WORDS;
uint8 * REC_BUFF_PTR_BYTES;
uint8 i;
uint16 ii,length;
OS_ENTER_CRITICAL();

rea1:
page(0);
i=ReadFromNet(0X07);    //读取中断状态
if((i&0x90)!=0)//如果复位或益出就重新初试化
    {
    InitNic(0);
    OS_EXIT_CRITICAL();
    return(0);
    }
bnry=ReadFromNet(0X03);    //bnry page have read 读页指针//----------bnry:指向最后一个已读的页地址
page(1);
curr=ReadFromNet(0X07);    //curr writepoint 8019写页指针//----------curr:指向当前即将写的页地址的后边一个页地址
page(0);

if(curr==0)//-----------------curr肯定大于0x40,因为SRAM的地址范围是:0x4000~0x7fff,大小为16K=84页(每页256K)
    {
    OS_EXIT_CRITICAL();
    return(0); //读的过程出错
    }

bnry++;//bnry=bnry+1;//-----------------------------------由于bnry指向当前已经读取的页,故本次执行要读的页地址是bnry+1
//--------------------------------------------------------下面的程序是基于bnry编写的,故在执行前使用bnry++

if(bnry>0x7f)//---------------curr肯定小于0x7f,因为SRAM的地址范围是:0x4000~0x7fff,大小为16K=84页(每页256K)
    {
    bnry=0x4c;//--------------------bnry返回到PStart,实现缓冲区的循环利用
    //crt++;//统计内部16K循环了多少次(仿真器29次后出错)52、
    }

if(bnry!=curr) //此时表示有新的数据包在缓冲区里
       {     //在任何操作都最好返回page0
    if(REC_BUFF_NUM==MAX_REC_BUFF)//接收缓冲区号清零
     {
     REC_BUFF_NUM=0;//---------------------------------实现以太太网包缓冲区的循环使用,有点类似网卡中发送缓冲区的使用方法
     }
    REC_BUFF_PTR_WORDS=REC_BUFF[REC_BUFF_NUM].words;//设定接收缓冲区的起始地址,REC_BUFF[REC_BUFF_NUM].words是一个数组的首地址,详见变量的定义
    //----------------------------------REC_BUFF变量类型是REC_BUFF_UNION(以太网层的接收缓冲区数据结)
    //----------------------------------每个缓冲区大小为1536字节/768字/384双字        
  
    //==============================================================================================
    WriteToNet(0x09,bnry);//RSAR1写入读页地址的高字节 //-------------------
    WriteToNet(0x08,0x00);//RSAR0写入读页地址的低字节 //--将要传送的数据---
    WriteToNet(0x0b,0x00);//RSCR1写入读取字节计数高字节 //---的信息设置好----
    WriteToNet(0x0a,18);    //RSCR0写入读取字节计数高字节 //-------------------
    WriteToNet(0x00,0x0a);//启动Remote DMA读操作//--------将上面设置的信息传给DMA,并启动DAM控制器,
    //----------------------------------------------------也就是说下面指定次数(由RSCR1:RSCR0指定)的数据读写均由DMA负责传送
    //----------------------------------------------------10H~17H的8个地址是一样的,都可以用来做DMA端口,只要用其中的一个就可以了
    //----------------------------------------------------每次LPC2100向指定的DMA端口请求数据,网卡中的DMA控制器都会将数据发到指定端口

  
    //读取一包的前4个字节:4字节的8019头部//---------------8019会在接收到的帧的前面加上四个字节的关于所接收帧的说明
    //----------------------------------------------------每个字节的含义----0:接收状态;1:下一包的指针;2:本包长度低位;3:本包长度高位;
    //----------------------------------------------------RSR中是它的一份拷贝,详见文档RTL8019AS使用方法.doc
    for(i=0;i<2;i++)//------------------------------------将四个字节的帧的说明读到tmp[i]数组中
     {
     *REC_BUFF_PTR_WORDS=ReadFromNet(0x10);//----------10H~17H的8个地址是一样的,都可以用来做DMA端口,只要用其中的一个就可以了
     //------------------------------------------------我的理解是:ReadFromNet(0x10)将第一个字字放到低位,第二个字节放到高位
     tmp[i]=*REC_BUFF_PTR_WORDS;
     REC_BUFF_PTR_WORDS++;
     }//-----------------------------------------------每个字节的含义----0:接收状态;1:下一包的指针;2:本包长度低位;3:本包长度高位;
    //----------------------------------------------------字节0在RSR寄存器中有一份挎贝RSR: Receive Status Register (0CH; Type=R in Page0),详细说明见8019的文档
  
    //=======================================中止DMA操作
    WriteToNet(0x0b,0x00); //RSCR1写入读取字节计数高字节
    WriteToNet(0x0a,0x00); //RSCR0写入读取字节计数高字节
    WriteToNet(0x00,0x22); //结束或放弃DMA操作
  
    //=======================================
    tmp[1]=tmp[1]-4;//去掉4个字节的CRC//------------------tmp[1]中是包的长度,实现将长度减4
    //----------------------------------------------------???高低位是否颠倒???

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -