[源创] 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)每次添加完 ...
随机推荐
- 威联通(NAS)应用篇:自建OwnCloud网盘(百度网盘,拜拜~~~)
基础环境: 威联通一台 已安装好 ContainerStation 公网 IP(非必须) 自有公网域名 下载镜像文件 提醒:建议先把威联通的自带镜像源改为国内的阿里云镜像源,教程:https://ww ...
- Thinkphp 缓存RCE
5.0.0<=ThinkPHP5<=5.0.10 . 漏洞利用条件: 1.基于tp5开发的代码中使用了Cache::set 进行缓存 2.在利用版本范围内 3.runtime目录可以 ...
- pytest文档38-allure.setp添加测试用例步骤
前言 一般流程性的测试用例,写成自动化用例时,步骤较多写起来会比较长.在测试用例里面添加详细的步骤有助于更好的阅读,也方便报错后快速的定位到问题. 举个常见的测试场景用例:从登陆开始,到浏览商品添加购 ...
- 大富翁 线段树+二分 +dfs
https://csustacm.fun/problem/2033 这个题目还是比较简单的,但是比赛的时候没有像清楚,用了一个不太熟悉的数据结构主席树, 所以出现了bug,主席树的bug是真的难找. ...
- 最终父类【根类】:Object类&Objects类
一.java.lang.Object类 1.Object类介绍 Object类是所有类的父类.一个类都会直接或间接继承自该类: 该类中提供了一些非常常用的方法! 2.toString()方法 A: ...
- 一次内核 crash 的排查记录
一次内核 crash 的排查记录 使用的发行版本是 CentOS,内核版本是 3.10.0,在正常运行的情况下内核发生了崩溃,还好有 vmcore 生成. 准备排查环境 crash 内核调试信息rpm ...
- Coursera课程笔记----P4E.Capstone----Week 2&3
Building a Search Engine(week 2&3) Search Engine Architecture Web Crawling Index Building Search ...
- QtCreator中使用链接库
说明 之前讨论的DLL的静态链接和动态连接都是基于 MSVC 编译器,但是 MinGW 似乎有另外一套类似但是不相同的机制.下文均在 windows 下使用 Qt Creator 中使用 MinGW ...
- 新书《OpenShift云原生架构:原理与实践》第一章第三节:企业级PaaS平台OpenShift
近十年来,信息技术领域在经历一场技术大变革,这场变革正将我们由传统IT架构及其所支撑的臃肿应用系统时代,迁移至云原生架构及其所支撑的敏捷应用系统时代.在这场变革中,新技术的出现.更新和淘汰之迅速,以及 ...
- imos-累积和法
在解AOJ 0531 Paint Color时,学到了一个累积和的妙用--imos法,由于原文是日语,所以特意翻译过来.值得一提的是,作者Kentaro Imajo跟鄙人同龄,却已取得如此多的成就,而 ...