1.bootLoader文件按正常工程编译。
2.app文件需要编译成bin格式文件。
hex文件与bin文件区别
a.hex文件中包含了数据和地址信息,bin文件中只包含了数据。
b.bin文件右键单击属性可以看到其文件大小,hex文件看到的不是实际大小,其中包含了其他信息。
bin文件格式
对二进制文件而言,其实没有”格式”。文件只是包括了纯粹的二进制数据。
hex文件格式
A record (line of text) consists of six fields (parts) that appear in order from left to right:
HEX文件都是由记录(RECORD)组成的。在HEX文件里面,每一行代表一个记录。记录的基本格式为:
+---------------------------------------------------------------+
| RECORD | RECLEN | LOAD | RECTYPE | INFO or DATA | CHKSUM |
| MARK ':' | | OFFSET | | | |
+---------------------------------------------------------------+
| 1-byte | 1-byte | 2-byte | 1-byte | n-byte | 1-byte |
+---------------------------------------------------------------+
记录类型包括:
'00' Data Rrecord:用来记录数据,HEX文件的大部分记录都是数据记录
'01' End of File Record: 用来标识文件结束,放在文件的最后,标识HEX文件的结尾
'04' Extended Linear Address Record: 用来标识扩展线性地址的记录
'02' Extended Segment Address Record: 用来标识扩展段地址的记录
在上面的后2种记录,都是用来提供地址信息的。每次碰到这2个记录的时候,都可以根据记录计算出一个“基”地址。
对于后面的数据记录,计算地址的时候,都是以这些“基”地址为基础的。
数据记录的具体格式:
+---------------------------------------------------------------+
| RECORD | RECLEN | LOAD | RECTYPE | INFO or DATA | CHKSUM |
| MARK ':' | | OFFSET | '00' | | |
+---------------------------------------------------------------+
| 1-byte | 1-byte | 2-byte | 1-byte | n-byte | 1-byte |
+---------------------------------------------------------------+
看个例子:
:020000040000FA
:10000400FF00A0E314209FE5001092E5011092E5A3
:00000001FF
对上面的HEX文件进行分析:
第1条记录的长度为02,LOAD OFFSET为0000,RECTYPE为04,说明该记录为扩展段地址记录。数据为0000,校验和为
FA。从这个记录的长度和数据,我们可以计算出一个基地址,这个地址为0X0000。后面的数据记录都以这个地址为基
地址。
第2条记录的长度为10(16),LOAD OFFSET为0004,RECTYPE为00,说明该记录为数据记录。
数据为FF00A0E314209FE5001092E5011092E5,共16个BYTE。这个记录的校验和为A3。此时的基地址为0X0000,加上OFFSET,
这个记录里的16BYTE的数据的起始地址就是0x0000 + 0x0004 = 0x0004.
第3条记录的长度为00,LOAD OFFSET为0000,TYPE = 01,校验和为FF。说明这个是一个END OF FILE RECORD,标识
文件的结尾。
在上面这个例子里,实际的数据只有16个BYTE:FF00A0E314209FE5001092E5011092E5,其起始地址为0x4
3.uint8_t UART_RX_BUF[1024] __attribute__ ((at(0X20001000)));
//将接收到的app程序写入到ram 0x20001000之后的地方
//ram起始位置0x20000000 存放中断向量位置,所以从0x20001000存放app代码
__attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。
在TC下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式)
在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非紧凑模式)
在GCC下:struct my{ char ch; int a;}__attrubte__ ((packed)) sizeof(int)=4;sizeof(my)=5
__attribute__ ((packed))关键字 用来指定变量或结构位域的特殊属性,该关键字后的双括弧中的内容是属性说明。
at 关键字 用来设置变量的绝对地址,通过这个关键字指定某个变量处于内存里面的某个指定的地址。
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)
//判断是否为0X08XXXXXX.
//app程序计划放在起始位置为0x8010000
Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。
u16 iapbuf[1024]; //中转数组 2K内容
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
u16 t;
u16 i=0;
u16 temp;
u32 fwaddr=appxaddr;//写入的地址
u8 *dfu=appbuf;
for(t=0;t
temp=(u16)dfu[1]<<8;
temp+=(u16)dfu[0]; //小端数据存储 整合为半字(16bit)
dfu+=2;//偏移2个字节 //指向字节 需要跳过两字节
iapbuf[i++]=temp;
if(i==1024) //2k内容已经装满
{
i=0;
STMFLASH_Write(fwaddr,iapbuf,1024);
fwaddr+=2048;//偏移2048 跳到下一扇区 跳过2k
}
}
if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
}
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u32 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(16位字计算)
u16 i;
u32 offaddr; //去掉0X08000000后的地址
if(WriteAddr
FLASH_Unlock(); //解锁
offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. 与0x08000000偏移多少
secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 第几个扇区 0~127 for STM32F103RBT6
secoff=(offaddr%STM_SECTOR_SIZE)/2;//在扇区内的偏移(2个字节为基本单位.) 写入的起始地址与此扇区首地址偏移多少
secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
if(NumToWrite<=secremain)secremain=NumToWrite;//写入的数据小于2k
while(1)
{
STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
for(i=0;i
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
}
if(i
FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
for(i=0;i
STMFLASH_BUF[i+secoff]=pBuffer[i];
}
STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(NumToWrite==secremain)break;//写入结束 表示数据个数等于剩余的secremain 否则继续写
else//写入未结束
{
secpos++; //扇区地址增1 使用下个扇区
secoff=0; //偏移位置为0 从该扇区首地址写
pBuffer+=secremain; //数据指针偏移 撇开先前写过的数据
WriteAddr+=secremain; //写地址偏移 跳下一扇区 跳过2k
NumToWrite-=secremain; //字节(16位)数递减 减掉已经写过的数据个数
if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
else secremain=NumToWrite;//下一个扇区可以写完了
}
};
FLASH_Lock();//上锁
}
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u16 i;
for(i=0;i
FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
WriteAddr+=2;//地址增加2.一次写2字节,跳过2个字节位置
}
}
参考原子哥iap代码,芯片stm32c8t6https://download.csdn.net/download/goldbr/10646028
芯片stm32vct6https://download.csdn.net/download/goldbr/10646038
原理图就不上了。