-
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