说在开头
正点原子F429开发板主芯片采用的是STM32F429IGT6,网络PHY芯片采用的是LAN8720A(V1)和YT8512C(V2),采用的是RMII连接,PHY_ADDR为0;在代码中将会对不同的芯片做出适配。
CubeMX版本:6.6.1;
F4芯片组Pack包版本:STM32Cube FW_F4 V1.27.0;
本实验代码以《基于正点原子阿波罗F429开发板的LWIP应用(3)——Netbiosns功能》一章的代码作为基础;
第一部分:SNTP功能实现
SNTP的全称是“简单网络时间协议”,是一种由NTP协议改编而来的网络时间协议,主要用来同步因特网中的计算机时钟。
在此实验中开发板做SNTP客户端,通过单播模式定期访问SNTP服务器获得准确的时间进而不断校准单片机的RTC。
本次实验的工作逻辑如下:首先完成RTC和LWIP的初始化,在初始化完成后开始启动SNTP服务。在完成第一次SNTP授时后还是间隔1S从RTC读取时间并从串口打印出来。
代码修改(代码注释很详细我就不再讲解了)
1、 首先将"Middlewares/Third_Party/LwIP/src/apps/sntp/sntp.c"添加进工程“Application/User/LWIP/App”分组中;
2、在“LWIP/App”中新建“sntp_client.c ”、“sntp_client.h”2个文件并添加到工程“Application/User/LWIP/App”分组中,两个文件分别添加以下代码:
/******************sntp_client.c文件代码**********************/
#include "sntp_client.h"#include "lwip/apps/sntp.h"
#include "time.h"
#include "rtc.h"/*1、中国国家授时中心NTP服务器“域名:ntp.ntsc.ac.cnIP:202.120.2.101 ---> 0X650278CA2、阿里云NTP服务器地址:域名:ntp.aliyun.comIP:110.75.8.1
*//*!
* @brief 设置 SNTP 的服务器地址,
* 加入多个 IP 以免某个 IP 获取不了时间
* 执行条件:无
*
* @retval: 无
*/
void set_sntp_server_list(void)
{uint32_t server_list[SNTP_MAX_SERVERS] = { 0X650278CA, //国家授时中心0x0B6C1978,0x0B0C5CB6,0x58066BCB,0xC51F70CA,0x521D70CA,0x820176CA,0x510176CA,};ip_addr_t sntp_server;for(int i = 0; i < SNTP_MAX_SERVERS; i++){sntp_server.addr = server_list[i];sntp_setserver(i, &sntp_server); // 国家授时中心}
}/*!
* @brief Lwip 的 SNTP 初始化封装接口
* 执行条件:无
*
* @retval: 无
*/
void bsp_sntp_init(void)
{/*设置 SNTP 的获取方式SNTP_OPMODE_POLL //单播模式,客户端主动发送获取时间请求SNTP_OPMODE_LISTENONLY //组播模式,等待授时服务器主动发送更新时间请求(请求发送时间不确定)*/sntp_setoperatingmode(SNTP_OPMODE_POLL);//SNTP 初始化sntp_init();//加入授时中心的IP信息set_sntp_server_list();/*通过修改sntp_opts.h中的SNTP_UPDATE_DELAY宏定义的值来修改同步时间的间隔默认1小时——3600000,目前修改成1分钟:60000*/
}/*!
* @brief SNTP 获取时间戳的处理函数
* 执行条件:无
*
* @param [in] : sntp 获取的时间戳
*
* @retval: 无
*/
void sntp_set_time(uint32_t sntp_time)
{if(sntp_time == 0){printf("sntp_set_time: wrong!@@\n");return;}struct tm *time;sntp_time += (8 * 60 * 60); ///北京时间是东8区需偏移8小时time = localtime(&sntp_time);//RTC校准RTC_calibration((time->tm_year - 100), (time->tm_mon + 1), time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec, time->tm_wday);if(RTC_calibrate_flag == 0) RTC_calibrate_flag =1;// printf("sntp_set_time: 20%d-%02d-%02d %d:%d:%d, 星期%d", \
// time->tm_year - 100,
// time->tm_mon + 1,
// time->tm_mday,
// time->tm_hour,
// time->tm_min,
// time->tm_sec,
// (time->tm_wday==0 ? 7 : time->tm_wday));//星期需要注意:0表示周天,1-6表示周一到周六}/******************sntp_client.h文件代码**********************/
#ifndef __SNTP_CLIENT_H
#define __SNTP_CLIENT_H#include "main.h"void bsp_sntp_init(void);
void sntp_set_time(uint32_t sntp_time);
#endif
3、在“lwipopts.h”文件中添加以下内容:
#define LWIP_SNTP 1
#define SNTP_MAX_SERVERS 8
#include "sntp_client.h"
#define SNTP_SET_SYSTEM_TIME sntp_set_time //定义 Lwip SNTP 的 处理函数
#define SNTP_UPDATE_DELAY 60000 //授时请求发送间隔:1分钟
4、rtc.c文件添加以下内容:
/******************rtc.c文件添加代码**********************/
void RTC_calibration(uint8_t year,uint8_t month,uint8_t day,uint8_t hour,uint8_t minute,uint8_t sec, uint8_t week)//RTC校准
{RTC_DateTypeDef sdatestructure;RTC_TimeTypeDef stimestructure;sdatestructure.Year = year;//年 自2000开始,0x24表示24年sdatestructure.Month = month;//月sdatestructure.Date = day;//日sdatestructure.WeekDay = week;//星期if(HAL_RTC_SetDate(&hrtc,&sdatestructure,RTC_FORMAT_BIN) != HAL_OK){Error_Handler(); }stimestructure.Hours = hour;stimestructure.Minutes = minute;stimestructure.Seconds = sec;stimestructure.TimeFormat = 0;stimestructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;stimestructure.StoreOperation = RTC_STOREOPERATION_RESET;if(HAL_RTC_SetTime(&hrtc,&stimestructure,RTC_FORMAT_BIN) != HAL_OK){Error_Handler();}
}void Get_time_info(uint8_t *info)
{RTC_DateTypeDef sdatestructureget;RTC_TimeTypeDef stimestructureget;uint8_t aShowTime[50] = {0};uint8_t aShowDate[50] = {0};/* Get the RTC current Time */HAL_RTC_GetTime(&hrtc, &stimestructureget, RTC_FORMAT_BIN);/* Get the RTC current Date */HAL_RTC_GetDate(&hrtc, &sdatestructureget, RTC_FORMAT_BIN);/* Display time Format : hh:mm:ss */info[0] = sdatestructureget.Year;//2000开始info[1] = sdatestructureget.Month;//1至12info[2] = sdatestructureget.Date;//1至31info[3] = stimestructureget.Hours; info[4] = stimestructureget.Minutes;info[5] = stimestructureget.Seconds;info[6] = sdatestructureget.WeekDay; //0至6表示周日到周六sprintf((char*)aShowTime,"%02d:%02d:%02d",stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);/* Display date Format : mm-dd-yy */sprintf((char*)aShowDate,"%02d-%02d-%02d,week=%d",sdatestructureget.Month, sdatestructureget.Date, 2000 + sdatestructureget.Year, sdatestructureget.WeekDay); printf("%s %s\n",aShowTime,aShowDate);
}/******************rtc.h文件添加代码**********************/
void RTC_calibration(uint8_t year,uint8_t month,uint8_t day,uint8_t hour,uint8_t minute,uint8_t sec, uint8_t week);
void Get_time_info(uint8_t *info);
5、main.c文件增加以下内容:
/******************main.c开头增加以下内容**********************/
uint8_t RTC_calibrate_flag = 0;//RTC校准标志位
uint8_t Internet_timeinfo[7] = { NULL };//网络时间中的:年 月 日 时 分 秒 星期 /******************main函数while(1)前增加以下内容**********************/
bsp_sntp_init();//SNTP初始化/******************main.h增加以下内容**********************/
extern uint8_t RTC_calibrate_flag;//RTC校准标志位
extern uint8_t Internet_timeinfo[7];
6、stm32f4xx_it.c文件增加以下内容:
/******************stm32f4xx_it.c开头增加以下内容**********************/
extern void Get_time_info(uint8_t *info);
uint16_t time_read_count = 0;/******************SysTick_Handler函数最后增加以下内容**********************/time_read_count++;if(time_read_count >= 1000){time_read_count = 0;if(RTC_calibrate_flag) Get_time_info(Internet_timeinfo);//从RTC获取时间}
至此就修改完成了,编译0警告0错误。烧录进开发板可以看到DS0一直闪烁,同时串口间隔1S打印RTC的时间,打印时间和串口接收的时间戳相差不超过1S钟,串口打印如下图:
第二部分:lwiperf测速实现
1、 首先将"Middlewares/Third_Party/LwIP/src/apps/lwiperf/lwiperf.c"添加进工程“Application/User/LWIP/App”分组中;
2、在“LWIP/App”中新建“speed_test.c ”、“speed_test.h”2个文件并添加到工程“Application/User/LWIP/App”分组中,两个文件分别添加以下代码:
/******************speed_test.c内容如下**********************/
#include "speed_test.h"#include "lwip.h"#include "lwip/apps/lwiperf.h"uint8_t info_buf[30];/* 报告状态 */
const char *report_type_str[] =
{"TCP_DONE_SERVER", /* LWIPERF_TCP_DONE_SERVER,*/"TCP_DONE_CLIENT", /* LWIPERF_TCP_DONE_CLIENT,*/"TCP_ABORTED_LOCAL", /* LWIPERF_TCP_ABORTED_LOCAL, */"TCP_ABORTED_LOCAL_DATAERROR", /* LWIPERF_TCP_ABORTED_LOCAL_DATAERROR, */"TCP_ABORTED_LOCAL_TXERROR", /* LWIPERF_TCP_ABORTED_LOCAL_TXERROR, */"TCP_ABORTED_REMOTE", /* LWIPERF_TCP_ABORTED_REMOTE, */"UDP_STARTED", /* LWIPERF_UDP_STARTED, */"UDP_DONE", /* LWIPERF_UDP_DONE, */"UDP_ABORTED_LOCAL", /* LWIPERF_UDP_ABORTED_LOCAL, */"UDP_ABORTED_REMOTE" /* LWIPERF_UDP_ABORTED_REMOTE */
};/* 当测试结束以后会调用此函数,此函数用来报告测试结果 */
/*** @brief 当测试结束以后会调用此函数,此函数用来报告测试结果* @param 无* @retval 无*/
static void lwiperf_report(void *arg,enum lwiperf_report_type report_type,const ip_addr_t *local_addr,u16_t local_port,const ip_addr_t *remote_addr,u16_t remote_port,u32_t bytes_transferred,u32_t ms_duration,u32_t bandwidth_kbitpsec)
{printf("-------------------------------------------------\r\n");if (((int)report_type < (sizeof(report_type_str)/sizeof(report_type_str[0]))) && local_addr && remote_addr){printf(" %s \r\n", report_type_str[report_type]);printf(" Local address : %u.%u.%u.%u ", ((u8_t *)local_addr)[0], ((u8_t *)local_addr)[1],((u8_t *)local_addr)[2], ((u8_t *)local_addr)[3]);printf(" Port %d \r\n", local_port);printf(" Remote address : %u.%u.%u.%u ", ((u8_t *)remote_addr)[0], ((u8_t *)remote_addr)[1],((u8_t *)remote_addr)[2], ((u8_t *)remote_addr)[3]);printf(" Port %d \r\n", remote_port);printf(" Bytes Transferred %d \r\n", (int)bytes_transferred);printf(" Duration (ms) %d \r\n", (int)ms_duration);printf(" Bandwidth (kbitpsec) %d \r\n", (int)bandwidth_kbitpsec);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%u.%u.%u.%u",((u8_t *)local_addr)[0], ((u8_t *)local_addr)[1],((u8_t *)local_addr)[2], ((u8_t *)local_addr)[3]); /* 显示动态IP地址 */printf("%s\n",info_buf);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%d",local_port); /* 显示本地端口 */printf("%s\n",info_buf);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%u.%u.%u.%u",((u8_t *)remote_addr)[0], ((u8_t *)remote_addr)[1],((u8_t *)remote_addr)[2], ((u8_t *)remote_addr)[3]); /* 显示远程IP地址 */printf("%s\n",info_buf);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%d",remote_port); /* 显示远程IP端口 */printf("%s\n",info_buf);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%d",(int)bytes_transferred/1024); /* 转换速度 */printf("%s\n",info_buf);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%d",(int)ms_duration); /* 持续时间 */printf("%s\n",info_buf);memset(info_buf,0,sizeof(info_buf));sprintf((char*)info_buf,"%d",(int)bandwidth_kbitpsec); /* 带宽 */printf("%s\n",info_buf);}else{printf(" IPERF Report error\r\n");}
}/*** @brief lwip_demo实验入口* @param 无* @retval 无*/
void speed_test(void)
{lwiperf_start_tcp_server_default(lwiperf_report,NULL);
}/******************speed_test.h内容如下**********************/
#ifndef __SPEED_TEST_H
#define __SPEED_TEST_H#include "main.h"void speed_test(void);#endif
3、main.c文件增加以下内容:
/******************main.c开头增加以下内容**********************/
#include "speed_test.h"/******************main函数while(1)前增加以下内容**********************/
speed_test();
至此就修改完成了,编译0警告0错误。烧录进开发板串口打印模块IP。
之后运行JPerf 网络测速工具(会和实验源码一起分享),按下图操作即可进行测速:
测速完成后串口也会打印相关信息:
看到这个结果大家肯定会纳闷了,官方不是宣称开发板带的是百兆网络吗,为什么这里测出来才23M呢?那接下来就告诉大家该如何进行优化:在“lwipopts.h”文件中修改以下参数的值可以提升网速(没有就新增):
/* 堆内存的大小,如果需要更大的堆内存,那么设置高一点 */
#define MEM_SIZE (30*1024)
/* MEMP_NUM_PBUF: 设置内存池的数量 */
#define MEMP_NUM_PBUF 25
/* MEMP_NUM_UDP_PCB: UDP 协议控制块的数量. */
#define MEMP_NUM_UDP_PCB 4
/* MEMP_NUM_TCP_PCB: TCP 的数量. */
#define MEMP_NUM_TCP_PCB 8
/* MEMP_NUM_TCP_PCB_LISTEN: 监听 TCP 的数量. */
#define MEMP_NUM_TCP_PCB_LISTEN 2
/* MEMP_NUM_TCP_SEG: 同时排队的 TCP 的数量段. */
#define MEMP_NUM_TCP_SEG 150
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#define PBUF_POOL_SIZE 20
/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)//1536
/* TCP 接收窗口 */
#define TCP_WND (20*TCP_MSS)
编译完成后再次测试,虽然没有达到100M,但也可以稳定在86M,欢迎大家继续优化并把优化好的参数分享在评论区: