由于项目需要,了解IAP对程序进行升级操作,可以不用st—link烧写,减轻工作量。
IAP是先运行bootloader,再去运行APP程序。
bootloader程序
bootloader最重要是2个部分,从应用层读取升级文件,覆盖之前的APP程序(当然也可以不用覆盖以前的APP程序,直接运行接受到的升级程序,那么需要注意新APP的flash起始地址。),还有从BootLoader跳转到APP程序,代码如下:
#define APPLICATION_ADDRESS 0x08004000
//检查栈顶地址是否合法.
if((*( __IO uint32_t*)(APPLICATION_ADDRESS)&0x2FFE0000)==0x20000000) {
JumpAddress = *( __IO uint32_t*) (APPLICATION_ADDRESS + 4) ;
// 指向用户程序复位函数所在的地址
Jump_To_Application = ( pFunction ) JumpAddress;
//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
__set_MSP( *( __IO uint32_t* ) APPLICATION_ADDRESS );
/* Jump to application */
Jump_To_Application();
} else{
printf("error\r\n");
}
FLASH的读写操作,要把APP的.bin文件写入FLASH中。
在写入之前要先擦除FLASH。
/*==============================================================================
函数名 : IapEraseSector
功能 : 擦除相关扇区
输入参数说明: addr_start: 起始地址
------------------------------------------------------------------------------*/
fmc_state_enum IapEraseSector(uint32_t addr_start)
{
uint8_t i;
printf("\r\nerase on\r\n");
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END);
fmc_flag_clear(FMC_FLAG_WPERR);
fmc_flag_clear(FMC_FLAG_PGSERR);
fmc_flag_clear(FMC_FLAG_PGMERR);
printf("%#X\r\n",addr_start);
if(FMC_READY != fmc_sector_erase(CTL_SN(1)))
{
return -1;
}
fmc_flag_clear(FMC_FLAG_END);
fmc_flag_clear(FMC_FLAG_WPERR);
fmc_flag_clear(FMC_FLAG_PGSERR);
fmc_flag_clear(FMC_FLAG_PGMERR);
fmc_lock();
printf("erase off\r\n");
return 0;
}
FLASH 写操作
/*==============================================================================
函数名 : FLASH_If_Write
功能 : 写入FLASH
输入参数说明:
dwAddr: 写入Flash的起始地址, 从0开始的偏移地址
dwLen : 写入数据的字节数
pbyBuf: 写入数据的buf
返回值说明 : 成功返回0; 失败返回-1或错误码
------------------------------------------------------------------------------*/
uint32_t FLASH_If_Write(uint32_t dwAddr, uint8_t *pbyBuf, uint32_t dwLen)
{
uint32_t i=0;
uint32_t new_addr;
uint8_t data=0;
fmc_unlock();
for(i=0;i<dwLen;i++)
{
data=*(pbyBuf+i);
new_addr = dwAddr+i*sizeof(uint8_t);
if(FMC_READY != fmc_byte_program(new_addr,data))
{
return -1;
}
fmc_flag_clear(FMC_FLAG_END);
fmc_flag_clear(FMC_FLAG_WPERR);
fmc_flag_clear(FMC_FLAG_PGSERR);
fmc_flag_clear(FMC_FLAG_PGMERR);
}
fmc_lock();
return 0;
}
YMODE协议的移植
**
* @brief 使用ymodem协议接收文件
* @param buf: 第一个字节的地址
* @retval 文件的大小
*/
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, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
uint32_t flashdestination,ramsource;
/* Initialize flashdestination variable */
flashdestination = APPLICATION_ADDRESS;
//SerialPutString("*****test1****\n\r");
//printf("buf:%s\r\n",buf);
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);)
{
FileName[i++] = *file_ptr++;
}
FileName[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 > (FMC_PAGE_SIZE - 1))
{
//结束
Send_Byte(CA);
Send_Byte(CA);
return -1;
}
IapEraseSector(flashdestination); //清空flash扇区
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;
if(packet_length == PACKET_SIZE) //整包
{
FLASH_If_Write(flashdestination, (uint8_t *)ramsource, packet_length); //写flash
if (*(uint32_t *)flashdestination != *(uint32_t *)ramsource) //错误
{
Send_Byte(CA);
Send_Byte(CA);
return -2;
}
flashdestination += packet_length;
}
else //尾包
{
FLASH_If_Write(flashdestination, (uint8_t *)ramsource, (size%PACKET_SIZE)); //写flash
}
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;
}
APP程序没什么大的改动,需要在keil修改ROM的起始地址。
这里需要了解一下GD32F4的闪存结构。F4系列FLSASH是扇区的概念,booyloader放在扇区0
APP程序放在了扇区1里。
我的APP程序起始地址为0x08004000 大小是14K左右
注意:
SCB->VTOR = 0x08004000;
要在APP程序的开头加一句程序,中断向量表跳偏移地址 ,地址为你的APP起始地址。
如果不加这句,程序就会跳不进来。
最后在生成文件的时候要生成.bin文件。
代码自取:
链接:https://pan.baidu.com/s/1JUxivJ-m1lIxnEiK2ES1hA
提取码:r4q1