本章節爲你們講解RL-TCPnet的SNTP應用,學習本章節前,務必要優先學習第29章的NTP基礎知識。有了這些基礎知識以後,再搞本章節會有事半功倍的效果。php
本章教程含STM32F407開發板和STM32F429開發板。數組
30.1 初學者重要提示服務器
30.2 可用的NTP服務器網絡
30.3 SNTP函數app
30.4 SNTP配置說明(Net_Config.c)tcp
30.5 SNTP調試說明(Net_Debug.c)ide
30.6 網絡調試助手和板子的操做步驟函數
30.7 實驗例程說明(裸機)學習
30.8 實驗例程說明(RTX)測試
30.9 總結
國內可用的NTP服務器已經在帖子:http://bbs.armfly.com/read.php?tid=31397 裏面進行了簡單的總結,咱們這裏使用IP地址爲182.16.3.162的NTP服務器。
涉及到SNTP的,僅有以下一個函數:
關於這個函數的講解及其使用方法能夠看教程第 3 章 3.4 小節裏面說的參考資料 rlarm.chm 文件:
注意,這個函數不支持重入,也就是不支持多任務調用。
函數原型:
BOOL sntp_get_time ( U8* ipadr, /* NTP/SNTP服務器IP地址 */ void (*cbfunc)( /* 回調函數,接收到NTP消息會觸發 */ U32 utc_time) ); /* 接收到的UNIX時間戳,從1970.1.1開始所經歷的秒數 */
函數描述:
函數sntp_get_time用於從NTP服務器得到UNIX時間戳,這個函數支持單播和廣播兩種模式。單播方式下,經過此函數給遠程NTP服務器發送獲取時間消息。廣播模式下,將打開UDP Socket接收NTP廣播消息,若是局域網內有NTP服務器,能夠採用這種模式。(廣播和單播模式是在Net_Config.c文件中設置的,若是沒有選擇廣播Broadcast模式,就表示單播,不然表示廣播。)
使用這個函數要注意如下問題:
使用舉例:
/* ********************************************************************************************************* * 用於本文件的調試 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************** 變量 ********************************************************************************************************** */ U8 ntp_server[4] = {182,16,3,162}; /* SNTP服務器地址 */ /* ********************************************************************************************************* * 函 數 名: time_cback * 功能說明: SNTP獲取時間回到函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void time_cback (uint32_t time) { struct tm *t_tm; if (time == 0) { printf_debug ("錯誤, 服務器未響應或者網絡狀態比較差\r\n"); } else { /* 因爲是格林尼治時間,因此北京時間要加8個小時 */ time += 8*60*60; /* 將獲取的UNIX時間戳轉換成年月日時分秒的格式 */ t_tm = localtime((unsigned int *)&time); /* 格林尼治時間是從1900年開始的,這要加上 */ t_tm->tm_year += 1900; /* 獲取的月份範圍是0-11,這裏要加1 */ t_tm->tm_mon += 1; printf_debug ("UNIX時間戳:%d 日期:%02d/%02d/%02d 時間:%02d:%02d:%02d\r\n", time, t_tm->tm_year, t_tm->tm_mon, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec); } } /* ********************************************************************************************************* * 函 數 名: get_time * 功能說明: 從SNTP服務器獲取當前時間 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void get_time (void) { if (sntp_get_time((U8 *)&ntp_server[0], &time_cback) == __TRUE) { printf_debug ("SNTP請求已經發送成功\r\n"); } else { printf_debug ("失敗, SNTP未就緒或者參數錯誤\r\n"); } } /* ********************************************************************************************************* * 函 數 名: tcpnet_poll * 功能說明: 使用TCPnet必需要一直調用的函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void tcpnet_poll(void) { if(bsp_CheckTimer(0)) { bsp_LedToggle(2); /* 此函數堅定不能夠放在中斷裏面跑 */ timer_tick (); } main_TcpNet (); } /* ********************************************************************************************************* * 函 數 名: TCPnetTest * 功能說明: TCPnet應用 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TCPnetTest(void) { /* 初始化網絡協議棧 */ init_TcpNet (); /* 建立一個週期是100ms的軟定時器 */ bsp_StartAutoTimer(0, 100); /* 再建立一個週期是1000ms的軟定時器 */ bsp_StartAutoTimer(1, 1000); while (1) { /* TCP輪詢 */ tcpnet_poll(); /* 每秒從NTP服務器獲取一次時間 */ if(bsp_CheckTimer(1)) { get_time(); } } }
(本章節配套例子的配置與本小節的說明相同)
RL-TCPnet的配置工做是經過配置文件Net_Config.c實現。在MDK工程中打開文件Net_Config.c,能夠看到下圖所示的工程配置嚮導:
RL-TCPnet要配置的選項很是多,咱們這裏把幾個主要的配置選項簡單介紹下。
System Definitions
(1)Local Host Name
局域網域名。
這裏起名爲armfly,使用局域網域名限制爲15個字符。
(2)Memory Pool size
參數範圍1536-262144字節。
內存池大小配置,單位字節。另外注意一點,配置嚮導這裏顯示的單位是字節,若是看原始定義,MDK會作一個自動的4字節倍數轉換,好比咱們這裏配置的是8192字節,那麼原始定義是#define MEM_SIZE 2048,也就是8192/4 = 2048。
(3)Tick Timer interval
可取10,20,25,40,50,100,200,單位ms。
系統滴答時鐘間隔,也就是網絡協議棧的系統時間基準,默認狀況下,取值100ms。
Ethernet Network Interface
以太網接口配置,勾選了此選項就能夠配置了,若是沒有使能DHCP的話,將使用這裏配置的固定IP。
(1) MAC Address
局域網內能夠隨意配置,只要不跟局域網內其它設備的MAC地址衝突便可。
(2)IP Address
IP地址。
(3)Subnet mask
子網掩碼。
(4)Default Gateway
默認網關。
Ethernet Network Interface
以太網接口配置,這個配置裏面還有以下兩項比較重要的配置須要說明。
(1)NetBIOS Name Service
NetBIOS局域網域名服務,這裏打上對勾就使能了。這樣咱們就能夠經過前面配置的Local Host Name局域網域名進行訪問,而不須要經過IP地址訪問了。
(2)Dynaminc Host Configuration
即DHCP,這裏打上對勾就使能了。使能了DHCP後,RL-TCPnet就能夠從外接的路由器上得到動態IP地址。
UDP Sockets
UDP Sockets配置,打上對勾就使能了此項功能
(1) Number of UDP Sockets
用於配置可建立的UDP Sockets數量,這裏配置了5個。
範圍1 – 20。
TCP Sockets
TCP Sockets配置,打上對勾就使能了此項功能
(1) Number of TCP Sockets
用於配置可建立的TCP Sockets數量。
(2)Number of Retries
範圍0-20。
用於配置重試次數,TCP數據傳輸時,若是在設置的重試時間內得不到應答,算一次重試失敗,這裏就是配置的最大重試次數。
(3)Retry Timeout in seconds
範圍1-10,單位秒。
重試時間。若是發送的數據在重試時間內得不到應答,將從新發送數據。
(4)Default Connect Timeout in seconds
範圍1-600,單位秒。
用於配置默認的保持鏈接時間,即咱們常說的Keep Alive時間,若是時間到了將斷開鏈接。經常使用於HTTP Server,Telnet Server等。
(5)Maximum Segment Size
範圍536-1460,單位字節。
MSS定義了TCP數據包可以傳輸的最大數據分段。
(6)Receive Window Size
範圍536-65535,單位字節。
TCP接收窗口大小。
SNTP Client
SNTP配置,打上對勾就使能了此項功能
(1) Broadcast Mode
打上對勾表示使能廣播模式,不選擇表示單播模式。
(2)NTP Server
這裏是NTP服務器的IP地址。
實際應用中,這兩個選項的做用看本章30.3.1小節的函數sntp_get_time便可。
(重要說明,RL-TCPnet的調試是經過串口打印出來的)
RL-TCPnet的調試功能是經過配置文件Net_Debug.c實現。在MDK工程中打開文件Net_Debug.c,能夠看到以下圖所示的工程配置嚮導:
Print Time Stamp
勾選了此選項的話,打印消息時,前面會附帶時間信息。
其它全部的選項
默認狀況下,全部的調試選項都關閉了,每一個選項有三個調試級別可選擇,這裏咱們以SNTP Debug爲例,點擊下拉列表,能夠看到裏面有Off,Errors only和Full debug三個調試級別可供選擇,每一個調試選項裏面都是這三個級別。
Off:表示關閉此選項的調試功能。
Errors only:表示僅在此選項出錯時,將其錯誤打印出來。
Full debug:表示此選項的全功能調試。
下面是對SNTP Debug配置爲Full debug時,打印出來的消息(NTP服務器採用的182.16.3.162):
本章的操做相對比較簡單,用戶務必將板子鏈接到可以聯網的路由器或者交換機上。串口會每秒打印一次獲取的UNIX時間戳(波特率115200,數據位8,奇偶校驗位無,中止位1):
配套例子:
V5-1040_RL-TCPnet實驗SNTP應用(裸機)
實驗目的:
實驗內容:
實驗操做:
詳見本章節30.6小節。
配置嚮導文件設置(Net_Config.c):
詳見本章節30.4小節。
調試文件設置(Net_Debug.c):
詳見本章節30.5小節。
程序設計:
主函數初始化
在main.c文件實現:
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標準c程序入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main (void) { /* 初始化外設 */ bsp_Init(); /* 進入RL-TCPnet測試函數 */ TCPnetTest(); }
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化全部的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只須要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 因爲ST固件庫的啓動文件已經執行了CPU系統時鐘的初始化,因此沒必要再次重複配置系統時鐘。 啓動文件配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。 系統時鐘缺省配置爲168MHz,若是須要更改,能夠修改 system_stm32f4xx.c 文件 */ /* 優先級分組設置爲4,可配置0-15級搶佔式優先級,0級子優先級,即不存在子優先級。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按鍵變量(必須在 bsp_InitTimer() 以前調用) */ bsp_InitLed(); /* 初始LED指示燈端口 */ bsp_InitTimer(); /* 初始化滴答定時器 */ }
RL-TCPnet功能測試
這裏專門建立了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,此文件主要實現從遠程NTP服務器獲取當前日期和時間,精度到秒。
/* ********************************************************************************************************* * 用於本文件的調試 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************** 變量 ********************************************************************************************************** */ U8 ntp_server[4] = {182,16,3,162}; /* SNTP服務器地址 */ /* ********************************************************************************************************* * 函 數 名: time_cback * 功能說明: SNTP獲取時間回到函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void time_cback (uint32_t time) { struct tm *t_tm; if (time == 0) { printf_debug ("錯誤, 服務器未響應或者網絡狀態比較差\r\n"); } else { /* 因爲是格林尼治時間,因此北京時間要加8個小時 */ time += 8*60*60; /* 將獲取的UNIX時間戳轉換成年月日時分秒的格式 */ t_tm = localtime((unsigned int *)&time); /* 格林尼治時間是從1900年開始的,這要加上 */ t_tm->tm_year += 1900; /* 獲取的月份範圍是0-11,這裏要加1 */ t_tm->tm_mon += 1; printf_debug ("UNIX時間戳:%d 日期:%02d/%02d/%02d 時間:%02d:%02d:%02d\r\n", time, t_tm->tm_year, t_tm->tm_mon, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec); } } /* ********************************************************************************************************* * 函 數 名: get_time * 功能說明: 從SNTP服務器獲取當前時間 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void get_time (void) { if (sntp_get_time((U8 *)&ntp_server[0], &time_cback) == __TRUE) { printf_debug ("SNTP請求已經發送成功\r\n"); } else { printf_debug ("失敗, SNTP未就緒或者參數錯誤\r\n"); } } /* ********************************************************************************************************* * 函 數 名: tcpnet_poll * 功能說明: 使用TCPnet必需要一直調用的函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void tcpnet_poll(void) { if(bsp_CheckTimer(0)) { bsp_LedToggle(2); /* 此函數堅定不能夠放在中斷裏面跑 */ timer_tick (); } main_TcpNet (); } /* ********************************************************************************************************* * 函 數 名: TCPnetTest * 功能說明: TCPnet應用 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TCPnetTest(void) { /* 初始化網絡協議棧 */ init_TcpNet (); /* 建立一個週期是100ms的軟定時器 */ bsp_StartAutoTimer(0, 100); /* 再建立一個週期是1000ms的軟定時器 */ bsp_StartAutoTimer(1, 1000); while (1) { /* TCP輪詢 */ tcpnet_poll(); /* 每秒從NTP服務器獲取一次時間 */ if(bsp_CheckTimer(1)) { get_time(); } } }
配套例子:
V6-1040_RL-TCPnet實驗SNTP應用(裸機)
實驗目的:
實驗內容:
實驗操做:
詳見本章節30.6小節。
配置嚮導文件設置(Net_Config.c):
詳見本章節30.4小節。
調試文件設置(Net_Debug.c):
詳見本章節30.5小節。
程序設計:
主函數初始化
在main.c文件實現:
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標準c程序入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main (void) { /* 初始化外設 */ bsp_Init(); /* 進入RL-TCPnet測試函數 */ TCPnetTest(); }
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化全部的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只須要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 因爲ST固件庫的啓動文件已經執行了CPU系統時鐘的初始化,因此沒必要再次重複配置系統時鐘。 啓動文件配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。 系統時鐘缺省配置爲168MHz,若是須要更改,能夠修改 system_stm32f4xx.c 文件 */ /* 優先級分組設置爲4,可配置0-15級搶佔式優先級,0級子優先級,即不存在子優先級。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按鍵變量(必須在 bsp_InitTimer() 以前調用) */ bsp_InitLed(); /* 初始LED指示燈端口 */ bsp_InitTimer(); /* 初始化滴答定時器 */ }
RL-TCPnet功能測試
這裏專門建立了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,此文件主要實現從遠程NTP服務器獲取當前日期和時間,精度到秒。
/* ********************************************************************************************************* * 用於本文件的調試 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************** 變量 ********************************************************************************************************** */ U8 ntp_server[4] = {182,16,3,162}; /* SNTP服務器地址 */ /* ********************************************************************************************************* * 函 數 名: time_cback * 功能說明: SNTP獲取時間回到函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void time_cback (uint32_t time) { struct tm *t_tm; if (time == 0) { printf_debug ("錯誤, 服務器未響應或者網絡狀態比較差\r\n"); } else { /* 因爲是格林尼治時間,因此北京時間要加8個小時 */ time += 8*60*60; /* 將獲取的UNIX時間戳轉換成年月日時分秒的格式 */ t_tm = localtime((unsigned int *)&time); /* 格林尼治時間是從1900年開始的,這要加上 */ t_tm->tm_year += 1900; /* 獲取的月份範圍是0-11,這裏要加1 */ t_tm->tm_mon += 1; printf_debug ("UNIX時間戳:%d 日期:%02d/%02d/%02d 時間:%02d:%02d:%02d\r\n", time, t_tm->tm_year, t_tm->tm_mon, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec); } } /* ********************************************************************************************************* * 函 數 名: get_time * 功能說明: 從SNTP服務器獲取當前時間 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void get_time (void) { if (sntp_get_time((U8 *)&ntp_server[0], &time_cback) == __TRUE) { printf_debug ("SNTP請求已經發送成功\r\n"); } else { printf_debug ("失敗, SNTP未就緒或者參數錯誤\r\n"); } } /* ********************************************************************************************************* * 函 數 名: tcpnet_poll * 功能說明: 使用TCPnet必需要一直調用的函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void tcpnet_poll(void) { if(bsp_CheckTimer(0)) { bsp_LedToggle(2); /* 此函數堅定不能夠放在中斷裏面跑 */ timer_tick (); } main_TcpNet (); } /* ********************************************************************************************************* * 函 數 名: TCPnetTest * 功能說明: TCPnet應用 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TCPnetTest(void) { /* 初始化網絡協議棧 */ init_TcpNet (); /* 建立一個週期是100ms的軟定時器 */ bsp_StartAutoTimer(0, 100); /* 再建立一個週期是1000ms的軟定時器 */ bsp_StartAutoTimer(1, 1000); while (1) { /* TCP輪詢 */ tcpnet_poll(); /* 每秒從NTP服務器獲取一次時間 */ if(bsp_CheckTimer(1)) { get_time(); } } }
配套例子:
V5-1041_RL-TCPnet實驗SNTP應用(RTX)
實驗目的:
實驗內容:
實驗操做:
詳見本章節30.6小節。
配置嚮導文件設置(Net_Config.c):
詳見本章節30.4小節。
調試文件設置(Net_Debug.c):
詳見本章節30.5小節。
RTX配置:
RTX配置嚮導詳情以下:
Task Configuration
(1)Number of concurrent running tasks
容許建立6個任務,實際建立了以下5個任務:
AppTaskUserIF任務 :按鍵消息處理。
AppTaskLED任務 :LED閃爍。
AppTaskMsgPro任務 :按鍵檢測。
AppTaskTCPMain任務:RL-TCPnet測試任務。
AppTaskStart任務 :啓動任務,也是最高優先級任務,這裏實現RL-TCPnet的時間基準更新。
(2)Number of tasks with user-provided stack
建立的5個任務都是採用自定義堆棧方式。
(3)Run in privileged mode
設置任務運行在非特權級模式。
RTX任務調試信息:
程序設計:
任務棧大小分配:
static uint64_t AppTaskUserIFStk[1024/8]; /* 任務棧 */
static uint64_t AppTaskLEDStk[1024/8]; /* 任務棧 */
static uint64_t AppTaskMsgProStk[1024/8]; /* 任務棧 */
static uint64_t AppTaskTCPMainStk[2048/8]; /* 任務棧 */
static uint64_t AppTaskStartStk[1024/8]; /* 任務棧 */
將任務棧定義成uint64_t類型能夠保證任務棧是8字節對齊的,8字節對齊的含義就是數組的首地址對8求餘等於0。若是不作8字節對齊的話,部分C語言庫函數、浮點運算和uint64_t類型數據運算會出問題。
系統棧大小分配:
RTX初始化:
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標準c程序入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main (void) { /* 初始化外設 */ bsp_Init(); /* 建立啓動任務 */ os_sys_init_user (AppTaskStart, /* 任務函數 */ 5, /* 任務優先級 */ &AppTaskStartStk, /* 任務棧 */ sizeof(AppTaskStartStk)); /* 任務棧大小,單位字節數 */ while(1); }
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化全部的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只須要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 因爲ST固件庫的啓動文件已經執行了CPU系統時鐘的初始化,因此沒必要再次重複配置系統時鐘。 啓動文件配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。 系統時鐘缺省配置爲168MHz,若是須要更改,能夠修改 system_stm32f4xx.c 文件 */ /* 優先級分組設置爲4,可配置0-15級搶佔式優先級,0級子優先級,即不存在子優先級。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); bsp_InitDWT(); /* 初始化DWT */ bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按鍵變量(必須在 bsp_InitTimer() 以前調用) */ bsp_InitLed(); /* 初始LED指示燈端口 */ }
RTX任務建立:
/* ********************************************************************************************************* * 函 數 名: AppTaskCreate * 功能說明: 建立應用任務 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void AppTaskCreate (void) { HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任務函數 */ 1, /* 任務優先級 */ &AppTaskUserIFStk, /* 任務棧 */ sizeof(AppTaskUserIFStk)); /* 任務棧大小,單位字節數 */ HandleTaskLED = os_tsk_create_user(AppTaskLED, /* 任務函數 */ 2, /* 任務優先級 */ &AppTaskLEDStk, /* 任務棧 */ sizeof(AppTaskLEDStk)); /* 任務棧大小,單位字節數 */ HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro, /* 任務函數 */ 3, /* 任務優先級 */ &AppTaskMsgProStk, /* 任務棧 */ sizeof(AppTaskMsgProStk)); /* 任務棧大小,單位字節數 */ HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain, /* 任務函數 */ 4, /* 任務優先級 */ &AppTaskTCPMainStk, /* 任務棧 */ sizeof(AppTaskTCPMainStk)); /* 任務棧大小,單位字節數 */ }
五個RTX任務的實現:
/* ********************************************************************************************************* * 函 數 名: AppTaskUserIF * 功能說明: 按鍵消息處理 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 1 (數值越小優先級越低,這個跟uCOS相反) ********************************************************************************************************* */ __task void AppTaskUserIF(void) { uint8_t ucKeyCode; while(1) { ucKeyCode = bsp_GetKey(); if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { /* K1鍵按下 */ case KEY_DOWN_K1: printf("K1鍵按下 \r\n"); break; /* K2鍵按下 */ case KEY_DOWN_K2: printf("K2鍵按下 \r\n"); break; /* K3鍵按下 */ case KEY_DOWN_K3: printf("K3鍵按下 \r\n"); break; /* 其餘的鍵值不處理 */ default: break; } } os_dly_wait(20); } } /* ********************************************************************************************************* * 函 數 名: AppTaskLED * 功能說明: LED閃爍。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 500; /* 延遲週期 */ /* 設置延遲週期 */ os_itv_set(usFrequency); while(1) { bsp_LedToggle(2); /* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/ os_itv_wait(); } } /* ********************************************************************************************************* * 函 數 名: AppTaskMsgPro * 功能說明: 按鍵檢測 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 3 ********************************************************************************************************* */ __task void AppTaskMsgPro(void) { while(1) { bsp_KeyScan(); os_dly_wait(10); } } /* ********************************************************************************************************* * 函 數 名: AppTaskTCPMain * 功能說明: RL-TCPnet測試任務 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 4 ********************************************************************************************************* */ __task void AppTaskTCPMain(void) { while (1) { TCPnetTest(); } } /* ********************************************************************************************************* * 函 數 名: AppTaskStart * 功能說明: 啓動任務,也是最高優先級任務,這裏實現RL-TCPnet的時間基準更新。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 5 ********************************************************************************************************* */ __task void AppTaskStart(void) { /* 初始化RL-TCPnet */ init_TcpNet (); /* 建立任務 */ AppTaskCreate(); os_itv_set (100); while(1) { os_itv_wait (); /* RL-TCPnet時間基準更新函數 */ timer_tick (); } }
RL-TCPnet功能測試
這裏專門建立了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,此文件主要實現從遠程NTP服務器獲取當前日期和時間,精度到秒。
#include "includes.h" /* ********************************************************************************************************* * 用於本文件的調試 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************** 變量 ********************************************************************************************************** */ U8 ntp_server[4] = {182,16,3,162}; /* SNTP服務器地址 */ /* ********************************************************************************************************* * 函 數 名: time_cback * 功能說明: SNTP獲取時間回到函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void time_cback (uint32_t time) { struct tm *t_tm; if (time == 0) { printf_debug ("錯誤, 服務器未響應或者網絡狀態比較差\r\n"); } else { /* 因爲是格林尼治時間,因此北京時間要加8個小時 */ time += 8*60*60; /* 將獲取的UNIX時間戳轉換成年月日時分秒的格式 */ t_tm = localtime((unsigned int *)&time); /* 格林尼治時間是從1900年開始的,這要加上 */ t_tm->tm_year += 1900; /* 獲取的月份範圍是0-11,這裏要加1 */ t_tm->tm_mon += 1; printf_debug ("UNIX時間戳:%d 日期:%02d/%02d/%02d 時間:%02d:%02d:%02d\r\n", time, t_tm->tm_year, t_tm->tm_mon, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec); } } /* ********************************************************************************************************* * 函 數 名: get_time * 功能說明: 從SNTP服務器獲取當前時間 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void get_time (void) { if (sntp_get_time((U8 *)&ntp_server[0], &time_cback) == __TRUE) { printf_debug ("SNTP請求已經發送成功\r\n"); } else { printf_debug ("失敗, SNTP未就緒或者參數錯誤\r\n"); } } /* ********************************************************************************************************* * 函 數 名: TCPnetTest * 功能說明: TCPnet應用 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TCPnetTest(void) { uint32_t tstart, tend; /* 初始化起始時間 */ tstart = os_time_get(); while (1) { /* RL-TCPnet處理函數 */ main_TcpNet(); /* 每秒從NTP服務器獲取一次時間 */ tend = os_time_get() - tstart; if(tend >= 1000) { tstart = os_time_get(); get_time(); } os_dly_wait(2); } }
配套例子:
V6-1041_RL-TCPnet實驗SNTP應用(RTX)
實驗目的:
實驗內容:
實驗操做:
詳見本章節30.6小節。
配置嚮導文件設置(Net_Config.c):
詳見本章節30.4小節。
調試文件設置(Net_Debug.c):
詳見本章節30.5小節。
RTX配置:
RTX配置嚮導詳情以下:
Task Configuration
(1)Number of concurrent running tasks
容許建立6個任務,實際建立了以下5個任務:
AppTaskUserIF任務 :按鍵消息處理。
AppTaskLED任務 :LED閃爍。
AppTaskMsgPro任務 :按鍵檢測。
AppTaskTCPMain任務:RL-TCPnet測試任務。
AppTaskStart任務 :啓動任務,也是最高優先級任務,這裏實現RL-TCPnet的時間基準更新。
(2)Number of tasks with user-provided stack
建立的5個任務都是採用自定義堆棧方式。
(3)Run in privileged mode
設置任務運行在非特權級模式。
RTX任務調試信息:
程序設計:
任務棧大小分配:
static uint64_t AppTaskUserIFStk[1024/8]; /* 任務棧 */
static uint64_t AppTaskLEDStk[1024/8]; /* 任務棧 */
static uint64_t AppTaskMsgProStk[1024/8]; /* 任務棧 */
static uint64_t AppTaskTCPMainStk[2048/8]; /* 任務棧 */
static uint64_t AppTaskStartStk[1024/8]; /* 任務棧 */
將任務棧定義成uint64_t類型能夠保證任務棧是8字節對齊的,8字節對齊的含義就是數組的首地址對8求餘等於0。若是不作8字節對齊的話,部分C語言庫函數、浮點運算和uint64_t類型數據運算會出問題。
系統棧大小分配:
RTX初始化:
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標準c程序入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main (void) { /* 初始化外設 */ bsp_Init(); /* 建立啓動任務 */ os_sys_init_user (AppTaskStart, /* 任務函數 */ 5, /* 任務優先級 */ &AppTaskStartStk, /* 任務棧 */ sizeof(AppTaskStartStk)); /* 任務棧大小,單位字節數 */ while(1); }
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化全部的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只須要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 因爲ST固件庫的啓動文件已經執行了CPU系統時鐘的初始化,因此沒必要再次重複配置系統時鐘。 啓動文件配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。 系統時鐘缺省配置爲168MHz,若是須要更改,能夠修改 system_stm32f4xx.c 文件 */ /* 優先級分組設置爲4,可配置0-15級搶佔式優先級,0級子優先級,即不存在子優先級。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); SystemCoreClockUpdate(); /* 根據PLL配置更新系統時鐘頻率變量 SystemCoreClock */ bsp_InitDWT(); /* 初始化DWT */ bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按鍵變量(必須在 bsp_InitTimer() 以前調用) */ bsp_InitExtIO(); /* FMC總線上擴展了32位輸出IO, 操做LED等外設必須初始化 */ bsp_InitLed(); /* 初始LED指示燈端口 */ }
RTX任務建立:
/* ********************************************************************************************************* * 函 數 名: AppTaskCreate * 功能說明: 建立應用任務 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void AppTaskCreate (void) { HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任務函數 */ 1, /* 任務優先級 */ &AppTaskUserIFStk, /* 任務棧 */ sizeof(AppTaskUserIFStk)); /* 任務棧大小,單位字節數 */ HandleTaskLED = os_tsk_create_user(AppTaskLED, /* 任務函數 */ 2, /* 任務優先級 */ &AppTaskLEDStk, /* 任務棧 */ sizeof(AppTaskLEDStk)); /* 任務棧大小,單位字節數 */ HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro, /* 任務函數 */ 3, /* 任務優先級 */ &AppTaskMsgProStk, /* 任務棧 */ sizeof(AppTaskMsgProStk)); /* 任務棧大小,單位字節數 */ HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain, /* 任務函數 */ 4, /* 任務優先級 */ &AppTaskTCPMainStk, /* 任務棧 */ sizeof(AppTaskTCPMainStk)); /* 任務棧大小,單位字節數 */ }
五個RTX任務的實現:
/* ********************************************************************************************************* * 函 數 名: AppTaskUserIF * 功能說明: 按鍵消息處理 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 1 (數值越小優先級越低,這個跟uCOS相反) ********************************************************************************************************* */ __task void AppTaskUserIF(void) { uint8_t ucKeyCode; while(1) { ucKeyCode = bsp_GetKey(); if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { /* K1鍵按下 */ case KEY_DOWN_K1: printf("K1鍵按下 \r\n"); break; /* K2鍵按下 */ case KEY_DOWN_K2: printf("K2鍵按下 \r\n"); break; /* K3鍵按下 */ case KEY_DOWN_K3: printf("K3鍵按下 \r\n"); break; /* 其餘的鍵值不處理 */ default: break; } } os_dly_wait(20); } } /* ********************************************************************************************************* * 函 數 名: AppTaskLED * 功能說明: LED閃爍。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 500; /* 延遲週期 */ /* 設置延遲週期 */ os_itv_set(usFrequency); while(1) { bsp_LedToggle(2); /* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/ os_itv_wait(); } } /* ********************************************************************************************************* * 函 數 名: AppTaskMsgPro * 功能說明: 按鍵檢測 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 3 ********************************************************************************************************* */ __task void AppTaskMsgPro(void) { while(1) { bsp_KeyScan(); os_dly_wait(10); } } /* ********************************************************************************************************* * 函 數 名: AppTaskTCPMain * 功能說明: RL-TCPnet測試任務 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 4 ********************************************************************************************************* */ __task void AppTaskTCPMain(void) { while (1) { TCPnetTest(); } } /* ********************************************************************************************************* * 函 數 名: AppTaskStart * 功能說明: 啓動任務,也是最高優先級任務,這裏實現RL-TCPnet的時間基準更新。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 5 ********************************************************************************************************* */ __task void AppTaskStart(void) { /* 初始化RL-TCPnet */ init_TcpNet (); /* 建立任務 */ AppTaskCreate(); os_itv_set (100); while(1) { os_itv_wait (); /* RL-TCPnet時間基準更新函數 */ timer_tick (); } }
RL-TCPnet功能測試
這裏專門建立了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,此文件主要實現從遠程NTP服務器獲取當前日期和時間,精度到秒。
#include "includes.h" /* ********************************************************************************************************* * 用於本文件的調試 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************** 變量 ********************************************************************************************************** */ U8 ntp_server[4] = {182,16,3,162}; /* SNTP服務器地址 */ /* ********************************************************************************************************* * 函 數 名: time_cback * 功能說明: SNTP獲取時間回到函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void time_cback (uint32_t time) { struct tm *t_tm; if (time == 0) { printf_debug ("錯誤, 服務器未響應或者網絡狀態比較差\r\n"); } else { /* 因爲是格林尼治時間,因此北京時間要加8個小時 */ time += 8*60*60; /* 將獲取的UNIX時間戳轉換成年月日時分秒的格式 */ t_tm = localtime((unsigned int *)&time); /* 格林尼治時間是從1900年開始的,這要加上 */ t_tm->tm_year += 1900; /* 獲取的月份範圍是0-11,這裏要加1 */ t_tm->tm_mon += 1; printf_debug ("UNIX時間戳:%d 日期:%02d/%02d/%02d 時間:%02d:%02d:%02d\r\n", time, t_tm->tm_year, t_tm->tm_mon, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec); } } /* ********************************************************************************************************* * 函 數 名: get_time * 功能說明: 從SNTP服務器獲取當前時間 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void get_time (void) { if (sntp_get_time((U8 *)&ntp_server[0], &time_cback) == __TRUE) { printf_debug ("SNTP請求已經發送成功\r\n"); } else { printf_debug ("失敗, SNTP未就緒或者參數錯誤\r\n"); } } /* ********************************************************************************************************* * 函 數 名: TCPnetTest * 功能說明: TCPnet應用 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TCPnetTest(void) { uint32_t tstart, tend; /* 初始化起始時間 */ tstart = os_time_get(); while (1) { /* RL-TCPnet處理函數 */ main_TcpNet(); /* 每秒從NTP服務器獲取一次時間 */ tend = os_time_get() - tstart; if(tend >= 1000) { tstart = os_time_get(); get_time(); } os_dly_wait(2); } }
本章節就爲你們講解這麼多,本章節的內容相對比較簡單,主要是函數sntp_get_time的使用,但願你們熟練掌握。