說在開頭
? ? ? ? 正點原子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,歡迎大家繼續優化并把優化好的參數分享在評論區: