本例程实现功能:
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()<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);
}
}
本例程源码: