IAP程序的功能
再看自己的用户程序--用户程序自己也做了些设置
对了关于我为什么拷贝到Flash里面------自己用的单片机的RAM不够用,存不了用户程序,所以自己就定义了一个小点的数组(环形队列),串口一边接收,一边往Flash里面写,环形队列可是帮了大忙了,,,,
把IAP升级程序下进去,以后就直接通过串口发送自己的用户程序就行了...什么都不需要做了,先说一下操作过程吧!最后有自己的源码
IAP程序软件不需要任何配置
波特率太快的话,数据来不及写入Flash,环形队列容易溢出,,太慢的话,程序发送的慢。。。
用户程序软件需要一些配置
8006000告诉编译器我的用户程序打算在这里开始,你帮我设置一下吧,默认是在8000000开始的
0x1A000,就是告诉编译器我的程序空间有这么大。
我的用户程序里面也是设置的6000,这个一定要和程序设置的一样哈
关于这个我后面会说为什么这样设置。。。当然也可以百度一下。
其实我的本来是
20000换成十进制就是131072个字节 除以1024 等于128
由于我先把IAP程序下进去了,IAP程序也需要空间来运行,,,我就给了他6000 换成十进制就是24576 除以1024就是24K
我的总共是128K然后去掉IAP暂用的24K就是 128-24 = 104K = 106496个字节 换成16进制就是 1A000
所以我上面写了1A000
对了如果您的板子是大容量的如果您非常明白就自己随意修改把,别忘了修改程序里的那个,,,
如果不是很明白按照上面修改就行,后面会让您明白
这个呢就是让Keil软件帮忙生成bin文件
F:Keil4&&MDK4.70AARMARMCCbinfromelf.exe --bin -o .ProgectProgect.bin ..ProgectoutputProgect.axf
F:Keil4&&MDK4.70AARMARMCCbinfromelf.exe --bin -o 这个是执行的命令,就是生成bin文件,根据自己的安装路径找哈
.ProgectProgect.bin 就是告诉他把生成的bin文件放在哪个地方
..ProgectoutputProgect.axf 这个就是自己工程编译的时候产生的.axf文件,根据自己的找到
./当前目录
../上一级目录
../../上上一级目录
关于Bin文件和Hex文件
http://blog.sina.com.cn/s/blog_6b94d5680100lo2h.html
咱们自己设置好写到哪里了,所以前头的就不需要了,后面的校验也不需要了,,不过呢应该向他那样加上校验,数据对了再写进去!!!!
好生成了bin文件
然后
假设修改了程序了,再升级
您再升级就再升级把!!
再升级
不要老是升级哈!!!玩坏了Flash可就不好玩了
自己用的F103RBT6单片机的RAM只有
5000 也就是20480个字节,,但是自己的程序已经超过了这个字节数
所以自己就不能先定义一个很大的数组然后然后把程序先存在里面了,列如很多都是:
u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.//把数据固定的存在以0X20001000为起始地址的RAM里面
自己呢就是用的环形队列一边接收,一边写入,,,关于环形队列可以看我的环形队列的文章,,,
http://www.cnblogs.com/yangfengwu/p/6822984.html
就再说一下自己的程序的一些地方
串口接收的
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
PutData(&Res,1); //把数据存入队列
Usart1RecCnt ++; //数据个数
}
}
用的系统定时器中断来检测的串口空闲,判断接没接收到一条完整的数据--方法呢是看到人家的一种方法,感觉比自己以前的好,所以直接拿过来用了
关于单片机空闲中断可以看一下自己以前的
http://www.cnblogs.com/yangfengwu/p/6746403.html
/*系统定时器中断*/
void SysTick_Handler(void)
{
SysTickCnt ++;
SysTickCnt1++;
SysTickCnt2++;
if(SysTickCnt1>=10)//每隔10毫秒检测一次
{
SysTickCnt1 = 0;
if(Usart1RecCnt)//如果接收到数据了
{
if(IdleCnt == Usart1RecCnt)//10ms时间数据没了变化
{
Usart1RecCntCopy = Usart1RecCnt;//拷贝数据个数
Usart1RecCnt = 0;//清零数据个数
IdleCnt = 0;//清零
Usart1Flage = 1;//接收到一条数据
// rbDelete(&pRb);测试的时候销毁
// rbCreate(&pRb,ReceBuff,USART_REC_LEN);//创建接收环形队列
}
else
{
IdleCnt = Usart1RecCnt;
}
}
}
}
我的IAP的接收的数据往Flash里面写和用户程序的往Flash里面写有一点不同,其实用户程序的往Flash里面写的程序是后期的改进...
先看我的IAP的
if(rbCanRead(&pRb)>1)//如果环形队列里面的数据个数大于1
{
rbRead(&pRb, &ReadDat, 2);//读取两个数据
ReadDat16 = (u16)ReadDat[1]<<8;
ReadDat16 = ReadDat16|ReadDat[0];
STMFLASH_Write(addr2,&ReadDat16,1);
addr2+=2;
}
if(rbCanRead(&pRb)>1)
因为一次性要往Flash里面写16位数据,所以才会判断数据个数大于一个的时候再往里面写.
虽然现在程序没有问题,但是我还在想如果程序的个数是奇数个就完啦!但是好像程序的个数总是偶数个....其实可以在判断接收完成的里面
做一下判断,如果数据还残留一个,那就写进去....
好,那就看一下判断接收完程序
if(Usart1Flage == 1)//数据接收完成
{
addr2 = FLASH_APP2_ADDR;//存储数据的地址
Usart1Flage =0;//清零
if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
printf("准备执行APP代码!!rn");
UserDataAddr = FLASH_DATA_ADDR;//存储其余的数据地址
ReadDat16 = 0x55;//写入标志告诉IAP程序有可更新的用户程序
STMFLASH_Write(UserDataAddr,&ReadDat16,1);
UserDataAddr+=2;
printf("写入0x55标志!!rn");
ReadDat16 = (u16)((Usart1RecCntCopy>>16)&0xffff);//存储接收到多少数据高位
STMFLASH_Write(UserDataAddr,&ReadDat16,1);
UserDataAddr+=2;
ReadDat16 = (u16)(Usart1RecCntCopy&0xffff);//存储接收到多少数据低位
STMFLASH_Write(UserDataAddr,&ReadDat16,1);
UserDataAddr+=2;
Usart1RecCntCopy = 0;
printf("开始复位重启!!rn");
Reset_MCU();
}
else
{
printf("非FLASH应用程序,无法执行!rn");
}
// printf("Cnt=%drn",Usart1RecCntCopy);
// for(i=0;i // STMFLASH_Read(addr1,&ReadDat16,1); // addr1+=2;//偏移2048 16=2*8.所以要乘以2. // if((ReadDat16&0x00ff)<=15) // { // printf("0%x ",ReadDat16&0x00ff); // } // else // { // printf("%x ",ReadDat16&0x00ff); // } // // if((ReadDat16>>8)<=15) // { // printf("0%x ",ReadDat16>>8); // } // else // { // printf("%x ",ReadDat16>>8); // } // } // addr1 = FLASH_APP1_ADDR; // for(i=0;i<40;i++) // { // STMFLASH_Erase(addr1,1024);//擦除FLASH_APP1_ADDR地址以及以上40页 // addr1 +=2048; // } // addr1 = FLASH_APP1_ADDR; } 后边屏蔽的是测试的时候,看一下写入的数据,然后和源数据对比一下,看一下写入的对不对 if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000) 这句话 先问一个问题,怎么知道接收过来的是用户程序呢????要是别的数据怎么办???,,必须有一个判断依据才行对吧!! 就必须找到用户程序中永恒不变的变量.... 然后呢,我是看别人的程序说,数据的第一个4个字节为栈顶地址,数据的第二个4字节为复位中断向量的入口地址 FLASH_APP2_ADDR+4指针就移动到了IAP升级程序的E9或者说电压电流采集程序的D5上 (*(vu32*)(FLASH_APP2_ADDR+4))然后强制型的转成32位的,然后取出来,就是IAP升级程序的E9 20 00 08 或者说电压电流采集程序的D5 7E 00 08 还有一件事就是STM32是小端模式,,,,所谓小端模式就是低位在低地址,高位在高地址 举个例子 把60000存到STM32的Flash的,60000转换成16进制是EA60 EA是高8位,60是低八位,,存到Flash里面就是60EA这样存的 60存到了低地址,EA存到了高地址,,,,,当然有小端模式就有大端模式,,,大端模式就是低位在高地址,高位在低地址,电脑就是这样... 说到这里就要说一下共用体 typedef union Resolver_I { long Data; char Data_Table[4]; }Resolver_iData; typedef union Resolver_f { float Data; char Data_Table[4]; }Resolver_fData; Resolver_iData Resolver_7758; //解析7758数据 Resolver_fData Resolver_Usart; //解析串口数据 一个整形数据快速的转换成16进制存到数组里面 Resolver_7758.Data = 60000; 那么Resolver_7758.Data_Table[0] = 0x60; Resolver_7758.Data_Table[1] = 0xEA; Resolver_7758.Data_Table[2] = 0x00; Resolver_7758.Data_Table[3] = 0x00; 一个浮点型的数据转换成16进制存到数组里面--其实也是按照IEEE754规约来计算的 Resolver_Usart.Data = 220.5; 那么Resolver_Usart.Data_Table[0] = 0x00; Resolver_Usart.Data_Table[1] = 0x80; Resolver_Usart.Data_Table[2] = 0x5C; Resolver_Usart.Data_Table[3] = 0x43; 所以说上面的数据取出来就是08 00 20 E9然后&0xFF000000 肯定就等于 0x08000000啦 其实这样还有一个原因是因为32的地址是从08000000开始的,复位中断向量的地址的最高两位肯定是08啦!!!! 然后还有个if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.