最近要完成stm32F107的这个升级固件任务,做个自己的学习记录,也和各位分享一下
一、概括综述
工程主要所需软件:STM32CubeMx,keil5,STM32CubeProgrammer
辅助软件:串口助手,wireshark,tftpd64
本次介绍的是基于STM32F107的LWIP协议栈通过TFTP的文件传输协议进行IAP程序升级。采用的PHY层芯片是LAN8742A,将单片机搭建为TFTP服务器,PC端作为客户端对其下发升级固件。在进行此项工程编写之前,需要了解单片机FLASH操作相关知识,我就最简单实现IAP升级功能,主要将FLASH分成两个部分,一部分作为BootLoader区,一部分作为功能实现的APP区。在这里理论知识就不讲了,主要讲我实操是怎么完成的,看这一文基本可以解决你的问题实现功能。
二、创建工程
本次因为使用到LWIP协议栈,去官网下载源码移植较为繁琐,所以直接使用STM32CubeMx进行代码生成,BootLoade区r和APP区需要分成两个工程去编写,最主要的是BootLoader区的引导升级。CubeMx配置如下:
RCC配置:
开启ETH的连接,GPIO配置如下:(具体需要根据自己的原理图配置)
还有一个IO是ETH_RST复位引脚,需要单独配置,可以设为输出模式,用来拉高拉低复位LAN8742A芯片,此处我设置的是:
如果需要看串口的话,可以看单片机芯片资源随意配置一个USART,串口这里就不多讲了,我这里选择的是USART3:
接下来就是LWIP的配置直接贴图
配置静态IP,注意要和PC端同一网段
其他默认就好,生成keil5工程打开
查好芯片数据手册,设置BootLoader区大小(ps:最好要完整的页,F107这里一页为2k即0x800)
在main函数里添加函数
就可以ping通了
三、具体实现TFTP传输固件,以及IAP升级
具体TFTP实现简单文件传输,需要重写tftpserver函数,重写open,close,read和write的文件操作但CubeMx生成的代码中并未包含具体的例子。在lwip的开发master分支中,官方提交了有关tftp的示例代码,我们可以对此参考编写。
https://git.savannah.gnu.org/cgit/lwip.git/commit/?id=0be3e35bf2780feed59e09fd08954f80bd7b4c7e
使用TFTP传输文件我使用的有两种方法,第一种就是比较多人使用的tftpd64
https://bitbucket.org/phjounin/tftpd64/downloads/
另外一种就是使用Windows自带的TFTP客户端服务
在main函数中添加TFTP服务器初始化函数
成功现象:
可以用wireshark捕获数据流查看
STM32CubeProgrammer查看烧录成功
APP区,总的FLASH大小为128K即0x20000,剩余留给APP的大小为0x20000-0xB000=0x15000
添加这条命令才会在对应文件夹中生成.bin文件
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ..\OBJ\APP.bin ..\OBJ\AAP.axf
在main函数开始加入这句代码
SCB->VTOR = FLASH_BASE | 0xB000; /* Vector Table Relocation in Internal FLASH. */
如果需要从APP区再跳回去BootLoader区,这里套用正点原子的图,实现的就是④步骤
#define Bootloader_ADDR 0x08000000
static void JumpToBootloader(void)
{
uint32_t i=0;
void (*SysMemBootJump)(void); /* 声明一个函数指针 */
/* 关闭全局中断 */
__disable_irq();
/* 关闭滴答定时器,复位到默认值 */
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/* 设置所有时钟到默认状态,使用 HSI 时钟 */
HAL_RCC_DeInit();
/* 关闭所有中断,清除所有中断挂起标志 */
for (i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
/* 使能全局中断 */
__enable_irq(); //解除中断屏蔽
/* 跳转到系统 BootLoader,首地址是 MSP,地址+4 是复位中断服务程序地址 */
SysMemBootJump = (void (*)(void)) (*((uint32_t *) (Bootloader_ADDR + 4)));
/* 设置主堆栈指针 */
__set_MSP(*(uint32_t *)Bootloader_ADDR);
SCB->VTOR = FLASH_BASE | 0x0000;
/* 跳转到系统 BootLoader */
SysMemBootJump();
}
四、问题记录
1、ping不通,请求超时或者是无法访问主机
检查你的网卡设置,手动配置与单片机同一网段
软件复位ETH_RST,然后复位单片机
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
HAL_Delay(60);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);
HAL_Delay(60);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
HAL_Delay(60);
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin : PB14 */
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
2、接收数据无法写入flash,hal库自带的flash操作函数是有问题的,flash擦除的时候需要加适当延时和清除标志位
int8_t FLASH_If_Erase(uint32_t StartSector)
{
HAL_FLASH_Unlock(); //解锁
uint32_t FlashAddress;
FlashAddress = StartSector;
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
while (FlashAddress <= (uint32_t) USER_FLASH_LAST_PAGE_ADDRESS)
{
if (FLASH_PageErase(FlashAddress) == HAL_OK)
{
FLASH_WaitForLastOperation(50000); //等待上次操作完成
CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
FlashAddress += FLASH_PAGE_SIZE;
}
else
{
return (1);
}
}
HAL_FLASH_Lock(); //上锁
return (0);
}
四、重点关注,需要读懂的.c文件
六、过程中使用的软件分享
此次BootLoader所占空间其实偏大,但因为本人算是初学者,不会怎么去优化内存大小,如有不妥之处望指正谅解!
SerialDebug
https://gitee.com/Weli572872916/SerialDebug
wireshark