说明:

1.本例程参考正点原子的IAP的代码。
2.本例程基于STM32F103C8T6单片机编写。
3.本例程使用USART1进行程序下载,波特率115200
4.串口输出”Bootloder“字样后,请在3s内通过串口将APP的.bin文件传入单片机,之后会自动烧写并启动APP。若3s内未传入.bin文件或传入的文件有误,则会自动原来的启动APP。.bin文件最大只能15KByte
5.STM32F103C8T6拥有128KByte的flash20KByte的sram。本程序将0x0800 0000 - 0x0800 FFFF作为Bootloder空间,将0x0801 0000 - 0x0801 FFFF作为APP空间。
6.APP程序需要将flash中的向量表偏移0x10000

工程源码下载(fci5), 工程使用 Keil uVision5 创建 ,密码 fci5 。

XCOM V2.6下载(48cm),密码 48cm 。

程序源码:

  • main.c
#include "delay.h"
#include "usart.h"
#include "flash.h"
#include "iap.h"/***********************************************
*         支持最大15KByte的APP                 *
*                                                      *
*       0x0800 0000 - 0x0800 FFFF 为Bootloder空间  *
*   0x0801 0000 - 0x0801 FFFF 为APP空间        *
*                                              *
***********************************************/int main ()
{u8 flag_update = 0; //升级完成后置1u16 t = 0;u16 oldcount = 0;u16 applen = 0;//初始化delay_init();USART1_Init(115200);delay_ms(20);printf("Bootloder\r\n");while(1){if(USART1_RX_CNT) //如果接收到了数据{if(oldcount == USART1_RX_CNT) //如果新周期内没收到数据,则认为接收完成{applen = USART1_RX_CNT;oldcount = 0;USART1_RX_CNT = 0;if(((*(vu32*)(0x20001000+4)) & 0xFF000000) == 0x08000000) //判断是否为0x08XXXXXX,(是否为APP数据){printf("APP len : %d\r\n",applen);//开始升级APPprintf("Updating...\r\n");iap_write_appbin(FLASH_APP1_ADDR,USART1_RX_BUF,applen); //写入flashflag_update = 1;printf("Updae success !!!\r\n");}else printf("APP data error!!!\r\n");}else oldcount = USART1_RX_CNT; //更新接收到的字节数}  if(flag_update || (t == 300 & USART1_RX_CNT == 0)) //如果升级APP完成或3s内没收到正确的APP数据,则启动APP{printf("APP Start\r\n\r\n");iap_load_app(FLASH_APP1_ADDR);}       t++;delay_ms(10); }
}
  • delay.h
#ifndef _DELAY_H
#define _DELAY_H
#include "stm32f10x.h"void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);#endif
  • delay.c
#include "delay.h"static u8 fac_us = 0;     //us延时倍乘数
static u16 fac_ms = 0;    //ms延时倍乘数void delay_init(void)
{SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);      //选择外部时钟 HCL/8fac_us = SystemCoreClock/8000000;                        //为系统时钟的1/8fac_ms = (u16)fac_us * 1000;
}void delay_us(u32 nus)
{u32 temp;SysTick->LOAD = nus * fac_us;//时间加数SysTick->VAL = 0x00;//清空计数器SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//开始倒数do{temp = SysTick->CTRL;}while((temp & 0x01) &&! (temp & (1<<16))); //等待时间到达SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//关闭计数器、SysTick->VAL = 0x00;//清空计数器
}void delay_ms(u16 nms)
{u32 temp;SysTick->LOAD = (u32)nms * fac_ms;//时间加载(SysTick->LOAD为24bit)SysTick->VAL = 0x00;//清空计数器SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开始倒数do{temp = SysTick->CTRL;}while((temp & 0x01) &&! (temp & (1<<16))); //等待时间到达SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//关闭计数器SysTick->VAL = 0x00;//清空计数器
}
  • usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "stm32f10x.h"
#include "stdarg.h"#define PRINTF_USARTx USART1      //USARTx 映射到 printf#define EN_USART1_RX          1         //1使能接收,0失能接收。#define USART1_REC_LEN       15*1024   //最大接收15KByteextern u8  USART1_RX_BUF[USART1_REC_LEN];  //USART1 储存接收到的数据
extern u16 USART1_RX_STA;                    //USART1 接收状态即接收到的有效数据个数
extern u16 USART1_RX_CNT;void USART1_Init(unsigned int BaudRate); //USART1用户初始化函数void printf1(u8 *data,...);int fputc(int ch, FILE *f);
void _sys_exit(int x);
static char *itoa(int value,char *string,int radix);
#endif
  • usart.c
#include "usart.h"/*******************************************************************************************************************************************/
/*                                                          USART1                                                                         */
/*******************************************************************************************************************************************/void USART1_Init(unsigned int BaudRate)
{GPIO_InitTypeDef GPIO_InitStruct;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);           //使能USART1,GPIOA时钟/*初始化USART1的Tx、Rx引脚*///PA9 复用推挽输出GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//PA10浮空输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA,&GPIO_InitStruct);//    PA9outAF_PP();   //PA9 复用推挽输出
//  PA10inFLOATING();//PA10浮空输入USART_InitStructure.USART_BaudRate = BaudRate;                                                //波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;                             //字长8位USART_InitStructure.USART_StopBits = USART_StopBits_1;                                       //停止位1位USART_InitStructure.USART_Parity = USART_Parity_No;                                         //无奇偶校验USART_InitStructure.USART_HardwareFlowControl    = USART_HardwareFlowControl_None;      //无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                          //收/发模式 USART_Init(USART1, &USART_InitStructure);USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                               //开启接收中断USART_Cmd(USART1, ENABLE);//中断设置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);                   //中断分组1:1位抢占优先级,3位响应优先级NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;             //中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;           //子优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                    //使能中断NVIC_Init(&NVIC_InitStructure);}/*****************************************************
如果开启了接收
*****************************************************/
#if EN_USART1_RX //如果开启了USART1接收
u8 USART1_RX_BUF[USART1_REC_LEN] __attribute__ ((at(0x20001000)));  //接收缓冲,最大USART1_REC_LEN个字节。 设定起始地址为0x20001000
u16 USART1_RX_STA = 0;      //接收状态标记
u16 USART1_RX_CNT = 0;      //接收的字节数void USART1_IRQHandler(void)
{u8 res;if(USART1->SR & (1<<5)){res = USART1->DR;if(USART1_RX_CNT<USART1_REC_LEN){USART1_RX_BUF[USART1_RX_CNT] = res;USART1_RX_CNT++;}}
}
#endif/*******************************************************************************************************************************************/
/*                                     以下代码实现printf输出(无需MircoLIB)                                                              */
/*******************************************************************************************************************************************//*****************************************************
*function:  写字符文件函数
*param1:    输出的字符
*param2:    文件指针
*return:    输出字符的ASCII码
******************************************************/
int fputc(int ch, FILE *f)
{while((USART1->SR & 0x40) == 0);USART1->DR = (u8) ch;return ch;
}/********** 禁用半主机模式 **********/
#pragma import(__use_no_semihosting)struct __FILE
{int handle;
};FILE __stdout;void _sys_exit(int x)
{x=x;
}/*******************************************************************************************************************************************/
/*                                                                     itoa                                                                */
/*******************************************************************************************************************************************/
static char *itoa(int value,char *string,int radix)
{int i,d;int flag = 0;char *ptr = string;if(radix != 10){*ptr = 0;return string;}if(!value){*ptr++ = 0x30;*ptr = 0;return string;}if(value < 0){*ptr++ = '-';value *= -1;}for(i=10000;i>0;i /= 10){d = value / i;if(d || flag){*ptr++ = (char)(d + 0x30);value -= (d * i);flag = 1;}}*ptr = 0;return string;
}/*******************************************************************************************************************************************/
/*                                                  用户自定义的USART1输出函数 printf1                                                     */
/*******************************************************************************************************************************************/
void printf1(u8 *data,...)
{const char *s;int d;char buf[16];va_list ap;va_start(ap,data);while( *data != 0){if( *data == 0x5C ) // '\'{switch ( *++data ){case 'r':USART_SendData(USART1,0x0D);data++;break;case 'n':USART_SendData(USART1,0x0A);data++;break;default:data++;break;}}else if( *data == '%' ){switch ( *++data ){case 's': //字符串s = va_arg(ap,const char *);for(;*s;s++){USART_SendData(USART1,*s);while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );}data++;break;case 'd':d = va_arg(ap,int);itoa(d,buf,10);for(s = buf;*s;s++){USART_SendData(USART1,*s);while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );}data++;break;default:data++;break;}}else USART_SendData(USART1,*data++);while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);}
}
  • flash.h
#ifndef _FLASH_H
#define _FLASH_H
#include "stm32f10x.h"/*********************** 移植 *************************/
#define FLASH_SIZE 128          //FLASH容量(KByte)
#define FLSAH_EN_WRITE 1        //1使能写入,0失能写入
/******************************************************/#define FLSAH_BASH 0x08000000//判断页大小
#if FLASH_SIZE < 256
#define SECTOR_SIZE 1024
#else
#define SECTOR_SIZE 2048
#endif//提供给用户的函数
void FLASH_Write(u32 Addr,u16 *pBuff,u16 len);u16 FLASH_ReadHalfWord(u32 faddr);                        //读取一个半字(16位)void FLASH_Read(u32 Addr,u16 *pBuff,u16 len);             //读出一段数据(16位)void FLASH_Writ_NoCheck(u32 Addr,u16 *pBuff,u16 len);     //不检查的写入#endif
  • falsh.c
#include "flash.h"u16 FLASH_BUF[SECTOR_SIZE/2]; //1KByte//提供给用户的函数
void FLASH_Write(u32 Addr,u16 *pBuff,u16 len)
{u32 Sector_Number;  //第几扇区u32 Sector_Relative;   //扇区内偏移地址(按16位计算)u32 Sector_Remain;     //扇区内剩余地址(按16位计算)u32 OffAddr;           //减去0x08000000后的地址u16 i;//判断地址合理性if(Addr<FLASH_BASE || Addr>=(FLASH_BASE+1024*FLASH_SIZE))return;FLASH_Unlock();OffAddr = Addr - FLASH_BASE;Sector_Number = OffAddr/SECTOR_SIZE;Sector_Relative = (OffAddr%SECTOR_SIZE)/2;Sector_Remain = SECTOR_SIZE/2-Sector_Relative;//如果一页能够写完if(len <= Sector_Remain) Sector_Remain = len;//写入while(1){//整页读出FLASH_Read(Sector_Number*SECTOR_SIZE+FLASH_BASE,FLASH_BUF,SECTOR_SIZE/2);for(i=0;i<Sector_Remain;i++){if(FLASH_BUF[Sector_Relative+i] != 0xFFFF) break; //需要擦除}if(i<Sector_Remain) //需要擦除{FLASH_ErasePage(Sector_Number*SECTOR_SIZE+FLASH_BASE);//替换数据for(i=0;i<Sector_Remain;i++){FLASH_BUF[Sector_Relative+i] = pBuff[i];}//写入FLASHFLASH_Writ_NoCheck(Sector_Number*SECTOR_SIZE+FLASH_BASE,FLASH_BUF,SECTOR_SIZE/2);   }else{FLASH_Writ_NoCheck(Addr,pBuff,Sector_Remain);}if(len == Sector_Remain) break; //写入结束else{Sector_Number++;          //页+1Sector_Relative = 0;      //页内偏移0pBuff += Sector_Remain;   //更新传入的数组指针Addr += Sector_Remain*2;    //写地址偏移len -= Sector_Remain;     //更新写长度if(len>(SECTOR_SIZE/2)) Sector_Remain = SECTOR_SIZE/2;else Sector_Remain = len;}}FLASH_Lock();
}//读取一个半字(16位)
u16 FLASH_ReadHalfWord(u32 faddr)
{return *(vu16*)faddr;
}//读出一段数据(16位)
void FLASH_Read(u32 Addr,u16 *pBuff,u16 len)
{u16 i;for(i=0;i<len;i++){pBuff[i] = FLASH_ReadHalfWord(Addr);Addr += 2;}
}#if FLSAH_EN_WRITE //如果使能了写//不检查的写入
void FLASH_Writ_NoCheck(u32 Addr,u16 *pBuff,u16 len)
{u16 i;for(i=0;i<len;i++){FLASH_ProgramHalfWord(Addr,pBuff[i]);Addr += 2;}
}#endif
  • iap.h
#ifndef _IAP_H
#define _IAP_H
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "flash.h"
#define FLASH_APP1_ADDR 0x08010000 //第一个应用程序起始地址typedef void (*iapfun) (void); //定义一个函数类型的参数(?)void iap_load_app(u32 addr); //执行flash里的app程序
void iap_load_appsram(u32 addr); //执行sram里的app程序
void iap_write_appbin(u32 addr,u8 *buf,u32 len); //将程序写入到指定flash地址#endif
  • iap.c
#include "iap.h"/*******************************************************0x0800 0000 - 0x0800 FFFF 为Bootloder空间0x0801 0000 - 0x0801 FFFF 为APP空间
*******************************************************/
iapfun jump2app; //(?)u16 iapbuf[1024]; //按2KByte合并接收到的数据,然后写入flash//将程序写入到指定flash地址
void iap_write_appbin(u32 addr,u8 *buf,u32 len)
{u16 t;u16 i = 0;u16 temp;u32 fwaddr = addr;u8 *dfu = buf;for(t=0;t<len;t+=2){//将8位数据合并为16位数据temp = (u16)dfu[1]<<8;temp += (u16)dfu[0];dfu += 2;iapbuf[i++] = temp; //将合并完成的16位数据储存在数组中if(i == 1024) //合并的数据填满iapbuf缓冲区后,开始写入falsh{i=0;FLASH_Write(fwaddr,iapbuf,1024);fwaddr += 2048;}}if(i)FLASH_Write(fwaddr,iapbuf,i); //将最后一些内容写入flash}//执行flash里的app程序
void iap_load_app(u32 addr)
{if(((*(vu32*)addr) & 0x2FFE0000) == 0x20000000) //检查栈顶地址是否合法(?){jump2app = (iapfun)*(vu32*)(addr + 4); //用户代码区第二个字为程序开始地址(复位地址)MSR_MSP(*(vu32*)addr); //初始化app栈顶指针(用户区的第一个字用于存放栈顶地址)jump2app(); //跳转到APP}
}

基于STM32F103C8T6 的 Bootloder 程序源码(另附百度网盘下载链接)相关推荐

  1. 基于STM32F103C8T6四路AD采集数据显示在oled屏上非DMA传输方式(附百度网盘下载链接)

    本文采用四路AD采集光照强度.烟雾浓度.一氧化碳.空气质量等四个物理量,并采用中位值平均滤波(防脉冲干扰平均滤波法)算法对偶然出现的脉冲性干扰,消除由其引起的采样值偏差. ADC简介 STM32F10 ...

  2. ThinkPHP php 仿千图网源码_仿百度网盘文件分享dzzoffice网盘系统源码_PHP

    声明:"本专区源码来源于各大收费资源站转载 ,都是花钱买的,但是站内不测试 ,仅供学习参考,请勿用作商业用途,签到投稿即可获得积分下载!" 本网盘修复的焦点功效 1.断点续传功效. ...

  3. matlab exm,exm 《Experiments with MATLAB》这本书的程序源码,附中文注释,简单易懂,是学习 238万源代码下载- www.pudn.com...

    文件名称: exm下载 收藏√  [ 5  4  3  2  1 ] 开发工具: matlab 文件大小: 1154 KB 上传时间: 2014-11-21 下载次数: 6 提 供 者: 刘晏池 详细 ...

  4. 壁纸小程序源码,支持图片搜索,下载,分享,可对接流量主

    很多小伙伴都喜欢给自己的博客站,搞一个小程序,让用户能方便访问.为什么小程序如此受青睐呢?因为小程序与APP的直接区别就是,我能在微信上直接得到我想要东西,比如看文章,下载壁纸,而不用繁琐的去下载AP ...

  5. (已更新)壁纸小程序源码,支持图片搜索,下载,分享

    很多小伙伴都喜欢给自己的博客站,搞一个小程序,让用户能方便访问.为什么小程序如此受青睐呢?因为小程序与APP的直接区别就是,我能在威信上直接得到我想要东西,比如看文章,下载壁纸,而不用繁琐的去下载AP ...

  6. 网盘搜索引擎php源码,2016最新百度云网盘搜索引擎源码,附带Python爬虫+PHP网站+Xunsearch搜索引擎...

    源码简介 : 适用范围:百度云网盘 搜索引擎 源码,百度 搜索引擎 源码,网盘搜索 爬虫 源码 演示地址:(以截图为准) 运行环境:PHP.MYSQL 其他说明:分享的是一款 搜索引擎 源码,百度云盘 ...

  7. 20201217网警考试题目及题目源码(百度云网盘下载链接)

    20201217网警考试题目及题目源码(百度云网盘下载链接) 解题答案视频教程请关注"极客易先生"微信公众号 链接:https://pan.baidu.com/s/1pzimgPk ...

  8. 黑马程序员:Xcode8网盘下载链接及更新简述

    6月14日,苹果公司在WWDC 大会上公布了四大系统watchOS 3,tvOS,macOS Sierra和iOS 10.但既然是开发者大会,就一定会有除系统本身之外,对iOS开发技术上的内容更新.所 ...

  9. 【java毕业设计】基于JAVA+JSP+strust2的电子政务网设计与实现(毕业论文+程序源码)——电子政务网

    基于JAVA+JSP+strust2的电子政务网设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于JAVA+JSP+strust2的电子政务网设计与实现,文章末尾附有本毕业设计的论文和源码下 ...

最新文章

  1. java spring框架 注解_史上最全的java spring注解
  2. python与excel结合-Python与Excel之间的交互
  3. 免费mysql空间_php+mysql免费空间
  4. php数组foreach循环添加键值对_循环 - PHP二维数组根据键值对获取一组数组 (不使用foreach)...
  5. JEECG社区第六期架构培训班报名
  6. linux很容易忽略的rz上传、sz下载命令
  7. pymysql 返回数据为字典形式(key:value--列:值)
  8. UVALive - 6442
  9. Python入门--字典元素的操作,key的判断(in not in),字典元素删除(del),字典元素的增加,清空(clear()),修改
  10. 因果推断笔记——python 倾向性匹配PSM实现示例(三)
  11. 怎么把html背景图片,css如何设置背景图片?
  12. 7-8 黑洞数 (20 分)
  13. contos7改分辨率_Centos 7 修改系统屏幕分辨率
  14. 【PS】4组318张超高清叠加层+PS动作图片素材
  15. 模拟美式橄榄球比赛数据(R)
  16. JAVA-DDD项目结构
  17. 我的世界服务器物品怎么上锁,我的世界怎么给箱子上锁_我的世界箱子上锁指令用法及解锁方法_玩游戏网...
  18. PySide2学习总结(十二)打开文件对话框--FileDialog
  19. 华为:交付服务体系怎么提升一线作业人员的工作体验?
  20. 让我们旋转跳跃不停歇~~~当3D打印遇上八音盒!(三)

热门文章

  1. 芯片老化验证流程_一种霍尔芯片老化测试装置及测试方法与流程
  2. IBM人工智能驱动的隐形恶意软件工具DeepLo​​cker
  3. 简单工厂模式之实例一:简单电视工厂
  4. 【IoT】物联网NB-IoT之移动oneNET平台硬件接入
  5. WebMercator和geographic互相转换
  6. 基于Java的音游项目
  7. 如何开发音游所用的节奏点编辑器
  8. PBD加密专家(pb混淆加密大师)(pb obfuscator) 最新说明书
  9. linux安装mt7601网卡固件,上网本Debian下装无线网卡7601的驱动过程
  10. 微信小程序的动画效果