uip在stm32上移植问题总结

Uip + Stm32移植问题总结

uIP 由瑞典计算机科学学院(网络嵌入式系统小组)的Adam Dunkels (http://dunkels.com/adam/uip/)开发。其源代码由C 语言编写,并完全公开,有了这个TCP/IP协议栈,让嵌入式可以实现的功能更为丰富。可以作为 WebClient 向指定网站提交数据,可以作为 WebServer作为网页服务器,提供一个小型的动态页面访问功能。由于是开源的免费协议栈,据说Uip没有考虑协议安全的问题。
 
首先介绍下移植的环境: stm32 + ENC28J60网络模块 
 
IMG_20100101_103506.jpg
 
Enc28j60是带SPI 接口的独立以太网控制器,可以用mcu控制spi来实现tcp/ip数据流的收发,所以要先完成Enc28j60的驱动程序,再整合Uip。Uip是用标准的C语言实现,所以移植Uip在51单片机和stm32上类似。
 
经过几天的琢磨,已经将Uip的几个示例稳定运行。Uip中apps下的例子相互之间存在冲突,源程序中也有一些Error 要修改,我将Uip的文件结构做了一些调整。

 
Uip文件结构
 
先介绍下Uip下各个目录文件的功能:
 
├─apps                               apps目录 下为uip提供的一些应用示例
│  ├─dhcpc
│  ├─hello-world
│  ├─resolv
│  ├─smtp
│  ├─telnetd
│  ├─webclient
│  └─webserver
│      └─httpd-fs
├─doc                              doc下放置的为说明文档,程序中用不上  
│  └─html
├─lib                                lib下为内存块管理函数源码
├─uip                               uip下为uip和核心实现源码 
└─unix                              unix环境里的uip应用例子,可以参照这个例子实现应用
 
Uip+stm32 MDK下工程建立
 
QQ截图20120726204704.png
stm32的目录结构建立可以参考  stm32 开发环境MDK+库文件配置 
 
User 放置 stm32 SPI配置以及Uip配置和Enc28j60和Uip的接口函数
 
 
 
 
uip下为uip的核心实现源码以及内存管理源码(即为Uip/uip+Uip/lib)
dev下为Enc28j60的驱动函数源码
apps为uip的各个示例应用源码(Uip/apps下的文件)包括smtp,rsolve,dhcp,telnetd,以及webclient
 
webserver 的文件结构较为复杂,独立一个文件夹
 
 
Uip移植
 
Uip的移植可以参考uip的unix的文件结构。
 
1. Uip的数据通过网卡Enc28j60从物理层剥离,所以需要先配置Uip和Enc28j60的数据交互。这个部分在tapdev.c文件中:
01 #include "uip.h"
02 #include "ENC28J60.h"
03  
04 /*---------------------------------------------------------------------------*/
05 void
06 tapdev_init(unsigned char *my_mac)
07 {
08     enc28j60Init(my_mac);
09 }
10 /*---------------------------------------------------------------------------*/
11 unsigned int
12 tapdev_read(void)
13 {
14     return enc28j60PacketReceive(UIP_CONF_BUFFER_SIZE,uip_buf);
15 }
16 /*---------------------------------------------------------------------------*/
17 void
18 tapdev_send(void)
19 {
20     enc28j60PacketSend(uip_len,uip_buf);
21 }
22 /*---------------------------------------------------------------------------*/
写网卡驱动程序,与具体硬件相关。这一步比较费点时间,不过好在大部分网卡芯片的驱动程序都有代码借鉴或移植。驱动需要提供三个函数,以Enc28j60 驱动为例。
tapdev_init():网卡初始化函数,初始化网卡的工作模式。
tapdev_read(void):读包函数。将网卡收到的数据放入全局缓存区uip_buf 中,返回包的长度,赋给uip_len。
void tapdev_send(void):发包函数。将全局缓存区uip_buf 里的数据(长度放在uip_len 中)发送出去。
 
2.由于uIP 协议栈需要使用时钟,为TCP 和ARP 的定时器服务。因此使用单片机的定时器或是stm32的滴答定时器用作时钟,每20ms 让计数tick_cnt 加1,这样,25 次计数(0.5S)满了后可以调用TCP 的定时处理程序。10S 后可以调用ARP 老化程序。uIP1.0 版本,增加了timer.c/timer.h,专门用来管理时钟,修改clock-arch.c如下:
01 #include "clock-arch.h"
02 #include "stm32f10x.h"
03  
04 extern __IO int32_t g_RunTime;
05 /*---------------------------------------------------------------------------*/
06 clock_time_t
07 clock_time(void)
08 {
09     return g_RunTime;
10 }
11 /*---------------------------------------------------------------------------*/
使用stm32 滴答定时器中断代码:
 
User/stm32f10x_it.c
01 __IO int32_t g_RunTime = 0;
02 void SysTick_Handler(void)
03 {
04     static uint8_t s_count = 0;
05     if (++s_count >= 10)
06     {
07         s_count = 0;
08  
09         g_RunTime++;    /* 全局运行时间每10ms增1 */
10         if (g_RunTime == 0x80000000)
11         {
12             g_RunTime = 0;
13         }      
14     }
15 }
3.uipopt.h/uip-conf.h 是配置文件,用来设置本地的IP 地址、网关地址、MAC 地址、全局缓冲区的大小、支持的最大连接数、侦听数、ARP 表大小等。可以根据需要配置。
 
#define UIP_FIXEDADDR 1
决定uIP是否使用一个固定的IP地址。
如果uIP使用一个固定的IP地址,应该置位(set)这些uipopt.h中的选项。如果不的话,则应该使用宏uip_sethostaddr(),uip_setdraddr() 和 uip_setnetmask()。
 
#define UIP_PINGADDRCONF 0            Ping IP地址赋值。
#define UIP_FIXEDETHADDR 0            指明uIP ARP模块是否在编译时使用一个固定的以太网MAC地址。
#define UIP_TTL 255                           uIP发送的IP packets的IP TTL (time to live)。
#define UIP_REASSEMBLY 0                uIP支持IP packets的分片和重组。
#define UIP_REASS_MAXAGE 40          一个IP fragment在被丢弃之前可以在重组缓冲区中存在的最大时间。
#define UIP_UDP 0                              是否编译UDP的开关。
#define UIP_ACTIVE_OPEN 1                决定是否支持uIP打开一个连接。
#define UIP_CONNS 10                        同时可以打开的TCP连接的最大数目。由于TCP连接是静态分配的,减小这个数目将占用更少的RAM。每一个TCP连接需要大约30字节的内存。
#define UIP_LISTENPORTS 10                同时监听的TCP端口的最大数目。每一个TCP监听端口需要2个字节的内存。
#define UIP_RECEIVE_WINDOW 32768   建议的接收窗口的大小。如果应用程序处理到来的数据比较慢,那么应该设置的小一点(即,相对与uip_buf缓冲区的大小来说),相反如果应用程序处理数据很快,可以设置的大一点(32768字节)。
#define UIP_URGDATA 1                       决定是否支持TCP urgent data notification。
#define UIP_RTO 3                                The initial retransmission timeout counted in timer pulses.不要改变
#define UIP_MAXRTX 8                         在中止连接之前,应该重发一个段的最大次数。不要改变
#define UIP_TCP_MSS (UIP_BUFSIZE – UIP_LLH_LEN – 40)             TCP段的最大长度。它不能大于UIP_BUFSIZE – UIP_LLH_LEN – 40.
#define UIP_TIME_WAIT_TIMEOUT 120    一个连接应该在TIME_WAIT状态等待多长。不要改变
#define UIP_ARPTAB_SIZE 8                    ARP表的大小。如果本地网络中有许多到这个uIP节点的连接,那么这个选项应该设置为一个比较大的值。
#define UIP_BUFSIZE 1500                        uIP packet缓冲区不能小于60字节,但也不必大于1500字节。
#define UIP_STATISTICS 1                        决定是否支持统计数字。统计数字对调试很有帮助,并展示给用户。
#define UIP_LOGGING 0                        输出uIP登陆信息。
#define UIP_LLH_LEN 14                        链接层头部长度。对于SLIP,应该设置成0。
 
uip-conf.h 中增加几个主要结构体定义,不include任何应用
 
01 #define UIP_CONF_LOGGING         0                //logging off
02  
03 typedef int uip_tcp_appstate_t;         //出错可注释
04 typedef int uip_udp_appstate_t;         //出错可注释
05  
06 /*#include "smtp.h"*/
07 /*#include "hello-world.h"*/
08 /*#include "telnetd.h"*/
09 /*#include "webserver.h"*/
10 /*#include "dhcpc.h"*/
11 /*#include "resolv.h"*/
12 /*#include "webclient.h"*/
13  
14 #include "app_call.h"                    //加入一个Uip的数据接口文件
uIP 在接受到底层传来的数据包后,调用UIP_APPCALL( ),将数据送到上层应用程序处理。
User/app_call.c
01 #include "stm32f10x.h"
02  
03 #ifndef UIP_APPCALL
04     #define UIP_APPCALL                 Uip_Appcall
05 #endif
06  
07 #ifndef UIP_UDP_APPCALL
08     #define UIP_UDP_APPCALL             Udp_Appcall
09 #endif
10  
11 void Uip_Appcall(void);
12 void Udp_Appcall(void);
13  
14  
15 void Uip_Appcall(void)
16 {
17      
18 }
19  
20 void Udp_Appcall(void)
21 {
22      
23 }
 
4.加入uIP 的的主循环代码架构
User/main.c
001 #include "stm32f10x.h"
002 #include "stdio.h"
003 #include "string.h"
004  
005 #include "uip.h"
006 #include "uip_arp.h"
007 #include "tapdev.h"
008 #include "timer.h"
009 #include "ENC28J60.h"
010 #include "SPI.h"
011  
012 #define  PRINTF_ON  1
013  
014 #define BUF ((struct uip_eth_hdr *)&uip_buf[0])
015  
016 #ifndef NULL
017 #define NULL (void *)0
018 #endif /* NULL */
019  
020 static unsigned char mymac[6] = {0x04,0x02,0x35,0x00,0x00,0x01};
021  
022 void RCC_Configuration(void);
023 void GPIO_Configuration(void);
024 void USART_Configuration(void);
025  
026 int main(void)
027 {
028     int i;
029     uip_ipaddr_t ipaddr;
030     struct timer periodic_timer, arp_timer;
031  
032     RCC_Configuration();
033     GPIO_Configuration();
034     USART_Configuration();
035     SPInet_Init();
036  
037     timer_set(&periodic_timer, CLOCK_SECOND / 2);
038     timer_set(&arp_timer, CLOCK_SECOND * 10);
039  
040     SysTick_Config(72000);          //配置滴答计时器
041  
042     //以太网控制器驱动初始化
043     tapdev_init(mymac);
044    
045     //Uip 协议栈初始化
046     uip_init();
047  
048     uip_ipaddr(ipaddr, 192, 168, 1, 15);     //配置Ip
049     uip_sethostaddr(ipaddr);
050     uip_ipaddr(ipaddr, 192, 168, 1, 1);     //配置网关
051     uip_setdraddr(ipaddr);
052     uip_ipaddr(ipaddr, 255, 255, 255, 0);   //配置子网掩码
053     uip_setnetmask(ipaddr);
054  
055     while(1){
056    
057         uip_len = tapdev_read();                                //从网卡读取数据
058          
059         if(uip_len > 0)
060         {                                                       //如果数据存在则按协议处理
061             if(BUF->type == htons(UIP_ETHTYPE_IP)) {         //如果收到的是IP数据,调用uip_input()处理
062  
063                 uip_arp_ipin();                                    
064                 uip_input();
065  
066                 /* If the above function invocation resulted in data that
067                    should be sent out on the network, the global variable uip_len is set to a value > 0. */
068  
069                 if(uip_len > 0)
070                 {
071                   uip_arp_out();
072                   tapdev_send();
073                 }
074  
075             }else if(BUF->type == htons(UIP_ETHTYPE_ARP)){    //如果收到的是ARP数据,调用uip_arp_arpin处理
076  
077                 uip_arp_arpin();
078  
079                 /* If the above function invocation resulted in data that
080                    should be sent out on the network, the global variable uip_len is set to a value > 0. */
081  
082                 if(uip_len > 0)
083                 {
084                   tapdev_send();
085                 }
086             }
087      
088         }else if(timer_expired(&periodic_timer)){           //查看0.5s是否到了,调用uip_periodic处理TCP超时程序
089  
090               timer_reset(&periodic_timer);
091               for(i = 0; i < UIP_CONNS; i++) {
092      
093                     uip_periodic(i);
094      
095                     /* If the above function invocation resulted in data that
096                        should be sent out on the network, the global variable uip_len is set to a value > 0. */
097      
098                     if(uip_len > 0)
099                     {
100                       uip_arp_out();
101                       tapdev_send();
102                     }
103               }
104                                  
105               for(i = 0; i < UIP_UDP_CONNS; i++)
106               {
107      
108                     uip_udp_periodic(i);                                //处理udp超时程序
109      
110                     /* If the above function invocation resulted in data that
111                        should be sent out on the network, the global variable uip_len is set to a value > 0. */
112      
113                     if(uip_len > 0)
114                     {
115                       uip_arp_out();
116                       tapdev_send();
117                     }
118               }
119            
120               /* Call the ARP timer function every 10 seconds. */            //10s到了就处理ARP
121               if(timer_expired(&arp_timer))
122               {
123                     timer_reset(&arp_timer);
124                     uip_arp_timer();
125               }
126         }
127     }
128  
129 }
130 /*******************************Stm32 Set***************************************/
131  
132 void GPIO_Configuration(void)
133 {
134     GPIO_InitTypeDef GPIO_InitStructure;                                                                                                                                                                                                                                                       
135     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
136  
137     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
138     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        
139     GPIO_Init(GPIOA , &GPIO_InitStructure);
140      
141     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
142     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;          
143     GPIO_Init(GPIOA , &GPIO_InitStructure);
144 }
145  
146 void RCC_Configuration(void)
147 {
148     /* 定义枚举类型变量 HSEStartUpStatus */
149     ErrorStatus HSEStartUpStatus;
150  
151     /* 复位系统时钟设置*/
152     RCC_DeInit();
153     /* 开启HSE*/
154     RCC_HSEConfig(RCC_HSE_ON);
155     /* 等待HSE起振并稳定*/
156     HSEStartUpStatus = RCC_WaitForHSEStartUp();
157     /* 判断HSE起是否振成功,是则进入if()内部 */
158     if(HSEStartUpStatus == SUCCESS)
159     {
160         /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
161         RCC_HCLKConfig(RCC_SYSCLK_Div1);
162         /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
163         RCC_PCLK2Config(RCC_HCLK_Div1);
164         /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
165         RCC_PCLK1Config(RCC_HCLK_Div2);
166         /* 设置FLASH延时周期数为2 */
167         FLASH_SetLatency(FLASH_Latency_2);
168         /* 使能FLASH预取缓存 */
169         FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
170         /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
171         RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
172         /* 使能PLL */
173         RCC_PLLCmd(ENABLE);
174         /* 等待PLL输出稳定 */
175         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
176         /* 选择SYSCLK时钟源为PLL */
177         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
178         /* 等待PLL成为SYSCLK时钟源 */
179         while(RCC_GetSYSCLKSource() != 0x08);
180     }
181     /* 打开APB2总线上的GPIOA时钟*/
182     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
183          
184 }
185  
186   
187 void USART_Configuration(void)
188 {
189     USART_InitTypeDef USART_InitStructure;
190     USART_ClockInitTypeDef USART_ClockInitStructure;
191  
192     USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
193     USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
194     USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                     
195     USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
196     USART_ClockInit(USART1 , &USART_ClockInitStructure);
197  
198     USART_InitStructure.USART_BaudRate = 9600;
199     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
200     USART_InitStructure.USART_StopBits = USART_StopBits_1;
201     USART_InitStructure.USART_Parity = USART_Parity_No;
202     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
203     USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
204     USART_Init(USART1,&USART_InitStructure);
205  
206     USART_Cmd(USART1,ENABLE);
207 }
208  
209 #if  PRINTF_ON
210  
211 int fputc(int ch,FILE *f)
212 {
213     USART_SendData(USART1,(u8) ch);
214     while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
215     return ch;
216 }
217  
218 #endif
 
5.解决编译过程中的错误。归总如下:
  • Uip/uip-split.c  注释所有的 tcpip_output()函数  消除uip_fw_output()函数的注释
  • Uip/memb.c 中 memb_free()函数 返回值 return -1 改为 return 1
  • Apps/resolv.c 中resolv_conf() 中 
                 //resolv_conn = uip_udp_new(dnsserver, HTONS(53));
                  resolv_conn = uip_udp_new((uip_ipaddr_t*)dnsserver, HTONS(53));
 
解决完所有问题后,编译成功后下载到stm32,ping 测试。。
 
QQ截图20120726201101.png
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F103移植FreeModbus实现ModbusTCP是指在STM32F103系列芯片上将FreeModbus协议栈移植并实现ModbusTCP通信功能。有一些工程和案例已经验证了该功能的可行性,可以直接使用或稍加修改后应用于实际工程或作为学习ModbusTCP的案例。 该移植实现了基本的功能码,括01、02、03、04、05、06、15、16等,在代码实现了读线圈、写线圈、读离散输入、读输入寄存器、读保持寄存器、写保持寄存器等功能,可以与PLC等设备进行通信。 因此,通过在STM32F103芯片上移植FreeModbus协议栈,可以实现ModbusTCP通信,并具备了基本的Modbus功能码的支持。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STM32F103移植FreeModbus同时实现ModbusRTU和ModbusTCP.zip](https://download.csdn.net/download/qq_15181569/12527801)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [基于STM32_DM9000_UIP_FreeModbus的Modbus-TCP功能实现工程下载](https://download.csdn.net/download/tcjy1000/7325947)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [STM32移植FreeModbus实现ModbusRTU.zip](https://download.csdn.net/download/qq_15181569/12264170)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值