<div id="content_views" class="htmledit_views">
<p> 以前在学习<span class="words-blog hl-git-1" data-tit="STM32" data-pretit="stm32">STM32</span>的时候,看到有的开发板用BootLoader下载程序,觉得脱离下载器程序下载进去挺有意思的,于是就自己琢磨着也做一个,采用RL-TCPnet实现网络通信,还涉及到RTX<a href="https://so.youkuaiyun.com/so/search?q=%E5%B5%8C%E5%85%A5%E5%BC%8F&spm=1001.2101.3001.7020" target="_blank" class="hl hl-1" data-report-click="{"spm":"1001.2101.3001.7020","dest":"https://so.youkuaiyun.com/so/search?q=%E5%B5%8C%E5%85%A5%E5%BC%8F&spm=1001.2101.3001.7020","extra":"{\"searchword\":\"嵌入式\"}"}" data-tit="嵌入式" data-pretit="嵌入式">嵌入式</a>操作系统(不用也行的,没多大必要,因为例程自带RTX所以我用了,注意用RTX前要用注册机添加RTX的注册码)。</p>
一、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_Jump
2App)();
-
-
/
/传入要跳转到的程序地址进行跳转
-
void Boot_LoadApp(DWORD dwAddr)
-
{
-
BYTE i;
-
-
/
/检查栈顶地址是否合法
-
if (((
*(vu
32
*)dwAddr)
&
0x
2FFE
0000)
=
=
0x
20000000)
-
{
-
/
/设置跳转地址
-
Boot_Jump
2App
= (void(
*)())
*(vu
32
*)(dwAddr
+
4);
-
/
/初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
-
__
set_MSP(
*(vu
32
*)dwAddr);
-
/
/关闭所有中断
-
for (i
=
0; i
<
8; i
+
+)
-
{
-
NVIC-
>ICER[i]
=
0xFFFFFFFF;
-
NVIC-
>ICPR[i]
=
0xFFFFFFFF;
-
}
-
/
/跳转到APP
Code
-
Boot_Jump
2App();
-
/
/跳转之前用死循环卡住
-
while (
1);
-
}
-
}
AI助手
不知道大家会不会这样想,如果我又想从APP段跳回BootLoader,不想以断电的方式重新运行BootLoader下载程序,怎么做?我的想法是调用STM32的软件复位函数NVIC_SystemReset()。
-
__set_FAULTMASK(
1);
-
NVIC_SystemReset();
AI助手
为什么前面加了一句__set_FAULTMASK(1),作用是关闭所有中断,这里有一个要注意的问题:如果只调用NVIC_SystemReset(),从SYSRESETREQ 被置为有效,到复位发生器执行复位命令,往往会有一个延时。在此延时期间,处理器仍然可以响应中断请求。但我们的本意往往是要程序执行到此为止,不要再做任何其它事情了。所以,最好在发出复位请求前,先把FAULTMASK 置位才万无一失。
四、BootLoader程序和APP程序的配置
BootLoader程序有一点要注意,要对应BootLoader区地址进行如下设置。
APP程序有三点要注意,(1)也要设置对应的开始地址和大小
(2)APP程序还要在程序一开始设置中断向量的偏移,就是程序一开始调用这样一条语句,0x80000是APP程序的起始偏移,根据实际情况填写。
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x80000);
AI助手
(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);
-
}
-
}
AI助手
我们只关心TCP数据收发任务AppTaskTCPMain和用户任务AppTaskUserIF。
介绍这两个任务之前,先介绍一下程序里涉及到的几个结构体,首先是tyDownloadHead,就是下载bin文件之前要先发送含有特定格式的下载头,验证成功后才能进行程序下载,否则随便乱发了一些数据过来也当做文件数据的话就太不可靠了。
-
#define BYTE unsigned char
-
#define WORD unsigned short
-
#define DWORD unsigned int
-
-
#define DOWNLOAD_HEAD_FLAG
0x
55591012
-
-
typedef struct
-
{
-
DWORD dwStructFLag;
/
/识别头
-
DWORD dwStructFLagInverse;
/
/识别头反码
-
BYTE bFileType;
/
/文件类型,
0:APP程序,
1:BootLoader程序
-
BYTE bReserve[
3];
/
/预留,四字节对齐
-
DWORD dwFileLen;
/
/下载文件大小
-
DWORD dwFileCRC;
/
/文件数据校验
-
DWORD dwStructCRC;
/
/结构体校验
-
}tyDownloadHead;
AI助手
还有就是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;
AI助手
最后一个是socketNature_t,是对TCP收发数据的控制。
-
typedef struct{
-
U
8
*recvBuf;
/
/ 接收数据缓存
-
U
8
*sendBuf;
/
/ 发送数据缓存
-
U
16 port;
/
/ 套接字端口
-
U
8 id;
/
/ 套接字id
-
U
8 recvFlag;
/
/ 接收到数据的标志:
0空闲,
1有数据,
2正在处理
-
U
8 recvLen;
/
/ 接收到数据的长度
-
U
8 sendFlag;
/
/ 待发送标志
-
U
8 sendLen;
/
/ 要发送的长度
-
} socketNature_t;
/
/ 套接字描述结构
AI助手
TCP数据收发任务只调用了下面这个函数,完成下载端口和Debug端口的创建,随后不断的侦测是否有数据需要收发,至于RL-TCPnet的具体使用设置网上资料很多,就不解释了。
-
void TCPnetTest(void)
-
{
-
static U
8 socket_pc_recvBuf[
256];
-
static U
8 socket_pc_sendBuf[
256];
-
static U
8 socket_debug_recvBuf[
256];
-
static U
8 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);
-
}
-
}
AI助手
同样这一堆代码我们只关心socket_callback这个回调函数和send_poll这个发送数据函数。
(1)socket_callback函数如下
-
U
16 socket_callback (U
8 soc, U
8 evt, U
8
*ptr, U
16 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);
-
}
AI助手
这段代码完成对数据的接收,同时对下载头进行校验,校验成功后转到下载模式接收文件。
(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)
-
{
-
U
8
*sendbuf;
-
S
32 iCount;
-
U
16 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);
-
}
-
}
AI助手
纯粹就是发送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 uint
64_t AppTaskStartStk[
1024
/
8];
-
static uint
64_t RL_TCPnet_timer_tick_stk[
1028
/
8];
-
static uint
64_t AppTaskTCPMainStk[
2048
/
8];
-
static uint
64_t AppTaskUserIFStk[
2028
/
8];
-
static uint
64_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,
0x
80000);
-
-
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)
-
{
-
U
32 CpuID[
3];
-
U
8 mac_adr[
6]
= {
0,
1,
2,
50,
60,
70 };
-
extern U
8 own_hw_adr[];
-
extern U
8 lhost_name[];
-
extern LOCALM localm[];
-
-
/
/根据CPU唯一ID设置MAC地址
-
/
/CpuID[
0]
=
*(vu
32
*)(
0x
1ffff
7e
8);
/
/获取CPU唯一ID(F
1的)
-
/
/CpuID[
1]
=
*(vu
32
*)(
0x
1ffff
7
ec);
-
/
/CpuID[
2]
=
*(vu
32
*)(
0x
1ffff
7f
0);
-
CpuID[
0]
=
*(vu
32
*)(
0x
1FFF
7A
10);
/
/获取CPU唯一ID(F
4的)
-
CpuID[
1]
=
*(vu
32
*)(
0x
1FFF
7A
10
+
4);
-
CpuID[
2]
=
*(vu
32
*)(
0x
1FFF
7A
10
+
8);
-
mac_adr[
0]
= (u
8)((CpuID[
1]
&
0x
00FF
0000)
>>16);
-
mac_adr[
1]
= (u
8)((CpuID[
1]
&
0xFF
000000)
>>24);
-
mac_adr[
2]
= (u
8)( CpuID[
2]
&
0x
000000FF);
-
mac_adr[
3]
= (u
8)((CpuID[
2]
&
0x
0000FF
00)
>>8);
-
mac_adr[
4]
= (u
8)((CpuID[
2]
&
0x
00FF
0000)
>>16);
-
mac_adr[
5]
= (u
8)((CpuID[
2]
&
0xFF
000000)
>>24);
-
-
/
/设置网卡物理地址
-
mem_
copy (own_hw_adr, (U
8
*)mac_adr,
6);
-
-
init_TcpNet ();
/
* 初始化RL-TCPnet
*
/
-
-
/
/设置主机的名字
-
str_
copy (lhost_name,
"BootLoader");
-
-
/
/设置ip地址等
-
dhcp_disable();
-
mem_
copy((U
8
*)
&localm[NETIF_ETH], (U
8
*)
&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)
-
{
-
-
U
8 flag
=
0;
-
-
while(
1)
-
{
-
BOARD_LED
= flag;
-
flag
= flag ?
0 :
1;
-
os_dly_wait(
50);
-
}
-
}
-
-
AI助手
(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,U
8 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;
-
/
/ }
-
-
U
16 socket_callback (U
8 soc, U
8 evt, U
8
*ptr, U
16 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 U
8 socket_pc_recvBuf[
256];
-
static U
8 socket_pc_sendBuf[
256];
-
static U
8 socket_debug_recvBuf[
256];
-
static U
8 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)
-
{
-
U
8
*sendbuf;
-
S
32 iCount;
-
U
16 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);
-
}
-
}
-
AI助手
(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 3
-
-
typedef
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
AI助手
(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, (u
32
*)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_Jump
2App)();
-
-
void Boot_LoadApp(DWORD dwAddr)
-
{
-
BYTE i;
-
-
/
/检查栈顶地址是否合法
-
if (((
*(vu
32
*)dwAddr)
&
0x
2FFE
0000)
=
=
0x
20000000)
-
{
-
/
/设置跳转地址
-
Boot_Jump
2App
= (void(
*)())
*(vu
32
*)(dwAddr
+
4);
-
/
/初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
-
__
set_MSP(
*(vu
32
*)dwAddr);
-
/
/关闭所有中断
-
for (i
=
0; i
<
8; i
+
+)
-
{
-
NVIC-
>ICER[i]
=
0xFFFFFFFF;
-
NVIC-
>ICPR[i]
=
0xFFFFFFFF;
-
}
-
/
/跳转到APP
Code
-
Boot_Jump
2App();
-
/
/跳转之前用死循环卡住
-
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;
-
}
-
AI助手
五、客户端实现
江湖有句传言“人生苦短,我用Python”,基于Python的高效率开发,所以我用了Python来实现TCP客户端,废话少说,直接上代码:
-
from socket import
*
-
import os
-
import ctypes
-
-
class DownloadTool():
-
def __init__(
self):
-
pass
-
-
def Connect(
self):
-
self.tcpHost
=
'localhost'
-
self.tcpPort
=
5198
-
self.tcpRecBufSize
=
2048
-
self.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
=
0
-
-
while len(buf)
>
0:
-
crc
= crc ^ (int.
from_bytes(buf, byteorder
=
'little', signed
=
False))
-
buf
=
file.
read(
4)
-
-
file.
close()
-
-
return
size, crc
-
-
def DownloadFile(
self):
-
#传输下载头
-
fileLen, fileCRC
=
self.CalculateFileSizeAndCRC(
"MC-IOV3248.bin")
-
-
headInfo
= []
-
headInfo.append(ctypes.c_uint
32(
0x
55591012))
-
headInfo.append(ctypes.c_uint