本例程實現功能:
1、IAP固件程序實現固件APP搬移,跳轉至APP
2、APP固件程序實現自定義功能,接收上位機下發的bin文件
3、上位機加載APPbin文件,分割下發至APP固件程序(本例程使用QT開發)
一、IAP升級簡單介紹
基本原理不做贅述,參見:https://blog.csdn.net/wzy15965343032/article/details/88545225
兩種方式:
方式1: IAP固件直接接收上位機下發的程序,將程序寫入APP區域,完成後跳轉至APP部分。這種方法正點原子有相應的例程;
方式2:
FALSH區域分爲4部分:IAP區、APP運行區、APP下載區、參數區域
1、IAP部分檢測APP程序是否有更新,有更新需從APP下載區搬運程序至APP運行區,完成後跳轉至APP區域;如無更新,直接跳轉。
2、APP區域負責運行程序主流程,如是APP升級數據包,則將此包寫入APP下載區,同時更新參數區升級標誌
3、APP下載區主要存放更新程序
4、參數區域存放升級標誌以及其他參數
本例程採用方式二升級
二、固件程序
STM32F103CBT6芯片使用HAL庫開發
1、flash區域劃分
分區 | 大小 | 扇區 | 地址 |
IAP | 10k | 0-9 | 0x08000000-0x080027FF |
APP運行區 | 58k | 10-57 | 0x08002800-0x08010FFF |
APP下載區 | 58k | 58-126 | 0x08011000-0x0801F7FF |
參數區 | 2k | 127-128 | 0x0801F800-0x0801FFFF |
2、IAP程序開發
根據需求,IAP程根據參數區標誌位判定是否要升級,只需要完成flash讀寫即可,爲方便調試附帶串口打印功能;
跳轉APP部分程序一爲固定代碼,如下程序流程圖如下:
程序例程:
//跳轉到應用程序段
//ulAddr_App:APP運行區地址.
void IAP_ExecuteApp ( uint32_t ulAddr_App )
{
pIapFun_TypeDef pJump2App;
//檢查棧頂地址是否合法.
if ( ( ( * ( __IO uint32_t * ) ulAddr_App ) & 0x2FFE0000 ) == 0x20000000 )
{
pJump2App = ( pIapFun_TypeDef ) * ( __IO uint32_t * ) ( ulAddr_App + 4 );
//用戶代碼區第二個字爲程序開始地址(復位地址)
MSR_MSP( * ( __IO uint32_t * ) ulAddr_App );
//初始化APP堆棧指針(用戶代碼區的第一個字用於存放棧頂地址)
pJump2App ();
//跳轉到APP.
}
}
void IAP_Copy_App(void)
{
uint8_t i;
uint8_t buf[2] = {0x96,0x69};
//擦除App扇區
Flash_EraseSector(10,67);
for(i = 0;i < 58;i++)
{
FLASH_ReadPage(68 + i,FLASH_BUFF);
HAL_Delay(10);
FLASH_WritePage(10 + i,FLASH_BUFF);
}
FLASH_WriteNData(FLASH_APP_UPTADE,buf,2);
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
USER_UART_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_Delay(100);
printf("IAP start\r\n");
while(1){
uint16_t ret = FLASH_ReadHalfWord(FLASH_APP_UPTADE);
printf("update = %04x \n",ret);
if(ret == UPTADE_FALG)
{
printf("Updata App...\r\n");
IAP_Copy_App();
printf("Updata App Succeed...\r\n");
}
IAP_ExecuteApp(FLASH_APP_START);
}
/* USER CODE END 3 */
}
3、APP程序開發
APP程序需要:1、flash讀寫;2、串口接收,發送;3、中斷向量表重定義;4、程序重啓
程序主流程:串口接收數據包(1024byte)直接寫入flash(58扇區開始,往後寫),爲方便方便理解移植,本demo接收16K文件然後重啓。(正式版本可以通過通信協議控制)
void IAP_WriteBin(int ii,uint8_t *pBuff,uint32_t Len)
{
uint8_t buf[2] = {0xAA,0x55};
//擦除DOWNLOAD扇區
Flash_EraseSector(68+ii,68+ii+1);
//寫入程序
FLASH_WritePage(68+ ii,pBuff);
if (ii > 15){
//更新標記
FLASH_WriteNData(FLASH_APP_UPTADE,buf,2);
//復位單片機
NVIC_SystemReset();
}
}
void cmdProcess()
{
if(g_rx232_end_flag == 1)
{
IAP_WriteBin(current,g_rx232_buffer,g_rx232_len);
current ++;
HAL_Delay(10);
memset(g_rx232_buffer,0,g_rx232_len);/*清接收緩存 */
g_rx232_len=0; /*清除計數 */
g_rx232_end_flag=0; /*清除接收結束標誌位*/
HAL_UART_Receive_DMA(&huart3,g_rx232_buffer,BUFFER_SIZE);/*重新打開DMA接收*/
}
if(g_rx485_len)
{
IAP_WriteBin(current,g_rx232_buffer,g_rx232_len);
current ++;
HAL_Delay(10);
memset(g_rx485_buffer,0,g_rx485_len);/*清接收緩存 */
HAL_UART_Receive_DMA(&huart1,g_rx485_buffer,BUFFER_SIZE);
g_rx485_len = 0;
}
}
三、上位機程序
使用QT進行開發
1、加載BIN文件
2、串口操作
3、程序分割下載(1024字節bin文件數據包)
void MainWindow::on_pushButton_loadfile_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,QString::fromLocal8Bit("bin file"),qApp->applicationDirPath(),
QString::fromLocal8Bit("bin File(*.bin)"));//新建文件打開窗口
if (fileName.isEmpty())//如果未選擇文件便確認,即返回
return;
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly)
ui->label_filepath->setText(file.errorString());//文件打開錯誤顯示錯誤信息
arry=file.readAll();//讀取文件
ui->label_filepath->setText(fileName);
ui->textEdit_3->append(arry.toHex());
qDebug()<<arry.length();
file.close();
}
void MainWindow::on_pushButton_download_clicked()
{
if (arry.length() <= 0){
qDebug()<<"part num = 0 ";
return;
}
unsigned char temp[1200];
unsigned char crc[2];
QString string;
int j = 0;
QByteArray partbuff;
partbuff.resize(1200);
partbuff.clear();
upgrade.sum_part = arry.length()/1024;
for(int i = 1 ;i <= upgrade.sum_part; i++)
{
partbuff.clear();
partbuff.append(arry.mid(j,1024));
memcpy( temp,partbuff,1024);
myCom->write(partbuff,1024);
delay_MSec(3000);
//升級判定返回
ui->progressBar->setValue(0);
j = j + 1024;
string = "\n" + QString::number(i) + "\n" +partbuff.toHex() +ui->textEdit_4->toPlainText();
ui->textEdit_4->clear();
ui->textEdit_4->setText(string);
delay_MSec(100);
}
}
本例程源碼: