STM32 + UIP + ENC28J60 实现TCP 简单通讯

摘要:
MCU:STM32F103C6T6背景上次介绍了怎么把UIP移植到STM32中来,并最后实现一个ping操作,这次在上次基础上实现MCU当TCP服务端,电脑当客户端通过TCP端链接MCU,实现通讯。在使用UIPTCP功能前,需要可以让MCU获取当前时间,主要为实现每10ms毫秒处理一次TCP连接,和每5s秒刷新一次ARP;例如HAL库中有一个HAL_GetTick(),可以获取当前毫秒时间。TCP操作网卡部分初始化、UIP初始化、设置IP、网关等信息不在重复介绍,主要介绍TCP操作部分。声明UIP回调函数UIP_APPCALL实现UIP_APPCALL也是一个宏。

MCU: STM32F103C6T6

背景

上次介绍了怎么把UIP移植到STM32中来,并最后实现一个ping操作,这次在上次基础上实现MCU当TCP服务端,电脑当客户端通过TCP端链接MCU,实现通讯。

为保证程序尽量精简,程序在接受到TCP数据后,会原封不动返回给客户端(电脑), 并通过串口打印。

在使用UIP TCP功能前,需要可以让MCU获取当前时间,主要为实现每10ms毫秒处理一次TCP连接,和每5s秒刷新一次ARP;例如HAL库中有一个HAL_GetTick(),可以获取当前毫秒时间。

操作流程

整体TCP使用流程:

1. 初始化enc28j60、UIP

2. 设置IP、网关、子网掩码

3. 开启端口监听

4. 处理ARP请求、响应

5. 每10ms处理一次TCP请求

6. 每5秒刷新一次ARP

7. 通过UIP提供的UIP_APPCALL回调,接收TCP数据、发送TCP数据

main主循环中每10ms处理一批TCP请求、每5s 刷新一次ARP.在操作前,需保证网卡初始化正常,UIP运行正常。

TCP操作

网卡部分初始化、UIP初始化、设置IP、网关等信息不在重复介绍,主要介绍TCP操作部分。

设置TCP端口

通过调用uip的uip_listen函数,来设置要监听的TCP端口:

uip_listen(HTONS(8099));//8099为端口号

这里注意,uip_listen内套了一个HTONS函数(宏),端口号是用HTONS包裹的。

声明UIP回调函数UIP_APPCALL实现

UIP_APPCALL也是一个宏。UIP会在各种事件触发的时候调用它(个人理解..)

TCP的接收、发送也是在此处做;

首先,需要先创建一个函数,例如在main.c中声明一个main_appcall:

//处理接收到的TCP消息
voidmain_appcall() {
     //处理UIP各种事件,TCP数据接收、发送,就在此处处理
     if( uip_newdata() ){
         //有新数据,uip_appdata为TCP数据、uip_len为TCP数据长度
         print3(uip_appdata, uip_len);//可取消,调试信息
        uip_send(uip_appdata,uip_len);//发送TCP数据
}
}

然后在uip-conf.h中声明UIP_APPCALL宏

voidmain_appcall();
#define UIP_APPCALL main_appcall

最后在设置UIP监听、主循环中处理TCP请求:

uip_listen(HTONS(1234));//设置监听
...
while(1){
//UIP_COUNTS : TCP最大连接数
            for(uint8_t i = 0; i < UIP_CONNS; i++)
                {
                        uip_periodic(i);
                        if(uip_len > 0)
                        {
                                //有TCP新数据
                                print("tcp data handler...");
                                uip_arp_out();
                                tapdev_send();
                        }
                }
}

最后附上main.c部分代码:

#define UIP_BUF ((struct uip_eth_hdr *)&uip_buf[0]);
voidmain_appcall() {
     //处理UIP各种事件,TCP数据接收、发送,就在此处处理
     if( uip_newdata() ){
         //有新数据,uip_appdata为TCP数据、uip_len为TCP数据长度
         print3(uip_appdata, uip_len);//可取消,调试信息
         uip_send(uip_appdata,uip_len);//发送TCP数据
}
}

//记录ARP、TCP刷新时间,目的实现定时处理TCP请求、刷新ARP
uint32_t lastTimer = 0,lastTimerARP = 0;

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 */
  MX_USART1_UART_Init();
    //设置开始时间
    lastTimer =HAL_GetTick();
    lastTimerARP =HAL_GetTick();
    //等待一些时间,准备初始化ENC28J60
  /*USER CODE BEGIN 2 */
    for(int i = 0;i < 20;i++) {
        //enc28j60PhyWrite(PHLCON,0x7a4);    
        HAL_Delay(500);
        print("begin init enc28j60...");
    }
    //开始初始化,如果初始化不成功会阻塞
    //enc28j60_init(my_mac);
    tapdev_init();//初始化enc28j60
    //表示初始化成功,说明接线正常
    print("init enc28j60 success!!!");
    uip_init();
    uip_ipaddr_t ipaddr;
    uip_ipaddr(ipaddr, 192, 168, 1, 8);
    uip_sethostaddr(ipaddr);
    uip_ipaddr(ipaddr, 192, 168, 1, 1);
    uip_setdraddr(ipaddr);
    uip_ipaddr(ipaddr, 255, 255, 252, 0);
    uip_setnetmask(ipaddr);
    
    //开启指定端口监听, 注意端口处HTONS,不是直接写的端口号!
    uip_listen(HTONS(1234));
    
  /*USER CODE END 2 */

  /*Infinite loop */
  /*USER CODE BEGIN WHILE */
    
  while (1)
  {
    /*USER CODE END WHILE */
        //处理IP、ARP、Ping消息
        uip_len =tapdev_read();
        if(uip_len != 0){
            if(UIP_BUF->type ==htons(UIP_ETHTYPE_ARP)) {
                uip_arp_arpin();
                if(uip_len>0){
                    print("rarp package send!!!");
                    tapdev_send();
                }
            }
            if(UIP_BUF->type ==htons(UIP_ETHTYPE_IP)) {
                uip_arp_ipin();
                uip_input();
                if(uip_len > 0){
                    uip_arp_out();
                    tapdev_send();
                }
            }
        }
        
        //处理TCP链接,10毫秒处理一次
        if((HAL_GetTick() - lastTimer) > 10) {
            //UIP_COUNTS : TCP最大连接数
            for(uint8_t i = 0; i < UIP_CONNS; i++)
                {
                        uip_periodic(i);
                        if(uip_len > 0)
                        {
                                //有TCP新数据
                                print("tcp data handler...");
                                uip_arp_out();
                                tapdev_send();
                        }
                }
                lastTimer =HAL_GetTick();
        }
        //ARP 刷新
        if((HAL_GetTick() - lastTimerARP) > 5000) {
            lastTimerARP =HAL_GetTick();
            uip_arp_timer();
        }
        
    /*USER CODE BEGIN 3 */}
  /*USER CODE END 3 */}

最后附上全部代码(keil5),已放在蓝奏云:https://wwb.lanzouw.com/ieZu2y933pe

非常感谢xukai871105总结的笔记,本次TCP成功跑通全靠这篇文章,推荐去看一看,写的很详细:https://blog.csdn.net/xukai871105/article/details/17471865

免责声明:文章转载自《STM32 + UIP + ENC28J60 实现TCP 简单通讯》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇angularjs-双向数据绑定之输入框信息随时显示Android酷炫实用的开源框架(UI框架)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

STM32F1库函数初始化系列:DMA—ADC采集

1 void ADC_Configure(void) 2 { 3 ADC_InitTypeDef ADC_InitStructure; 4   GPIO_InitTypeDef GPIO_InitStructure; 5 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); 6   RCC_AHBPer...

查看 Apache并发请求数及其TCP连接状态

服务器上的一些统计数据:1)统计80端口连接数netstat -nat|grep -i "80"|wc -l2)统计httpd协议连接数ps -ef|grep httpd|wc -l3)、统计已连接上的,状态为“establishednetstat -na|grep ESTABLISHED|wc -l4)、查出哪个IP地址连接最多,将其封了.netstat...

12.bss段的初始化

12.bss段的初始化 在C代码:有初始化全局的数据段,局部的栈,malloc部分的堆,未初始化的全局的bss段。 从上面的编译的信息知道: Bss段的起始地址:0001052c Bss段的结束地址:00010534 我们的hello变量的地址:00010530是落在bss段里的地址的。所以是存在bss段的。这就是未初始化的全局变量存放在bss段...

Laya自动图集原理

关于Laya自动图集 Laya会把size小于512*512的图片打入自动大图集中。如果图片被打入自动图集中,图片的内存就交由Laya自动处理,开发者不能手动删除。 Laya最多生成6张2048*2048的自动图集,3D为2张。可以通过AtlasResourceManager.maxTextureCount设置。 如果不想将图片打图自动图集有三种方法:...

TCP/UDP socket

TCP socket:有链接,绑定端口,接着去侦听,若有请求,那么accept(),获得新的socket,并且去接收/发送数据报. UDP socket:无连接,不需要侦听,也不用一个新的socket去接收/发送数据包,但发送时就取决本身绑定的端口. UDP和TCP的对比:从以上的流程图看出,tcp步骤会比udp多,(tcp三次握手我没有列出来)UD...

TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端

目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP部分的使用 框架源码结构 补充说明 源码地址 说明 之前有好几篇博客在讲TCP/UDP通信方面的内容,也有做过一些Demo(包括整理出来的、可供学习使用的简单通信框架)。具体可以参见以下博客: http://www.cnblogs.com/xia...