STM32串口升级
-
STM32升级功能分为2部分,一部分是bootloader程序,另一部分是用户应用程序组成(以STM32F103C8为例来说明 )
bootloader工程分析
启动文件的复位部分
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
系统复位后会调用SystemInit函数,此函数在system_stm32f10x.c文件中,其中有个宏决定程序flash或SRAM偏移定义的宏
/*!< Uncomment the following line if you need to relocate your vector Table in
Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
在SystemInit函数的最后设置这个偏移值
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
启动方试有SRAM和FLASH两种,由宏VECT_TAB_SRAM来决定,SRAM_BASE基地址和FLASH_BASE基地址定义如下
#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
通常bootloader程序会放在偏移为0开始的一段空间,刚好是FLASH_BASE基址与偏移值为0加起来的地址值,在keil中地址配置如下
程序SystemInit执行完后进入main函数
int main(void)
{
FLASH_Unlock();
UART_Init();
BspTim2Init();
Main_Menu ();
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
{
BspTim2Close();
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();
}
else
{
SerialPutString("no user Program\r\n\n");
}
while(1);
}
- 首先调用FLASH_Unlock函数解锁flash操作
- UART_Init函数初时化,为接收升级文件操作作准备
- BspTim2Init函数初时化定时器,提供超时计数用
- Main_Menu函数升级菜单操作功能实现
- 测试用户app地址是不是在APPLICATION_ADDRESS位置。检测栈顶的地址,来检验app是否下载成功
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000){}
其中ApplicationAddress为用户应用程序起始地址,假如从0x8003000开始,则定义如下
#define ApplicationAddress 0x8003000
6. APPLICATION_ADDRESS + 4对应的是app中断向量表的第二项,复位地址
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
7.把地址强转为函数指针
Jump_To_Application = (pFunction) JumpAddress;
JumpAddress 和Jump_To_Application定义如下
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;
8. 设置主函数栈指针
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
9.调用函数,跳转到app复位地址去执行复位操作
Jump_To_Application();
写升级内容到flash,数据接收采用ymode协议进行数据接收,具体协议可找度娘了解
/*******************************************************************************
* @函数名称:Ymodem_Receive
* @函数说明:通过 ymodem协议接收一个文件
* @输入参数:buf: 首地址指针
* @输出参数:无
* @返回参数:文件长度
* @历史记录:
<作者> <时间> <修改记录>
*******************************************************************************/
int32_t Ymodem_Receive (uint8_t *buf)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
int32_t i, j, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
//初始化Flash地址变量
FlashDestination = ApplicationAddress;
for (session_done = 0, errors = 0, session_begin = 0; ;)
{
for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
{
switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
{
case 0:
errors = 0;
switch (packet_length)
{
//发送端终止
case - 1:
Send_Byte(ACK);
return 0;
//结束传输
case 0:
Send_Byte(ACK);
file_done = 1;
break;
//正常的数据包
default:
if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
Send_Byte(NAK);
}
else
{
if (packets_received == 0)
{
//文件名数据包
if (packet_data[PACKET_HEADER] != 0)
{
//文件名数据包有效数据区域
for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
{
file_name[i++] = *file_ptr++;
}
file_name[i++] = '\0';
for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
{
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
Str2Int(file_size, &size);
//测试数据包是否过大
if (size > (FLASH_SIZE - 1))
{
//结束
Send_Byte(CA);
Send_Byte(CA);
return -1;
}
//计算需要擦除Flash的页
NbrOfPage = FLASH_PagesMask(size);
//擦除Flash
for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
}
Send_Byte(ACK);
Send_Byte(CRC16);
}
//文件名数据包空,结束传输
else
{
Send_Byte(ACK);
file_done = 1;
session_done = 1;
break;
}
}
//数据包
else
{
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
RamSource = (uint32_t)buf;
for (j = 0; (j < packet_length) && (FlashDestination < ApplicationAddress + size); j += 4)
{
//把接收到的数据编写到Flash中
FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);
if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
{
//结束
Send_Byte(CA);
Send_Byte(CA);
return -2;
}
FlashDestination += 4;
RamSource += 4;
}
Send_Byte(ACK);
}
packets_received ++;
session_begin = 1;
}
}
break;
case 1:
Send_Byte(CA);
Send_Byte(CA);
return -3;
default:
if (session_begin > 0)
{
errors ++;
}
if (errors > MAX_ERRORS)
{
Send_Byte(CA);
Send_Byte(CA);
return 0;
}
Send_Byte(CRC16);
break;
}
if (file_done != 0)
{
break;
}
}
if (session_done != 0)
{
break;
}
}
return (int32_t)size;
}
1.收到文件名及长度会计算大小然后擦除flash
//计算需要擦除Flash的页
NbrOfPage = FLASH_PagesMask(size);
//擦除Flash
for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
}
2.接收到文件内容写对应的flash,从用户程序 ApplicationAddress地址开始写
uint32_t FlashDestination = ApplicationAddress;
写操作如下:
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
RamSource = (uint32_t)buf;
for (j = 0; (j < packet_length) && (FlashDestination < ApplicationAddress + size); j += 4)
{
//把接收到的数据编写到Flash中
FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);
if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
{
//结束
Send_Byte(CA);
Send_Byte(CA);
return -2;
}
FlashDestination += 4;
RamSource += 4;
}
Send_Byte(ACK);
写入后会读回内容,如果写入的与读回的内容不一致,则会终止升级操作
-
应用程序部分
此部分只需注意以下几点即可
1. 起始地址设置,即bootloader跳转到用户程序的地址
2.偏移地址设置
/*!< Uncomment the following line if you need to relocate your vector Table in
Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x3000 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
升级时要使用有ymode协议的串口工具才能正常升级,最后,demo下载地址:
https://download.csdn.net/download/mygod2008ok/12082368
上一篇: CentOS 7无人值守网络装机
下一篇: (嵌入式)STM32串口通讯
推荐阅读
-
基于SQL2005 SQL2008 表结构信息查询升级版的详解(含外键信息)
-
华为Mate 30系列EMUI 10.1内测:多屏协同升级 电脑接听手机来电
-
叕叕又升级啦!PDF文档管理控件Aspose.PDF 11月新更!支持居中对齐输出HTML
-
360随身wifi4G版怎么激活使用 360随身wifi4G版固件升级方法步骤
-
Mysql升级到5.7后遇到的group by查询问题解决
-
Win下Mysql5.6升级到5.7的方法
-
斗鱼怎么获得粉丝徽章? 斗鱼粉丝徽章升级与维持方法
-
win10升级助手在哪里?win10升级助手下载使用教程
-
win10升级助手是什么?win10升级助手官方下载地址
-
今年新iPhone将有三款:Pro版本后置三摄、升级版XR加入绿色后壳