[源创] STM32F103ZET6 基于XMODEM 通讯的 BOOTLOADER案列IAP
网上好多初学者 都想知道如何更好的用IAP,BOOTLOADER 功能
我给大家一个我自己的基于Xmodem的例子,
开发环境 KEIL 5.14 + STD标准库
芯片 STM32F103ZET6 外部晶振8MHz
用串口1通讯,通讯收发都用查询方式,没有用中断
另外用了systick 来做固定时间的延时程序
下面直接上代码
延时程序部分,
其实这个代码我也是从网上摘录的,不过希望大家好好理解一下
udelay.c
#include "stm32f10x.h" static u8 fac_us=;//us延时倍乘数
static u16 fac_ms=;//ms延时倍乘数 //初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟 void uDelay_SysTick_init(u8 SYSCLK)
{
SysTick->CTRL&=0xfffffffb;//bit2清空,选择外部时钟 HCLK/8 //b->1011
fac_us=SYSCLK/;
fac_ms=(u16)fac_us*;
} //延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 void uDelay_SysTick_ms(u16 nms)
{
u32 temp; SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit) SysTick->VAL =0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数 do
{
temp=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(<<)));//等待时间到达 SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
} //延时nus
//nus为要延时的us数. void uDelay_SysTick_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数 do
{
temp=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(<<)));//等待时间到达 SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
补一下 串口查询发送的 代码给你们
void uUART_PutChar(USART_TypeDef* USARTx, uint8_t Data)
{
//while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){}
USARTx->SR;
USART_SendData(USARTx, Data);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET){}
}
以下就是今天的核心内容,大家注意看啊
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "stdio.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/
#define CLI() __set_PRIMASK(1) //关闭总中断
#define SEI() __set_PRIMASK(0) //打开总中断
/* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/
extern void uUART_PutChar(USART_TypeDef* USARTx, uint8_t Data);
extern void uUSART_Init(void); extern void uDelay_SysTick_init(u8 SYSCLK);
extern void uDelay_SysTick_ms(u16 nms);
extern void uDelay_SysTick_us(u32 nus); #define def_FLASH_BASE (u32)(0x08000000)
#define def_FLASH_PAGESIZE (u32)(2048)
#define def_FLASH_PAGECUNT (u32)(256)
#define def_USERAPP_START (u32)(def_FLASH_BASE + (def_FLASH_PAGESIZE * 20))
#define def_USERAPP_BOTTOM (u32)(def_FLASH_BASE+def_FLASH_PAGESIZE * 256-def_FLASH_PAGESIZE*2) u32 gFlash_User_Address; //FLASH地址
u16 gFlash_128Bytes_Cnt=; //----------------------------------------------------------------------------- //定义Xmoden控制字符
#define XMODEM_NUL 0x00
#define XMODEM_SOH 0x01
#define XMODEM_STX 0x02
#define XMODEM_EOT 0x04
#define XMODEM_ACK 0x06
#define XMODEM_NAK 0x15
#define XMODEM_CAN 0x18
#define XMODEM_EOF 0x1A
#define XMODEM_WAIT_CHAR XMODEM_NAK #define dST_WAIT_START 0x00 //等待启动
#define dST_BLOCK_OK 0x01 //接收一个数据块成功
#define dST_BLOCK_FAIL 0x02 //接收一个数据块失败
#define dST_OK 0x03 //完成 //定义全局变量 struct str_XMODEM
{
unsigned char SOH; //起始字节
unsigned char BlockNo; //数据块编号
unsigned char nBlockNo; //数据块编号反码
unsigned char Xdata[]; //数据128字节
unsigned char CheckSum; //CheckSum校验数据
}strXMODEM; //XMODEM的接收数据结构 unsigned char gXM_BlockCount; //数据块累计(仅8位,无须考虑溢出)
unsigned char gXM_STATUS; //运行状态
//----------------------------------------------------------------------------- //接收指定字节数据(带超时控制)
// *ptr 数据缓冲区
// len 数据长度
// timeout 超时设定
// 返回值 已接收字节数目 unsigned char get_data(unsigned char *ptr,unsigned char len,u32 timeout)
{
unsigned count=;
do
{
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) //recive
{
*ptr++ = USART_ReceiveData(USART1);//如果接收到数据,读出 USART_ClearFlag(USART1, USART_FLAG_RXNE); count++; if (count>=len)
{
break; //够了?退出
}
continue;
} uDelay_SysTick_us();
timeout--;
}
while (timeout);
return count;
} unsigned char uCheckSum(unsigned char *ptr,unsigned char count)
{
unsigned char CheckSum=; while (count--)
{
CheckSum = CheckSum + *ptr++ ;
}
return CheckSum;
} void uXMODEM_Process()
{
unsigned char c;
u16 i;
unsigned char CheckSum;
uint16_t dataIndex;
uint16_t *pBuffer; //向PC机发送开始提示信息
printf("--> User program upgrade \r\n");
printf("--> Press the [d] key in 60 seconds . \r\n");
printf("--> After 60 seconds running the user program . \r\n"); //60秒种等待PC下发“d”,否则退出Bootloader程序 c=; get_data(&c,,**); //限时60秒,接收一个数据 if ((c=='d')||(c=='D'))
{
gXM_STATUS=dST_WAIT_START; //并且数据='d'或'D',进入XMODEM
printf("--> Please use the XMODEM protocol to transfer the BIN file. \r\n");
printf("--> The maximum of BIN file size is xxxKB \r\n");
}
else
{
gXM_STATUS=dST_OK; //退出Bootloader程序
} //进入XMODEM模式 gXM_BlockCount=0x01; gFlash_128Bytes_Cnt = ; gFlash_User_Address = def_USERAPP_START; while (gXM_STATUS!=dST_OK) //循环接收,直到全部发完
{
if (gXM_STATUS==dST_WAIT_START)
{
//XMODEM未启动
uUART_PutChar(USART1,XMODEM_WAIT_CHAR); //发送请求XMODEM_WAIT_CHAR
} i=get_data(&strXMODEM.SOH,,*); //限时1秒,接收133字节数据 if (i)
{
//分析数据包的第一个数据 SOH/EOT/CAN
switch (strXMODEM.SOH)
{
case XMODEM_SOH: //收到开始符SOH
if (i>=)
{
gXM_STATUS=dST_BLOCK_OK;
}
else
{ //如果数据不足,要求重发当前数据块
gXM_STATUS=dST_BLOCK_FAIL;
uUART_PutChar(USART1,XMODEM_NAK);
}
break; case XMODEM_EOT: //收到结束符EOT
uUART_PutChar(USART1,XMODEM_ACK); //通知PC机全部收到
gXM_STATUS=dST_OK; printf("--> User program upgrade finished \r\n");
break; case XMODEM_CAN: //收到取消符CAN
uUART_PutChar(USART1,XMODEM_ACK); //回应PC机
gXM_STATUS=dST_OK; printf("--> Warning: Cancel the upgrade, the user program may not complete .\r\n");
break; default: //起始字节错误
uUART_PutChar(USART1,XMODEM_NAK); //要求重发当前数据块
gXM_STATUS=dST_BLOCK_FAIL;
break;
}
} if (gXM_STATUS==dST_BLOCK_OK) //接收133字节OK,且起始字节正确
{
if (gXM_BlockCount != strXMODEM.BlockNo) //核对数据块编号正确
{
uUART_PutChar(USART1,XMODEM_NAK); //数据块编号错误,要求重发当前数据块
continue;
}
if (gXM_BlockCount !=(unsigned char)(~strXMODEM.nBlockNo))
{
uUART_PutChar(USART1,XMODEM_NAK); //数据块编号反码错误,要求重发当前数据块
continue;
} CheckSum=strXMODEM.CheckSum;
if (uCheckSum(&strXMODEM.Xdata[],)!=CheckSum)
{
uUART_PutChar(USART1,XMODEM_NAK); //CheckSum错误,要求重发当前数据块
continue;
}
//------------------------------------------------------------------------------------
//正确接收128个字节数据 if ((def_USERAPP_START <= gFlash_User_Address) &&(gFlash_User_Address <= def_USERAPP_BOTTOM))
{ FLASH_Unlock(); //解锁写保护 if((gFlash_128Bytes_Cnt==) && ((gFlash_128Bytes_Cnt %)==))
{
//擦除一整页
FLASH_ErasePage(gFlash_User_Address);//擦除这个扇区
} //写128字节
pBuffer =(u16 *) &strXMODEM.Xdata[]; for(dataIndex=;dataIndex<;dataIndex++)
{
FLASH_ProgramHalfWord(gFlash_User_Address+dataIndex*,pBuffer[dataIndex]);
} FLASH_Lock();//上锁写保护 gFlash_128Bytes_Cnt++;
gFlash_User_Address = def_USERAPP_START + *gFlash_128Bytes_Cnt; }
else
{
uUART_PutChar(USART1,XMODEM_CAN); //程序已满,取消传送
uUART_PutChar(USART1,XMODEM_CAN);
uUART_PutChar(USART1,XMODEM_CAN); gXM_STATUS=dST_OK; printf("--> The Flash Rom is full , Transfer stop \r\n"); break; } //------------------------------------------------------------------------------------ uUART_PutChar(USART1,XMODEM_ACK); //回应已正确收到一个数据块
gXM_BlockCount++; //数据块累计加1
}
} } //主程序
int main(void)
{ //考虑到BootLoader可能由应用程序中跳转过来,所以所用到的模块需要全面初始化
//这个BootLoader没有使用中断 uUSART_Init();
uDelay_SysTick_init();
CLI(); while()
{ printf(" \r\n");
printf("--> ******************************************************* \r\n");
printf("--> Programmer : Cao henglin \r\n");
printf("--> TEL : 15050225228 (wechat) \r\n");
printf("--> Q Q : 88410664 \r\n");
printf("--> E-MAIL : caohenglin@outlook.com \r\n");
printf("--> ******************************************************* \r\n"); uXMODEM_Process(); printf("--> Run ! \r\n"); //下面执行 跳转到USER APP 中执行
//代码我不写了,你们自己补一下,哈哈哈!!!
//对了一定要处理中断向量表啊,别忘记!!!
} }
以上就是实现 XModem的 全部代码了
跳转代码 我没有写,你们自己动动小手自己解决一下吧
这个Xmodem 主体代码,我参照了 AVR专家 马老师的例子,感谢马老师的分享!
----------------------------------
以下我是用的超级终端,WIN7以后电脑自身不带超级终端了,可以把超级终端拷贝过来使用
我就是在WIN10下用的,尽管图标显示有问题,已经很熟悉了,我不介意,哈哈哈
以上要注意,传输的文件只能是.bin 文件的格式 二进制的,keil 如何生成 .bin文件
还要我讲一遍么?
算了 还是说一下吧,谁叫我这么好呢
fromelf.exe --bin --output .\Objects\MDKT3.bin .\Objects\MDKT3.axf
直接看图吧,别告诉我不懂啊
看看 就是这么的酷 非常好,已经下载到 我们指定的用户程序区那边了
用JLINK 把下载后的单片机 程序都读上来,检查一下我们下载的程序,有没有放在0x0800A000这个用户区
检查没有问题
非常好
检查末尾也没有问题
非常成功!
[源创] STM32F103ZET6 基于XMODEM 通讯的 BOOTLOADER案列IAP的更多相关文章
- 【深圳】OSC源创会第44期 开始报名
时间:2016-03-19 14:00 地点: 深圳 南山区海德三道天利中央商务广场B座负一楼(意启创业) 费用:50元/人(现场交),女士.50积分的账号.开源软件作者.学生免费 (用于准备茶歇小食 ...
- AtomicInteger源码分析——基于CAS的乐观锁实现
AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...
- String,StringBuffer和StringBuilder源码解析[基于JDK6]
最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西.其实,JDK的源码是越读越有味的.下面总结一下 ...
- 项目源码--JAVA基于LBS地理位置信息应用的服务端
技术要点: 1. LBS应用框架服务端实现 2. JAVA服务端技术 3. MYSQL数据库技术 4. 源码带详细的中文注释 ...... 详细介绍: 1. LBS应用框架服务端实现 此套源码是基 ...
- ArrayList 源码(基于Java1.8)
ArrayList 源码 ArrayList 基于数组实现,也就是类对变量 Object[]系列操作,封装为常用的add,remove,indexOf, contains本质是通过 size 计数器对 ...
- wp8使用Beetle.NetPackage实现基于TCP通讯的订单查询
在新版本的Beetle.NetPackage中提供了对Protobuf和Controller的支持,所以在WP8下使用Beetle.NetPackage进行基于TCP的数据交互则一件非常简单事情.下面 ...
- 实操重写IK分词器源码,基于mysql热更新词库
实操重写IK分词器源码,基于mysql热更新词库参考网址:https://blog.csdn.net/wuzhiwei549/article/details/80451302 问题一:按照这篇文章的介 ...
- 并发-AtomicInteger源码分析—基于CAS的乐观锁实现
AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...
- 32.修改IK分词器源码来基于mysql热更新词库
主要知识点, 修改IK分词器源码来基于mysql热更新词库 一.IK增加新词的原因 在第32小节中学习到了直接在es的词库中增加词语,来扩充自已的词库,但是这样做有以下缺点: (1)每次添加完 ...
随机推荐
- Java并发编程实战 03互斥锁 解决原子性问题
文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 摘要 在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和 ...
- 软件——Ubuntu16.04设置静态ip地址
1.获取网卡名称 在命令行输入ifconfig -a 2.修改网卡配置文件 sudo vim /etc/network/interfaces 加上下面的配置,IP地址可以成适合你的 auto eth0 ...
- Linova and Kingdom(树型-贪心)
题目大意:给定一棵树,1为首都(首都可以是工业城市也可以是旅游城市),一共有n个点. 其中要选出k个工业城市,每个工业城市出一个代表去首都,其快乐值是其途径旅游城市(非工业)的个数 求所有快乐值相加的 ...
- openCV从入门到放弃
与图像处理之间的关系,opencv的简介和使用定位 如题...因为偶然的机会需要用到图像处理,像我这么爱学习 并且动手能力又强的人怎么能没有心得笔记呢,哇哈哈哈.非要说的low逼点这玩意儿这玩意儿就是 ...
- Mybatis使用ResultMap
解决字段名和属性名不一致的问题 - 新建数据库表的字段-这里就不贴上了 在下面链接有 https://www.cnblogs.com/rzkwz/p/12853899.html 设置实体类和数据库字段 ...
- 【Hadoop离线基础总结】oozie调度shell脚本
目录 1.解压官方提供的调度案例 2.创建工作目录 3.拷贝任务模板到工作目录当中去 4.随意准备一个shell脚本 5.修改模板下的配置文件 6.上传调度任务到hdfs上面去 7.执行调度任务 1. ...
- React Native 架构一览
一.架构设计 整体上分为三大块,Native.JavaScript 与 Bridge: Native 管理 UI 更新及交互,JavaScript 调用 Native 能力实现业务功能,Bridge ...
- 【蓝桥杯C组】备赛基础篇之高精度算法
一.高精度加法 思路: 运用vector数组(c选手可用len来记录数组长度,数组去保存数字)将存入字符串里面的数字符倒叙保存,按照小学的加法列式,相加保存进位即可.具体参考代码. 详细代码解析: # ...
- 封装组件el-upload通过v-model (一): 上传单张图片
ElementUI 中的el-upload 上传图片 我进行了二次封装.(默认大家都是有一定的vue基础的,细节就不过多的讲了) 在项目中我们主要拿到图片或者其他的一些参数 ,我这里是上传后返回的Gu ...
- 经典sql语句大全,【转载】
经典SQL查询语句大全 一.基础1.说明:创建数据库CREATE DATABASE database-name2.说明:删除数据库drop database dbname3.说明:备份sql se ...