現(xiàn)在,TCP/IP協(xié)議的應(yīng)用無處不在。隨著物聯(lián)網(wǎng)的火爆,嵌入式領(lǐng)域使用TCP/IP協(xié)議進行通訊也越來越廣泛。在我們的相關(guān)產(chǎn)品中,也都有應(yīng)用,所以我們結(jié)合應(yīng)用實際對相關(guān)應(yīng)用作相應(yīng)的總結(jié)。
1 、技術(shù)準備
我們采用的開發(fā)平臺是STM32F407和LwIP協(xié)議棧。在開始之前,我們需要做必要的準備工作。
首先要獲得LwIP的源碼,在網(wǎng)上有很多,不同版本及不同平臺的都有,不過我們還是建議直接從官方網(wǎng)站獲得。
其次,需要硬件平臺,我們采用了STM32F407ZG+DM9161的網(wǎng)絡(luò)接口方式,這并不是必須的,其他硬件平臺也是一樣的。
最后,因為我們后面要在操作系統(tǒng)下移植,采用的操作系統(tǒng)是FreeRTOS,所以還需下載FreeRTOS的源碼。
2 、 LwIP****簡要說明
LwIP是一款免費的TCP/IP協(xié)議棧,但它的功能趨勢十分完備。LwIP 具有三種應(yīng)用編程接口 (API):
- Raw API :為原始的 LwIP API。它通過事件回調(diào)機制進行應(yīng)用開發(fā)。該 API 提供了最好的性能和優(yōu)化的代碼長度,但增加了應(yīng)用開發(fā)的復(fù)雜性。
- Netconn API :為高層有序 API,需要實時操作系統(tǒng) (RTOS)的支持 (提供進程間通訊的方法)。 Netconn API 支持多線程工作。
- BSD Socket API :類似 Berkeley 的套接字 API (開發(fā)于 Netconn API 之上) 。
對于以上三種接口,前一種只需要裸機即可調(diào)用,后兩種需要操作系統(tǒng)才能調(diào)用。所以據(jù)此LwIP存在兩種移植方式:一是,只移植內(nèi)核,此時應(yīng)用程序的編寫只能基于RAW/Callback API進行。二是,移植內(nèi)核和上層API,此時應(yīng)用程序編寫可以使用3種API,即:RAW/Callback API、Sequential API和Socket API。
3 、 LwIP****的帶操作系統(tǒng)基本移植
帶操作系統(tǒng)的移植首先是建立在無操作系統(tǒng)移植基礎(chǔ)之上的。在無操作系統(tǒng)移植時,定義的數(shù)據(jù)類型和宏都是有效的,只需要對lwipopts.h配置文件做簡單修改,并根據(jù)sys_arch.txt移植說明文件編寫sys_arch.c和sys_arch.h兩個文件以實現(xiàn)操作系統(tǒng)模擬層就可以了。
操作系統(tǒng)模擬層的功能再以為協(xié)議棧提供郵箱、信號量、互斥量等機制,用以保證內(nèi)核與上層API的通訊。這些操作系統(tǒng)模擬層函數(shù)均在sys.h中已經(jīng)聲明,我們一般在sys_arch.c文件中完成其定義。所以,我們很清楚,帶操作系統(tǒng)的移植就是在無操作系統(tǒng)的基礎(chǔ)上添加操作系統(tǒng)模擬層。在接下來我們就看看操作系統(tǒng)模擬層的編寫。
在操作系統(tǒng)已經(jīng)正確移植的基礎(chǔ)上,我們根據(jù)sys_arch.txt移植說明文件的描述,還需要移植的宏定義及函數(shù)等如下:
名稱 | 屬性 | 功能 |
---|---|---|
sys_mbox_t | 數(shù)據(jù)類型 | 指針類型,指向系統(tǒng)郵箱 |
sys_sem_t | 數(shù)據(jù)類型 | 指針類型,指向系統(tǒng)信號量 |
sys_mutex_t | 數(shù)據(jù)類型 | 指針類型,指向系統(tǒng)互斥量 |
sys_thread_t | 數(shù)據(jù)類型 | 系統(tǒng)任務(wù)標識 |
SYS_MBOX_NULL | 宏 | 郵箱指針指向的空值 |
SYS_SEM_NULL | 宏 | 信號量指針指向的空值 |
sys_init | 函數(shù) | 初始化系統(tǒng)模擬層 |
sys_sem_new | 函數(shù) | 生成一個信號量 |
sys_sem_free | 函數(shù) | 刪除一個信號量 |
sys_sem_signal | 函數(shù) | 釋放一個信號量 |
sys_arch_sem_wait | 函數(shù) | 等待一個信號量 |
sys_sem_valid | 函數(shù) | 判斷一個信號量是否有效 |
sys_sem_set_invalid | 函數(shù) | 將一個信號量置為無效 |
sys_mutex_new | 函數(shù) | 生成一個新的互斥量 |
sys_mutex_free | 函數(shù) | 刪除一個互斥量 |
sys_mutex_lock | 函數(shù) | 鎖住一個互斥量 |
sys_mutex_unlock | 函數(shù) | 解鎖一個互斥量 |
sys_mutex_valid | 函數(shù) | 判斷一個互斥量是否有效 |
sys_mutex_set_invalid | 函數(shù) | 將一個互斥量置為無效 |
sys_mbox_new | 函數(shù) | 新建一個郵箱 |
sys_mbox_free | 函數(shù) | 刪除一個郵箱 |
sys_mbox_post | 函數(shù) | 向郵箱投遞消息,阻塞 |
sys_mbox_trypost | 函數(shù) | 嘗試向郵箱投遞消息,不阻塞 |
sys_arch_mbox_fetch | 函數(shù) | 從郵箱獲取消息,阻塞 |
sys_arch_mbox_tryfetch | 函數(shù) | 嘗試從郵箱獲取消息,不阻塞 |
sys_mbox_valid | 函數(shù) | 判斷一個郵箱是否有效 |
sys_mbox_set_invalid | 函數(shù) | 將一個郵箱設(shè)置為無效 |
sys_thread_new | 函數(shù) | 創(chuàng)建新進程 |
sys_arch_protect | 函數(shù) | 臨界區(qū)保護 |
sys_arch_unprotect | 函數(shù) | 退出臨界區(qū)保護 |
從上表中我們可以發(fā)現(xiàn),這些變量和函數(shù)主要是面向信號量、互斥量及郵箱,包括新建、刪除、釋放、獲取等各類操作,我們需要根據(jù)操作系統(tǒng)的規(guī)定來實現(xiàn)這些函數(shù),我們在這里使用的FreeRTOS,所以我根據(jù)FreeRTOS對信號量、互斥量及郵箱的操作來實現(xiàn)這些函數(shù)。我們列舉郵箱的各操作函數(shù)實現(xiàn)如下:
1 /*創(chuàng)建一個空的郵箱。*/
2 err_t sys_mbox_new(sys_mbox_t *mbox, int size)
3 {
4 osMessageQDef(QUEUE, size, void *);
5
6 *mbox = osMessageCreate(osMessageQ(QUEUE), NULL);
7
8 #if SYS_STATS
9 ++lwip_stats.sys.mbox.used;
10 if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {
11 lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
12 }
13 #endif /* SYS_STATS */
14 if (*mbox == NULL)
15 return ERR_MEM;
16
17 return ERR_OK;
18 }
19
20 /*重新分配一個郵箱。如果郵箱被釋放時,郵箱中仍有消息,在lwIP中這是出現(xiàn)編碼錯誤的指示,并通知開發(fā)人員。*/
21 void sys_mbox_free(sys_mbox_t *mbox)
22 {
23 if( osMessageWaiting(*mbox) )
24 {
25 portNOP();
26 #if SYS_STATS
27 lwip_stats.sys.mbox.err++;
28 #endif /* SYS_STATS */
29 }
30
31 osMessageDelete(*mbox);
32
33 #if SYS_STATS
34 --lwip_stats.sys.mbox.used;
35 #endif /* SYS_STATS */
36 }
37
38 /*發(fā)送消息到郵箱*/
39 void sys_mbox_post(sys_mbox_t *mbox, void *data)
40 {
41 while(osMessagePut(*mbox, (uint32_t)data, osWaitForever) != osOK);
42 }
43
44 /*嘗試將消息發(fā)送到郵箱*/
45 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
46 {
47 err_t result;
48
49 if ( osMessagePut(*mbox, (uint32_t)msg, 0) == osOK)
50 {
51 result = ERR_OK;
52 }
53 else {
54 result = ERR_MEM;
55
56 #if SYS_STATS
57 lwip_stats.sys.mbox.err++;
58 #endif /* SYS_STATS */
59
60 }
61
62 return result;
63 }
64
65 /*阻塞進程從郵箱獲取消息*/
66 u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
67 {
68 osEvent event;
69 uint32_t starttime = osKernelSysTick();;
70
71 if(timeout != 0)
72 {
73 event = osMessageGet (*mbox, timeout);
74
75 if(event.status == osEventMessage)
76 {
77 *msg = (void *)event.value.v;
78 return (osKernelSysTick() - starttime);
79 }
80 else
81 {
82 return SYS_ARCH_TIMEOUT;
83 }
84 }
85 else
86 {
87 event = osMessageGet (*mbox, osWaitForever);
88 *msg = (void *)event.value.v;
89 return (osKernelSysTick() - starttime);
90 }
91 }
92
93 /*嘗試從郵箱獲取消息*/
94 u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
95 {
96 osEvent event;
97
98 event = osMessageGet (*mbox, 0);
99
100 if(event.status == osEventMessage)
101 {
102 *msg = (void *)event.value.v;
103 return ERR_OK;
104 }
105 else
106 {
107 return SYS_MBOX_EMPTY;
108 }
109 }
110
111 /*判斷一個郵箱是否有效*/
112 int sys_mbox_valid(sys_mbox_t *mbox)
113 {
114 if (*mbox == SYS_MBOX_NULL)
115 return 0;
116 else
117 return 1;
118 }
119
120 /*設(shè)置一個郵箱無效*/
121 void sys_mbox_set_invalid(sys_mbox_t *mbox)
122 {
123 *mbox = SYS_MBOX_NULL;
124 }
125
126 // 創(chuàng)建一個新的信號量。而 "count"參數(shù)指示該信號量的初始狀態(tài)
127 err_t sys_sem_new(sys_sem_t *sem, u8_t count)
128 {
129 osSemaphoreDef(SEM);
130
131 *sem = osSemaphoreCreate (osSemaphore(SEM), 1);
132
133 if(*sem == NULL)
134 {
135 #if SYS_STATS
136 ++lwip_stats.sys.sem.err;
137 #endif /* SYS_STATS */
138 return ERR_MEM;
139 }
140
141 if(count == 0) // Means it can't be taken
142 {
143 osSemaphoreWait(*sem,0);
144 }
145
146 #if SYS_STATS
147 ++lwip_stats.sys.sem.used;
148 if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) {
149 lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
150 }
151 #endif /* SYS_STATS */
152
153 return ERR_OK;
154 }
此外還有一些函數(shù)也是協(xié)議棧需要的函數(shù),特別是sys_thread_new函數(shù),不但協(xié)議棧在初始化是需要用到,在后續(xù)我們實現(xiàn)各類基于LwIP的應(yīng)用時也需要用到,其實現(xiàn)如下:
1 sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
2 {
3 const osThreadDef_t os_thread_def = { (char *)name, (os_pthread)thread, (osPriority)prio, 0, stacksize};
4 return osThreadCreate(&os_thread_def, arg);
5 }
6 osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
7 {
8 TaskHandle_t handle;
9
10 #if( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
11 if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {
12 handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
13 thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
14 thread_def->buffer, thread_def->controlblock);
15 }
16 else {
17 if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
18 thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
19 &handle) != pdPASS) {
20 return NULL;
21 }
22 }
23 #elif( configSUPPORT_STATIC_ALLOCATION == 1 )
24
25 handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
26 thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
27 thread_def->buffer, thread_def->controlblock);
28 #else
29 if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
30 thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
31 &handle) != pdPASS) {
32 return NULL;
33 }
34 #endif
35
36 return handle;
37 }
至此,基于FreeRTOS操作系統(tǒng)的LwIP移植結(jié)算完成了,我們編譯下載就可以對其進行驗證。
4 、結(jié)論
前面已經(jīng)移植了基于操作系統(tǒng)的LwIP,那怎么知道我們的移植是否成功呢?接下來我們對它進行必要的驗證。
首先我們查看目標板在網(wǎng)絡(luò)上的配置是否正確。我們打開命令行窗口,運行ipconfig命令,查看MAC地址和IP地址配置:
我們配置的MAC地址00:08:E1:00:00:00和IP地址192.168.2.110顯示正常。接下來我們采用ping命令測試網(wǎng)絡(luò)鏈接:
上圖顯示網(wǎng)絡(luò)連接正常,經(jīng)此測試,說明我們的LwIP在有操作系統(tǒng)情況下移植正常。
-
服務(wù)器
+關(guān)注
關(guān)注
12文章
9123瀏覽量
85328 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6801瀏覽量
123285 -
TCP
+關(guān)注
關(guān)注
8文章
1353瀏覽量
79055 -
LwIP
+關(guān)注
關(guān)注
2文章
86瀏覽量
27149
發(fā)布評論請先 登錄
相關(guān)推薦
評論