我将通过 USB 进行固件升级作为参考、目前我正在尝试在 HTTP 服务器的帮助下通过以太网执行固件升级。 我们尝试实现的方案是将闪存分为引导加载程序、应用程序和二进制三个部分。
1.用户通过托管的网页上载 bin 文件,我们将上传的文件存储到二进制段的地址中。
2.进入自定义引导加载程序并执行应用程序部分的擦除、并将二进制部分中的固件刷写到应用程序部分。
3.然后加载应用程序。
这是一种可能的实现方法、以及需要对此进行哪些更改。
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
我将通过 USB 进行固件升级作为参考、目前我正在尝试在 HTTP 服务器的帮助下通过以太网执行固件升级。 我们尝试实现的方案是将闪存分为引导加载程序、应用程序和二进制三个部分。
1.用户通过托管的网页上载 bin 文件,我们将上传的文件存储到二进制段的地址中。
2.进入自定义引导加载程序并执行应用程序部分的擦除、并将二进制部分中的固件刷写到应用程序部分。
3.然后加载应用程序。
这是一种可能的实现方法、以及需要对此进行哪些更改。
您好、Mohammed、
我希望您已经了解了 TivaWare 库中的以太网引导加载程序示例。 首先如何对应用程序进行编程? 应用程序是否会通过 JTAG 编程到闪存中、或者也喜欢通过以太网上传二进制文件?
如果要通过以太网上传应用程序和二进制文件、则需要在引导加载程序中添加一些智能功能。 引导加载程序需要知道何时加载应用程序段、何时加载二进制段以及何时从二进制段重新编程应用程序。 如果应用程序已经通过 JTAG 加载、如引导加载程序本身、则会稍微简单一些。 引导加载程序需要首先将二进制文件从 http 服务器上载到闪存中的二进制部分。 在某种程度上、您的应用程序将接收到某种类型的命令或仅按下引脚、并将控制权转移到引导加载程序。 在本例中、引导加载程序将仅擦除应用程序、并根据存储在二进制段中的二进制文件再次对应用程序段进行编程。 擦除和程序只能使用 TivaWare API,例如 FlashEras()和 FlashProgram()。 请参阅 TivaWare 外设驱动程序库用户指南。
话虽如此、我很好奇您为什么要拆分为三个部分、而不是两个部分-引导加载程序和应用程序部分。 闪存最初将仅包含引导加载程序。 引导加载程序将首先向服务器发送 BOOTP 请求。 然后、服务器将通过以太网上传应用程序。 应用程序将像正常运行一样运行。 如果服务器要再次更新应用程序,则只需执行此操作即可。 服务器将向应用程序发送一些魔术包。 接收到魔术包后、应用程序将再次将控制权转移到引导加载程序。 一旦传输到引导加载程序、引导加载程序将从服务器上载新的应用程序代码。 这基本上就是 TivaWare 以太网引导加载程序示例的功能。
您好、Charles、
非常感谢您的回复、请查找您所关注问题的回复。
1.控制器本身从托管 HTTP 服务器将固件下载到二进制部分。 从网页上传完成后、我们将切换到引导加载程序模式。
不是、我们不打算使用 JTAG 或任何程序、如 LM 闪存。 这就是我们希望从 HTTP 服务器按需加载新固件的原因。
3.我想引导加载程序不具备托管 http 服务器和执行从服务器下载操作的能力。 因此、我们从应用程序代码下载、然后切换到引导加载程序。
4.第3点解释了我们需要二进制部分的原因、因为固件不像 LM 闪存编程器那样通过 TFTP 加载生动的内容、而是从闪存本身的另一个位置进行刷写。
我希望我已清楚说明我们选择这种方法的要求和原因。 请确认这项工作的可行性以及我们可能面临的任何挑战。
您好!
好的、感谢您的澄清。 我想我理解您为何要实现这一目标。 您的应用将需要对二进制部分进行编程。 在 TivaWare 引导加载程序示例中、它将通过 TFTP 上传固件并对应用部分进行编程/更新。 在您的情况下、您的应用需要像引导加载程序那样处理此问题、并将更新二进制部分。 因此、我将建议您的应用参考如何在 TivaWare 引导加载程序示例中完成以更新固件。 应用程序更新二进制部分后、您将跳转到引导加载程序。 需要修改引导加载程序,以便从二进制部分将 FlashEras()和 FlashProgram()执行到应用部分。
TivaWare 引导加载程序示例中的固件下载方法和通过 HTTP 服务器上传的方法存在差异
当前引导加载程序示例:
我们将需要 LM 闪存编程器通过 TFTP 将文件加载到器件中。 但我尝试的方法是通过控制器本身托管的 HTTP 服务器发送文件。
尝试实现的方法:
要通过托管 HTTP 服务器加载文件、我要生成的二进制 bin 文件大约为160KB、由于堆内存分配、上载失败。 整个文件以文本字符串的形式发送到器件、因此需要在堆存储器上进行分配。
typedef 结构{ 字符*Name; /* NULL 终止的"name"*/ char *文件名; /* NULL 终止了"filename",如果不是文件,则为 NULL */ char *类型; /* NULL 终止了"Content-Type",如果没有类型,则为 NULL */ char *数据; /*项目数据的指针(在字符串上终止为 NULL)*/ Int DataSize; /*数据长度(对字符串也有效)*/ }CGIPARSEREC;
在增加堆存储器大小时、我能够上传高达130KB。 这是在将 BIOS.heapSize 更改为一个非常大的值之后。 请告诉我、我们应该能够将整个文件上传到器件中。
那么、这些将是有关这方面的问题、
1.是否可以通过 HTTP 服务器将二进制文件作为字符串发送到器件?
2.我们是否应将完整的二进制文件制作成单独的块并在单独的实例中上传?
3.接收完整个文件后,如何将其复制到闪存中的二进制段(特定地址位置),我们有任何特定的 API 用于此操作?
您好!
我想您将参考 BL_FLASH.c 来编程和擦除应用部分。 要对二进制段进行编程和擦除、您应该使用类似的方法。 请参阅 bl_emac.c 文件中的以下引导加载程序代码、其中解析了 TFTP 数据、然后通过调用 bl_flash_erase_fn_hook 和 bl_flash_programme_fn_hook 函数将数据编程到应用部分。 数据被编程到 APP_START_ADDRESS 的开头。 BL_FLASH_ERASE_FN_Hook 和 BL_FLASH_program_FN_Hook 在 BL_FLASH_C 中定义 我的建议是、您可以参考如何在 TivaWare 引导加载程序中完成它。 您的应用程序将需要将收到的 HTTP 数据和程序解析到二进制部分。
// // //! 解析数据包检查 TFTP 数据包。 //! //! 此函数解析数据包以确定它是否为// 的 TFTP 数据包! 输出当前 TFTP 传输。 如果找到有效的数据包、 //! 数据包被编程到闪存中。 //! //! 如果此数据包是 TFTP 数据的最后一个数据包 //! 传输、否则为0。 //// ***************** 静态 uint32_t ParseTFPData (void) { uint8_t * pui8Packet =(uint8_t *) uip_appdata; uint32_t ui32FlashAddr; uint32_t ui32Idx; // //查看这是否是 TFTP 数据包。 // if ((pui8Packet[0]!=(tftf_data >> 8)&& 0xff))|| (pui8Packet[1]!=(TFTP_DATA & 0xff))) { 返回(0); } // //如果连接上的远程端口仍然是 TFTP 服务器端口(即 //这是第一个数据包),然后复制的事务 ID //将 TFTP 数据连接到我们的连接中。 这将确保我们的 //响应将发送到正确的端口。 // if (g_PCONN->rport == HTONS (TFTP_PORT)) { G_PCONN->rport = (((struct uip_udpip_HDR *)&uip_buf[uip_lh_LEN])->SrcPort; } // //查看这是否是正确的数据包。 // if ((pui8Packet[2]!=(g_ui32TTPBlock >>8)& 0xff))|| (pui8Packet[3]!=(g_ui32TFPBlock & 0xff))) { // //由于发送了错误的数据包,因此请重新发送其 ACK //我们已经处理了它。 // pui8Packet[0]=(TFTP_ACK >> 8)& 0xff; pui8Packet[1]= TFTP_ACK 和0xff; UIP_UDP_SEND (4); // //忽略此数据包。 // 返回(0); } // //我们要编程到哪个地址? // ui32FlashAddr = ((g_ui32TFPBlock - 1)* tftf_block_size)+ app_start_address; // //如果数据超出闪存的末尾,请勿将其编程到闪存中。 // if (ui32FlashAddr < g_ui32FlashEnd) { // //如果这是第一个块,并且我们已经得到了启动 //挂钩函数、在此处调用它以指示我们即将开始 //刷写新图像。 // #ifdef BL_START_FN_HOOK if (g_ui32TFPBlock = 1) { BL_START_FN_HOOK(); } #endif // //清除任何闪存错误指示符。 // BL_FLASH_CL_ERR_FN_HOOK(); // //如果这是第一个数据包并且启用了代码保护, //然后擦除整个闪存。 // #ifdef flash_code_protection if (g_ui32TFPBlock = 1) { // //循环浏览闪存中的页面,不包括那些页面 //包含引导加载程序和可选的保留空间。 // for (ui32Idx = app_start_address;ui32Idx < g_ui32FlashEnd; ui32Idx += FLASH_PAGE_SIZE) { // //擦除闪存的这个块。 // bl_flash_erase_fn_hook ((ui32Idx); } } #else // //闪存代码保护未启用,因此请查看其中的数据 //数据包将编程到闪存块的开头。 我们 //假设闪存块大小始终是1KB 的倍数,因此, //因为每个 TFTP 数据包为512字节,并且必须始终启动 //位于闪存页边界上,我们可以确保我们将单击 //接收数据包时每页的开头。 // if (!(ui32FlashAddr &(flash_page_size - 1))) { // //擦除闪存的这个块。 // bl_flash_erase_fn_hook (ui32FlashAddr); } #endif // //根据需要解密数据。 // #ifdef BL_decring_FN_hook bl_decrype_fn_hook (pui8Packet + 4、uip_len - 4); #endif // //将此数据块编程到闪存中。 // bl_flash_program_fn_hook (ui32FlashAddr、(pui8Packet + 4)、 (uip_len - 4)); // //如果已提供进度报告挂钩函数,请调用它 //此处。 TFTP 协议不让我们知道映像的大小 //在它开始传输之前,我们将0作为 ui32Total 传递 //参数来指示这一点。 // #ifdef BL_Progress_fn_hook bl_progress_fn_hook ((((ui32FlashAddr - app_start_address)+ (uip_len - 4))、0); #endif } // //递增到下一个块。 // G_ui32TFPTPBlock++; // //保存数据包长度。 // ui32Idx = uIP_len; // //我们是否看到任何错误? // if (BL_FLASH_ERROR_FN_HOOK()) { // //是-发回错误数据包。 // SendTFPError (2、"错误编程闪存。"); } 其他 { // //没有报告错误,因此构建 ACK 数据包。 块编号 //字段已正确,因此不需要设置。 // pui8Packet[0]=(TFTP_ACK >> 8)& 0xff; pui8Packet[1]= TFTP_ACK 和0xff; // //将 ACK 数据包发送到 TFTP 服务器。 // UIP_UDP_SEND (4); } // //如果数据包短于 TFTP_BLOCK_SIZE 字节,则为 //文件中的最后一个数据包。 // if (ui32Idx!=(TFTP_BLOCK_SIZE + 4)) { // //如果已提供结束信号挂钩函数,请在此处调用它。 // #ifdef BL_END_FN_Hook bl_end_fn_hook (); #endif 返回(1); } // //要读取的数据更多。 // 返回(0); }
您好、Charles、
再次感谢、这些函数调用帮助我在新位置刷写新的 blinky.bin (非常基本的二进制2 KB)文件。 我现在想说两件事、
1.从二进制部分引导我的器件(新编程位置),如果收到另一个固件升级命令,则返回第一部分。
2.或从引导加载程序将新的二进制文件复制到 app_BASE。
您会建议采用哪种方法?
如果我错了、我猜 PC、矢量表和存储器映射会起作用、我应该为引导加载程序提供这方面的智能吗? 让我知道需要处理的事项。
您好、Mohammed、
很高兴您取得了巨大进展。
您已经证明了第二种方法、因此、您可以更轻松地继续开发。 我认为您最初放弃了第一种方法、因为引导加载程序将无法支持 HTTP、因为您希望使其保持简单紧凑。 如果这不再是要求-使引导加载程序支持 http、我甚至会考虑第三个选项-即引导加载相同的段、而不是在应用程序段和二进制段之间进行交替。 换言之、您将只有与 TivaWare 示例类似的引导加载程序和应用程序部分、但区别在于您的引导加载程序将支持 http。 这样做的好处是、您可以在仅受闪存大小限制的情况下扩展应用程序、而不是仅受闪存大小的一半限制。
查看 TivaWare 引导加载程序、首先将向量表复制到 RAM。 当 CPU 接收到一个异常/中断时、它将跳转至 RAM 中的矢量表。 我认为如果您使用相同的方法、应该可以。
您好、Charles、
很抱歉我不能回答,我现正推行这项建议。 将 boot_demo_emac_flash 项目用作将器件从应用推送到引导加载程序的演示。 我引用 了 SoftwareUpdateBegin 函数、从任务调用该函数时出现以下错误。 执行此操作的方法应该是什么、因为我是从任务中调用此函数。
SoftwareUpdateBegin (g_ui32SysClockFreq);
void SoftwareUpdateBegin (uint32_t ui32SysClock) { // //禁用所有处理器中断。 与一次禁用它们 //(如果 添加了新的源//可能会丢失中断)不同,直接写入 NVIC 可禁用所有 //外设中断。 // HWREG (NVIC_DIS0)= 0xffffffff; HWREG (NVIC_DIS1)= 0xffffffff; HWREG (NVIC_DIS2)= 0xffffffff; HWREG (NVIC_DIS3)= 0xffffffff; HWREG (NVIC_DIS4)= 0xffffffff; // //也禁用 SysTick 中断。 // SysTickIntDisable(); SysTickDisable(); // //将控制权返回到引导加载程序。 这是对 基于闪存的引导加载程序中的 SVC //处理程序的调用、或者对 ROM 的调用(如果已配置)。 // #if ((定义的 ROM_UpdateEMAC)&&!(定义的 use_flash_boot_loader)) ROM_UpdateEMAC (ui32SysClock); #else (*(void (*)(void)(void)(*(t *) 0x2C))))(); #endif }
00008.400 TcpTimeoutRexmt:retransmit Timeout ti.sysbios.family.arm.m3.Hwi:line 1095:e_hardFault:Forced ti.sysbios.family.arm.m3.Hwi:line 1140:e_memFault:IACCVIOL:指令访问违规,address:e000ed34 异常发生在 PC 线程的后台线程= fffffffe。 内核0:ThreadType_Task 中发生异常。 任务名称:{unknown-instance-name}、句柄:0x2002aa10。 任务堆栈基地址:0x20028120。 任务堆栈大小:0x800。 R0 = 0xffffffff R8 = 0xffffffff R1 = 0x00000000 R9 = 0xffffff643 R2 = 0x00000000 R10 = 0xffffffff R3 = 0x2002b5bc R11 = 0xffffffff R4 = 0x0000028f R12 = 0x2002aec8 R5 = 0x0000FFFF SP (R13)= 0x200287b8 RFFFF = 0x0007 = 0xFFFF R15 = 0xFFFF R7 = 0xFFFF R7 = 0xFFFF R7 = 0xFFFF R15 = 0xFFFF RFFFF R7 = 0xFFFF R15 = 0xFFFF R15 0x21000000 ICSR = 0x00400803 MMFSR = 0x01 BFSR = 0x00 UFSR = 0x0000 HFSR = 0x40000000 DFSR = 0x0000000b MMAR = 0xe000ed34 BFAR = 0xe000ed38 AFSR = 0x00000000 终止执行...
您好!
您能否尝试在该帖子中完成哪些操作?