51单片机简单Ping的实现

发布者:WhisperingLight最新更新时间:2012-10-12 来源: 21IC 关键字:51单片机  Ping  DOS 手机看文章 扫描二维码
随时随地手机看文章

    Ping(Packet Internet Gopher分组网间网探测器)利用了ICMP(Internet Control Message Protocol互联网控制报文协议)协议的“回响”功能来实现主机/服务器是否有应答的测试。ICMP为路由器和主机提供了正常情况以外的通信,它是IP的一个完整的组成部分。ICMP包括降低传送速率的源站抑制报文、请求主机改变选路表的重定向报文以及主机可用来决定目的站是否可达的回送请求/回答报文。ICMP报文在IP数据报的数据区中传送。当主机/服务器接收到具有回响类型的ICMP报文时,就响应1个“回响应答”报文。本地机器收到该报文并确认之后即可认为该主机/服务器处于活动状态,从而本机与远程主机/服务器之间能够连通,也可以互相通信。
    仿照DOS下的ping命令并根据51单片机资源现状,我实现了一个简单的ping功能。它的使用方法如下:
    (1)单片机-->PC机 在Shell里使用“ping XXX.XXX.XXX.XXX”,如果连通,显示“Reply from XXX.XXX.XXX.XXX: bytes=32 TTL=XXX”,否则,显示“Request timed out.(XXX.XXX.XXX.XXX)”。
    (2)PC机-->单片机 按照DOS里的常规操作即可
    每个ping命令重复测试8次,即显示8次信息。
    注意到显示内容与PC机上稍有不同,这是由于此处ping工作在多任务单窗口环境下,为了区分响应发送源,有必要增加源IP地址信息。另外,由于51资源限制,取消了time参数(time是本机与对方主机往返一次所用时间)显示。具体简化内容如下:
    (1)只支持“ping+IP地址”命令格式,域名方式和其他可选项均不可用
    (2)固定32字节测试包
    (3)不计算本机与对方主机往返一次所用时间,测试用时为1到2秒
    总之,经过简化的ping能够完成最基本的连通测试功能。
   
    0            8         16                     31
    ------------------------------------------------
    | 类型(8或0) | 代码(0) |        校验和         |
    ------------------------------------------------
    |        标识符        |         序号          |
    ------------------------------------------------
    |                  可选数据                    |
    ------------------------------------------------
    |                    。。。                    |
    ------------------------------------------------
   
             图1 ICMP回送请求或回答报文格式
   
                     PingCycle
                        | 定时操作
                        V
    PingCmd  ----------------            --------------
    -------->|   PingRequest|----------->|            |
      命令   |              |    请求    |            |
             |              |            |            |
             |    A         |            |    B       |
             |              |            |            |
    <--------|PingEcho      |<-----------|PingAnswer  |
      回显   ----------------    应答    --------------
     
    图2 A Ping B 过程(全双工操作,反过来亦可,未画出反向过程)
   
    图1所示为ICMP回送请求/回答报文格式(即Ping包格式),在实现网卡驱动程序后,Ping的实现简化为填写报文(详见伪代码清单)。如图2所示,完整ping过程我主要用4个函数实现。Ping请求(PingRequest)、Ping应答(PingAnswer)、收到应答后回显(PingEcho)、定时操作(PingCycle)。
    PingRequest完成Ping请求。当输入Ping命令后,调用此函数,发出请求包。
    PingAnswer完成Ping应答。它工作在后台,每当收到发给自己IP的请求包就自动应答。
    PingEcho显示应答信息。每当收到应答就立即显示相关信息。
    PingCycle定时操作pingbuf记录缓冲区。每秒钟扫描一次,并根据当前情况和状态进行状态变迁。

    - ----------------------------------------------------
    | | 状态(status) | 次数(times) | 内存句柄(memhandle) |
    | ----------------------------------------------------
   N个|                    。。。                        |
    | ----------------------------------------------------
    | | 状态(status) | 次数(times) | 内存句柄(memhandle) |
    - ----------------------------------------------------
   
      N=MaxLenPingBuf
                 图3 pingbuf记录缓冲区格式

    IP地址要先转换成MAC地址才能在以太网上传输,如果ARP缓存里没有对应项,则需要较长时间查找(网络传输时间相对于CPU时间)。为了不阻塞进程,我设计了一个pingbuf记录缓冲区,如图3所示。此表1秒钟被查询一次,根据当前情况改变状态。它包括状态、次数、内存句柄三个域。“次数”字段指示测试剩余数,为0表示测完,同时改变状态为0以表明此记录项现在空闲。“内存句柄”字段保存ICMP报文缓冲区首址指针。“状态”字段记录状态号,含义如下:
    0---空闲
    1---已发出但无应答
    2---已发出且应答
    3---等ARP
    4---第一次准备发(用于同步1秒时钟)
    状态变迁图如图4所示。
   
    图4略,详见伪代码清单PingCycle,请自行画出状态图。
   
    这个实现里还使用了ping命令处理、IP地址转换、校验和计算等辅助程序,详见伪代码清单。IP协议使用统一的CheckSum算法计算和验证数据报的首部校验和。将首部视为一个16位的整数序列,并定义校验和是首部中所有16位整数的和的二进制反码。同样和数及补码也被定义使用二进制反码算法。所有TCP/IP协议的校验和计算和数据包的校验均由CheckSum子程序完成。不过需要注意的是TCP和UDP的校验需要加上伪头部。需要首部校验和计算及验证的包为:IP、ICMP、UDP、TCP。相互间的差别仅在于求和数据不同,算法都采用CheckSum。详见源程序清单。(提示:IP包头从版本号、首部长度、服务类型到目的站IP地址(如果不含IP选项)共20字节;ICMP校验和只覆盖ICMP报文。对比UDP、TCP伪首部和IP首部相似点,可以不必单独分配伪首部缓冲区,而直接利用IP缓冲区计算校验和。观察知IP头邻接数据,采取一定措施可实现直接计算。即先将IP寿命字段清0,协议字段赋相应值,校验和赋UDP/TCP包长度值,并加上12,表示伪首部的3长字长度,完成计算后向IP包首部添入正确寿命、校验和值,见图5。)

    0      8      16          31         0    8      16           31
    ----------------------------         ---------------------------
    | 寿命 | 协议 | 首部校验和 |         |        源站IP地址       |
    ----------------------------         --------------------------- 
    |        源站IP地址        |         |       目的站IP地址      |
    ----------------------------         ---------------------------
    |       目的站IP地址       |         | 零 | 协议 | UDP/TCP长度 |
    ----------------------------         ---------------------------
    |            数据          |         |       UDP/TCP包数据     |
    ----------------------------         ---------------------------
           IP包格式(局部)                   UDP、TCP伪首部+数据格式
                图5 IP包格式(局部)和UDP、TCP伪首部格式对比图

伪代码清单:

PingRequest(IP地址)    //Ping请求
{
    //申请小号内存
    pICMP=OSMemGet();
   
    //填写以太网帧
    目的MAC地址=ping命令传入的IP地址解析后得到的物理地址
    源MAC地址=本节点MAC地址
    类型=0x0800    //IP包
   
    //填写IP帧
    版本号&首部长度=0x45
    服务类型=0
    总长度=60
    标识符=GlobalID++    //全局变量16位GlobalID加1
    标志&分片偏移量=0x0000
    寿命=0x80
    协议=0x0001    //icmp
    首部校验和=0x0000
    源IP地址=本节点IP地址
    目的IP地址=ping命令传入的IP地址
    首部校验和=CheckSum(IP首部和长度)    //计算IP包首部校验和
   
    //填写ICMP帧
    类型=8    //8 请求  0 应答
    代码=0
    校验和=0x0
    标识符=0x0300
    序号=GlobalID
    校验和=CheckSum(ICMP首部和长度)    //计算ICMP包首部校验和
   
    //将ICMP包登记在PingBuf中
    for(i=0;i        if(PingBuf[i].status==0){
            PingBuf[i].times=0x8;    //测试8次
            PingBuf[i].ip=ping命令传入的IP地址;
            PingBuf[i].memhandle=内存句柄;
            PingBuf[i].status=4;    //第一次准备发(用于同步1秒时钟)
            break;
        }
    }
    if(i==MaxLenPingBuf) 释放内存;
}

PingAnswer(*输入包缓冲区首址pBUF)    //Ping应答
{   
    //改写以太网帧
    目的MAC地址=源MAC地址
    源MAC地址=本节点MAC地址
   
    //改写IP帧
    寿命=寿命-1
    目的IP地址=源IP地址
    源IP地址=本节点IP地址
    首部校验和=0x0000
    首部校验和=CheckSum(IP首部和长度)    //计算IP包首部校验和
   
    //改写ICMP帧
    类型=0    //8 请求  0 应答
    校验和=0x0
    校验和=CheckSum(ICMP首部和长度)    //计算ICMP包首部校验和
   
    //直接发送ICMP包至TxQFIFO缓存
    OSQSend(QID,*pBUF);
}[page]

PingEcho(*输入包缓冲区首址pBUF)    //收到应答后回显
{
    //打印ping响应,因为51定时器较慢,time参数省略(time是本机与对方主机往返一次所用时间)。
    PrintStr(" \\tReply from IP=");
    PrintStr(输入包之源IP地址);
    PrintStr(": bytes=32");
    PrintStr(" TTL=");
    PrintByte(输入包之TTL);
    PrintStr(" ");
   
    //处理PingBuf的记录
    for(i=0;i        if(PingBuf[i].status==1){
            if(PingBuf[i].ip==pBUF.ipframe.ip){
                PingBuf[i].status=2;    //已发出且应答
                break;
            }
        }
    }
}

PingCycle()    //定时操作,放在1秒循环任务中
{
    for(;;){
    taskDelay(1秒);
        for(i=0;i            switch(PingBuf[i].status)
            case 0:    //空闲
                break;
               
            {
            case 1:    //已发出但无应答
           
                //打印超时信息
                PrintStr(" \\tRequest timed out.(");
                PrintStr(PingBuf[i].ip);
                PrintStr(") ");
           
            case 2:    //已发出且应答
           
                //状态变迁
                PingBuf[i].times=PingBuf[i].times-1;
                if(PingBuf[i].times==0)
                    PingBuf[i].status=0;
                else{
               
            case 4:    //第一次准备发(用于同步1秒时钟)
               
                    //查ARP缓存
                    if(ARP缓存有对应项){               
                       
                        //直接发送ICMP包至TxQFIFO缓存
                        OSQSend(QID,*pBUF);
                   
                        PingBuf[i].status=1;    //已发出但无应答
                    }
                    else PingBuf[i].status=3;    //等ARP
                }
                break;
            }           
            case 3:    //等ARP
            {
                //查ARP缓存
                if(ARP缓存有对应项){
                    //直接发送ICMP包至TxQFIFO缓存
                    OSQSend(QID,*pBUF);
                }
                PingBuf[i].status=1;    //已发出但无应答
            }
            default:    //其他状态,错误
                PingBuf[i].status=0;
        }
    }
}

void PingCommand(WORDTABLE *WordTable)//PING命令处理
{
    if(WordTable->Num==1)
        PrintStr(" Please input IP address! ");
    else{
        if(IPadrToHEX(WordTable->wt[1].Str,&ping_ip_address)==0){
            PrintStr(" IP address error! ");return;
        }
        else
            PingRequest(ping_ip_address);
    }
}

INT16U CheckSum(INT16U *buf,INT16U length)    //校验和计算
{
    INT16U len;
    INT32U sum;
   
    len=length>>1;
    for(sum=0;len>0;len--)
        sum+=*buf++;
    if(length&1)
        sum+=(*buf&0xFF00);
    sum=(sum>>16)+(sum&0xFFFF);
    sum+=(sum>>16);
   
    return(~sum);
}

union ip_address_type{    //ip地址数据结构
 unsigned long dwords;
 unsigned int words[2];
 unsigned char bytes[4];
};

bit IPadrToHEX(unsigned char *Str,union ip_address_type *ip)    //IP字符串转换到IP地址值
{
    unsigned char i,j,ch,x;

    ch=*Str++;

    for(j=0;j<3;j++){
        x=0;
        for(i=0;i<4;i++){
            if(ch==\'.\') {ch=*Str++;break;}
            else if(i==3) return 0;
            else if(ch<0&&ch>9) return 0;
            else
                x=10*x+(ch-\'0\');
            ch=*Str++;
        }
        ip->bytes[j]=x;
    }

    x=0;
    for(i=0;i<4;i++){
        if(ch==\'\\0\') {ch=*Str++;break;}
        else if(i==3) return 0;
        else if(ch<0&&ch>9) return 0;
        else
            x=10*x+(ch-\'0\');
        ch=*Str++;
    }
    ip->bytes[3]=x;
    return 1;
}

void HEXToIPadr(unsigned char *Str,union ip_address_type *ip)    //IP地址值转换到IP字符串
{
    unsigned char i;
    unsigned char x,y;

    for(i=0;i<4;i++){
        x=ip->bytes[i];
        if(x>99){
            y=x/100;*Str++=y+\'0\';
            x=x-100*y;y=x/10;*Str++=y+\'0\';
            x=x-10*y;*Str++=x+\'0\';
            if(i==3) *Str++=\'\\0\';
            else *Str++=\'.\';
        }
        else if(x>9){
            y=x/10;*Str++=y+\'0\';
            x=x-10*y;*Str++=x+\'0\';
            if(i==3) *Str++=\'\\0\';
            else *Str++=\'.\';
        }
        else{
            *Str++=x+\'0\';
            if(i==3) *Str++=\'\\0\';
            else *Str++=\'.\';
        }
    } 
}


参考文献:
1。《用TCP/IP进行网际互连》(第3版)

关键字:51单片机  Ping  DOS 引用地址:51单片机简单Ping的实现

上一篇:51单片机ARP协议实现原理
下一篇:51单片机通用汇编延时子程序

推荐阅读最新更新时间:2024-03-16 13:10

51单片机教程(四):独立-矩阵键盘应用与设计
简介:上讲介绍并应用了单片机的串口通信,并给出了实例。从这一讲开始将介绍单片机的外围电路。这讲向大家介绍单片机外接键盘电路,通过该讲,读者可以掌握单片机外接键盘的工作原理从而实现电路与程序设计。 一、原理简介 键盘接口电路是单片机系统设计非常重要的一环,作为人机交互界面里最常用的输入设备。我们可以通过键盘输入数据或命令来实现简单的人机通信。在设计键盘电路与程序前,我们需要了解键盘和组成键盘的按键的一些知识。 1. 按键的分类 一般来说,按键按照结构原理可分为两类,一类是触点式开关按键,如机械式开关、导电橡胶式开关等;另一类是无触点式开关按键,如电气式按键,磁感应按键等。前者造价低,后者寿命长。目前,微机系统中最常见的是
[单片机]
<font color='red'>51单片机</font>教程(四):独立-矩阵键盘应用与设计
51单片机与A/D转换器MAX195的接口设计
  MAX195是16位逐次逼近方式的ADC。它将高精度、高速度、低电源功耗(消耗电流仅10μA)的关闭方式等性能结合在一起。内部校准电路对线性度与偏置误差进行校正,所以无需外部调整便可达到全部额定的性能指标。电容性的DAC结构使之具有特有的85kbps跟踪/保持功能,变换时间仅需9.4μs。三态串行数据输出及引脚可选的单极性(0~VREF)或双极性(-VREF~+VREF)的输入范围使之可广泛应用于便携式仪表、医用信号采集及多传感器测量等系统中。   1 MAX195引脚及说明   MAX195有16个引脚,其排列如图1所示。   2 MAX195转换原理及时序   MAX195片内含有电容性的数字模拟变换器(DAC
[单片机]
<font color='red'>51单片机</font>与A/D转换器MAX195的接口设计
基于51单片机的太阳能路灯控制系统设计方案
简介:太阳能路灯控制系统:51单片机练手项目,简单可复制。 带太阳能充电功能,oled显示, 白天根据光强判断开关灯晚上开灯,二级菜单可以设置时间日期。 太阳能充电:传统锂电池充电芯片TP4056,使用6V太阳能板,给3.7V18650电池充电。 经过资料显示 18650电池尽量不要让其电压低于2.7V,所以后级供电电路(5V升压电路)MT3608启动引脚EN脚 连接了LM393制成的电压比较器。和电池电压比较,电池电压低于2.7v,MT3608启动脚拉低关断。 供电:使用升压芯片MT3608给单片机供电,让电池电压稳定在5.1V,来提供稳定电压。 #include reg52.h #include oled.h #i
[单片机]
基于<font color='red'>51单片机</font>的太阳能路灯控制系统设计方案
51单片机音乐盒的仿真课程设计(8首歌)
仿真成功 初始化显示P.521061P. 显示歌曲编号。 8首歌的音乐盒课程设计。 一个很简单的程序 分享一下 仿真原理图如下 单片机源程序如下: ////////////////////////////////////////////// //51单片机音乐播放器程序 // //共三个按键:上一曲、下一曲、播放暂停 // //播放完一首歌曲无按键按下时顺序播放下一曲 // ////////////////////////////////////////////// #include REG51.H //#define sound_amount 5 //歌曲的数量
[单片机]
<font color='red'>51单片机</font>音乐盒的仿真课程设计(8首歌)
20-基于51单片机的遥控开关仿真
具体实现功能 实现功能: 本课题研究的是一款遥控开关,采用51单片机进行发射电路与接收电路的设计,发射电路由单片机最小系统及四个按键构成, 接收电路由单片机最小系统、继电器及LED灯构成。 由于仿真无法模拟无线模块通信,故利用单片机RXD、TXD端口,通过发射电路的开关实现对接收电路LED灯的控制, 仿真实现LED灯的远程点亮及熄灭。 单片机介绍 51单片是一种低功耗、高性能CMOS8位微控制器,具有 8K 在系统可编程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C51为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。具有以下标准功能:8k字节Flash,
[单片机]
20-基于<font color='red'>51单片机</font>的遥控开关仿真
51单片机系列之LCD1602
名称:LCD1602液晶屏显示(并口) 平台:Keil 4, Ly-51S学习板 引脚定义如下:1-VSS 2-VDD 3-V0 4-RS 5-R/W 6-E 7-14 DB0-DB7 15-BLA 16-BLK 与51连接:RS-P2.4 RW-P2.5 EN-P2.6 DB-P0 -----------------------------------------------------*/ #include reg52.h #define DB P0 sbit RS =P2^4; sbit RW =P2^5; sbit EN =P2^6; /*判忙函数*/ bit LCD_check_b
[单片机]
51单片机使用printf函数带参数打印不正常的解决办法
本人在调试STC8A8K64S4单片机时遇到一个很奇葩的问题,使用printf函数打印一个带参数的调试信息时出现异常,具体表现定义了8位的变量使用%X格式打印出的值却变成了16位,且赋值为0后任然打印出16位的随即数值。由于之前使用stm32单片机一直使用printf打印没有出现过类似情况,因此没有怀疑是函数问题而是怀疑是否为单片机某处寄存器设置错误。在看了很久手册之后没发现问题,最后自己写了一个带参数打印的函数发现数值没有错误。由于项目匆忙未继续深入探究原因,只能初步断定在51单片机使用printf函数带参数打印时数值不正常。 当然使用printf带参数打印调试是非常方便的,因此我尝试使用sprintf先将参数装到一个buf
[单片机]
基于51单片机的超声波测距设计(带温度补偿)
利用STC89C52单片机、超声波模块(HC-RS04)、DS18B20数字温度传感器和LCD1602液晶显示模块制作一个超声波测距仪,液晶屏第一行显示温度和超声波速度“T:30°C V:349m/s”,第二行显示测量距离“S=X.XXXm”。 所设计的测距仪测量单位为米,精确到小数点后面3位(毫米),测量范围:0.05m~5m。 (一)基本部分 1、液晶显示功能 (1)开机时,液晶屏第一行显示温度和超声波速度,例如“T:30°C V:349m/s”,第二行显示测量距离“S=0.000m” (2)操作对应的功能按键时,液晶屏第一行显示温度和超声波速度,例如“T:30°C V:349m/s”,第二行显示测量距离“S=X.XXXm”
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • ARM裸机篇--按键中断
    先看看GPOI的输入实验:按键电路图:GPF1管教的功能:EINT1要使用GPF1作为EINT1的功能时,只要将GPFCON的3:2位配置成10就可以了!GPF1先配 ...
  • 网上下的--ARM入门笔记
    简单的介绍打今天起菜鸟的ARM笔记算是开张了,也算给我的这些笔记找个存的地方。为什么要发布出来?也许是大家感兴趣的,其实这些笔记之所 ...
  • 学习ARM开发(23)
    三个任务准备与运行结果下来看看创建任务和任运的栈空间怎么样的,以及运行输出。Made in china by UCSDN(caijunsheng)Lichee 1 0 0 ...
  • 学习ARM开发(22)
    关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个 ...
  • 学习ARM开发(21)
    先要声明任务指针,因为后面需要使用。 任务指针 volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • 学习ARM开发(20)
  • 学习ARM开发(19)
  • 学习ARM开发(14)
  • 学习ARM开发(15)
何立民专栏 单片机及嵌入式宝典

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

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