OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 8148|回复: 16

STM32的IAP功能,程序大小问题

[复制链接]

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
发表于 2019-6-4 17:05:14 | 显示全部楼层 |阅读模式
10金钱
本帖最后由 shenqihao 于 2019-6-5 14:54 编辑

最近在学习IAP功能,使用f103rc这颗。
学习原子哥pdf教程,有2个疑问。
这颗芯片SRAM为48K,FLASH为256K。
只是用FLASH APP功能
1、升级的程序最大能多大?
Bootloader里面有个数组,USART_RX_BUF,大小为41K,USART_REC_LEN                          41*1024
SRAM限制了这个数组大小吗,数组极限就是48K?
那烧写进去的程序最大为48K,那这个FLASH岂不是浪费不少。
2、如果需要升级大程序,比如200k,有什么办法吗?
串口边读数据边写进入FLASH?

最佳答案

查看完整内容[请看2#楼]

一般都有定制的上位机,原子哥的那个只是演示BootLoader。实际项目不能那样用。你在内存中开一个数组,比如512或1024byte之类的缓存,每次收到上位机数据以后解析通过就写入flash。你升级500G或1TB也没问题,不受RAM影响。
做一个相信自己的人
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2019-6-5 16:26:18 | 显示全部楼层
问题解决了,采用2个数组,都是20K大小,轮流接收APP程序,一个满了就写入另一个,轮流写入Flash保存。用这种方法,原子哥的程序不匹配,不能一边擦除一边写入又一边接收数据,数据会不全。
确认要下载APP程序之前,先把预留的程序空间都擦除了。
原子哥原来的函数修改,去掉了擦除功能。
[mw_shl_code=c,true]void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{                                           
        u16 i;
        for(i=0;i<NumToWrite;i++)
        {
                STMFLASH_WriteHalfWord(WriteAddr,pBuffer);
            WriteAddr+=2;//地址增加2.
        }  
}
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
                 
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位字计算)             
        u32 offaddr;   //去掉0X08000000后的地址
        if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
        STMFLASH_Unlock();                                                //解锁
        offaddr=WriteAddr-STM32_FLASH_BASE;                //实际偏移地址.
        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;//不大于该扇区范围
        while(1)
        {       
//                STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
//                for(i=0;i<secremain;i++)//校验数据
//                {
//                        if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除            
//                }
//                if(i<secremain)//需要擦除
//                {
//                        STMFLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
//                        for(i=0;i<secremain;i++)//复制
//                        {
//                                STMFLASH_BUF[i+secoff]=pBuffer;          
//                        }
//                        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;//写入结束了
                else//写入未结束
                {
                        secpos++;                                //扇区地址增1
                        secoff=0;                                //偏移位置为0          
                           pBuffer+=secremain;          //指针偏移
                        WriteAddr+=secremain*2;        //写地址偏移(16位数据地址,需要*2)          
                           NumToWrite-=secremain;        //字节(16位)数递减
                        if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
                        else secremain=NumToWrite;//下一个扇区可以写完了
                }         
        };       
        STMFLASH_Lock();//上锁
}[/mw_shl_code]
烧写APP之前的整片擦除程序
[mw_shl_code=c,true]//擦除函数
void iap_EraseFlashAPP(void)
{
        u16 i=0;
        STMFLASH_Unlock();                                                //解锁
        while(1)//擦除整片程序区
        {
                if((FLASH_APP1_ADDR+i*STM_SECTOR_SIZE)>=FLASH_STORAGE_ADDR)//预留的变量断电保存区域,不能擦除
                {
                        break;
                }
                STMFLASH_ErasePage(FLASH_APP1_ADDR+i*STM_SECTOR_SIZE);
                i++;
        }
        STMFLASH_Lock();//上锁
}[/mw_shl_code]
数组的定义与申明
[mw_shl_code=applescript,true]u8 USART_RX_BUF1[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.   
u8 USART_RX_BUF2[USART_REC_LEN] __attribute__ ((at(0X20006000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20006000.

#define USART_REC_LEN                          (20*1024) //定义最大接收字节数 20K
extern u8  USART_RX_BUF1[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8  USART_RX_BUF2[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
[/mw_shl_code]


接收数据

[mw_shl_code=applescript,true]u8 IAP_EN1=0;//IAP使能标志位1
u8 IAP_EN2=0;//IAP使能标志位2
u8 IAP_Receive_EN=0;//IAP接收使能标志位
u8 IAP_Receive_done=0;//IAP接收结束标志位
u32 IAP_RX_CNT=0;                        //接收的字节数
u16 IAP_RX_CNT1=0;                //数组1接收的字节数
u16 IAP_RX_CNT2=0;                //数组2接收的字节数
u8 IAPUpdataNum=0;//更新次数

//串口4中断函数
void UART4_IRQHandler(void)
{
        u8 res;
        if(UART4->SR&(1<<5))//接收到数据
        {
                res=UART4->DR;
                if(IAP_Receive_EN==1)//IAP接收使能
                {
                        if((IAP_RX_CNT-USART_REC_LEN*IAPUpdataNum)==USART_REC_LEN)//
                        {
                                if(IAPUpdataNum%2==0)
                                {
                                        IAP_Receive_done=1;
                                        IAP_RX_CNT1=0;
                                }
                                else
                                {
                                        IAP_Receive_done=2;
                                        IAP_RX_CNT2=0;
                                }
                                IAPUpdataNum++;
                        }
                        if(IAPUpdataNum%2==0)       
                        {
                                USART_RX_BUF1[IAP_RX_CNT1++]=res;
                        }
                        else
                        {
                                USART_RX_BUF2[IAP_RX_CNT2++]=res;
                        }
                        IAP_RX_CNT++;                                                                                                      
                }
}
}[/mw_shl_code]
主函数中的处理代码
[mw_shl_code=c,true]                if(IAP_RX_CNT)//允许更新
                {
                        if(oldcount==IAP_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成.
                        {
                                applenth=IAP_RX_CNT;
                                oldcount=0;
                                IAP_RX_CNT=0;
                                IAP_EN1=0;
                                IAP_EN2=0;
                                IAP_Receive_EN=0;
                                if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
                                {         
                                        if(IAPUpdataNum%2==0)
                                        {
                                                iap_write_appbin(FLASH_APP1_ADDR+(USART_REC_LEN*IAPUpdataNum),USART_RX_BUF1,applenth-(USART_REC_LEN*IAPUpdataNum));//更新FLASH代码                        
                                        }
                                        else
                                        {
                                                iap_write_appbin(FLASH_APP1_ADDR+(USART_REC_LEN*IAPUpdataNum),USART_RX_BUF2,applenth-(USART_REC_LEN*IAPUpdataNum));//更新FLASH代码                        
                                        }
                                        send_uart4_string("User program reception completed!\r\n");
                                        send_uart4_string("Firmware update completed!\r\n");
                                        send_uart4_string("APP Length: ");
                                        send_uart4_string(DataChangeNumberToString(applenth));
                                        send_uart4_string("!\r\n ");
                                }
                                else
                                {
                                        send_uart4_string("No APP!\r\n");
                                }
                        }
                        else
                        {
                                oldcount=IAP_RX_CNT;
                        }
                        if(IAP_Receive_done==1)//更新第一数组FLASH代码
                        {
                                iap_write_appbin(FLASH_APP1_ADDR+((IAPUpdataNum-1)*USART_REC_LEN),USART_RX_BUF1,USART_REC_LEN);
                                IAP_Receive_done=0;
                                send_uart4_string("Updata 1 Array!\r\n");
                        }
                        if(IAP_Receive_done==2)//更新第二数组FLASH代码
                        {
                                iap_write_appbin(FLASH_APP1_ADDR+((IAPUpdataNum-1)*USART_REC_LEN),USART_RX_BUF2,USART_REC_LEN);
                                IAP_Receive_done=0;
                                send_uart4_string("Updata 2 Array!\r\n");
                                send_uart4_string("APP Length: ");
                                send_uart4_string(DataChangeNumberToString(applenth));
                                send_uart4_string("!\r\n ");
                        }
                }[/mw_shl_code]
做一个相信自己的人
回复

使用道具 举报

11

主题

49

帖子

0

精华

初级会员

Rank: 2

积分
190
金钱
190
注册时间
2016-11-30
在线时间
92 小时
发表于 2019-6-4 17:05:15 | 显示全部楼层
一般都有定制的上位机,原子哥的那个只是演示BootLoader。实际项目不能那样用。你在内存中开一个数组,比如512或1024byte之类的缓存,每次收到上位机数据以后解析通过就写入flash。你升级500G或1TB也没问题,不受RAM影响。
回复

使用道具 举报

109

主题

5564

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
10572
金钱
10572
注册时间
2017-2-18
在线时间
1914 小时
发表于 2019-6-4 18:21:19 | 显示全部楼层
1.主芯片自带48K字节RAM,限制了数组的大小,也限制了一次可接收的数据大小,你可以将程序分成2个 ,并写接收程序支持
2.200K的固件(FLASH APP)分成多个小于40K字节的固件,写程序支持每接收一个固件,就copy固件到FLASH中运行,FLASH足够存储200K了
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2019-6-4 19:40:21 | 显示全部楼层
peng1554 发表于 2019-6-4 18:21
1.主芯片自带48K字节RAM,限制了数组的大小,也限制了一次可接收的数据大小,你可以将程序分成2个 ,并写接 ...

谢谢解答。
还有问题,200k的固件,.bin文件,怎么分割,单片机接收到然后进行分割,还是用什么软件分割?
做一个相信自己的人
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2019-6-5 09:05:24 | 显示全部楼层
本帖最后由 shenqihao 于 2019-6-5 09:55 编辑
南征北战 发表于 2019-6-5 08:22
一般都有定制的上位机,原子哥的那个只是演示BootLoader。实际项目不能那样用。你在内存中开一个数组,比如 ...

谢谢。
按你的方法,选择全擦除后,第一次可以,再次下载APP就不行了,不知道为什么。每下一次引导程序,可以成功下载一次APP,第二次就不行。用了2个数组
#define USART_REC_LEN                          (20*1024) //定义最大接收字节数 20K
#define EN_USART4_RX                         1                //使能(1)/禁止(0)串口4接收
         
extern u8  USART_RX_BUF1[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8  USART_RX_BUF2[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
u8 USART_RX_BUF1[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.   
u8 USART_RX_BUF2[USART_REC_LEN] __attribute__ ((at(0X20006000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20006000.
APP程序30多K

111题.png
做一个相信自己的人
回复

使用道具 举报

11

主题

49

帖子

0

精华

初级会员

Rank: 2

积分
190
金钱
190
注册时间
2016-11-30
在线时间
92 小时
发表于 2019-6-6 08:12:20 | 显示全部楼层
shenqihao 发表于 2019-6-5 09:05
谢谢。
按你的方法,选择全擦除后,第一次可以,再次下载APP就不行了,不知道为什么。每下一次引导程序 ...

写哪里就擦哪里,不用全部擦除,还有就是你每次写入之前都要擦除。你看看在下载APP的时候有没有影响BootLoader。具体问题还的细查
回复

使用道具 举报

37

主题

596

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1574
金钱
1574
注册时间
2017-7-17
在线时间
308 小时
发表于 2019-6-6 08:38:24 | 显示全部楼层
实际应用中最好不要程序运行的时候进行升级,而是在上电的时候检查是否需要升级。升级的代码大小,受限于FLASH的大小,或者内存大小(因为有的产品线FLASH很小,内存却比较大,代码要放到内存运行)
回复

使用道具 举报

0

主题

1

帖子

0

精华

新手上路

积分
25
金钱
25
注册时间
2018-11-9
在线时间
4 小时
发表于 2019-6-6 09:02:36 | 显示全部楼层
shenqihao 发表于 2019-6-4 19:40
谢谢解答。
还有问题,200k的固件,.bin文件,怎么分割,单片机接收到然后进行分割,还是用什么软件分割 ...

2种方法你可以自己开发一个上位机软件把一包bin文件分包发送,另一种方法网上下载个(水淼·分割合并助手)软件把bin文件包写分割好在传输    我去年用的也是这个芯片做的远程升级程序。
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2019-6-10 14:34:11 | 显示全部楼层
南征北战 发表于 2019-6-6 08:12
写哪里就擦哪里,不用全部擦除,还有就是你每次写入之前都要擦除。你看看在下载APP的时候有没有影响BootL ...

嗯,分了区块,只擦除程序区
做一个相信自己的人
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2019-6-10 14:35:03 | 显示全部楼层
candylife9 发表于 2019-6-6 08:38
实际应用中最好不要程序运行的时候进行升级,而是在上电的时候检查是否需要升级。升级的代码大小,受限于FL ...

谢谢,就是准备上电烧写
做一个相信自己的人
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2019-6-10 14:36:44 | 显示全部楼层
Braon123456 发表于 2019-6-6 09:02
2种方法你可以自己开发一个上位机软件把一包bin文件分包发送,另一种方法网上下载个(水淼·分割合并助手 ...

谢谢,我用单片机分割了,应用程序这块不太擅长,自己写的qt程序,能发送就行
做一个相信自己的人
回复

使用道具 举报

13

主题

38

帖子

1

精华

中级会员

Rank: 3Rank: 3

积分
274
金钱
274
注册时间
2011-10-27
在线时间
26 小时
发表于 2019-12-2 14:26:55 | 显示全部楼层
Mark。。。。。
回复

使用道具 举报

0

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
119
金钱
119
注册时间
2016-11-29
在线时间
31 小时
发表于 2021-9-23 15:16:37 | 显示全部楼层
楼主把工程发一下出来参考一下
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
12
金钱
12
注册时间
2020-12-2
在线时间
3 小时
发表于 2021-10-18 15:54:52 | 显示全部楼层
题主您好,可以分享一下程序例程么,我的IAP程序超过了SRAM的大小,不知道怎么搞
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2021-11-1 21:48:37 | 显示全部楼层
做一个相信自己的人
回复

使用道具 举报

84

主题

347

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2008
金钱
2008
注册时间
2014-7-1
在线时间
189 小时
 楼主| 发表于 2021-11-1 21:49:01 | 显示全部楼层
dongxuezhi 发表于 2021-10-18 15:54
题主您好,可以分享一下程序例程么,我的IAP程序超过了SRAM的大小,不知道怎么搞

http://www.openedv.com/forum.php ... p;page=1#pid1224852
做一个相信自己的人
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-15 14:36

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表