本例程僅供參考(個(gè)人學(xué)習(xí)總結(jié)_有需要文中有的封裝好的跳轉(zhuǎn)函數(shù)可私信),
例程可舉一反三完成FDCAN通信和USART通信。
目錄
簡介
1.APP程序配置步驟
APP 程序起始地址設(shè)置方法
中斷向量表的偏移量設(shè)置方法
KEIL5生成bin文件步驟
2.IAP(BootLoader 程序)配置(HAL庫,Cubemax)
2.1RCC配置
2.2時(shí)鐘樹配置
2.3CAN配置(版本例程CAN接收數(shù)據(jù)和發(fā)送數(shù)據(jù)為普通模式,配合TIM2定時(shí)器使用)
2.4TIM2定時(shí)器配置
2.5USART配置
3.IAP(BootLoader)代碼程序配置
? ? ? ? 3.1CAN過濾器,發(fā)送,接收函數(shù)配置
3.2CAN發(fā)送配置
3.3CAN.h函數(shù)聲明
3.4CAN測(cè)試函數(shù)
3.5CAN測(cè)試效果
3.6USART接收
3.7Printf重映射函
3.8USART主函數(shù)測(cè)試
3.9USART測(cè)試效果
4.Flash寫入數(shù)據(jù)
5.跳轉(zhuǎn)至APP函數(shù)(即IAP)
6.?Bootloader 程序
7.Main程序代碼
8.升級(jí)效果
8.1CAN升級(jí)效果
8.2USART升級(jí)效果
9.注意事項(xiàng)
簡介
IAP 是用戶自己的程序在運(yùn)行過程中對(duì) User Flash 的部分區(qū)域進(jìn)行燒寫,目的是為了在產(chǎn) 品發(fā)布后可以方便地通過預(yù)留的通信口對(duì)產(chǎn)品中的固件程序進(jìn)行更新升級(jí)。通常實(shí)現(xiàn) IAP 功能時(shí),即用戶程序運(yùn)行中作自身的更新操作,需要在設(shè)計(jì)固件程序時(shí)編寫兩個(gè)項(xiàng)目代碼,
第一個(gè)項(xiàng)目程序不執(zhí)行正常的功能操作,而只是通過某種通信方式(如 USB、USART)接收程序或數(shù)據(jù),執(zhí)行對(duì)第二部分代碼的更新;
第二個(gè)項(xiàng)目代碼才是真正的功能代碼。這兩部分項(xiàng)目代碼都同時(shí)燒錄在 User Flash 中,當(dāng)芯片上電后,首先是第一個(gè)項(xiàng)目代碼開始運(yùn)行,它做如下操作:
1)檢查是否需要對(duì)第二部分代碼進(jìn)行更新
2)如果不需要更新則轉(zhuǎn)到 4)
3)執(zhí)行更新操作
4)跳轉(zhuǎn)到第二部分代碼執(zhí)行
第一部分代碼必須通過其它手段,如 JTAG 或 ISP 燒入;第二部分代碼可以使用第一部分代碼 IAP 功能燒入,也可以和第一部分代碼一起燒入,以后需要程序更新時(shí)再通過第一部分 IAP 代碼更新。
我們將第一個(gè)項(xiàng)目代碼稱之為 Bootloader 程序,第二個(gè)項(xiàng)目代碼稱之為 APP 程序,他們存放在 STM32G4??FLASH 的不同地址范圍,一般從最低地址區(qū)開始存放 Bootloader,緊跟其后的就是 APP 程序(注意,如果 FLASH 容量足夠,是可以設(shè)計(jì)很多 APP 程序的),這樣我們就是要實(shí)現(xiàn) 2 個(gè)程序:Bootloader 程序和 APP程序。
STM32G4 的 APP 程序不僅可以放到 FLASH 里面運(yùn)行,也可以放到 SRAM 里面運(yùn)行,我們選用在 FLASH 運(yùn)行。Flash存儲(chǔ)器是一種非易失性存儲(chǔ)器,可以在掉電之后保存數(shù)據(jù),通常用于存儲(chǔ)程序代碼。Flash存儲(chǔ)器的可寫入次數(shù)有限,且需要執(zhí)行擦除操作才能寫入新的數(shù)據(jù),因此,在使用過程中需要注意擦寫周期和數(shù)據(jù)備份問題。SRAM存儲(chǔ)器則是一種易失性存儲(chǔ)器,具有相對(duì)較快的讀寫速度和無限的讀寫次數(shù),但掉電時(shí)將會(huì)丟失所有內(nèi)容。SRAM存儲(chǔ)器主要用于暫存數(shù)據(jù)和臨時(shí)變量,讀寫操作由CPU直接完成,訪問速度較快。
單片機(jī)的Flash存儲(chǔ)器和SRAM存儲(chǔ)器都嵌入在單片機(jī)芯片內(nèi)部,能夠方便的實(shí)現(xiàn)對(duì)程序和數(shù)據(jù)、變量的讀寫操作。通常,編譯器會(huì)把程序燒錄在Flash存儲(chǔ)器,并使用SRAM存儲(chǔ)器來存儲(chǔ)變量、函數(shù)堆棧以及其他的臨時(shí)數(shù)據(jù)
1.APP程序配置步驟
-
APP 程序起始地址設(shè)置方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
圖中 IROM1 的起始地址(Start)為 0x08000000,大?。⊿ize)為 0x80000,即從 0x08000000 開始的 512K 空間為我們的程序存儲(chǔ)區(qū)。
APP程序?qū)⒃O(shè)置起始地址(Start)為 0x08010000,即偏移量為 0x10000(64K?字節(jié),即留給 BootLoader 的空間),因而,留給 APP 用的 FLASH 空間(Size)只有 0x80000- 0x10000=0x70000(448K 字節(jié))大小了。設(shè)置好 Start 和 Size,就完成 APP 程序的起始地址設(shè)置。IRAM 是內(nèi)存的地址,APP 可以獨(dú)占這些內(nèi)存,不需要修改。
注意:需要確保 APP 起始地址在 Bootloader 程序結(jié)束位置之后,并且偏移量為 0x200 的倍數(shù)即可
-
中斷向量表的偏移量設(shè)置方法
VTOR 寄存器存放的是中斷向量表的起始地址。默認(rèn)的情況它由 BOOT 的啟動(dòng)模式?jīng)Q定,對(duì)于 STM32G4 來說就是指向 0x0800 0000 這個(gè)位置,也就是從默認(rèn)的啟動(dòng)位置加載中斷向量等信息,不過 ST 允許重定向這個(gè)位置,這樣就可以從 Flash 區(qū)域的任意位置啟動(dòng)我們的代碼了。我們可以通過調(diào)用 sys.c 里面的 sys_nvic_set_vector_table 函數(shù)實(shí)現(xiàn),該函數(shù)定義如下:/** * @brief 設(shè)置中斷向量表偏移地址 * @param baseaddr : 基址 * @param offset : 偏移量 * @retval 無 */ void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset) { /* 設(shè)置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */ SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00); }
該函數(shù)用于設(shè)置中斷向量偏移,baseaddr 為基地址(即 APP 程序首地址),Offset 為偏移量,需要根據(jù)自己的實(shí)際情況進(jìn)行設(shè)置。比如 FLASH APP 設(shè)置中斷向量表偏移量為0x10000,調(diào)用情況如下:
/* 設(shè)置中斷向量表偏移量為 0x10000 */ sys_nvic_set_vector_table(FLASH_BASE, 0x10000);
通過以上兩個(gè)步驟的設(shè)置,我們就可以生成 APP 程序了,只要 APP 程序的 FLASH大小不超過我們的設(shè)置即可? ?MDK 默認(rèn)生成的文件是.hex 文件,并不方便我們用作 IAP更新,工程直接生成.bin 文件,可以方便進(jìn)行 IAP 升級(jí)。
-
KEIL5生成bin文件步驟
通過 MDK 自帶的格式轉(zhuǎn)換工具 fromelf.exe 來生成 bin 文件。注意:如果用戶安裝 MDK 在 C 盤的默認(rèn)路徑,那它的位置一般C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe。
其他位置可點(diǎn)擊鼠標(biāo)右鍵 KEIL 軟件 打開文件所在目錄 ,然后跳轉(zhuǎn)到上一級(jí)文件目錄的ARM ->>ARMCC ->>獲得絕對(duì)路徑。
點(diǎn)擊Options for Target→User,在 After Build/Rebuild 一欄中,勾選 Run #1,輸入轉(zhuǎn)換語句
D:\A_A_ProgramFiles\A_Keil5\ARM\ARMCC\bin\fromelf --bin -o ..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\Output\@L.bin ..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\MDK-ARM\Can_IAP_003\%L
推薦相對(duì)地址,(不同的工程文件地址可能不同,需要根據(jù)自己的工程來定,文件路徑較深推薦用絕對(duì)路徑。)
轉(zhuǎn)換語句的組成解釋如下:
首先是MDK 自帶的格式轉(zhuǎn)換工具 fromelf.exe的絕對(duì)路徑 + 命令fromelf --bin -o?路徑(生成bin文件保存路徑)\@L.bin??路徑(生.axf的文件路徑)%L。
D:\A_A_ProgramFiles\A_Keil5\ARM\ARMCC\bin\fromelf --bin -o?..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\Output\@L.bin?..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\MDK-ARM\Can_IAP_003\%L
總結(jié)APP 程序的生成步驟
- 設(shè)置 APP 程序的起始地址和存儲(chǔ)空間大小
- 設(shè)置中斷向量表偏移量
- 設(shè)置編譯后運(yùn)行 fromelf.exe,生成.bin 文件
以上 3 個(gè)步驟,就可以得到一個(gè).bin 的 APP 程序,通過 Bootlader 程序即可實(shí)現(xiàn)更新
APP程序中加入文件夾中的
.c和.h文件在工程中添加包含路徑,并在Main函數(shù)最開始部分添加語句
2.IAP(BootLoader 程序)配置(HAL庫,Cubemax)
2.1RCC配置
2.2時(shí)鐘樹配置
2.3CAN配置(版本例程CAN接收數(shù)據(jù)和發(fā)送數(shù)據(jù)為普通模式,配合TIM2定時(shí)器使用)
2.4TIM2定時(shí)器配置
2.5USART配置
生成工程
3.IAP(BootLoader)代碼程序配置
? ? ? ? 3.1CAN過濾器,發(fā)送,接收函數(shù)配置
/* USER CODE BEGIN 0 */
FDCAN_FilterTypeDef sFilterConfig; //接收過濾器類型定義結(jié)構(gòu)體
/* USER CODE END 0 */
/* USER CODE BEGIN FDCAN1_Init 2 *///配置接收過濾
sFilterConfig.IdType = FDCAN_STANDARD_ID;
// 配置為過濾標(biāo)準(zhǔn)幀
sFilterConfig.FilterIndex = 0;
// 過濾器的索引號(hào) 如果有2組標(biāo)準(zhǔn)幀過濾,那么就是通過index區(qū)別,0 1 2這種
sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
// 過濾方式為范圍,即從FilterID1~FilterID2之間的值
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x0000;
sFilterConfig.FilterID2 = 0x07ff;
//標(biāo)準(zhǔn)幀為11位ID,即0x7ff,本例配置為接收所有幀
/*設(shè)置接收過濾器,根據(jù)FDCAN_FilterTypeDef結(jié)構(gòu)中指定的參數(shù)配置FDCAN接收篩選器。*/
if(HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
/*配置FDCAN全局過濾器*/
if(HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,FDCAN_REJECT,FDCAN_REJECT,DCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
{
Error_Handler();
}
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END FDCAN1_Init 2 */
3.2CAN發(fā)送配置
/*發(fā)送函數(shù)*/
void CAN_Send_Msg(FDCAN_HandleTypeDef hcan,uint32_t can_id,uint8_t tx_buff[])
{
FDCAN_TxHeaderTypeDef TxHeader;
TxHeader.Identifier = can_id; /* 32位ID */
TxHeader.IdType = FDCAN_STANDARD_ID; /* 標(biāo)準(zhǔn)ID */
TxHeader.TxFrameType = FDCAN_DATA_FRAME; /* 數(shù)據(jù)幀 */
TxHeader.DataLength = FDCAN_DLC_BYTES_8; /* 數(shù)據(jù)長度 */
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch = FDCAN_BRS_ON; //用于設(shè)置發(fā)送是否波特率可變。
TxHeader.FDFormat = FDCAN_CLASSIC_CAN; //普通can還是FDcan
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
//是用于設(shè)置發(fā)送事件FIFO控制事件是否保存
TxHeader.MessageMarker = 0;
//marker++; //用于設(shè)置復(fù)制到TX EVENT FIFO的消息Maker,來識(shí)別消息狀態(tài),范圍0到0xFF
HAL_FDCAN_AddMessageToTxFifoQ(&hcan, &TxHeader, tx_buff); //發(fā)送
}
void CAN_Receive_Msg(void)
{ /*函數(shù)的作用是獲取CAN接收FIFO的填充級(jí)別。*/
if(HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1,FDCAN_RX_FIFO0) >= 1)
{
/*從Rx FIFO 收取一個(gè) CAN 幀*/
if(HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &CANRec_msg, CANRec_buff) == HAL_OK ) {
CANRecFlag = 1;//接收完成標(biāo)志位
}
}
}
在數(shù)函數(shù)中全局變量
uint8_t CANRecFlag ; //CAN接收表示,完成 1
uint8_t CANRec_buff[8]; //CAN接受到的數(shù)據(jù)位
FDCAN_RxHeaderTypeDef CANRec_msg;
在Main.h中extern
extern uint8_t CANRecFlag ;
extern uint8_t CANRec_buff[8];
extern FDCAN_RxHeaderTypeDef CANRec_msg;
3.3CAN.h函數(shù)聲明
void CAN_Send_Msg(FDCAN_HandleTypeDef hcan,uint32_t can_id,uint8_t tx_buff[]);
void CAN_Receive_Msg(void);
void Power_ON_Start_TX(void);
3.4CAN測(cè)試函數(shù)
void Power_ON_Start_TX(void)
{
FDCAN_TxHeaderTypeDef TxHeader;
uint8_t tx_data[8] = {0x20, 0x23, 0x11, 0x09, 0x12, 0x20, 0x00, 0x00};
uint32_t can_id = 0x601;
TxHeader.Identifier = can_id;
TxHeader.IdType = FDCAN_STANDARD_ID;
TxHeader.TxFrameType = FDCAN_DATA_FRAME;
TxHeader.DataLength = FDCAN_DLC_BYTES_8;
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch = FDCAN_BRS_ON; //用于設(shè)置發(fā)送是否波特率可變。
TxHeader.FDFormat = FDCAN_CLASSIC_CAN; //普通can還是FDcan
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
//是用于設(shè)置發(fā)送事件FIFO控制事件是否保存
TxHeader.MessageMarker = 0;
//marker++; //用于設(shè)置復(fù)制到TX EVENT FIFO的消息Maker,來識(shí)別消息狀態(tài),范圍0到0xFF
HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, tx_data);//向 Tx 郵箱中增加一個(gè)消息,并且激活對(duì)應(yīng)的傳輸請(qǐng)求
}
在#include "stm32g4xx_it.c文件中添加CAN頭文件和TIM2中斷函數(shù)中添加接收函數(shù)
#include "fdcan.h"
int TickMs = 0;
void TIM2_IRQHandler(void)
{
/* USER CODE BEGIN TIM2_IRQn 0 */
/* USER CODE END TIM2_IRQn 0 */
HAL_TIM_IRQHandler(&htim2);
/* USER CODE BEGIN TIM2_IRQn 1 */
CAN_Receive_Msg();//FDCAN接收函數(shù)
TickMs++;//定時(shí)器計(jì)數(shù)
/* USER CODE END TIM2_IRQn 1 */
}
主函數(shù)Main.c中while中添加如下代碼
//while前
HAL_TIM_Base_Start_IT(&htim2); //定時(shí)器 中斷
Power_ON_Start_TX(); //上電后發(fā)送一串?dāng)?shù)據(jù)
//While中
if (CANRecFlag)
{
CANRecFlag = 0;
CAN_Send_Msg(hfdcan1,CANRec_msg.Identifier,CANRec_buff);
//接收到數(shù)據(jù)網(wǎng)CAN上位機(jī)反饋一下
}
3.5CAN測(cè)試效果
上電程序運(yùn)行,會(huì)發(fā)送一組數(shù)據(jù) 20 23 11 09 12 20 00 00,然后上位機(jī)發(fā)送數(shù)據(jù)后會(huì)將發(fā)送的數(shù)據(jù)進(jìn)行反饋給上位機(jī),表示數(shù)據(jù)接收成功。
3.6USART接收
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1) /* 如果是串口1 */
{
if ( UART_RX_Cont < USART1_MAX_REC_LENGTH)
{
UART1_Rx_Buff[UART_RX_Cont] = UART1_Rx_Temp[0];
UART_RX_Cont++;
}
/* 接收完成后在開啟寫一次中斷接收 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)UART1_Rx_Temp, RXBUFF_ERSIZE);
}
}
串口相關(guān)定義
/* USER CODE BEGIN 0 */
uint16_t UART1_Rx_Sta=0; /* 接收狀態(tài) */
uint8_t UART1_Rx_Buff[USART1_MAX_REC_LENGTH]; /* HAL庫使用的串口接收緩沖 */
uint8_t UART1_Rx_Temp[RXBUFF_ERSIZE]; /* 接收字節(jié) */
uint32_t UART_RX_Cont = 0;
UART_HandleTypeDef huart1;
/* USER CODE END 0 */
usart.h聲明
#include "stdio.h"
int fputc(int ch, FILE *f);
#define RXBUFF_ERSIZE 1 /* 緩存大小 */
#define USART1_MAX_REC_LENGTH 64*1024 /* 定義最大接收字節(jié)數(shù) 200 */
extern uint8_t UART1_Rx_Buff[USART1_MAX_REC_LENGTH];
/* 接收緩沖,最大USART_REC_LEN個(gè)字節(jié).末字節(jié)為換行符 */
extern uint16_t UART1_Rx_Sta; /* 接收狀態(tài)標(biāo)記 */
extern uint8_t UART1_Rx_Temp[RXBUFF_ERSIZE]; /* HAL庫USART接收Buffer */
extern uint32_t UART_RX_Cont ; /* 接收到的app代碼長度 */
Main.c
uint32_t lastcount = 0; /* 上一次串口接收數(shù)據(jù)值 */
uint32_t applenth = 0; /* 接收到的app代碼長度 */
printf("STM32_CAN_UASRT_IAP_End\n"); //程序串口運(yùn)行發(fā)送STM32_CAN_UASRT_IAP_End
在usart.c中MX_USART1_UART_Init函數(shù)中,打開中斷接收
/* USER CODE BEGIN USART1_Init 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)UART1_Rx_Temp, RXBUFF_ERSIZE);//開啟中斷
/* USER CODE END USART1_Init 2 */
3.7Printf重映射函
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch};
HAL_UART_Transmit(&huart1, temp, 1, 0xffff);
return ch;
}
3.8USART主函數(shù)測(cè)試
/*----------串口接收1--------------------------------------*/
if (UART_RX_Cont)
{
if (lastcount == UART_RX_Cont) /* 新周期內(nèi),沒有收到任何數(shù)據(jù),認(rèn)為本次數(shù)據(jù)接收完成 */
{
applenth = UART_RX_Cont;
lastcount = 0;
UART_RX_Cont = 0;
for(int i = 0; i<applenth;i++)
{
printf("UART1_Rx_Buff[%d] == %x \r\n",i,UART1_Rx_Buff[i]);
}
printf("用戶程序接收完成!\r\n");
printf("代碼長度:%dBytes\r\n", applenth);
}
else lastcount = UART_RX_Cont;
}
3.9USART測(cè)試效果
上電發(fā)送STM32_CAN_UASRT_IAP_End,通過上位機(jī)發(fā)送數(shù)據(jù),將數(shù)據(jù)每一位轉(zhuǎn)化成ascll碼后16進(jìn)制進(jìn)行打印,發(fā)送的bin文件的所有數(shù)據(jù)都存放在UART1_Rx_Buff的數(shù)組中
4.Flash寫入數(shù)據(jù)
FLASH寫入數(shù)據(jù)是引用封裝后的函數(shù),在文件夾的
文件中的
文件可以直接添加到工程中,并添加頭文件的路徑。使用時(shí)直接引用相關(guān)函數(shù)。
5.跳轉(zhuǎn)至APP函數(shù)(即IAP)
跳轉(zhuǎn)函數(shù)同樣也是引用的封裝好的函數(shù),在跳轉(zhuǎn)函數(shù)中做了一些改進(jìn),可以直接添加工程中直接進(jìn)行使用
注意:由于是因引用的封裝后的庫,建議將
中的所有源文件(.c)和頭文件(.h)同時(shí)添加到工程文件中。
到此部分Bootloader 程序的CAN通訊和usart通訊已經(jīng)完成(接收升級(jí)固件包數(shù)據(jù))
下面程序的升級(jí)邏輯介紹
6.?Bootloader 程序
升級(jí)程序流程圖如下
7.Main程序代碼
函數(shù)聲明部分
/* USER CODE BEGIN Includes */
#include "sys.h"
#include "delay.h"
#include "iap.h"
uint8_t CANRecFlag ; //CAN接收表示,完成 1
uint8_t CANRec_buff[8]; //CAN接受到的數(shù)據(jù)位
FDCAN_RxHeaderTypeDef CANRec_msg;
#define APP_LEN_MAX 60*1024
uint8_t APP_RX_BUF[APP_LEN_MAX] = {0}; //接收緩沖,最大64kb.
uint32_t APPLength = 0; //升級(jí)程序數(shù)據(jù)長度
extern int TickMs;
/* USER CODE END Includes */
Bootloader程序升級(jí)或跳轉(zhuǎn)選擇while
/* USER CODE BEGIN 2 */
//delay_init(170); //若有報(bào)錯(cuò)打開注釋
uint32_t lastcount = 0; /* 上一次串口接收數(shù)據(jù)值 */
uint32_t applenth = 0; /* 接收到的app代碼長度 */
HAL_TIM_Base_Start_IT(&htim2); //定時(shí)器 中斷
Power_ON_Start_TX(); //上電后發(fā)送一串?dāng)?shù)據(jù)
printf("STM32_IAP_02\n");
while(1)
{
if(TickMs>5000)//5秒后跳轉(zhuǎn)
{
if (((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000)
/* 判斷FLASH里面是否有APP,有的話執(zhí)行 */
{
printf("開始執(zhí)行FLASH用戶代碼!!\r\n\r\n");
printf("RUN addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR )));
iap_load_app(FLASH_APP1_ADDR); /* 執(zhí)行FLASH APP代碼 */ }else{
printf("非APP———FLASH應(yīng)用程序,無法執(zhí)行!\r\n");
printf("進(jìn)入主程序等待升級(jí)!!\r\n");
break;
}
}
if(CANRecFlag)//升級(jí)命令CAN口有接收任意消息
{
CANRecFlag = 0;
printf("進(jìn)入主程序等待升級(jí)!!\r\n");
break;
}
}
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/*-----------串口接收數(shù)據(jù)處理--------------------------------------*/
if (UART_RX_Cont)
{
if (lastcount == UART_RX_Cont) /* 新周期內(nèi),沒有收到任何數(shù)據(jù),認(rèn)為本次數(shù)據(jù)接收完成 */
{
applenth = UART_RX_Cont;
lastcount = 0;
UART_RX_Cont = 0;
// for(int i = 0; i<applenth;i++)
// {
// printf("UART1_Rx_Buff[%d] == %x \r\n",i,UART1_Rx_Buff[i]);
// }
printf("用戶程序接收完成!\r\n");
printf("代碼長度:%dBytes\r\n", applenth);
}
else lastcount = UART_RX_Cont;
}
/*----------CAN升級(jí)命令------------------------------*/
if (CANRecFlag)
{
CANRecFlag = 0;
// CAN_Send_Msg(hfdcan1,CANRec_msg.Identifier,CANRec_buff);
//接收到數(shù)據(jù)網(wǎng)CAN上位機(jī)反饋一下
if (CANRec_msg.Identifier ==0x123) //升級(jí)指令包
{
if (applenth < APP_LEN_MAX)
{
for(int i=0 ;i < 8;i++)
{
APP_RX_BUF[applenth] = CANRec_buff[i];
applenth++;
}
}else{
printf("緩沖區(qū)容量不足...\r\n");
}
}
if (CANRec_msg.Identifier ==0x111)
{
printf("APP_RX_BUF[%d] \n",applenth);//APPLength
// for(int i = 0; i<applenth;i++)
// {
// printf("APP_RX_BUF[%d] = %x\n",i,APP_RX_BUF[i]);
// }
if(applenth)
{
/*-------------串口升級(jí)----------------------------------------------------------------------*/ if (((*(volatile uint32_t *)(UART1_Rx_Buff + 4)) & 0xFF000000) == 0x08000000)
/* 判斷是否為0X08XXXXXX */
{
printf(" \n串口升級(jí)開始更新固件...\r\n");
printf("updata addr :%x \r\n",(*(volatile uint32_t *)(UART1_Rx_Buff + 4)) & 0xFF000000); iap_write_appbin(FLASH_APP1_ADDR, UART1_Rx_Buff, applenth);/* 更新FLASH代碼 */ printf("UPdata addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR ))); printf("\n?。?!串口升級(jí)固件更新完成!?。r\n");
}
/*---------------CAN升級(jí)-------------------------------------------------------------------*/
else if (((*(volatile uint32_t *)(APP_RX_BUF + 4)) & 0xFF000000) == 0x08000000)
/* 判斷是否為0X08XXXXXX */
{
printf(" \nCAN升級(jí)開始更新固件...\r\n");
printf("updata addr :%x \r\n",(*(volatile uint32_t *)(APP_RX_BUF + 4)) & 0xFF000000); iap_write_appbin(FLASH_APP1_ADDR, APP_RX_BUF, applenth); /* 更新FLASH代碼 */ printf("UPdata addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR ))); printf("\n?。?!固件更新完成!?。r\n");
}else{
printf("\n?。?!非FLASH應(yīng)用程序?。?!\r\n");
}
}else{
printf("沒有可以更新的固件!\r\n");
}
}
if (CANRec_msg.Identifier == 0x222) /* KEY1按鍵按下, 運(yùn)行FLASH APP代碼 */
{
printf("flash Run addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000);
if (((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000) /* 判斷FLASH里面是否有APP,有的話執(zhí)行 */
{
printf("開始執(zhí)行FLASH用戶代碼!!\r\n\r\n");
printf("RUN addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR )));
iap_load_app(FLASH_APP1_ADDR);/* 執(zhí)行FLASH APP代碼 */ }else {
printf("沒有可以運(yùn)行的固件!\r\n");
}
}
}
/*-------------------Bootloader LED反饋--------------------------------------------------*/
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_1|GPIO_PIN_0);
HAL_Delay(500);
}
/* USER CODE END 3 */
8.升級(jí)效果
8.1CAN升級(jí)效果
8.2USART升級(jí)效果
通過Ecantools發(fā)送更新,升級(jí)指令實(shí)現(xiàn)升級(jí)跳轉(zhuǎn)程序文章來源:http://www.zghlxwxcb.cn/news/detail-777211.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-777211.html
9.注意事項(xiàng)
- CAN接受數(shù)據(jù)時(shí)不可有延時(shí)出現(xiàn),否則會(huì)影響數(shù)據(jù)接收
- CAN通過上位機(jī)發(fā)送bin文件數(shù)據(jù)或其他方式發(fā)送文件數(shù)據(jù)時(shí),需要注意發(fā)送每一幀數(shù)據(jù)的時(shí)間間隔的控制,否則會(huì)出現(xiàn)數(shù)據(jù)丟幀,不能接收完整的數(shù)據(jù)包。
-
配置APP程序和Bootloader程序時(shí),需要注意起始地址和空間大小,具體看程序的大小從而進(jìn)行配置。本程序的起始控件大小如下
- 程序中有Printf重映射相關(guān)的函數(shù),需要將上圖中的 ??????????use MicroLIB
勾選上
到了這里,關(guān)于STM32G473 固件升級(jí)IAP(BootLoader)CAN/USART。(詳細(xì)步驟)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!