本帖最后由 xld0932 于 2023-2-27 21:49 编辑
IAP应用程序升级是最常用的技术了,也有很多开源的IAP程序框架,比如OpenBLT、mOTA等等,本文我们来基于对MCU内部FLASH读、写、擦除这3个基础操作,结合XMODEM传输协议来完成一个简易的IAP应用程序升级功能……首先我们需要有2个工程,一个是BOOTLOADER工程,它的作用是XMODEM协议传输、FLASH数据写入、以及程序跳转运行;另一个就是我们的应用程序工程,它是具体的应用……对不同的工程,我们需要有不同的配置……
BOOTLOADER工程:
XMODEM传输协议的实现:
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Xmodem_SendData
*
* @param None
*
* @retval None
*
*/
void Xmodem_SendData(uint8_t Data)
{
USART_TxData(USART1, Data);
while (RESET == USART_ReadStatusFlag(USART1, USART_FLAG_TXBE))
{
}
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Xmodem_CalcCheckCRC
*
* @param Buffer & Length
*
* @retval Result
*
*/
uint16_t Xmodem_CalcCheckCRC(uint8_t *Buffer, uint16_t Length)
{
uint32_t Result = 0;
Length += 2;
while (Length--)
{
uint8_t Value = (Length < 2) ? 0 : *Buffer++;
for (uint8_t j = 0; j < 8; j++)
{
Result <<= 1;
if (Value & 0x00080)
{
Result += 0x0001;
}
Value <<= 1;
if (Result & 0x10000)
{
Result ^= 0x1021;
}
}
}
Result &= 0xFFFF;
return (Result);
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Xmodem_CalcCheckSum
*
* @param Buffer & Length
*
* @retval Result
*
*/
uint8_t Xmodem_CalcCheckSum(uint8_t *Buffer, uint16_t Length)
{
uint16_t Result = 0;
while (Length--)
{
Result += *Buffer++;
}
Result &= 0xFF;
return (Result);
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Xmodem_RxHandler
*
* @param None
*
* @retval None
*
*/
void Xmodem_RxHandler(void)
{
uint32_t Address = (0x08000000 + 0x10000);
uint32_t StartTryTimtout = 0;
QUEUE_INIT();
Xmodem_State = XMODEM_RX_STATE_SOH;
while (1)
{
if (QUEUE_EMPTY() == 0)
{
uint8_t data = QUEUE_READ();
switch (Xmodem_State)
{
case XMODEM_RX_STATE_SOH:
if ((data == XMODEM_SOH) || (data == XMODEM_STX))
{
Xmodem_Length = (data == XMODEM_STX) ? 1024 : 128;
Xmodem_Index = 0;
Xmodem_State = XMODEM_RX_STATE_NUM;
}
else if (data == XMODEM_EOT)
{
Xmodem_SendData(XMODEM_ACK);
printf("\r\nXmodem Finish");
return;
}
else if (data == XMODEM_CAN)
{
printf("\r\nXmodem Cancle");
return;
}
else
{
}
break;
case XMODEM_RX_STATE_NUM:
Xmodem_Number[Xmodem_Index++] = data;
if (Xmodem_Index == 2)
{
Xmodem_Index = 0;
Xmodem_State = XMODEM_RX_STATE_DAT;
}
break;
case XMODEM_RX_STATE_DAT:
Xmodem_Buffer[Xmodem_Index++] = data;
if (Xmodem_Index == Xmodem_Length)
{
Xmodem_Index = 0;
Xmodem_State = XMODEM_RX_STATE_CHK;
}
break;
case XMODEM_RX_STATE_CHK:
Xmodem_CheckData[Xmodem_Index++] = data;
if ((Xmodem_CheckType == 1) && (Xmodem_Index == 1))
{
Xmodem_Index = 0;
if (Xmodem_Number[0] == ((Xmodem_Number[1] ^ 0xFF) & 0xFF))
{
if (Xmodem_CheckData[0] == Xmodem_CalcCheckSum(Xmodem_Buffer, Xmodem_Length))
{
if ((Address % FLASH_PAGE_SIZE) == 0)
{
FLASH_ErasePage(Address / FLASH_PAGE_SIZE);
}
FLASH_PageProgram(Address, Xmodem_Buffer, Xmodem_Length);
Address += Xmodem_Length;
Xmodem_SendData(XMODEM_ACK);
}
else
{
Xmodem_SendData(XMODEM_NAK);
}
}
else
{
Xmodem_SendData(XMODEM_NAK);
}
Xmodem_State = XMODEM_RX_STATE_SOH;
}
else if ((Xmodem_CheckType == 0) && (Xmodem_Index == 2))
{
Xmodem_Index = 0;
if (Xmodem_Number[0] == ((Xmodem_Number[1] ^ 0xFF) & 0xFF))
{
uint16_t Result = 0;
Result = Xmodem_CheckData[0];
Result <<= 8;
Result |= Xmodem_CheckData[1];
if (Result == Xmodem_CalcCheckCRC(Xmodem_Buffer, Xmodem_Length))
{
if ((Address % FLASH_PAGE_SIZE) == 0)
{
FLASH_ErasePage(Address / FLASH_PAGE_SIZE);
}
FLASH_PageProgram(Address, Xmodem_Buffer, Xmodem_Length);
Address += Xmodem_Length;
Xmodem_SendData(XMODEM_ACK);
}
else
{
Xmodem_SendData(XMODEM_NAK);
}
}
else
{
Xmodem_SendData(XMODEM_NAK);
}
Xmodem_State = XMODEM_RX_STATE_SOH;
}
else
{
/* do nothing */
}
break;
default:
break;
}
}
else
{
if (StartTryTimtout == 0)
{
if (Xmodem_CheckType)
{
Xmodem_SendData(XMODEM_NAK);
}
else
{
Xmodem_SendData('C');
}
}
StartTryTimtout = (StartTryTimtout + 1) % XMODEM_TRY_TIMEOUT;
}
}
}
FLASH读、写、擦除操作:
/*!
* @brief Xmodem_FLASH_ErasePage
*
* @param Page
*
* @retval None
*
*/
void FLASH_ErasePage(uint32_t Page)
{
FMC_Unlock();
FMC_ClearStatusFlag((FMC_FLAG_T)(FMC_FLAG_OC | FMC_FLAG_PE | FMC_FLAG_WPE));
FMC_ErasePage(FLASH_PAGE_SIZE * Page);
FMC_ClearStatusFlag(FMC_FLAG_OC);
FMC_Lock();
}
/*!
* @brief Xmodem_FLASH_PageProgram
*
* @param Address & Buffer & Length
*
* @retval None
*
*/
void FLASH_PageProgram(uint32_t Address, uint8_t *Buffer, uint32_t Length)
{
uint16_t *Data = (uint16_t *)Buffer;
FMC_Unlock();
FMC_ClearStatusFlag((FMC_FLAG_T)(FMC_FLAG_OC | FMC_FLAG_PE | FMC_FLAG_WPE));
for (uint32_t i = 0; i < Length; i += 2)
{
FMC_ProgramHalfWord(Address + i, *Data);
FMC_ClearStatusFlag(FMC_FLAG_OC);
if (*(volatile uint16_t *)(Address + i) != *Data)
{
while (1)
{
GPIO_WriteBitValue(GPIOE, GPIO_PIN_5, BIT_RESET);
GPIO_WriteBitValue(GPIOE, GPIO_PIN_6, BIT_RESET);
}
}
Data++;
}
FMC_Lock();
}
程序跳转运行:
int main(void)
{
Xmodem_Init(115200);
printf("\r\nAPM32F107VC MINIBOARD V1.0 %s %s--->Bootloader\r\n", __DATE__, __TIME__);
KEY_Init();
LED_Init();
while (1)
{
if((BIT_RESET == GPIO_ReadInputBit(GPIOA, GPIO_PIN_0)) ||
(BIT_RESET == GPIO_ReadInputBit(GPIOA, GPIO_PIN_1)) )
{
Xmodem_RxHandler();
}
else
{
if(*(volatile uint32_t *)(ApplicationAddress) != 0xFFFFFFFF)
{
USART_Disable(USART1);
JumpAddress = *(volatile uint32_t *)(ApplicationAddress + 4);
JumpToApplication = (pFunction)JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(volatile uint32_t *)ApplicationAddress);
JumpToApplication();
}
}
}
}
KEIL工程配置:
APPLICATION工程:
中断向量表重定义:
KEIL工程配置之程序空间划分:
KEIL工程配置之生成BIN文件:
IAP运行测试:
下载BOOTLOADER程序到开发板,通过串口终端软件进行监控:
按下开发板上的任一按键(除RST按键),MCU进入XMODEM传输模式:
XMODEM接收数据,并将数据写入到指定的FLASH空间:
XMODEM传输结束后,跳转到应用程序起始地址,开始运行:
软件工程源代码:
BOOTLOADER:
Bootloader.zip
(474.31 KB)
APPLICATION:
Application.zip
(489.23 KB)
|