STM32 :IAP实验 & 写入内部Flash

更新:__initial_sp是堆栈指针,它就是FLASH的0x8000000地址前面4个字节(它根据堆栈大小,由编译器自动生成)0x8000004才是复位向量,所以需要在add+4 

这两天再看IAP实验,觉得很实用,最近在做一个小项目,日后也肯定需要升级的。原子的程序是写在STM32的内部Flash,内部SRAM。还可以把程序写到外部Flash或者SD卡来更新。

目前做了第一步:从串口更新,移植的正点的代码。先将IAP bootload写入ROM起始地址,这里我的ROM总共256k,我分了一半给bootload,一半给app。注意设置地址问题。

                       只更新了Flash app。用的流水灯实验。SD卡FAT目前先没用,空间节省不少啊,这个RAM,ROM占用很厉害啊。这个问题还需要去解决。

                       串口接收APP设置了10k容量。

                       流程:1,Jlink写bootload到ROM起始,与平时一样

                               2,串口发送app.bin,串口接收

                               3,将接收的app.bin写入flash的指定app位置

                               4,使用bootload中的函数跳转到flash的指定app位置,执行app。

   

 1 #ifndef __IAP_H__
 2 #define __IAP_H__
 3 #include "sys.h"  
 4 //////////////////////////////////////////////////////////////////////////////////     
 5 //本程序只供学习使用,未经作者许可,不得用于其它任何用途
 6 //ALIENTEK战舰STM32开发板
 7 //IAP 代码       
 8 //正点原子@ALIENTEK
 9 //技术论坛:www.openedv.com
10 //修改日期:2012/9/24
11 //版本:V1.0
12 //版权所有,盗版必究。
13 //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
14 //All rights reserved                                      
15 //////////////////////////////////////////////////////////////////////////////////    
16 typedef  void (*iapfun)(void);                //定义一个函数类型的参数.
17 
18 #define FLASH_APP1_ADDR        0x08020000      //第一个应用程序起始地址(存放在FLASH)
19                                             //保留0X08000000~0X0800FFFF的空间为IAP使用
20 
21 void iap_load_app(u32 appxaddr);            //执行flash里面的app程序
22 void iap_load_appsram(u32 appxaddr);        //执行sram里面的app程序
23 void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen);    //在指定地址开始,写入bin
24 #endif
iap.h
 1 #include "sys.h"
 2 #include "systick_config.h"
 3 //#include "stmflash.h"
 4 #include "iap.h"
 5 //////////////////////////////////////////////////////////////////////////////////     
 6 //本程序只供学习使用,未经作者许可,不得用于其它任何用途
 7 //ALIENTEK战舰STM32开发板
 8 //IAP 代码       
 9 //正点原子@ALIENTEK
10 //技术论坛:www.openedv.com
11 //修改日期:2012/9/24
12 //版本:V1.0
13 //版权所有,盗版必究。
14 //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
15 //All rights reserved                                      
16 //////////////////////////////////////////////////////////////////////////////////    
17 
18 iapfun jump2app; 
19 u16 iapbuf[1024];   
20 //appxaddr:应用程序的起始地址
21 //appbuf:应用程序CODE.
22 //appsize:应用程序大小(字节).
23 void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
24 {
25     u16 t;
26     u16 i=0;
27     u16 temp;
28     u32 fwaddr=appxaddr;//当前写入的地址
29     u8 *dfu=appbuf;
30     for(t=0;t<appsize;t+=2)
31     {                            
32         temp=(u16)dfu[1]<<8;
33         temp+=(u16)dfu[0];      
34         dfu+=2;//偏移2个字节
35         iapbuf[i++]=temp;        
36         if(i==1024)
37         {
38             i=0;
39             STMFLASH_Write(fwaddr,iapbuf,1024);    
40             fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.
41         }
42     }
43     if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
44 }
45 
46 //跳转到应用程序段
47 //appxaddr:用户代码起始地址.
48 void iap_load_app(u32 appxaddr)
49 {
50     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)    //检查栈顶地址是否合法.
51     { 
52         jump2app=(iapfun)*(vu32*)(appxaddr+4);        //用户代码区第二个字为程序开始地址(复位地址)        
53         MSR_MSP(*(vu32*)appxaddr);                    //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
54         jump2app();                                    //跳转到APP.
55     }
56 }         
iap.c
  1 #include "stm32f10x.h"
  2 #include "stm32f10x_conf.h"
  3 #include "exti_interrupt.h"
  4 #include "usart_config.h" 
  5 #include "timer_config.h"  
  6 #include "key_config.h"       
  7 #include "sdspi_config.h"
  8 #include "Fatfs_config.h"
  9 #include "flashspi_config.h"
 10 #include "ff.h"
 11 #include "diskio.h"
 12 #include "iap.h"
 13 #include <stdio.h>
 14 #include <string.h>
 15 //#include "picture.h"
 16 
 17 //#define BmpHeadSize (54)
 18 FATFS fs;
 19 
 20 #ifdef __GNUC__
 21 /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
 22  set to 'Yes') calls __io_putchar() */
 23 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
 24 #else
 25   #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
 26 #endif /* __GNUC__ */
 27 
 28 static void Delay_ARMJISHU(__IO uint32_t nCount)
 29 {
 30   for (; nCount != 0; nCount--);
 31 }
 32 
 33 extern u8 USART_RX_BUF[];     //接收缓冲,最大USART_REC_LEN个字节.
 34 extern u16 USART_RX_CNT;
 35 //接收状态
 36 //bit15,    接收完成标志
 37 //bit14,    接收到0x0d
 38 //bit13~0,    接收到的有效字节数目
 39 extern u16 USART_RX_STA; 
 40 /**
 41   * @brief  Main program.
 42   * @param  None
 43   * @retval None
 44   */
 45 
 46 int main(void)
 47 {
 48          u8 len,t;
 49          u8 key;
 50          u16 oldcount=0;    //老的串口接收数据值
 51          u16 applenth=0;    //接收到的app代码长度
 52          u8 clearflag=0;
 53         
 54          /* 延时初始化 */
 55          delay_init();
 56 
 57          /* 串口设置 */
 58 //         Usart1_Config();
 59          Usart2_Config();
 60          USART_SendData(USART2,0x55);
 61          while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
 62          printf("english\r\n");
 63          printf("中文测试\r\n");
 64 
 65          /* 按键中断设置设置 */
 66          Key_Config();
 67 //         KEY_EXIT_Init();
 68 //         Key_NVIC_Config();     
 69 
 70    
 71 //         disk_initialize(0);
 72 //         printf("\n\r f_mount %d\r\n", f_mount(0, &fs));
 73 //         mf_scan_files("0:");
 74 
 75 //        printf("\r\n将要进入主程序while:\r\n\r\n");
 76          while(1)
 77          {     
 78 
 79                if(USART_RX_CNT)
 80             {
 81                 if(oldcount==USART_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成.
 82                 {
 83                     applenth=USART_RX_CNT;
 84                     oldcount=0;
 85                     USART_RX_CNT=0;
 86                     printf("用户程序接收完成!\r\n");
 87                     printf("代码长度:%dBytes\r\n",applenth);
 88                 }else oldcount=USART_RX_CNT;            
 89             }
 90 
 91             delay_ms(10);
 92 
 93             key=KEY_Scan();
 94             if(key==KEY1)
 95             {
 96                 printf("KEY1!!\r\n");
 97                 if(applenth)
 98                 {
 99                     printf("开始更新固件...\r\n");    
100 //                    LCD_ShowString(60,210,200,16,16,"Copying APP2FLASH...");
101                      if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
102                     {     
103                         iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码   
104                         delay_ms(100);
105 //                        LCD_ShowString(60,210,200,16,16,"Copy APP Successed!!");
106                         printf("固件更新完成!\r\n");    
107                     }else 
108                     {
109 //                        LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!  ");       
110                         printf("非FLASH应用程序!\r\n");
111                     }
112                  }
113                 else 
114                 {
115                     printf("没有可以更新的固件!\r\n");
116 //                    LCD_ShowString(60,210,200,16,16,"No APP!");    
117                 }
118                 clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示                                     
119             }
120             
121             if(key==KEY2)
122             {
123                 printf("KEY2!!\r\n");
124                 if(applenth)
125                 {                                                                     
126                     printf("固件清除完成!\r\n");    
127 //                    LCD_ShowString(60,210,200,16,16,"APP Erase Successed!");
128                     applenth=0;
129                 }else 
130                 {
131                     printf("没有可以清除的固件!\r\n");
132 //                    LCD_ShowString(60,210,200,16,16,"No APP!");
133                 }
134                 clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示                                     
135             }
136             if(key==KEY3)
137             {
138                 printf("开始执行FLASH用户代码!!\r\n");
139                 if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
140                 {     
141                     iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
142                 }else 
143                 {
144                     printf("非FLASH应用程序,无法执行!\r\n");
145 //                    LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!");       
146                 }                                     
147                 clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示      
148             }
149             if(key==KEY4)
150             {
151                 printf("开始执行SRAM用户代码!!\r\n");
152                 if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.
153                 {     
154                     iap_load_app(0X20001000);//SRAM地址
155                 }else 
156                 {
157                     printf("非SRAM应用程序,无法执行!\r\n");
158 //                    LCD_ShowString(60,210,200,16,16,"Illegal SRAM APP!");       
159                 }                                     
160                 clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示     
161             }                   
162                  
163           }          
164 
165 
166          
167 }
168 
169 /**
170   * @brief  Retargets the C library printf function to the USART.
171   * @param  None
172   * @retval None
173   */
174 PUTCHAR_PROTOTYPE
175 {
176   /* Place your implementation of fputc here */
177   /* e.g. write a character to the USART */
178 //  while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
179 //  {}
180 //
181 //  USART_SendData(USART1, (uint8_t) ch);
182 
183   while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
184   {}
185 
186   USART_SendData(USART2, (uint8_t) ch);
187 
188   /* Loop until the end of transmission */
189 //  while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
190 //  {}
191 
192   return ch;
193 }
194 
195 
196 #ifdef  USE_FULL_ASSERT
197 
198 /**
199   * @brief  Reports the name of the source file and the source line number
200   *   where the assert_param error has occurred.
201   * @param  file: pointer to the source file name
202   * @param  line: assert_param error line source number
203   * @retval None
204   */
205 void assert_failed(uint8_t* file, uint32_t line)
206 { 
207   /* User can add his own implementation to report the file name and line number,
208      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
209 
210   /* Infinite loop */
211   while (1)
212   {
213   }
214 }
215 #endif
216 
217 /**
218   * @}
219   */
220 
221 
222 /******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/
main

我们把程序自己写入片上flash,还需要一些函数

  1 #include "stm32f10x.h"
  2 #include "stm32f10x_gpio.h"
  3 #include "stm32f10x_rcc.h"
  4 #include "stm32f10x_rtc.h"
  5 #include "stmflash_config.h"
  6 #include "systick_config.h"
  7 
  8 //读取指定地址的半字(16位数据)
  9 //faddr:读地址(此地址必须为2的倍数!!)
 10 //返回值:对应数据.
 11 u16 STMFLASH_ReadHalfWord(u32 faddr)
 12 {
 13     return *(vu16*)faddr; 
 14 }
 15 
 16 #if STM32_FLASH_WREN    //如果使能了写   
 17 //不检查的写入
 18 //WriteAddr:起始地址
 19 //pBuffer:数据指针
 20 //NumToWrite:半字(16位)数   
 21 void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
 22 {                       
 23     u16 i;
 24     for(i=0;i<NumToWrite;i++)
 25     {
 26         FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
 27         WriteAddr+=2;//地址增加2.
 28     }  
 29 }
 30 
 31 //从指定地址开始写入指定长度的数据
 32 //WriteAddr:起始地址(此地址必须为2的倍数!!)
 33 //pBuffer:数据指针
 34 //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
 35 #if STM32_FLASH_SIZE<256
 36 #define STM_SECTOR_SIZE 1024 //字节
 37 #else 
 38 #define STM_SECTOR_SIZE    2048
 39 #endif         
 40 u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
 41 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)    
 42 {
 43     u32 secpos;       //扇区地址
 44     u16 secoff;       //扇区内偏移地址(16位字计算)
 45     u16 secremain; //扇区内剩余地址(16位字计算)       
 46      u16 i;    
 47     u32 offaddr;   //去掉0X08000000后的地址
 48     if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
 49     FLASH_Unlock();                        //解锁
 50     offaddr=WriteAddr-STM32_FLASH_BASE;        //实际偏移地址.
 51     secpos=offaddr/STM_SECTOR_SIZE;            //扇区地址  0~127 for STM32F103RBT6
 52     secoff=(offaddr%STM_SECTOR_SIZE)/2;        //在扇区内的偏移(2个字节为基本单位.)
 53     secremain=STM_SECTOR_SIZE/2-secoff;        //扇区剩余空间大小   
 54     if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
 55     while(1) 
 56     {    
 57         STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
 58         for(i=0;i<secremain;i++)//校验数据
 59         {
 60             if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除        
 61         }
 62         if(i<secremain)//需要擦除
 63         {
 64             FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
 65             for(i=0;i<secremain;i++)//复制
 66             {
 67                 STMFLASH_BUF[i+secoff]=pBuffer[i];      
 68             }
 69             STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
 70         }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.                    
 71         if(NumToWrite==secremain)break;//写入结束了
 72         else//写入未结束
 73         {
 74             secpos++;                //扇区地址增1
 75             secoff=0;                //偏移位置为0      
 76                pBuffer+=secremain;      //指针偏移
 77             WriteAddr+=secremain;    //写地址偏移       
 78                NumToWrite-=secremain;    //字节(16位)数递减
 79             if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
 80             else secremain=NumToWrite;//下一个扇区可以写完了
 81         }     
 82     };    
 83     FLASH_Lock();//上锁
 84 }
 85 #endif
 86 
 87 
 88 //从指定地址开始读出指定长度的数据
 89 //ReadAddr:起始地址
 90 //pBuffer:数据指针
 91 //NumToWrite:半字(16位)数
 92 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)       
 93 {
 94     u16 i;
 95     for(i=0;i<NumToRead;i++)
 96     {
 97         pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
 98         ReadAddr+=2;//偏移2个字节.    
 99     }
100 }
stmflash_config.c

主要就是在iap里面用了void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)

下一步计划:从SD卡更新

通过SD卡更新app搞定,写了一个新函数在iap.c当中

 1 // 从SD卡更新bin到片内Flash
 2 // appxaddr:片内Flash的起始地址
 3 //     fxpath:bin在SD的路径
 4 u8 update_app(u32 appxaddr,u8 *fxpath)
 5 {
 6     u32 tempaddr=appxaddr;//当前写入的地址
 7     FIL * fftemp;
 8     u8 *tempbuf;
 9      u8 res;    
10     u16 bread;
11     u8 rval;
12 
13     fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));    //分配内存    
14     if(fftemp==NULL)rval=1;
15     tempbuf=mymalloc(SRAMIN,4096);    //分配4096个字节空间
16     if(tempbuf==NULL)rval=1;
17 
18     res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ); 
19     while(res==FR_OK)//死循环执行
20     {
21          res=f_read(fftemp,tempbuf,4096,(UINT *)&bread);        //读取数据     
22         if(res!=FR_OK)break;                     //执行错误
23         iap_write_appbin(tempaddr,tempbuf, bread);
24         tempaddr+=bread;                                   
25         if(bread!=4096)break;                                //读完了.
26      }     
27     f_close(fftemp);
28     
29     return rval;    
30 }
update_app

思路很简单,从SD文件系统读出文件,每次4k,写入片内flash

posted @ 2013-05-15 17:44  wwjdwy  阅读(3365)  评论(0编辑  收藏  举报