上次介紹了ENC28J60在STM32上怎么寫好第一個Hello World,這次在上次修改好驅動的基礎上,使用UIP實現ARP包響應,ICMP響應(ping通MCU)
UIPgitee下載地址: https://gitee.com/jinling_gitee/uip.git github地址: https://github.com/adamdunkels/uip
下載后,項目路徑如下:
我們需要使用的文件主要是uip文件夾下的文件,和unix下的uip-conf.h(UIP配置項)和tapdev.h(底層SPI基礎讀寫實現)頭文件
在STM32中使用UIP時,由於UIP不是專門針對STM32設計的,所以需要修改一些東西才能正常使用:
1. 修改unix/uip-conf.h文件,這是UIP對用戶開放的一些配置文件
2. main.c中聲明UIP_APPCALL、uip_log回調函數,這是UIP提供的回調函數,為保證程序盡量簡單,本次ping沒有用到,暫時為空函數
3. 實現tapdev.h頭文件下聲明的tapdev_init ENC28J60初始化、tapdev_read 讀一包數據、tapdev_send 發送一包數據函數
修改uip-conf.h
對uip-conf.h做一些修改,才能正常使用:
1. 注釋webserver.h引入頭文件
實現UIP_APPCALL、uip_log函數
這兩個函數是UIP的回調函數,本次目的只為使用UIP ping通MCU,所以實現為空函數:
在main.c中聲明了兩個函數,分別對應UIP_APPCALL和uip_log,和timer.c中調用的clock_time
.... //UIP_APPCALL void main_appcall(){ } //UIP_APPCALL void main_uiplog(char* msg) { } int clock_time(){ } .....
回到uip-conf.h,聲明UIP_APPCALL和uip_log實現函數為main_appcall和main_uiplog:
#define UIP_APPCALL main_appcall #define uip_log main_uiplog
clock_time函數,已在clock.h中聲明,只不過沒有實現而已,所以此處不用重復聲明
uipopt.h中聲明uip_tcp_appstate_t appstate類型
在uipopt.h 503行位置,有已注釋掉的uip_tcp_appstate_t聲明,把此處打開:
或者去uip.h中注釋掉使用uip_tcp_appstate_t位置:
實現tapdev_init、tapdev_read、tapdev_send函數
新建tapdev.c,實現如下:
#include <tapdev.h> #include "enc28j60.h"//ENC28J60驅動 #include <uip.h> //引用外部文件uip.c中聲明的uip_ethaddr extern struct uip_eth_addr uip_ethaddr; //網卡地址 unsigned char my_mac[6] = {0x29, 0x7C, 0x07, 0x37, 0x24, 0x63}; void tapdev_init(void) { enc28j60_init(my_mac); for (int i = 0; i < 6; i++) { uip_ethaddr.addr[i] = my_mac[i]; } } //讀取一包數據 unsigned int tapdev_read(void){ enc28j60_packet_receive(uip_buf, MAX_FRAMELEN); } //發送一包數據 void tapdev_send(void) { enc28j60_packet_send(uip_buf, MAX_FRAMELEN); }
把UIP相關文件放在一起
最后把上述提到的unix/uip-conf.h、unix/tapdev.h放到UIP文件夾中,通過IDE(如keil5)設置頭文件路徑為UIP文件夾,添加如下圖紅框中的.c文件
keil5 添加下圖紅框中的.c文件
對於keil5 IDE來說,需要修改每個.c文件的引入頭文件,把""改為<>,例如:
最后,編譯成功,就可以進行下一步操作了。
main.c實現arp響應和icmp響應
在此之前,需要保證添加以上文件后,可以正常編譯通過:
首先在mainc.c中聲明UIP的UIP_BUF:
這個UIP_BUF主要用來在接收到數據后,判斷協議類型,源、目標MAC地址
#define UIP_BUF ((struct uip_eth_hdr *)&uip_buf[0])
處理ICMP請求操作:
處理ping請求過來的包
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_SPI1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ //初始化前等待一些時間 for(int i = 0;i < 20;i++) { //enc28j60PhyWrite(PHLCON,0x7a4); HAL_Delay(500); print("begin init enc28j60..."); } tapdev_init();//初始化enc28j60 print("init enc28j60 success!"); //初始化UIP uip_init(); uip_ipaddr_t ipaddr; uip_ipaddr(ipaddr, 192, 168, 1, 8);//MCU IP地址 uip_sethostaddr(ipaddr); uip_ipaddr(ipaddr, 192, 168, 1, 1);//網關,電腦和MCU經過路由器時使用 uip_setdraddr(ipaddr); uip_ipaddr(ipaddr, 255, 255, 252, 0);//子網掩碼 uip_setnetmask(ipaddr); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ //嘗試讀取一包數據 uip_len = tapdev_read(); if(uip_len == 0){ //沒有讀到數據 continue; } //是否是ARP請求 if(UIP_BUF->type == htons(UIP_ETHTYPE_ARP)) { //拼裝ARP響應請求 uip_arp_arpin(); if(uip_len>0){ //響應ARP請求發送數據 tapdev_send(); } } //是否是IPV4 if(UIP_BUF->type == htons(UIP_ETHTYPE_IP)) {
//ping過來時,會進入此判斷 uip_arp_ipin(); //UIP解析數據 uip_input(); if(uip_len > 0){ //必須調用,不然會出現ping不通情況,用抓包工具看,顯示收到數據包checksum bad 校驗和失敗 uip_arp_out(); tapdev_send(); } } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
電腦端鏈接MCU后,需要打開網絡設備器,設置為使用下面IP: