欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32 lwip freeRTOS实现ping

程序员文章站 2024-02-22 18:40:52
...

一、STM32CubeMX配置:

这里展示几个关键配置

1.打开ICMP功能,这个也是默认打开的。ping使用的就是ICMP协议

STM32 lwip freeRTOS实现ping

2.设置RAW模式接收字节数,这里默认是0,设置成和TCP、UDP一样

STM32 lwip freeRTOS实现ping

3.设置以太网中断优先级,这里需要调低,不然在freeRTOS中卡住,裸机中可不用设置

STM32 lwip freeRTOS实现ping

二、生成的代码中更改一些配置:

1.打开RAW功能

STM32 lwip freeRTOS实现ping

2.打开接收超时

STM32 lwip freeRTOS实现ping

三、代码

1.变量定义

typedef struct icmp_hdr

{

    unsigned char   icmp_type; // 消息类型

    unsigned char   icmp_code; // 代码

    unsigned short  icmp_checksum; // 校验和

    // 下面是回显头

    unsigned short  icmp_id; // 用来惟一标识此请求的ID号

    unsigned short  icmp_sequence; // ***

    unsigned long   icmp_timestamp; // 时间戳

} ICMP_HDR, *PICMP_HDR;



typedef struct _IPHeader// 20字节的IP头

{

    uint8_t     iphVerLen; // 版本号和头长度(各占4位)

    uint8_t     ipTOS; // 服务类型

    uint16_t    ipLength; // 封包总长度,即整个IP报的长度

    uint16_t    ipID;  // 封包标识,惟一标识发送的每一个数据报

    uint16_t    ipFlags; // 标志

    uint8_t     ipTTL; // 生存时间,就是TTL

    uint8_t     ipProtocol; // 协议,可能是TCP、UDP、ICMP等

    uint16_t    ipChecksum; // 校验和

    uint32_t    ipSource; // 源IP地址

    uint32_t    ipDestination; // 目标IP地址

} IPHeader, *PIPHeader;

typedef int SOCKET;

 

2.实现代码

int ETH_PingWork(const char * szDestIp)

{

long nRet = 0;

/*****************第一步:申请SOCKET************************************/

uint32_t ip = inet_addr(szDestIp);

SOCKET sRaw = lwip_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

if(sRaw == -1)

{

printf("Cannot create socket! Error %d\r\n", errno);

return errno;

}

// 设置接收超时

struct timeval tv_out;

tv_out.tv_sec = 1;

tv_out.tv_usec = 0;

nRet = setsockopt(sRaw, SOL_SOCKET,SO_RCVTIMEO, &tv_out, sizeof(tv_out));

if(-1 == nRet)

{

printf("Cannot set timeout! %d \r\n",errno);

}

/*****************第二步:组ICMP包************************************/

// 创建ICMP封包

char buff[sizeof(ICMP_HDR) + 32];

ICMP_HDR* pIcmp = (ICMP_HDR*)buff;

// 填写ICMP封包数据

pIcmp->icmp_type = 8; // 请求一个ICMP回显

pIcmp->icmp_code = 0;

pIcmp->icmp_id = (uint16_t)0x1234;

pIcmp->icmp_checksum = 0;

pIcmp->icmp_sequence = 0;

pIcmp->icmp_timestamp = 0xffff;

// 填充数据部分,可以为任意

memset(&buff[sizeof(ICMP_HDR)], 'E', 32);



/*****************第三步:发送ICMP包************************************/

//设置目的地址

struct sockaddr_in dest;

dest.sin_family = AF_INET;

dest.sin_port = htons(0);

dest.sin_addr.s_addr = ip;

//pIcmp->icmp_checksum = checksum((uint16_t*)buff, sizeof(ICMP_HDR) + 32);   //如果ping过去没回应需要屏蔽这条

//printf("icmp_checksum = %x\r\n",pIcmp->icmp_checksum);

nRet = (long)sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (struct sockaddr *)&dest, sizeof(dest));

if(nRet == -1)

{

printf(" sendto() failed: %d \r\n", errno);

return -1;

}

printf("sendto = %ld\r\n",nRet);        

/*****************第四步:接收ICMP包************************************/

char recvBuf[1024];

struct sockaddr_in from;

socklen_t nLen = sizeof(from);

nRet = (long)recvfrom(sRaw, recvBuf, 1024, 0, (struct sockaddr *)&from, &nLen);

if(nRet == -1)

{

printf(" recvfrom() failed: %d\r\n", errno);

return -1;

}

printf("recvfrom = %ld\r\n",nRet);

/*****************第五步:解析ICMP包************************************/

if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))

{

printf(" Too few bytes from %s \r\n", inet_ntoa(from.sin_addr));

}

#if 0 // IP头解析

IPHeader * header = (IPHeader*)recvBuf;

struct in_addr a;

a.s_addr = header->ipSource;

printf("source ip %s\n", inet_ntoa(a));

a.s_addr = header->ipDestination;

printf("dest ip %s\n", inet_ntoa(a));

#endif

ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头

if(pRecvIcmp->icmp_type != 0)// 回显

{

printf("nonecho type %d recvd \r\n", pRecvIcmp->icmp_type);

return -1;

}

if(pRecvIcmp->icmp_id != 0x1234)

{

printf(" someone else's packet! \r\n");

return -1;

}

printf(" %d bytes from %s:", (int)nRet, inet_ntoa(from.sin_addr));

printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence);

printf(" \r\n");

return 0;

}