STM32基于BootLoader进行网络远程更新程序

       以前在学习STM32的时候,看到有的开发板用BootLoader下载程序,觉得脱离下载器程序下载进去挺有意思的,于是就自己琢磨着也做一个,采用RL-TCPnet实现网络通信,还涉及到RTX嵌入式操作系统(不用也行的,没多大必要,因为例程自带RTX所以我用了,注意用RTX前要用注册机添加RTX的注册码)。

一、BootLoader的作用

      BootLoader也是一段程序,简单来说,就是运行应用程序之前,先运行BootLoader做一些必要的处理,再跳到应用程序去执行。对于咱今天要做的事情来说,就是通过局域网把程序下载到芯片里,实现远程升级。因为用下载器下载程序同样也是擦除Flash后把程序文件烧写进Flash,而STM32支持程序运行的时候擦写其他地方的Flash区域,所以可以实现下载功能。

 

二、Flash空间划分

     我用的芯片是STM32F4系列的,有1M的Flash大小,准备划分为以下几个区域。

(1)BootLoader区(0x8000000~0x8020000):128KB,放置BootLoader代码。

(2)Download区(0x8020000~0x8080000):384KB,放置下载的文件,因为下载过程中可能中途断电或者出错,如果直接把下载过程中的数据覆盖到APP区会导致应用程序遭到损坏,Download区起到暂存文件的作用。

(3)APP区(0x8080000~0x80E0000):384KB,放置要运行的应用程序,就是真正想跑的代码。

(4)Reserve区(0x80E0000~0x8100000):128KB,预留一段空间供用户保存信息,可以当做EEPROM用,掉电数据不丢失。

区间的划分可以根据实际使用情况划分。

 

三、程序怎样从BootLoader跳转到APP

     说再多不如直接上代码,把APP区的首地址传入就可以实现跳转了:

void(*Boot_Jump2App)();//传入要跳转到的程序地址进行跳转
void Boot_LoadApp(DWORD dwAddr)
{BYTE i;//检查栈顶地址是否合法if (((*(vu32*)dwAddr) & 0x2FFE0000) == 0x20000000)	{//设置跳转地址Boot_Jump2App = (void(*)())*(vu32*)(dwAddr + 4);		//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)__set_MSP(*(vu32*)dwAddr);//关闭所有中断for (i = 0; i < 8; i++){NVIC->ICER[i] = 0xFFFFFFFF;	NVIC->ICPR[i] = 0xFFFFFFFF;	}//跳转到APP CodeBoot_Jump2App();		//跳转之前用死循环卡住while (1);}
}

不知道大家会不会这样想,如果我又想从APP段跳回BootLoader,不想以断电的方式重新运行BootLoader下载程序,怎么做?我的想法是调用STM32的软件复位函数NVIC_SystemReset()。

__set_FAULTMASK(1);
NVIC_SystemReset();

为什么前面加了一句__set_FAULTMASK(1),作用是关闭所有中断,这里有一个要注意的问题:如果只调用NVIC_SystemReset(),从SYSRESETREQ 被置为有效,到复位发生器执行复位命令,往往会有一个延时。在此延时期间,处理器仍然可以响应中断请求。但我们的本意往往是要程序执行到此为止,不要再做任何其它事情了。所以,最好在发出复位请求前,先把FAULTMASK 置位才万无一失。

 

四、BootLoader程序和APP程序的配置

    BootLoader程序有一点要注意,要对应BootLoader区地址进行如下设置。

    APP程序有三点要注意,(1)也要设置对应的开始地址和大小 

(2)APP程序还要在程序一开始设置中断向量的偏移,就是程序一开始调用这样一条语句,0x80000是APP程序的起始偏移,根据实际情况填写。

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x80000);

(3)要下载的文件不是Keil编译生成的Hex文件,而是bin文件,怎么生成呢?照着下面依葫芦画瓢就可以了。

就是加上这一句 E:/keil/ARM/ARMCC/bin/fromelf.exe  --bin -o Flash/MC-IOV3248.bin Flash/Obj/output.axf

实际路径根据自己的情况修改。

 

四、程序实现流程 

     程序实现的大致流程如下图:

     BootLoader初始化IP地址为192.168.1.41,根据需要设置了两个端口可以分别独立进行通信。第一个端口为5198,为程序下载端口;第二个端口为5199,为Debug端口,用来输出调试信息和Boot Debug模式下使用,当然这个端口也可以用STM32的串口来代替输出调试信息,因为接线繁琐,所以用了网络端口调试。

    程序一开始创建4个任务如下:

int main (void) 
{bsp_Init();/* 创建启动任务 */os_sys_init_user (AppTaskStart,              /* 任务函数 */4,                         /* 任务优先级 */&AppTaskStartStk,          /* 任务栈 */sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */while(1);
}//启动任务,也是最高优先级任务,	创建任务 ,时间基准更新。
__task void AppTaskStart(void)
{//这里实现RL-TCPnet的时间基准更新。HandleTaskTCPMain = os_tsk_create_user(RL_TCPnet_timer_tick,4,&RL_TCPnet_timer_tick_stk,sizeof(RL_TCPnet_timer_tick_stk));//TCP数据收发处理任务HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,3,&AppTaskTCPMainStk,sizeof(AppTaskTCPMainStk));//用户任务HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,2,&AppTaskUserIFStk,sizeof(AppTaskUserIFStk));//LED闪烁任务HandleTaskTCPMain = os_tsk_create_user(led_bling_task,1,&led_bling_taskStk,sizeof(led_bling_taskStk));while(1){os_dly_wait(1000);}
}

我们只关心TCP数据收发任务AppTaskTCPMain和用户任务AppTaskUserIF。

介绍这两个任务之前,先介绍一下程序里涉及到的几个结构体,首先是tyDownloadHead,就是下载bin文件之前要先发送含有特定格式的下载头,验证成功后才能进行程序下载,否则随便乱发了一些数据过来也当做文件数据的话就太不可靠了。

#define BYTE unsigned char
#define WORD unsigned short
#define DWORD unsigned int#define DOWNLOAD_HEAD_FLAG			0x55591012typedef struct
{DWORD dwStructFLag;						//识别头 DWORD dwStructFLagInverse;              //识别头反码BYTE  bFileType;						//文件类型,0:APP程序,1:BootLoader程序BYTE  bReserve[3];						//预留,四字节对齐DWORD dwFileLen;						//下载文件大小DWORD dwFileCRC;						//文件数据校验DWORD dwStructCRC;						//结构体校验
}tyDownloadHead;

 还有就是tyBoot结构体,就是控制流程运行的一些变量。

#define BOOT_STATE_DISCONNECT  	        0  //未连接
#define BOOT_STATE_IDL				    1  //空闲
#define BOOT_STATE_DEBUG			    2  //Debug模式
#define BOOT_STATE_DOWNLOADING	        3  //下载模式typedef struct
{BYTE bBootState;          //连接状态BYTE bIsNeedSelCmd;BYTE bSelCmdNum;DWORD dwDownloadLen;DWORD dwRecFileLen;DWORD dwFileCRC;
}tyBoot;

最后一个是socketNature_t,是对TCP收发数据的控制。

typedef struct{U8 *recvBuf;  // 接收数据缓存U8 *sendBuf;  // 发送数据缓存U16 port;     // 套接字端口U8 id;        // 套接字idU8 recvFlag;  // 接收到数据的标志:0空闲,1有数据,2正在处理U8 recvLen;   // 接收到数据的长度U8 sendFlag;  // 待发送标志U8 sendLen;   // 要发送的长度
} socketNature_t; // 套接字描述结构

TCP数据收发任务只调用了下面这个函数,完成下载端口和Debug端口的创建,随后不断的侦测是否有数据需要收发,至于RL-TCPnet的具体使用设置网上资料很多,就不解释了。

void TCPnetTest(void)
{static U8 socket_pc_recvBuf[256];static U8 socket_pc_sendBuf[256];static U8 socket_debug_recvBuf[256];static U8 socket_debug_sendBuf[256];//创建TCP Socket并创建监听socket_pc.port = 5198;socket_pc.recvBuf = socket_pc_recvBuf;socket_pc.sendBuf = socket_pc_sendBuf;socket_pc.id = tcp_get_socket(TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, socket_callback);if(socket_pc.id!=0){tcp_listen(socket_pc.id,socket_pc.port);}//创建TCP Socket并创建监听socket_debug.port = 5199;socket_debug.recvBuf = socket_debug_recvBuf;socket_debug.sendBuf = socket_debug_sendBuf;socket_debug.id = tcp_get_socket(TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, socket_callback);if(socket_debug.id != 0){tcp_listen(socket_debug.id,socket_debug.port);}while (1) {main_TcpNet();/* RL-TCPnet处理函数 */send_poll(&socket_pc);send_poll(&socket_debug);os_dly_wait (2);}
}

同样这一堆代码我们只关心socket_callback这个回调函数和send_poll这个发送数据函数。

(1)socket_callback函数如下

U16 socket_callback (U8 soc, U8 evt, U8 *ptr, U16 par)
{switch (evt) {//远程客户端连接消息//1、数组ptr存储远程设备的IP地址,par中存储端口号。//2、返回数值1允许连接,返回数值0禁止连接。case TCP_EVT_CONREQ:return (1);case TCP_EVT_ABORT:break;/* 连接终止 */case TCP_EVT_CONNECT:if (soc == socket_pc.id){tBoot.bBootState = BOOT_STATE_IDL;}if (soc == socket_debug.id){tBoot.bBootState = BOOT_STATE_DEBUG;}break;/* Socket远程连接已经建立 */case TCP_EVT_CLOSE:break;/* 连接断开 */case TCP_EVT_ACK:break;/* 发送的数据收到远程设备应答 */case TCP_EVT_DATA:/* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */if (soc == socket_debug.id){socket_debug.recvLen = par;memcpy(socket_debug.recvBuf, ptr, socket_debug.recvLen);socket_debug.recvFlag = 1;socket_debug.sendLen = par;memcpy(socket_debug.sendBuf, ptr, socket_debug.sendLen);socket_debug.sendFlag = 1;}else if (soc == socket_pc.id){switch (tBoot.bBootState){case BOOT_STATE_IDL:if (DOWNLOAD_HEAD_FLAG == ((tyDownloadHead *)ptr)->dwStructFLag){if (sizeof(tyDownloadHead) == par &&~DOWNLOAD_HEAD_FLAG == ((tyDownloadHead *)ptr)->dwStructFLagInverse &&CalculateCRC((DWORD *)ptr, sizeof(tyDownloadHead) - 4, 0) == ((tyDownloadHead *)ptr)->dwStructCRC){tBoot.dwRecFileLen = ((tyDownloadHead *)ptr)->dwFileLen;tBoot.dwDownloadLen = 0;tBoot.dwFileCRC = ((tyDownloadHead *)ptr)->dwFileCRC;tBoot.bBootState = BOOT_STATE_DOWNLOADING;}}break;case BOOT_STATE_DOWNLOADING:socket_pc.recvLen = par;memcpy(socket_pc.recvBuf, ptr, socket_pc.recvLen);socket_pc.recvFlag = 1;socket_pc.sendLen = socket_pc.recvLen;memcpy(socket_pc.sendBuf, ptr, socket_pc.sendLen);socket_pc.sendFlag = 1;STMFLASH_Write(DOWNLOAD_ADDR + tBoot.dwDownloadLen, (DWORD *)socket_pc.recvBuf, socket_pc.recvLen / 4);tBoot.dwDownloadLen += socket_pc.recvLen;if (tBoot.dwDownloadLen >= tBoot.dwRecFileLen){tBoot.bBootState = BOOT_STATE_IDL;}break;default:break;}}break;}return (0);
}

 这段代码完成对数据的接收,同时对下载头进行校验,校验成功后转到下载模式接收文件。

(2)send_poll函数

void send_poll(socketNature_t *socket){switch (tcp_get_state(socket->id))/* 用于网线插拔的处理 */{case TCP_STATE_FREE:case TCP_STATE_CLOSED:tcp_listen (socket->id,socket->port);//网络断开边接后,开始监听return;case TCP_STATE_LISTEN://处于监听时不发送default:socket->sendFlag=0;return;case TCP_STATE_CONNECT://处于连接状态break;}if(socket->sendFlag){U8 *sendbuf;S32 iCount;U16 maxlen;socket->sendFlag=0;iCount = socket->sendLen;do{main_TcpNet();if (tcp_check_send(socket->id) == __TRUE){maxlen = tcp_max_dsize (socket->id);iCount -= maxlen;if(iCount < 0){maxlen = iCount + maxlen;/* 这么计算没问题的 */}sendbuf = tcp_get_buf(maxlen);memcpy(sendbuf,socket->sendBuf,maxlen);tcp_send (socket->id, sendbuf, maxlen);/* 测试发现只能使用获取的内存 */}}while(iCount > 0);}
}

纯粹就是发送TCP数据而已,当哪个port的sendBuf里有数据,就发出去。

下面贴出所有文件的实现代码:

(1)main.c

#include "includes.h"
#include "includeAll.H"
#include "bsp.h"
#include "bootLoader.h"__task void AppTaskStart(void);
__task void RL_TCPnet_timer_tick(void);
__task void AppTaskTCPMain(void);
__task void AppTaskUserIF(void);
__task void led_bling_task(void);/* 任务栈 */
static uint64_t AppTaskStartStk[1024/8];
static uint64_t RL_TCPnet_timer_tick_stk[1028/8];
static uint64_t AppTaskTCPMainStk[2048/8];
static uint64_t AppTaskUserIFStk[2028/8];
static uint64_t led_bling_taskStk[2028/8];/* 任务句柄 */
OS_TID HandleTaskUserIF = NULL;
OS_TID HandleTaskTCPMain = NULL;//IP地址等设置
LOCALM ip_config = {{ 192, 168, 1, 41 },             // IP address{ 192, 168, 1, 1 },               // Default Gateway{ 255, 255, 255, 0 },             // Net mask{ 194, 25, 2, 129 },              // Primary DNS server{ 194, 25, 2, 130 }               // Secondary DNS server
};int main (void) 
{、、NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x80000);bsp_Init();/* 创建启动任务 */os_sys_init_user (AppTaskStart,              /* 任务函数 */4,                         /* 任务优先级 */&AppTaskStartStk,          /* 任务栈 */sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */while(1);
}//启动任务,也是最高优先级任务,	创建任务 ,时间基准更新。
__task void AppTaskStart(void)
{//这里实现RL-TCPnet的时间基准更新。HandleTaskTCPMain = os_tsk_create_user(RL_TCPnet_timer_tick,4,&RL_TCPnet_timer_tick_stk,sizeof(RL_TCPnet_timer_tick_stk));//TCP数据收发处理任务HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,3,&AppTaskTCPMainStk,sizeof(AppTaskTCPMainStk));//用户任务HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,2,&AppTaskUserIFStk,sizeof(AppTaskUserIFStk));//LED闪烁任务HandleTaskTCPMain = os_tsk_create_user(led_bling_task,1,&led_bling_taskStk,sizeof(led_bling_taskStk));while(1){os_dly_wait(1000);}
}//这里实现RL-TCPnet的时间基准更新。
__task void RL_TCPnet_timer_tick(void)
{U32 CpuID[3];U8 mac_adr[6] = { 0, 1, 2, 50, 60, 70 };extern U8 own_hw_adr[];extern U8 lhost_name[];extern LOCALM localm[];//根据CPU唯一ID设置MAC地址//CpuID[0]=*(vu32*)(0x1ffff7e8);//获取CPU唯一ID(F1的)//CpuID[1]=*(vu32*)(0x1ffff7ec);//CpuID[2]=*(vu32*)(0x1ffff7f0);CpuID[0]=*(vu32*)(0x1FFF7A10);//获取CPU唯一ID(F4的)CpuID[1]=*(vu32*)(0x1FFF7A10 + 4);CpuID[2]=*(vu32*)(0x1FFF7A10 + 8);mac_adr[0] = (u8)((CpuID[1] & 0x00FF0000)>>16);mac_adr[1] = (u8)((CpuID[1] & 0xFF000000)>>24);mac_adr[2] = (u8)( CpuID[2] & 0x000000FF);mac_adr[3] = (u8)((CpuID[2] & 0x0000FF00)>>8);mac_adr[4] = (u8)((CpuID[2] & 0x00FF0000)>>16);mac_adr[5] = (u8)((CpuID[2] & 0xFF000000)>>24);//设置网卡物理地址mem_copy (own_hw_adr, (U8 *)mac_adr, 6);init_TcpNet ();/* 初始化RL-TCPnet *///设置主机的名字str_copy (lhost_name,"BootLoader");//设置ip地址等dhcp_disable();mem_copy((U8 *)&localm[NETIF_ETH], (U8 *)&ip_config, sizeof(ip_config));os_itv_set (100);while(1){timer_tick ();/* RL-TCPnet时间基准更新函数 */os_itv_wait ();}
}//TCP数据收发处理任务
__task void AppTaskTCPMain(void)
{while (1){TCPnetTest();}
}//用户任务
__task void AppTaskUserIF(void)
{BYTE overtimeCnt = 0;Boot_Init();while(1){if (BOOT_STATE_DOWNLOADING == tBoot.bBootState){Boot_Download();tBoot.bBootState = BOOT_STATE_IDL;overtimeCnt = 0;}else if(BOOT_STATE_DEBUG == tBoot.bBootState){Boot_Debug();overtimeCnt = 0;}overtimeCnt++;if(overtimeCnt > 50 && BOOT_STATE_DISCONNECT == tBoot.bBootState){Boot_LoadApp(APP_ADDR);}os_dly_wait(100);}
}//LED指示灯闪烁
__task void led_bling_task(void)
{U8 flag = 0;while(1){BOARD_LED = flag;flag = flag ? 0 : 1;os_dly_wait(50);}
}

(2)app_tcpnet_lib.c

#include "app_tcpnet_lib.h"
#include "includes.h"	
#include "bootLoader.h"socketNature_t socket_pc;   // 与pc端通讯的套接字描述
socketNature_t socket_debug;  // 与plc通讯的套接字描述/********************************************************************* 描述:套接字发送数据函数。* 参数:[*socket]套接字描述*     [len]要发送的数据长度********************************************************************/
void socket_send(socketNature_t *socket,U8 len){socket->sendLen=len;socket->sendFlag=1;
}void send_poll(socketNature_t *socket);
/*
*********************************************************************************************************
*	函 数 名: tcp_callback
*	功能说明: TCP Socket的回调函数
*	形    参: soc  TCP Socket类型
*             evt  事件类型
*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址
*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号
*	返 回 值:
*********************************************************************************************************
*/
//			if (soc == socket_debug.id)
//			{
//					char buf[50];
//					sprintf(buf, "This is BootLoader Code\r\n");
//				
//					socket_debug.sendLen = strlen(buf);
//					memcpy(socket_debug.sendBuf, buf, socket_debug.sendLen);
//					socket_debug.sendFlag = 1;
//			}U16 socket_callback (U8 soc, U8 evt, U8 *ptr, U16 par)
{switch (evt) {//远程客户端连接消息//1、数组ptr存储远程设备的IP地址,par中存储端口号。//2、返回数值1允许连接,返回数值0禁止连接。case TCP_EVT_CONREQ:return (1);case TCP_EVT_ABORT:break;/* 连接终止 */case TCP_EVT_CONNECT:if (soc == socket_pc.id){tBoot.bBootState = BOOT_STATE_IDL;}if (soc == socket_debug.id){tBoot.bBootState = BOOT_STATE_DEBUG;}break;/* Socket远程连接已经建立 */case TCP_EVT_CLOSE:break;/* 连接断开 */case TCP_EVT_ACK:break;/* 发送的数据收到远程设备应答 */case TCP_EVT_DATA:/* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */if (soc == socket_debug.id){socket_debug.recvLen = par;memcpy(socket_debug.recvBuf, ptr, socket_debug.recvLen);socket_debug.recvFlag = 1;socket_debug.sendLen = par;memcpy(socket_debug.sendBuf, ptr, socket_debug.sendLen);socket_debug.sendFlag = 1;}else if (soc == socket_pc.id){switch (tBoot.bBootState){case BOOT_STATE_IDL:if (DOWNLOAD_HEAD_FLAG == ((tyDownloadHead *)ptr)->dwStructFLag){if (sizeof(tyDownloadHead) == par &&~DOWNLOAD_HEAD_FLAG == ((tyDownloadHead *)ptr)->dwStructFLagInverse &&CalculateCRC((DWORD *)ptr, sizeof(tyDownloadHead) - 4, 0) == ((tyDownloadHead *)ptr)->dwStructCRC){tBoot.dwRecFileLen = ((tyDownloadHead *)ptr)->dwFileLen;tBoot.dwDownloadLen = 0;tBoot.dwFileCRC = ((tyDownloadHead *)ptr)->dwFileCRC;tBoot.bBootState = BOOT_STATE_DOWNLOADING;}}break;case BOOT_STATE_DOWNLOADING:socket_pc.recvLen = par;memcpy(socket_pc.recvBuf, ptr, socket_pc.recvLen);socket_pc.recvFlag = 1;socket_pc.sendLen = socket_pc.recvLen;memcpy(socket_pc.sendBuf, ptr, socket_pc.sendLen);socket_pc.sendFlag = 1;STMFLASH_Write(DOWNLOAD_ADDR + tBoot.dwDownloadLen, (DWORD *)socket_pc.recvBuf, socket_pc.recvLen / 4);tBoot.dwDownloadLen += socket_pc.recvLen;if (tBoot.dwDownloadLen >= tBoot.dwRecFileLen){tBoot.bBootState = BOOT_STATE_IDL;}break;default:break;}}break;}return (0);
}void TCPnetTest(void)
{static U8 socket_pc_recvBuf[256];static U8 socket_pc_sendBuf[256];static U8 socket_debug_recvBuf[256];static U8 socket_debug_sendBuf[256];//创建TCP Socket并创建监听socket_pc.port = 5198;socket_pc.recvBuf = socket_pc_recvBuf;socket_pc.sendBuf = socket_pc_sendBuf;socket_pc.id = tcp_get_socket(TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, socket_callback);if(socket_pc.id!=0){tcp_listen(socket_pc.id,socket_pc.port);}//创建TCP Socket并创建监听socket_debug.port = 5199;socket_debug.recvBuf = socket_debug_recvBuf;socket_debug.sendBuf = socket_debug_sendBuf;socket_debug.id = tcp_get_socket(TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, socket_callback);if(socket_debug.id != 0){tcp_listen(socket_debug.id,socket_debug.port);}while (1) {main_TcpNet();/* RL-TCPnet处理函数 */send_poll(&socket_pc);send_poll(&socket_debug);os_dly_wait (2);}
}void send_poll(socketNature_t *socket){switch (tcp_get_state(socket->id))/* 用于网线插拔的处理 */{case TCP_STATE_FREE:case TCP_STATE_CLOSED:tcp_listen (socket->id,socket->port);//网络断开边接后,开始监听return;case TCP_STATE_LISTEN://处于监听时不发送default:socket->sendFlag=0;return;case TCP_STATE_CONNECT://处于连接状态break;}if(socket->sendFlag){U8 *sendbuf;S32 iCount;U16 maxlen;socket->sendFlag=0;iCount = socket->sendLen;do{main_TcpNet();if (tcp_check_send(socket->id) == __TRUE){maxlen = tcp_max_dsize (socket->id);iCount -= maxlen;if(iCount < 0){maxlen = iCount + maxlen;/* 这么计算没问题的 */}sendbuf = tcp_get_buf(maxlen);memcpy(sendbuf,socket->sendBuf,maxlen);tcp_send (socket->id, sendbuf, maxlen);/* 测试发现只能使用获取的内存 */}}while(iCount > 0);}
}

(3)bootloader.h

#ifndef BOOT_LOADER_H_
#define BOOT_LOADER_H_#include "includes.h"
#include "includeAll.H"
#include "bsp.h"#define BOOT_ADDR 			0x8000000  			//bootLoader 128K 
#define DOWNLOAD_ADDR 	0x8020000  			//固件		 384K
#define APP_ADDR 				0x8080000				//应用程序区 384K
#define RESERVED_ADDR		0x80E0000				//预留空间	 128K#define TRUE 1
#define FALSE 0#define FUN_SUC 0
#define FUN_FAIL 1#define BYTE unsigned char
#define WORD unsigned short
#define DWORD unsigned int#define DOWNLOAD_HEAD_FLAG			0x55591012
#define DEBUG_HEAD_FLAG				0x55591013#define BOOT_STATE_DISCONNECT  	0
#define BOOT_STATE_IDL					1
#define BOOT_STATE_DEBUG				2
#define BOOT_STATE_DOWNLOADING	3typedef struct
{DWORD dwStructFLag;				//识别头 DWORD dwStructFLagInverse;		//识别头取反DWORD dwStructCRC;				//结构体校验
}tyDebugHead;typedef struct
{DWORD dwStructFLag;								//识别头 DWORD dwStructFLagInverse;				//识别头反码BYTE  bFileType;									//文件类型,0:APP程序,1:BootLoader程序BYTE  bReserve[3];								//预留,四字节对齐DWORD dwFileLen;									//下载文件大小DWORD dwFileCRC;									//文件数据校验DWORD dwStructCRC;								//结构体校验
}tyDownloadHead;typedef struct
{BYTE bBootState;  //连接状态 BYTE bIsNeedSelCmd;BYTE bSelCmdNum;DWORD dwDownloadLen;DWORD dwRecFileLen;DWORD dwFileCRC;
}tyBoot;extern tyBoot tBoot;void Boot_Init(void);  //初始化 
extern BYTE Boot_Debug(void);  //进入Debug模式
extern BYTE Boot_Download(void);  //进入下载模式
extern void Boot_LoadApp(DWORD dwAddr);  //跳转到APP
extern DWORD CalculateCRC(DWORD *pdwDataBuff, DWORD dwByteLen, DWORD dwOrigXorValue);  //计算CRC#endif

(4)bootloader.c

#include "bootLoader.h"tyBoot tBoot;void Boot_Init(void)
{tBoot.bIsNeedSelCmd = FALSE;tBoot.bSelCmdNum = 0xFF;tBoot.dwDownloadLen = 0;tBoot.dwRecFileLen = 0;tBoot.bBootState = BOOT_STATE_DISCONNECT;
}void Debug_Print(char *str)
{strcpy(socket_debug.sendBuf, str);socket_debug.sendLen = strlen(socket_debug.sendBuf);socket_debug.sendFlag = 1;while (1 == socket_debug.sendFlag){os_dly_wait(2);}
}BYTE Boot_Download(void)
{char bBuf[50] = { 0 };char overtimeCnt = 0;Debug_Print("\r\nDownload Start ...... ");while (tBoot.dwDownloadLen <  tBoot.dwRecFileLen){os_dly_wait(200);sprintf(bBuf, "\r\nDownloading Download/Total %d/%d ", tBoot.dwDownloadLen, tBoot.dwRecFileLen);Debug_Print(bBuf);overtimeCnt++;if(overtimeCnt > 50)  //下载超时{Debug_Print("\r\nDownload Overtime");return FUN_FAIL;}}Debug_Print("\r\nDownload Success, Checking File CRC ...... ");if(CalculateCRC((DWORD *)DOWNLOAD_ADDR, tBoot.dwRecFileLen, 0) != tBoot.dwFileCRC){Debug_Print("\r\nFile CRC Error");return FUN_FAIL;}Debug_Print("\r\nFile CRC Pass");sprintf(bBuf, "\r\nPrograming to App Area 0x%8X ...... ", APP_ADDR);Debug_Print(bBuf);//从Download区搬移程序到App区STMFLASH_Write(APP_ADDR, (u32 *)DOWNLOAD_ADDR, (APP_ADDR - DOWNLOAD_ADDR) / 4);Debug_Print("\r\nProgram Success");//break;Debug_Print("\r\nJump to APP Code ");Boot_LoadApp(APP_ADDR);return FUN_SUC;
}BYTE Boot_Debug(void)
{char bBuf[50] = { 0 };char i;//打印地址列表Debug_Print("\r\n******* BootLoader *******""\r\nBOOT_ADDR    : 0x8000000 - 0x8020000""\r\nDOWNLOAD_ADDR: 0x8020000 - 0x8080000""\r\nAPP_ADDR     : 0x8080000 - 0x80E0000""\r\nRESERVED_ADDR: 0x80E0000 - 0x8100000");//等待激活BootLoader命令
//	for (i = 3; i > 0; i--)
//	{
//		sprintf(bBuf, "\r\nPress any key to stop: %d", i);
//		Debug_Print(bBuf);
//		os_dly_wait(1000);//		if (socket_debug.recvFlag)
//		{
//			socket_debug.recvFlag = 0;
//			tBoot.bIsNeedSelCmd = TRUE;
//			break;
//		}
//	}tBoot.bIsNeedSelCmd = TRUE;//跳转到App执行或者选择BootLoader命令if (TRUE != tBoot.bIsNeedSelCmd){Debug_Print("\r\nJump to APP Code ");Boot_LoadApp(APP_ADDR);}else{Debug_Print("\r\n1 -> Download App\r\n2 -> Run App");//等待用户输入有效的序号while ((1 != tBoot.bSelCmdNum) && (2 != tBoot.bSelCmdNum)){os_dly_wait(100);if (socket_debug.recvFlag){socket_debug.recvFlag = 0;tBoot.bSelCmdNum = *socket_debug.recvBuf;}}//根据选择的序号处理命令switch (tBoot.bSelCmdNum){case 1:tBoot.bBootState = BOOT_STATE_IDL;break;case 2:Debug_Print("\r\nJump to APP Code ");Boot_LoadApp(APP_ADDR);break;default:break;}}return FUN_SUC;
}void(*Boot_Jump2App)();void Boot_LoadApp(DWORD dwAddr)
{BYTE i;//检查栈顶地址是否合法if (((*(vu32*)dwAddr) & 0x2FFE0000) == 0x20000000)	{//设置跳转地址Boot_Jump2App = (void(*)())*(vu32*)(dwAddr + 4);		//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)__set_MSP(*(vu32*)dwAddr);//关闭所有中断for (i = 0; i < 8; i++){NVIC->ICER[i] = 0xFFFFFFFF;	NVIC->ICPR[i] = 0xFFFFFFFF;	}//跳转到APP CodeBoot_Jump2App();		//跳转之前用死循环卡住while (1);}
}DWORD CalculateCRC(DWORD *pdwDataBuff, DWORD dwByteLen, DWORD dwOrigXorValue)
{DWORD i;DWORD dwXorResult;dwXorResult = dwOrigXorValue;for (i = 0; i < (dwByteLen / sizeof(DWORD)); i++){dwXorResult ^= pdwDataBuff[i];}return dwXorResult;
}

五、客户端实现

    江湖有句传言“人生苦短,我用Python”,基于Python的高效率开发,所以我用了Python来实现TCP客户端,废话少说,直接上代码:

from socket import *
import os
import ctypesclass DownloadTool():def __init__(self):passdef Connect(self):self.tcpHost = 'localhost'self.tcpPort = 5198self.tcpRecBufSize = 2048self.tcpAddr = ("192.168.1.41", self.tcpPort)self.tcpSock = socket(AF_INET, SOCK_STREAM)self.tcpSock.connect(self.tcpAddr)def CalculateFileSizeAndCRC(self, filePath):file = open(filePath, "rb")size = os.path.getsize(filePath)print("Bin Size = %d" % (size))buf = file.read(4)crc = 0while len(buf) > 0:crc = crc ^ (int.from_bytes(buf, byteorder='little', signed=False))buf = file.read(4)file.close()return size, crcdef DownloadFile(self):#传输下载头fileLen, fileCRC = self.CalculateFileSizeAndCRC("MC-IOV3248.bin")headInfo = []headInfo.append(ctypes.c_uint32(0x55591012))headInfo.append(ctypes.c_uint32(~0x55591012))headInfo.append(ctypes.c_uint32(0x00000000))headInfo.append(ctypes.c_uint32(fileLen))headInfo.append(ctypes.c_uint32(fileCRC))headInfo.append(ctypes.c_uint32(headInfo[0].value ^ headInfo[1].value ^ headInfo[2].value ^ headInfo[3].value ^ headInfo[4].value))DownloadHead = bytes()for item in headInfo:DownloadHead = DownloadHead + bytes(item)self.tcpSock.send(DownloadHead)//传输bin文件file = open("MC-IOV3248.bin", "rb")downloadLen = 0while True:buf = file.read(120)if len(buf) <= 0:break;self.tcpSock.send(buf)rec = self.tcpSock.recv(self.tcpRecBufSize)if buf != rec:print("check err")breakdownloadLen = downloadLen + len(buf)print("download/Total %d/%d" % (downloadLen, fileLen))self.tcpSock.close()if __name__ == '__main__':demo = DownloadTool()demo.Connect()demo.DownloadFile()

这样就完成了基本的BootLoader下载功能了。

 

六、实验演示

    用网络调试助手连接Debug端口,选择01号下载命令后启动Python程序对下载端口进行程序下载。

 网络调试助手输出信息如下:


[2019-10-27 21:04:44.420]# RECV ASCII>******* BootLoader *******
BOOT_ADDR    : 0x8000000 - 0x8020000
DOWNLOAD_ADDR: 0x8020000 - 0x8080000
APP_ADDR     : 0x8080000 - 0x80E0000
RESERVED_ADDR: 0x80E0000 - 0x8100000[2019-10-27 21:04:44.422]# RECV ASCII>1 -> Download App
2 -> Run App[2019-10-27 21:04:46.869]# SEND HEX>
01 [2019-10-27 21:04:46.872]# RECV ASCII>[2019-10-27 21:04:51.095]# RECV ASCII>Download Start ...... [2019-10-27 21:04:51.296]# RECV ASCII>Downloading Download/Total 5280/29532 [2019-10-27 21:04:51.500]# RECV ASCII>Downloading Download/Total 9480/29532 [2019-10-27 21:04:51.701]# RECV ASCII>Downloading Download/Total 13680/29532 [2019-10-27 21:04:51.905]# RECV ASCII>Downloading Download/Total 18720/29532 [2019-10-27 21:04:52.105]# RECV ASCII>Downloading Download/Total 23640/29532 [2019-10-27 21:04:52.306]# RECV ASCII>Downloading Download/Total 28920/29532 [2019-10-27 21:04:52.508]# RECV ASCII>Downloading Download/Total 29532/29532 [2019-10-27 21:04:52.512]# RECV ASCII>Download Success, Checking File CRC ...... [2019-10-27 21:04:52.516]# RECV ASCII>File CRC Pass
Programing to App Area 0x 8080000 ...... [2019-10-27 21:04:55.216]# RECV ASCII>Program Success[2019-10-27 21:04:55.220]# RECV ASCII>Jump to APP Code 

Python端输出信息如下 


Bin Size = 29532
download/Total 120/29532
download/Total 240/29532
download/Total 360/29532
download/Total 480/29532
download/Total 600/29532
download/Total 720/29532
download/Total 840/29532
download/Total 960/29532
download/Total 1080/29532
download/Total 1200/29532
download/Total 1320/29532
download/Total 1440/29532
download/Total 1560/29532
download/Total 1680/29532
download/Total 1800/29532
download/Total 1920/29532
download/Total 2040/29532
download/Total 2160/29532
download/Total 2280/29532
download/Total 2400/29532
download/Total 2520/29532
download/Total 2640/29532
download/Total 2760/29532
download/Total 2880/29532
download/Total 3000/29532
download/Total 3120/29532
download/Total 3240/29532
download/Total 3360/29532
download/Total 3480/29532
download/Total 3600/29532
download/Total 3720/29532
download/Total 3840/29532
download/Total 3960/29532
download/Total 4080/29532
download/Total 4200/29532
download/Total 4320/29532
download/Total 4440/29532
download/Total 4560/29532
download/Total 4680/29532
download/Total 4800/29532
download/Total 4920/29532
download/Total 5040/29532
download/Total 5160/29532
download/Total 5280/29532
download/Total 5400/29532
download/Total 5520/29532
download/Total 5640/29532
download/Total 5760/29532
download/Total 5880/29532
download/Total 6000/29532
download/Total 6120/29532
download/Total 6240/29532
download/Total 6360/29532
download/Total 6480/29532
download/Total 6600/29532
download/Total 6720/29532
download/Total 6840/29532
download/Total 6960/29532
download/Total 7080/29532
download/Total 7200/29532
download/Total 7320/29532
download/Total 7440/29532
download/Total 7560/29532
download/Total 7680/29532
download/Total 7800/29532
download/Total 7920/29532
download/Total 8040/29532
download/Total 8160/29532
download/Total 8280/29532
download/Total 8400/29532
download/Total 8520/29532
download/Total 8640/29532
download/Total 8760/29532
download/Total 8880/29532
download/Total 9000/29532
download/Total 9120/29532
download/Total 9240/29532
download/Total 9360/29532
download/Total 9480/29532
download/Total 9600/29532
download/Total 9720/29532
download/Total 9840/29532
download/Total 9960/29532
download/Total 10080/29532
download/Total 10200/29532
download/Total 10320/29532
download/Total 10440/29532
download/Total 10560/29532
download/Total 10680/29532
download/Total 10800/29532
download/Total 10920/29532
download/Total 11040/29532
download/Total 11160/29532
download/Total 11280/29532
download/Total 11400/29532
download/Total 11520/29532
download/Total 11640/29532
download/Total 11760/29532
download/Total 11880/29532
download/Total 12000/29532
download/Total 12120/29532
download/Total 12240/29532
download/Total 12360/29532
download/Total 12480/29532
download/Total 12600/29532
download/Total 12720/29532
download/Total 12840/29532
download/Total 12960/29532
download/Total 13080/29532
download/Total 13200/29532
download/Total 13320/29532
download/Total 13440/29532
download/Total 13560/29532
download/Total 13680/29532
download/Total 13800/29532
download/Total 13920/29532
download/Total 14040/29532
download/Total 14160/29532
download/Total 14280/29532
download/Total 14400/29532
download/Total 14520/29532
download/Total 14640/29532
download/Total 14760/29532
download/Total 14880/29532
download/Total 15000/29532
download/Total 15120/29532
download/Total 15240/29532
download/Total 15360/29532
download/Total 15480/29532
download/Total 15600/29532
download/Total 15720/29532
download/Total 15840/29532
download/Total 15960/29532
download/Total 16080/29532
download/Total 16200/29532
download/Total 16320/29532
download/Total 16440/29532
download/Total 16560/29532
download/Total 16680/29532
download/Total 16800/29532
download/Total 16920/29532
download/Total 17040/29532
download/Total 17160/29532
download/Total 17280/29532
download/Total 17400/29532
download/Total 17520/29532
download/Total 17640/29532
download/Total 17760/29532
download/Total 17880/29532
download/Total 18000/29532
download/Total 18120/29532
download/Total 18240/29532
download/Total 18360/29532
download/Total 18480/29532
download/Total 18600/29532
download/Total 18720/29532
download/Total 18840/29532
download/Total 18960/29532
download/Total 19080/29532
download/Total 19200/29532
download/Total 19320/29532
download/Total 19440/29532
download/Total 19560/29532
download/Total 19680/29532
download/Total 19800/29532
download/Total 19920/29532
download/Total 20040/29532
download/Total 20160/29532
download/Total 20280/29532
download/Total 20400/29532
download/Total 20520/29532
download/Total 20640/29532
download/Total 20760/29532
download/Total 20880/29532
download/Total 21000/29532
download/Total 21120/29532
download/Total 21240/29532
download/Total 21360/29532
download/Total 21480/29532
download/Total 21600/29532
download/Total 21720/29532
download/Total 21840/29532
download/Total 21960/29532
download/Total 22080/29532
download/Total 22200/29532
download/Total 22320/29532
download/Total 22440/29532
download/Total 22560/29532
download/Total 22680/29532
download/Total 22800/29532
download/Total 22920/29532
download/Total 23040/29532
download/Total 23160/29532
download/Total 23280/29532
download/Total 23400/29532
download/Total 23520/29532
download/Total 23640/29532
download/Total 23760/29532
download/Total 23880/29532
download/Total 24000/29532
download/Total 24120/29532
download/Total 24240/29532
download/Total 24360/29532
download/Total 24480/29532
download/Total 24600/29532
download/Total 24720/29532
download/Total 24840/29532
download/Total 24960/29532
download/Total 25080/29532
download/Total 25200/29532
download/Total 25320/29532
download/Total 25440/29532
download/Total 25560/29532
download/Total 25680/29532
download/Total 25800/29532
download/Total 25920/29532
download/Total 26040/29532
download/Total 26160/29532
download/Total 26280/29532
download/Total 26400/29532
download/Total 26520/29532
download/Total 26640/29532
download/Total 26760/29532
download/Total 26880/29532
download/Total 27000/29532
download/Total 27120/29532
download/Total 27240/29532
download/Total 27360/29532
download/Total 27480/29532
download/Total 27600/29532
download/Total 27720/29532
download/Total 27840/29532
download/Total 27960/29532
download/Total 28080/29532
download/Total 28200/29532
download/Total 28320/29532
download/Total 28440/29532
download/Total 28560/29532
download/Total 28680/29532
download/Total 28800/29532
download/Total 28920/29532
download/Total 29040/29532
download/Total 29160/29532
download/Total 29280/29532
download/Total 29400/29532
download/Total 29520/29532
download/Total 29532/29532
endProcess finished with exit code 0

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部