全部例程

Search, Configuration and OTA

W55MH32 其他标签

2025/02/12 更新

在本文中,我们将详细介绍如何在 W55MH32芯片之上实现上位机的搜索和配置功能,并说明如何通过上位机在局域网中搜索 W55MH32,并通过实际程序来配置网络地址。示例程序提供了一个开源的主机计算机配置工具 SmartConfigTool,支持搜索设备、设置网络地址参数以及进行固件升级等功能。

对于本文中使用的其他网络协议,例如 DHCP,请参考相关章节。关于 W55MH32 的初始化过程,请参考“网络安装”章节,此处不再赘述。

上位机简介

嵌入式上位机(Embedded Host)是指在嵌入式系统中,作为与嵌入式设备进行通信、控制和数据交换的上位设备。 它通常拥有更强的计算能力和存储资源,用于控制和监控下位嵌入式设备(如传感器、执行器、嵌入式控制器等)的运行。

特点

  • 高效性:上位机配置下位机可以大大提高控制系统的效率。上位机发出控制命令, 下位机接收并解释成相应的时序信号来直接控制设备,响应速度快,可靠性高。
  • 实时性: 下位机可以实时地响应上位机的控制指令,并对设备进行实时控制,确保系统的稳定性和安全性。 同时,下位机也可以实时地向上位机反馈设备状态数据,使得上位机可以及时了解系统状态并进行相应的控制调整。
  • 灵活性: 上位机和下位机可以灵活地组合和扩展,以满足不同系统的需求。上位机可以同时连接多个下位机, 对它们进行监控、控制和数据处理。同时,下位机也可以根据需要连接多个设备,实现设备的分布式控制。
  • 交互友好:上位机通常具有人机交互界面,为用户提供友好的图形界面或者文本界面,方便用户进行操作、配置和监控。
  • 应用场景

    W55MH32使用NetBIOS 协议可以进行以下几种应用:

  • 工业自动化:上位机发出控制指令,下位机接收并解释成相应的时序信号来直接控制设备,响应速度快,可靠性高。上位机可以监控生产过程、发出控制指令、 进行数据分析和优化等。下位机可以实时控制设备、采集设备状态数据、接收和执行控制指令等。
  • 物联网: 上位机可以远程监控和管理设备、进行数据分析和处理等。下位机可以接收和执行控制指令、采集和传输设备状态数据等。
  • 智能家居: 上位机可以发出控制指令、监控家庭网络等。下位机可以接收和执行控制指令、控制智能设备的运行和状态采集等。
  • 医疗设备: 上位机可以发出控制指令、远程监控和管理医疗设备等。下位机可以接收和执行控制指令、控制医疗设备的运行和状态采集等。
  • 搜索和配置的基本工作流程

    搜索:上位机通过 UDP 广播发送 FIND 命令,设备作为下位机在收到后将自身配置信息发送给上位机,上位机收到后呈现获取到的设备信息;

    配置:在已经搜索到设备的基础上,上位机向该设备发送 SETT 命令后,设备收到后根据上位机显示的网络信息对本地进行重新配置,并在串口显示。

    实现过程

    第1步:初始化检查

    首先,它会读取闪存中的参数,如果没有参数,那么它就会存储默认参数,并开始判断固件长度是否大于 0。如果大于 0,这意味着固件需要更新,然后会调用 app_copy() 函数将 APP 备份区中的固件复制到 APP 运行区。最后,它会确定 APP 运行区中是否有任何固件。如果有,那么它将直接跳转到 APP 固件。

    
    system_config_param_get().
     system_first_run_check().
     if (system_cfg.fw_len> 0 && system_cfg.fw_len ! = 0xFFFFFFFF)
     {
         if (app_copy(system_cfg.fw_len, system_cfg.fw_checksum)< 0)
     {    
     printf("Copy fw error\r\n").
     }    
         else
     {    
     system_cfg.fw_len= 0;
     system_cfg.fw_checksum= 0.
     system_config_param_save().
     reboot_app().
     }    
     }
     if (app_is_inside())
     {
     printf("reboot app\r\n").
     reboot_app().
     }
     else
     {
     printf("not app\r\n").
     }
                  

    第2步:在主循环中调用 do_udp_config() 和 fw_update_process() 这两个函数。

    参考代码:

    
    while (1)
     {
     do_udp_config(SOCK_UDP_CONFIG); // Run and precess UpperComputer command.
     fw_update_process().
         if (reboot_flag)
     {    
     system_reboot().
     }    
     }
    

    第 3 步:搜索、配置实施及固件升级

    do_udp_config() 函数如下所示:

    参考代码

    
    void do_udp_config(uint8_t sn)
    {
    uint16_t len = 0;
    uint8_t rip[4];
    uint16_t rport;
    uint16_t local_port = 1460;
    
    memset(RecvMsg.op, 0, sizeof(RecvMsg)); // clear RecvMsg
    
    switch (getSn_SR(sn))
    {
    case SOCK_UDP:
    if ((len = getSn_RX_RSR(sn)) > 0)
    {
        len = recvfrom(sn, (uint8_t *)&RecvMsg, len, rip, &rport);
        if (len > sizeof(CONFIG_MSG))
            break;
        {
            // FIND: searching, SETT: setting,
            if ((RecvMsg.op[0] == 'F') && (RecvMsg.op[1] == 'I') && (RecvMsg.op[2] == 'N') && (RecvMsg.op[3] == 'D'))
            {
                printf("Find from %d.%d.%d.%d\r\n", rip[0], rip[1], rip[2], rip[3]);
                memcpy(public_buff, &system_cfg, sizeof(CONFIG_MSG));
                memcpy(public_buff, "FIND", 4);
    
                sendto(sn, public_buff, sizeof(CONFIG_MSG), rip, rport); // return network info to uppercomputer.
            }
            else if ((RecvMsg.op[0] == 'S') && (RecvMsg.op[1] == 'E') && (RecvMsg.op[2] == 'T') && (RecvMsg.op[3] == 'T'))
            {
                printf("Sett\r\n");
    
                if ((RecvMsg.mac[0] == system_cfg.mac[0]) && (RecvMsg.mac[1] == system_cfg.mac[1]) && (RecvMsg.mac[2] == system_cfg.mac[2]) && (RecvMsg.mac[3] == system_cfg.mac[3]) && (system_cfg.mac[4] == system_cfg.mac[4]) && (RecvMsg.mac[5] == system_cfg.mac[5]))
                {
                    memcpy(system_cfg.lip, RecvMsg.lip, 4);
                    memcpy(system_cfg.sub, RecvMsg.sub, 4);
                    memcpy(system_cfg.gw, RecvMsg.gw, 4);
                    memcpy(system_cfg.dns, RecvMsg.dns, 4);
                    system_config_param_save();
                    reboot_flag = 1;
                    sendto(sn, (uint8_t *)&RecvMsg, sizeof(CONFIG_MSG), rip, rport);
                }
            }
            else if ((RecvMsg.op[0] == 'R') && (RecvMsg.op[1] == 'S') && (RecvMsg.op[2] == 'T') && (RecvMsg.op[3] == 'T'))
            {
                printf("Reset\r\n");
                reboot_flag = 1;
            }
            else if ((RecvMsg.op[0] == 'F') && (RecvMsg.op[1] == 'I') && (RecvMsg.op[2] == 'R') && (RecvMsg.op[3] == 'M'))
            {
                printf("Fire\r\n");
                if ((RecvMsg.mac[0] == system_cfg.mac[0]) && (RecvMsg.mac[1] == system_cfg.mac[1]) && (RecvMsg.mac[2] == system_cfg.mac[2]) && (RecvMsg.mac[3] == system_cfg.mac[3]) && (system_cfg.mac[4] == system_cfg.mac[4]) && (RecvMsg.mac[5] == system_cfg.mac[5]))
                {
                    system_cfg.state = FW_UPDATE_CONFIG;
                    sendto(sn, (uint8_t *)&RecvMsg, sizeof(CONFIG_MSG), rip, rport);
                }
            }
        }
    }
    
    break;
    case SOCK_CLOSED:
    socket(sn, Sn_MR_UDP, local_port, 0x00);
    break;
    }
    }
                  

    首先,它会运行一个 UDP 状态机,当接收到消息时,会判断该指令。 如果是“查找”指令,它会读取设备的网络地址信息并将其返回。 如果是“设置”指令,它会判断 MAC 地址是否与自身一致,如果一致,就会将主机计算机发出的配置更新到 W55MH32中。

    如果是“固件”指令,它会判断 MAC 地址是否与自身相同,如果相同,就会向主机计算机回复“固件”消息,然后主机计算机将通过 TCP 客户端连接到 W55MH32,并将固件发送给 W55MH32,随后将固件保存到应用程序运行区域。

    下载完成后,它会跳转到应用程序运行区域。 连接、接收固件以及存储固件的操作是在“fw_update_process()”函数中实现的,具体如下:

    
    void fw_update_process(void)
    {
    static uint32_t file_size = 0;
    static uint32_t file_recv_length = 0;
    
    static uint8_t is_erased_flag = 0;
    static uint32_t flash_offset = 0;
    
    uint32_t len = 0;
    
    switch (getSn_SR(SOCK_FW_UPDATE))
    {
    case SOCK_ESTABLISHED:
        if (getSn_IR(SOCK_FW_UPDATE) & Sn_IR_CON)
        {
            setSn_IR(SOCK_FW_UPDATE, Sn_IR_CON);
    
            printf("socket connected\r\n");
            is_erased_flag = 0;
    
            flash_offset = APPLICATION_START_ADDRESS;
    
            file_size = 0;
            file_recv_length = 0;
        }
        // some timeout function should be added here
        len = getSn_RX_RSR(SOCK_FW_UPDATE);
        if (len > 0)
        {
            if ((len == 4) && (is_erased_flag == 0))
            {
                recv(SOCK_FW_UPDATE, (uint8_t *)&file_size, 4);
    
                flash_erase(APPLICATION_START_ADDRESS, APPLICATION_AREA_SIZE);
                is_erased_flag = 1;
    
                // send the len to PC program to tell him flash erased over
                send(SOCK_FW_UPDATE, (uint8_t *)&file_size, 4);
    
                printf(">\r\n");
            }
            else
            {
                recv(SOCK_FW_UPDATE, public_buff, len);
    
                printf(".");
    
                flash_write(flash_offset, public_buff, len);
                flash_offset += len;
                file_recv_length += len;
    
                send(SOCK_FW_UPDATE, (uint8_t *)&len, (uint16_t)4);
    
                if (file_size == file_recv_length)
                {
                    disconnect(SOCK_FW_UPDATE);
                    // save fw checksum and module state
                    system_cfg.state = FW_APP_NORMAL;
                    system_cfg.fw_checksum = 0;
                    system_cfg.fw_len = 0;
                    system_config_param_save();
                    printf("\r\nFirmware is download\r\n");
                    reboot_app();
                }
            }
        }
        break;
    case SOCK_CLOSE_WAIT:
        disconnect(SOCK_FW_UPDATE);
        break;
    case SOCK_CLOSED:
        close(SOCK_FW_UPDATE);
        socket(SOCK_FW_UPDATE, Sn_MR_TCP, FW_UPDATE_PORT, Sn_MR_ND);
        break;
    case SOCK_INIT:
        listen(SOCK_FW_UPDATE);
        break;
    }
    }
              

    接下来要处理的是应用程序固件,这需要在主循环中调用一个程序来完成 UDP 配置以及固件更新的工作。

    
    while (1)
    	{
    		do_udp_config(SOCK_UDP_CONFIG); // Run and precess UpperComputer command.
    		fw_update_process();
    		if (reboot_flag)
    		{
    			system_reboot();
    		}
    	}
              

    其中,do_udp_config() 函数与启动固件保持一致,但 fw_update_process() 函数则略有不同,具体如下:

    
     void fw_update_process(void)
     {
         static uint32_t file_size= 0;
         static uint32_t file_recv_length= 0;
      
         static uint8_t is_erased_flag= 0;
         static uint32_t flash_offset= 0;
      
         uint32_t len= 0;
      
         if (system_cfg.state ! = FW_UPDATE_CONFIG)
     {    
             return;
     }    
      
         switch (getSn_SR(SOCK_FW_UPDATE))
     {    
         case SOCK_ESTABLISHED.
             if (getSn_IR(SOCK_FW_UPDATE) & Sn_IR_CON)
     {        
     setSn_IR(SOCK_FW_UPDATE, Sn_IR_CON).
      
     is_erased_flag= 0.
      
     flash_offset= APPLICATION_BACK_ADDRESS.
      
     file_size= 0.
     file_recv_length= 0;
     }        
      
             // some timeout function should be added here
     len= getSn_RX_RSR(SOCK_FW_UPDATE).
             if (len> 0)
     {        
                 if ((len== 4) && (is_erased_flag== 0))
     {            
     recv(SOCK_FW_UPDATE, (uint8_t *)&file_size, 4).
      
     flash_erase(APPLICATION_BACK_ADDRESS, APPLICATION_AREA_SIZE).
     is_erased_flag= 1.
      
                     // send the len to PC program to tell him flash erased over
     send(SOCK_FW_UPDATE, (uint8_t *)&file_size, 4).
      
     printf("file_size %d\r\n", file_size).
     printf(">").
     }            
                 else
     {            
     len= recv(SOCK_FW_UPDATE, public_buff, len);
      
     printf(".") ;
      
     flash_write(flash_offset, public_buff, len);
     flash_offset+= len.
     file_recv_length+= len;
      
     send(SOCK_FW_UPDATE, (uint8_t *)&len, (uint16_t)4).
      
                     if (file_size== file_recv_length)
     {                
     printf("\r\n").
     disconnect(SOCK_FW_UPDATE).
     system_cfg.fw_checksum= update_firmware_calc_checksum(file_size);
     system_cfg.fw_len= file_size.
     system_config_param_write(&system_cfg).
     reboot_flag= 1; // reboot
     }                
     }            
     }        
             break;
         case SOCK_CLOSE_WAIT.
             // disconnect(SOCK_FW_UPDATE);
             break;
         case SOCK_CLOSED.
     close(SOCK_FW_UPDATE).
     socket(SOCK_FW_UPDATE, Sn_MR_TCP, FW_UPDATE_PORT, Sn_MR_ND).
             break;
         case SOCK_INIT.
     listen(SOCK_FW_UPDATE).
     printf("fw update socket init\r\n");
             break;
     }    
     }
    
              

    在执行固件升级操作时,接收到的固件将直接存储到 APP 的备份区域中,接收完成后,固件的长度以及校验和信息将被存储到 FLASH 参数区域并进行重置。然后,可以通过检查并复制 BOOT 固件中的固件来完成升级操作。

    地址划分:
    BOOT:0x08000000->0x08007FFF(64KB)
    APP:0x08010000->0x08041FFF(200KB)
    APPBAK:0x08042000->0x08073FFF(200KB)
    未定义:0x08074000->0x080FEFFF(556KB)
    参数区:0x080FF000->0x08100000(4KB)

    运行结果

    请注意:

    测试实例需要PC端和W55MH32处于同一网段。

    在运行烧录 BOOT 程序之后,由于没有 APP 程序,首次运行将会配置默认参数,然后进入 BOOT 的配置程序。

    然后打开“配置工具”上行界面,在这里您可以设置网络地址信息以及进行固件升级操作。
    搜索示例:

    配置示例:

    固件更新示例:

    总结

    本文介绍了如何在 W55MH32 芯片上实现主机计算机搜索与配置功能以及固件更新功能,并演示了使用开源主机计算机配置工具 ConfigTool 在局域网中搜索 W55MH32 设备、配置网络地址以及通过实际例程更新固件的过程。文章详细介绍了主机计算机搜索与配置的概念、特点、应用场景以及基本工作流程,以帮助读者了解其在嵌入式设备管理中的实际应用价值。

    接下来的文章将重点介绍在 W55MH32 芯片上使用 TOE 中断的操作,分析 TOE 中断的核心原理和应用,并通过实际示例解释如何使用中断进行回环数据测试,敬请期待!

    下载本章例程

    我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

    开发环境: Keil MDK5 配套开发板