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


    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消息
    void main_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宏

    void main_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]);
    void main_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

  • 相关阅读:
    小师妹学JVM之:JDK14中JVM的性能优化
    小师妹学JVM之:深入理解JIT和编译优化-你看不懂系列
    小师妹学JVM之:GC的垃圾回收算法
    小师妹学JVM之:JVM的架构和执行过程
    小师妹学JavaIO之:用Selector来发好人卡
    小师妹学JavaIO之:NIO中那些奇怪的Buffer
    小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下
    小师妹学JavaIO之:NIO中Channel的妙用
    小师妹学JavaIO之:Buffer和Buff
    小师妹学JavaIO之:文件File和路径Path
  • 原文地址:https://www.cnblogs.com/GengMingYan/p/15738456.html
Copyright © 2020-2023  润新知