网上好多初学者 都想知道如何更好的用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这个用户区
检查没有问题
非常好
检查末尾也没有问题
非常成功!