CH395實現主動ping對端功能(程式碼及說明)

2023-12-18 18:02:09
目錄
1.PING原理
1.1簡介
1.2協定
1.3通訊流程
2.程式碼解釋
3.工程連結
  • PING原理

1.簡介

  PING是基於ICMP(Internet Control Message Protocol)協定工作的。ICMP報文是封裝在IP包中,工作在網路層。

2.協定

  ICMP包頭的型別欄位有2類,一類是查詢報文,用於檢測通訊鏈路是否正常;一類是差錯報文,通知出錯原因。

  回送訊息是用於進行通訊的主機間,判斷所發的封包是否已經成功到達對端的一種訊息,ping命令就是利用這個訊息實現的:可以向對端傳送回送請求的訊息(request,8),也可以接收對端主機傳送回來的應答(reply,0)

Type(8位元) Code(8位元)
Checksum(16位元)
Identifier(16位元)
Sequence Number(16位元)
選項資料

 

Type(型別):0代表回送應答,8代表回送請求

Code(程式碼):值為0

Checksum(校驗和)

 Identifier(識別符號):Unix系統在實現ping程式時,把ICMP報文中的識別符號欄位設定成傳送程序的PID號

Sequence Number(序號):從0開始,每傳送一次新的請求就加1

 抓包:

請求包(Response frame:40 表示應答此請求的reply包是N0.40這一包)

 應答包(Request frame:37 表示此包是對N0.37這一請求包的應答)

 3.通訊流程

  ①執行ping 192.168.x.x命令後,源主機會先構建一個ICMP請求訊息封包(型別為8)

  ②由ICMP協定將這個封包連同192.168.x.x這一地址一起交給IP層。IP層將以192.168.x.x作為目的地址,本地ip地址作為源地址,協定欄位置為1表示是ICMP協定,再加上一些其他的資訊構建成一個IP封包。

  ③之後,需要加入MAC頭,如果在本地ARP對映表中能直接查到192.168.x.x這一地址所對應的MAC地址,則可以直接使用。在獲取MAC地址後,由資料鏈路層構建一個資料框,其中,目的地址是剛剛找到的MAC地址,源地址是本機的MAC地址。

  ④對端裝置收到這個資料框後,將其中的目的MAC地址和自己的本地MAC地址對比,如果符合則接收,不符合就丟棄。如果MAC地址符合,那麼提取出IP封包,經IP層檢查後,再提取出有用的相關資訊給ICMP協定。

  ⑤識別出這是一個ping請求幀後,將構建一個ICMP應答包(型別為0),經過同樣的流程傳給傳送請求包的主機。

  • 程式碼解釋(CH395.c)

1.設定395相關網路引數及對端,將ip包協定欄位置為1,表示為Internet控制訊息 (ICMP)協定型別。

1 /* CH395相關定義 */
2 const  UINT8 CH395IPAddr[4]   = {192,168,1,126};                        /* CH395IP地址 */
3 const  UINT8 CH395GWIPAddr[4] = {192,168,1,1};                         /* CH395閘道器 */
4 const  UINT8 CH395IPMask[4]   = {255,255,255,0};                       /* CH395子網掩碼 */
5 const  UINT8 DestIPAddr[4]    = {192,168,1,23};                       /* 目的IP */
6 const  UINT8  IPRawProto      = 1;                                     /* IP包協定型別 */

 2.初始化395和socket的引數,並將socket設定為ipraw模式。(關於ipraw模式的介紹和使用可以查閱CH395的官方手冊)

 1 /**********************************************************************************
 2 * Function Name  : InitCH395InfParam
 3 * Description    : 初始化CH395Inf引數
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 **********************************************************************************/
 8 void InitCH395InfParam(void)
 9 {
10     memset(&CH395Inf,0,sizeof(CH395Inf));                            /* 將CH395Inf全部清零*/
11     memcpy(CH395Inf.IPAddr,CH395IPAddr,sizeof(CH395IPAddr));         /* 將IP地址寫入CH395Inf中 */
12     memcpy(CH395Inf.GWIPAddr,CH395GWIPAddr,sizeof(CH395GWIPAddr));   /* 將閘道器IP地址寫入CH395Inf中 */
13     memcpy(CH395Inf.MASKAddr,CH395IPMask,sizeof(CH395IPMask));       /* 將子網掩碼寫入CH395Inf中 */
14 }
15 
16 /**********************************************************************************
17 * Function Name  : InitSocketParam
18 * Description    : 初始化socket
19 * Input          : None
20 * Output         : None
21 * Return         : None
22 **********************************************************************************/
23 void InitSocketParam(void)
24 {
25     memset(&SockInf,0,sizeof(SockInf));                              /* 將SockInf[0]全部清零*/
26     memcpy(SockInf.IPAddr,DestIPAddr,sizeof(DestIPAddr));            /* 將目的IP地址寫入 */
27     SockInf.ProtoType = PROTO_TYPE_IP_RAW;                           /* IP RAW模式 */
28     SockInf.IPRAWProtoType = IPRawProto;
29 }
30 
31 /**********************************************************************************
32 * Function Name  : CH395SocketInitOpen
33 * Description    : 設定CH395 socket 引數,初始化並開啟socket
34 * Input          : None
35 * Output         : None
36 * Return         : None
37 **********************************************************************************/
38 void CH395SocketInitOpen(void)
39 {
40     UINT8 i;
41     /* socket 0為IP RAW模式 */
42     CH395SetSocketDesIP(0,SockInf.IPAddr);                           /* 設定socket 0目標IP地址 */
43     CH395SetSocketProtType(0,SockInf.ProtoType);                     /* 設定socket 0協定型別 */
44     CH395SetSocketIPRAWProto(0,SockInf.IPRAWProtoType);              /* 設定協定欄位 */
45 
46     i = CH395OpenSocket(0);                                          /* 開啟socket 0 */
47     mStopIfError(i);                                                   /* 檢查是否成功 */                                              /* 檢查是否成功 */
48 }

 3.在socket的接收中斷中,呼叫 CH395GetRecvLength()函數獲取當前緩衝區中的資料長度,在呼叫CH395GetRecvData()函數讀取資料,最後呼叫 CH395IcmpRecvData()函數對收到的ping包處理和分析。

 1 /**********************************************************************************
 2 * Function Name  : CH395SocketInterrupt
 3 * Description    : CH395 socket 中斷,在全域性中斷中被呼叫
 4 * Input          : sockindex
 5 * Output         : None
 6 * Return         : None
 7 **********************************************************************************/
 8 void CH395SocketInterrupt(UINT8 sockindex)
 9 {
10    UINT8  sock_int_socket;
11 
12    UINT16 len;
13 
14    sock_int_socket = CH395GetSocketInt(sockindex);                   /* 獲取socket 的中斷狀態 */
15   // printf("SOCK status : %02x\n",sock_int_socket);
16    if(sock_int_socket & SINT_STAT_SENBUF_FREE)                       /* 傳送緩衝區空閒,可以繼續寫入要傳送的資料 */
17    {
18    }
19    if(sock_int_socket & SINT_STAT_SEND_OK)                           /* 傳送完成中斷 */
20    {
21        IcmpSuc++;
22    }
23    if(sock_int_socket & SINT_STAT_RECV)                              /* 接收中斷 */
24    {
25        printf("recv back!\r\n");
26        len = CH395GetRecvLength(sockindex);                            /* 獲取當前緩衝區內資料長度 */
27        if(len == 0)return;
28        if(len > 512) len = 512;
29        CH395GetRecvData(sockindex,len,MyBuffer);                       /* 讀取資料 */
30        CH395IcmpRecvData(len,MyBuffer);
31    }
32    if(sock_int_socket & SINT_STAT_CONNECT)                          /* 連線中斷,僅在TCP模式下有效*/
33   {
34 
35   }
36     if(sock_int_socket & SINT_STAT_DISCONNECT)                        /* 斷開中斷,僅在TCP模式下有效 */
37     {
38     }
39    if(sock_int_socket & SINT_STAT_TIM_OUT)                           /* 超時中斷 */
40    {
41    }
42 }

 4.在CH395PINGInit()函數中,進行引腳初始化、復位395、初始化395、初始化socket、開啟socket等操作。

呼叫 CH395EnablePing(1)函數開啟ping功能(預設開啟)

InitParameter()--- PING引數變數初始化

InitPing()--- PING初始化,生成ping查詢報文

 1 /**********************************************************************************
 2 * Function Name  : main
 3 * Description    : main主函數
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 **********************************************************************************/
 8 void CH395PINGInit(void)
 9 {
10     UINT8 i;
11     Delay_Ms(100);
12     printf("CH395EVT Test Demo\n");
13     CH395_PORT_INIT();
14     CH395CMDReset();                                                 /* 復位CH395晶片 */
15     Delay_Ms(1000);                                                  /* 延時1000毫秒,要分開寫,否則無效 */
16 
17     i = CH395CMDGetVer();                                            /*獲取晶片以及韌體版本號 */
18     printf("韌體版本號:%02x\n",i);
19     InitCH395InfParam();                                             /* 初始化CH395相關變數 */
20     i = CH395Init();                                                 /* 初始化CH395晶片 */
21     printf("CH395Init:%02x\n",i);
22     mStopIfError(i);
23 
24     while(1)
25     {                                                                /* 等待乙太網連線成功*/
26        if(CH395CMDGetPHYStatus() == PHY_DISCONN)                     /* 查詢CH395是否連線 */
27        {
28            printf("CH395 DISCONN\n");
29            Delay_Ms(200);                                            /* 未連線則等待200MS後再次查詢 */
30        }
31        else
32        {
33            printf("CH395 Connect Ethernet\n");                       /* CH395晶片連線到乙太網,此時會產生中斷 */
34            break;
35        }
36     }
37     InitSocketParam();         //                                      /* 初始化socket相關變數 */
38     CH395SocketInitOpen();//
39 
40     //CH395EnablePing(1);//預設開啟
41     InitParameter();
42     InitPing();
43     printf("!!\r\n");
44 //    TIM2_Init();
45 //    Intervalometer_4ms();
46 }
 1 /**********************************************************************************
 2 * Function Name  : InitParameter
 3 * Description    : Ping引數初始化
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 **********************************************************************************/
 8 void InitParameter( void )
 9 {
10     UNREACH_COUNT=0;
11     TIMOUT_COUNT=0;
12     SUCRECV_COUNT=0;
13     IcmpCont=0;
14     IcmpSeq=0;
15     IcmpSuc=0;///
16     icmp_tmp=0;
17     CH395GetIPInf(CH395INF_BUF);
18 }
19 
20 /**********************************************************************************
21 * Function Name  : InitPing
22 * Description    : Ping初始化
23 * Input          : None
24 * Output         : None
25 * Return         : None
26 **********************************************************************************/
27 void InitPing( void )
28 {
29   IcmpHeader head;
30   UINT32 check_sum=0;
31   UINT8 i;
32 
33   IcmpCont++;
34   IcmpSeq += 1;
35   head.i_type = ICMP_HEAD_TYPE;//8
36   head.i_code = ICMP_HEAD_CODE;//0
37   head.i_id   = ICMP_HEAD_ID;//512
38   head.i_seq  = ICMP_HEAD_SEQ+IcmpSeq;//100+
39   memset(head.i_data,0,sizeof(head.i_data));
40 
41   for( i=0;i<ICMP_DATA_BYTES;i++ ){//32
42     if(i<26)
43         head.i_data[i] = i + 'a';
44     else
45         head.i_data[i] = i + 'a' - 26;
46     if(i%2==0)
47         check_sum += head.i_data[i]<<8;
48     else
49         check_sum += head.i_data[i];
50   }
51   check_sum += head.i_type<<8;
52   check_sum += head.i_code;
53   check_sum += head.i_id;
54   check_sum += head.i_seq;
55   head.i_cksum = check_sum>>16;
56   head.i_cksum += check_sum&0xffff;
57   head.i_cksum = 0xffff - head.i_cksum;
58   memset(SEND_BUF,0,sizeof(SEND_BUF));
59   memcpy(SEND_BUF,&head,sizeof(head));
60 
61   SEND_BUF[2] = head.i_cksum >> 8;
62   SEND_BUF[3] = head.i_cksum & 0xff;
63   SEND_BUF[4] = head.i_id >> 8;
64   SEND_BUF[5] = head.i_id & 0xff;
65   SEND_BUF[6] = head.i_seq >> 8;
66   SEND_BUF[7] = head.i_seq & 0xff;
67 }

 5.在main函數中,通過 CH395SendData()函數傳送 InitPing()函數生成的40位元組的資料,簡單實現按鍵按一下傳送一次ping請求包。

 1 /**********************************************************************************
 2 * Function Name  : main
 3 * Description    : main主函數
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 **********************************************************************************/
 8 int main(void)
 9 {
10 
11     Delay_Init();
12     USART_Printf_Init(115200);
13     Delay_Ms(100);
14     /* 延時100毫秒 */
15     GPIO_Toggle_INIT_C();
16     CH395PINGInit();
17 //
18 //Delay_Ms(2000);
19 //CH395SendData(0,MyBuffer1,2);
20 //Delay_Ms(20);
21 //            CH395SendData( 0,SEND_BUF,40 );
22 //            Delay_Ms(20);
23 //            CH395SendData( 0,SEND_BUF,40 );
24 //
25 //            Delay_Ms(20);
26 //            CH395SendData( 0,SEND_BUF,40 );
27 //            Delay_Ms(20);
28 //            CH395SendData( 0,SEND_BUF,40 );
29     while(1)
30     {
31         if(CH395_INT_WIRE == 0)
32         {
33             CH395GlobalInterrupt();
34         }
35         if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == RESET)
36         {
37             Delay_Ms(2);
38             if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == RESET)
39             {
40                 printf("PING %d times\r\n",++t);
41                 CH395SendData( 0,SEND_BUF,40 );
42                 while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == RESET);
43             }
44         }
45 //    CH395SendData( 0,SEND_BUF,40 );
46 //      Delay_Ms(1500);
47       /*查詢狀態執行相應命令*/
48 //      CH395_PINGCmd();
49      // InitPing();
50     }
51 }

效果如下,按下按鍵傳送一次ping請求包,對端reply一包資料。(一共發了3次ping_request,成功進入socket接收中斷接收處理回包)


 

工程程式碼及抓包:https://files.cnblogs.com/files/blogs/808422/CH395_%E4%B8%BB%E5%8A%A8ping.zip?t=1702869711&download=true