STM32 BootLoader更新代码

            <div id="content_views" class="htmledit_views">
                <p>&nbsp; &nbsp; &nbsp; &nbsp;以前在学习<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&amp;spm=1001.2101.3001.7020" target="_blank" class="hl hl-1" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.7020&quot;,&quot;dest&quot;:&quot;https://so.youkuaiyun.com/so/search?q=%E5%B5%8C%E5%85%A5%E5%BC%8F&amp;spm=1001.2101.3001.7020&quot;,&quot;extra&quot;:&quot;{\&quot;searchword\&quot;:\&quot;嵌入式\&quot;}&quot;}" 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区的首地址传入就可以实现跳转了:


 
 
  1. void( *Boot_Jump 2App)();
  2. / /传入要跳转到的程序地址进行跳转
  3. void Boot_LoadApp(DWORD dwAddr)
  4. {
  5. BYTE i;
  6. / /检查栈顶地址是否合法
  7. if ((( *(vu 32 *)dwAddr) & 0x 2FFE 0000) = = 0x 20000000)
  8. {
  9. / /设置跳转地址
  10. Boot_Jump 2App = (void( *)()) *(vu 32 *)(dwAddr + 4);
  11. / /初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
  12. __ set_MSP( *(vu 32 *)dwAddr);
  13. / /关闭所有中断
  14. for (i = 0; i < 8; i + +)
  15. {
  16. NVIC- >ICER[i] = 0xFFFFFFFF;
  17. NVIC- >ICPR[i] = 0xFFFFFFFF;
  18. }
  19. / /跳转到APP Code
  20. Boot_Jump 2App();
  21. / /跳转之前用死循环卡住
  22. while ( 1);
  23. }
  24. }
AI助手

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


 
 
  1. __set_FAULTMASK( 1);
  2. 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个任务如下:


 
 
  1. int main (void)
  2. {
  3. bsp_Init();
  4. / * 创建启动任务 * /
  5. os_sys_init_user (AppTaskStart, / * 任务函数 * /
  6. 4, / * 任务优先级 * /
  7. &AppTaskStartStk, / * 任务栈 * /
  8. sizeof(AppTaskStartStk)); / * 任务栈大小,单位字节数 * /
  9. while( 1);
  10. }
  11. / /启动任务,也是最高优先级任务, 创建任务 ,时间基准更新。
  12. __task void AppTaskStart(void)
  13. {
  14. / /这里实现RL-TCPnet的时间基准更新。
  15. HandleTaskTCPMain = os_tsk_create_user(RL_TCPnet_timer_tick,
  16. 4,
  17. &RL_TCPnet_timer_tick_stk,
  18. sizeof(RL_TCPnet_timer_tick_stk));
  19. / /TCP数据收发处理任务
  20. HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,
  21. 3,
  22. &AppTaskTCPMainStk,
  23. sizeof(AppTaskTCPMainStk));
  24. / /用户任务
  25. HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,
  26. 2,
  27. &AppTaskUserIFStk,
  28. sizeof(AppTaskUserIFStk));
  29. / /LED闪烁任务
  30. HandleTaskTCPMain = os_tsk_create_user(led_bling_task,
  31. 1,
  32. &led_bling_taskStk,
  33. sizeof(led_bling_taskStk));
  34. while( 1)
  35. {
  36. os_dly_wait( 1000);
  37. }
  38. }
AI助手

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

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


 
 
  1. #define BYTE unsigned char
  2. #define WORD unsigned short
  3. #define DWORD unsigned int
  4. #define DOWNLOAD_HEAD_FLAG 0x 55591012
  5. typedef struct
  6. {
  7. DWORD dwStructFLag; / /识别头
  8. DWORD dwStructFLagInverse; / /识别头反码
  9. BYTE bFileType; / /文件类型, 0:APP程序, 1:BootLoader程序
  10. BYTE bReserve[ 3]; / /预留,四字节对齐
  11. DWORD dwFileLen; / /下载文件大小
  12. DWORD dwFileCRC; / /文件数据校验
  13. DWORD dwStructCRC; / /结构体校验
  14. }tyDownloadHead;
AI助手

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


 
 
  1. #define BOOT_STATE_DISCONNECT 0 //未连接
  2. #define BOOT_STATE_IDL 1 //空闲
  3. #define BOOT_STATE_DEBUG 2 //Debug模式
  4. #define BOOT_STATE_DOWNLOADING 3 //下载模式
  5. typedef struct
  6. {
  7. BYTE bBootState; //连接状态
  8. BYTE bIsNeedSelCmd;
  9. BYTE bSelCmdNum;
  10. DWORD dwDownloadLen;
  11. DWORD dwRecFileLen;
  12. DWORD dwFileCRC;
  13. }tyBoot;
AI助手

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


 
 
  1. typedef struct{
  2. U 8 *recvBuf; / / 接收数据缓存
  3. U 8 *sendBuf; / / 发送数据缓存
  4. U 16 port; / / 套接字端口
  5. U 8 id; / / 套接字id
  6. U 8 recvFlag; / / 接收到数据的标志: 0空闲, 1有数据, 2正在处理
  7. U 8 recvLen; / / 接收到数据的长度
  8. U 8 sendFlag; / / 待发送标志
  9. U 8 sendLen; / / 要发送的长度
  10. } socketNature_t; / / 套接字描述结构
AI助手

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


 
 
  1. void TCPnetTest(void)
  2. {
  3. static U 8 socket_pc_recvBuf[ 256];
  4. static U 8 socket_pc_sendBuf[ 256];
  5. static U 8 socket_debug_recvBuf[ 256];
  6. static U 8 socket_debug_sendBuf[ 256];
  7. / /创建TCP Socket并创建监听
  8. socket_pc.port = 5198;
  9. socket_pc.recvBuf = socket_pc_recvBuf;
  10. socket_pc.sendBuf = socket_pc_sendBuf;
  11. socket_pc.id = tcp_ get_socket(TCP_ TYPE_SERVER | TCP_ TYPE_KEEP_ALIVE, 0, 10, socket_callback);
  12. if(socket_pc.id! = 0)
  13. {
  14. tcp_listen(socket_pc.id,socket_pc.port);
  15. }
  16. / /创建TCP Socket并创建监听
  17. socket_debug.port = 5199;
  18. socket_debug.recvBuf = socket_debug_recvBuf;
  19. socket_debug.sendBuf = socket_debug_sendBuf;
  20. socket_debug.id = tcp_ get_socket(TCP_ TYPE_SERVER | TCP_ TYPE_KEEP_ALIVE, 0, 10, socket_callback);
  21. if(socket_debug.id ! = 0)
  22. {
  23. tcp_listen(socket_debug.id,socket_debug.port);
  24. }
  25. while ( 1)
  26. {
  27. main_TcpNet(); / * RL-TCPnet处理函数 * /
  28. send_poll( &socket_pc);
  29. send_poll( &socket_debug);
  30. os_dly_wait ( 2);
  31. }
  32. }
AI助手

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

(1)socket_callback函数如下


 
 
  1. U 16 socket_callback (U 8 soc, U 8 evt, U 8 *ptr, U 16 par)
  2. {
  3. switch (evt)
  4. {
  5. / /远程客户端连接消息
  6. / / 1、数组ptr存储远程设备的IP地址,par中存储端口号。
  7. / / 2、返回数值 1允许连接,返回数值 0禁止连接。
  8. case TCP_EVT_CONREQ: return ( 1);
  9. case TCP_EVT_ABORT:break; / * 连接终止 * /
  10. case TCP_EVT_CONNECT:
  11. if (soc = = socket_pc.id)
  12. {
  13. tBoot.bBootState = BOOT_STATE_IDL;
  14. }
  15. if (soc = = socket_debug.id)
  16. {
  17. tBoot.bBootState = BOOT_STATE_DEBUG;
  18. }
  19. break; / * Socket远程连接已经建立 * /
  20. case TCP_EVT_ CLOSE:break; / * 连接断开 * /
  21. case TCP_EVT_ACK:break; / * 发送的数据收到远程设备应答 * /
  22. case TCP_EVT_ DATA: / * 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 * /
  23. if (soc = = socket_debug.id)
  24. {
  25. socket_debug.recvLen = par;
  26. memcpy(socket_debug.recvBuf, ptr, socket_debug.recvLen);
  27. socket_debug.recvFlag = 1;
  28. socket_debug.sendLen = par;
  29. memcpy(socket_debug.sendBuf, ptr, socket_debug.sendLen);
  30. socket_debug.sendFlag = 1;
  31. }
  32. else if (soc = = socket_pc.id)
  33. {
  34. switch (tBoot.bBootState)
  35. {
  36. case BOOT_STATE_IDL:
  37. if (DOWNLOAD_HEAD_FLAG = = ((tyDownloadHead *)ptr)- >dwStructFLag)
  38. {
  39. if (sizeof(tyDownloadHead) = = par & &
  40. ~DOWNLOAD_HEAD_FLAG = = ((tyDownloadHead *)ptr)- >dwStructFLagInverse & &
  41. CalculateCRC((DWORD *)ptr, sizeof(tyDownloadHead) - 4, 0) = = ((tyDownloadHead *)ptr)- >dwStructCRC)
  42. {
  43. tBoot.dwRecFileLen = ((tyDownloadHead *)ptr)- >dwFileLen;
  44. tBoot.dwDownloadLen = 0;
  45. tBoot.dwFileCRC = ((tyDownloadHead *)ptr)- >dwFileCRC;
  46. tBoot.bBootState = BOOT_STATE_DOWNLOADING;
  47. }
  48. }
  49. break;
  50. case BOOT_STATE_DOWNLOADING:
  51. socket_pc.recvLen = par;
  52. memcpy(socket_pc.recvBuf, ptr, socket_pc.recvLen);
  53. socket_pc.recvFlag = 1;
  54. socket_pc.sendLen = socket_pc.recvLen;
  55. memcpy(socket_pc.sendBuf, ptr, socket_pc.sendLen);
  56. socket_pc.sendFlag = 1;
  57. STMFLASH_ Write(DOWNLOAD_ADDR + tBoot.dwDownloadLen, (DWORD *)socket_pc.recvBuf, socket_pc.recvLen / 4);
  58. tBoot.dwDownloadLen + = socket_pc.recvLen;
  59. if (tBoot.dwDownloadLen >= tBoot.dwRecFileLen)
  60. {
  61. tBoot.bBootState = BOOT_STATE_IDL;
  62. }
  63. break;
  64. default:
  65. break;
  66. }
  67. }
  68. break;
  69. }
  70. return ( 0);
  71. }
AI助手

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

(2)send_poll函数


 
 
  1. void send_poll(socketNature_t *socket){
  2. switch (tcp_ get_state(socket- >id)) / * 用于网线插拔的处理 * /
  3. {
  4. case TCP_STATE_ FREE:
  5. case TCP_STATE_CLOSED:
  6. tcp_listen (socket- >id,socket- >port); / /网络断开边接后,开始监听
  7. return;
  8. case TCP_STATE_LISTEN: / /处于监听时不发送
  9. default:
  10. socket- >sendFlag = 0;
  11. return;
  12. case TCP_STATE_CONNECT: / /处于连接状态
  13. break;
  14. }
  15. if(socket- >sendFlag)
  16. {
  17. U 8 *sendbuf;
  18. S 32 iCount;
  19. U 16 maxlen;
  20. socket- >sendFlag = 0;
  21. iCount = socket- >sendLen;
  22. do{
  23. main_TcpNet();
  24. if (tcp_check_ send(socket- >id) = = __ TRUE)
  25. {
  26. maxlen = tcp_max_dsize (socket- >id);
  27. iCount - = maxlen;
  28. if(iCount < 0)
  29. {
  30. maxlen = iCount + maxlen; / * 这么计算没问题的 * /
  31. }
  32. sendbuf = tcp_ get_buf(maxlen);
  33. memcpy(sendbuf,socket- >sendBuf,maxlen);
  34. tcp_ send (socket- >id, sendbuf, maxlen); / * 测试发现只能使用获取的内存 * /
  35. }
  36. }while(iCount > 0);
  37. }
  38. }
AI助手

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

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

(1)main.c


 
 
  1. #include "includes.h"
  2. #include "includeAll.H"
  3. #include "bsp.h"
  4. #include "bootLoader.h"
  5. __task void AppTaskStart(void);
  6. __task void RL_TCPnet_timer_tick(void);
  7. __task void AppTaskTCPMain(void);
  8. __task void AppTaskUserIF(void);
  9. __task void led_bling_task(void);
  10. / * 任务栈 * /
  11. static uint 64_t AppTaskStartStk[ 1024 / 8];
  12. static uint 64_t RL_TCPnet_timer_tick_stk[ 1028 / 8];
  13. static uint 64_t AppTaskTCPMainStk[ 2048 / 8];
  14. static uint 64_t AppTaskUserIFStk[ 2028 / 8];
  15. static uint 64_t led_bling_taskStk[ 2028 / 8];
  16. / * 任务句柄 * /
  17. OS_TID HandleTaskUserIF = NULL;
  18. OS_TID HandleTaskTCPMain = NULL;
  19. / /IP地址等设置
  20. LOCALM ip_config = {
  21. { 192, 168, 1, 41 }, / / IP address
  22. { 192, 168, 1, 1 }, / / Default Gateway
  23. { 255, 255, 255, 0 }, / / Net mask
  24. { 194, 25, 2, 129 }, / / Primary DNS server
  25. { 194, 25, 2, 130 } / / Secondary DNS server
  26. };
  27. int main (void)
  28. {
  29. 、、NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x 80000);
  30. bsp_Init();
  31. / * 创建启动任务 * /
  32. os_sys_init_user (AppTaskStart, / * 任务函数 * /
  33. 4, / * 任务优先级 * /
  34. &AppTaskStartStk, / * 任务栈 * /
  35. sizeof(AppTaskStartStk)); / * 任务栈大小,单位字节数 * /
  36. while( 1);
  37. }
  38. / /启动任务,也是最高优先级任务, 创建任务 ,时间基准更新。
  39. __task void AppTaskStart(void)
  40. {
  41. / /这里实现RL-TCPnet的时间基准更新。
  42. HandleTaskTCPMain = os_tsk_create_user(RL_TCPnet_timer_tick,
  43. 4,
  44. &RL_TCPnet_timer_tick_stk,
  45. sizeof(RL_TCPnet_timer_tick_stk));
  46. / /TCP数据收发处理任务
  47. HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,
  48. 3,
  49. &AppTaskTCPMainStk,
  50. sizeof(AppTaskTCPMainStk));
  51. / /用户任务
  52. HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,
  53. 2,
  54. &AppTaskUserIFStk,
  55. sizeof(AppTaskUserIFStk));
  56. / /LED闪烁任务
  57. HandleTaskTCPMain = os_tsk_create_user(led_bling_task,
  58. 1,
  59. &led_bling_taskStk,
  60. sizeof(led_bling_taskStk));
  61. while( 1)
  62. {
  63. os_dly_wait( 1000);
  64. }
  65. }
  66. / /这里实现RL-TCPnet的时间基准更新。
  67. __task void RL_TCPnet_timer_tick(void)
  68. {
  69. U 32 CpuID[ 3];
  70. U 8 mac_adr[ 6] = { 0, 1, 2, 50, 60, 70 };
  71. extern U 8 own_hw_adr[];
  72. extern U 8 lhost_name[];
  73. extern LOCALM localm[];
  74. / /根据CPU唯一ID设置MAC地址
  75. / /CpuID[ 0] = *(vu 32 *)( 0x 1ffff 7e 8); / /获取CPU唯一ID(F 1的)
  76. / /CpuID[ 1] = *(vu 32 *)( 0x 1ffff 7 ec);
  77. / /CpuID[ 2] = *(vu 32 *)( 0x 1ffff 7f 0);
  78. CpuID[ 0] = *(vu 32 *)( 0x 1FFF 7A 10); / /获取CPU唯一ID(F 4的)
  79. CpuID[ 1] = *(vu 32 *)( 0x 1FFF 7A 10 + 4);
  80. CpuID[ 2] = *(vu 32 *)( 0x 1FFF 7A 10 + 8);
  81. mac_adr[ 0] = (u 8)((CpuID[ 1] & 0x 00FF 0000) >>16);
  82. mac_adr[ 1] = (u 8)((CpuID[ 1] & 0xFF 000000) >>24);
  83. mac_adr[ 2] = (u 8)( CpuID[ 2] & 0x 000000FF);
  84. mac_adr[ 3] = (u 8)((CpuID[ 2] & 0x 0000FF 00) >>8);
  85. mac_adr[ 4] = (u 8)((CpuID[ 2] & 0x 00FF 0000) >>16);
  86. mac_adr[ 5] = (u 8)((CpuID[ 2] & 0xFF 000000) >>24);
  87. / /设置网卡物理地址
  88. mem_ copy (own_hw_adr, (U 8 *)mac_adr, 6);
  89. init_TcpNet (); / * 初始化RL-TCPnet * /
  90. / /设置主机的名字
  91. str_ copy (lhost_name, "BootLoader");
  92. / /设置ip地址等
  93. dhcp_disable();
  94. mem_ copy((U 8 *) &localm[NETIF_ETH], (U 8 *) &ip_config, sizeof(ip_config));
  95. os_itv_ set ( 100);
  96. while( 1)
  97. {
  98. timer_tick (); / * RL-TCPnet时间基准更新函数 * /
  99. os_itv_wait ();
  100. }
  101. }
  102. / /TCP数据收发处理任务
  103. __task void AppTaskTCPMain(void)
  104. {
  105. while ( 1)
  106. {
  107. TCPnetTest();
  108. }
  109. }
  110. / /用户任务
  111. __task void AppTaskUserIF(void)
  112. {
  113. BYTE overtimeCnt = 0;
  114. Boot_Init();
  115. while( 1)
  116. {
  117. if (BOOT_STATE_DOWNLOADING = = tBoot.bBootState)
  118. {
  119. Boot_Download();
  120. tBoot.bBootState = BOOT_STATE_IDL;
  121. overtimeCnt = 0;
  122. }
  123. else if(BOOT_STATE_DEBUG = = tBoot.bBootState)
  124. {
  125. Boot_Debug();
  126. overtimeCnt = 0;
  127. }
  128. overtimeCnt + +;
  129. if(overtimeCnt > 50 & & BOOT_STATE_DISCONNECT = = tBoot.bBootState)
  130. {
  131. Boot_LoadApp(APP_ADDR);
  132. }
  133. os_dly_wait( 100);
  134. }
  135. }
  136. / /LED指示灯闪烁
  137. __task void led_bling_task(void)
  138. {
  139. U 8 flag = 0;
  140. while( 1)
  141. {
  142. BOARD_LED = flag;
  143. flag = flag ? 0 : 1;
  144. os_dly_wait( 50);
  145. }
  146. }
AI助手

(2)app_tcpnet_lib.c


 
 
  1. #include "app_tcpnet_lib.h"
  2. #include "includes.h"
  3. #include "bootLoader.h"
  4. socketNature_t socket_pc; / / 与pc端通讯的套接字描述
  5. socketNature_t socket_debug; / / 与plc通讯的套接字描述
  6. / ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
  7. * 描述:套接字发送数据函数。
  8. * 参数:[*socket]套接字描述
  9. * [len]要发送的数据长度
  10. ********************************************************************/
  11. void socket_ send(socketNature_t *socket,U 8 len){
  12. socket- >sendLen =len;
  13. socket- >sendFlag = 1;
  14. }
  15. void send_poll(socketNature_t *socket);
  16. / *
  17. ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *
  18. * 函 数 名: tcp_callback
  19. * 功能说明: TCP Socket的回调函数
  20. * 形 参: soc TCP Socket类型
  21. * evt 事件类型
  22. * ptr 事件类型是TCP_EVT_ DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址
  23. * par 事件类型是TCP_EVT_ DATA,记录接收到的数据个数,其余事件记录端口号
  24. * 返 回 值:
  25. ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *
  26. * /
  27. / / if (soc = = socket_debug.id)
  28. / / {
  29. / / char buf[ 50];
  30. / / sprintf(buf, "This is BootLoader Code\r\n");
  31. / /
  32. / / socket_debug.sendLen = strlen(buf);
  33. / / memcpy(socket_debug.sendBuf, buf, socket_debug.sendLen);
  34. / / socket_debug.sendFlag = 1;
  35. / / }
  36. U 16 socket_callback (U 8 soc, U 8 evt, U 8 *ptr, U 16 par)
  37. {
  38. switch (evt)
  39. {
  40. / /远程客户端连接消息
  41. / / 1、数组ptr存储远程设备的IP地址,par中存储端口号。
  42. / / 2、返回数值 1允许连接,返回数值 0禁止连接。
  43. case TCP_EVT_CONREQ: return ( 1);
  44. case TCP_EVT_ABORT:break; / * 连接终止 * /
  45. case TCP_EVT_CONNECT:
  46. if (soc = = socket_pc.id)
  47. {
  48. tBoot.bBootState = BOOT_STATE_IDL;
  49. }
  50. if (soc = = socket_debug.id)
  51. {
  52. tBoot.bBootState = BOOT_STATE_DEBUG;
  53. }
  54. break; / * Socket远程连接已经建立 * /
  55. case TCP_EVT_ CLOSE:break; / * 连接断开 * /
  56. case TCP_EVT_ACK:break; / * 发送的数据收到远程设备应答 * /
  57. case TCP_EVT_ DATA: / * 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 * /
  58. if (soc = = socket_debug.id)
  59. {
  60. socket_debug.recvLen = par;
  61. memcpy(socket_debug.recvBuf, ptr, socket_debug.recvLen);
  62. socket_debug.recvFlag = 1;
  63. socket_debug.sendLen = par;
  64. memcpy(socket_debug.sendBuf, ptr, socket_debug.sendLen);
  65. socket_debug.sendFlag = 1;
  66. }
  67. else if (soc = = socket_pc.id)
  68. {
  69. switch (tBoot.bBootState)
  70. {
  71. case BOOT_STATE_IDL:
  72. if (DOWNLOAD_HEAD_FLAG = = ((tyDownloadHead *)ptr)- >dwStructFLag)
  73. {
  74. if (sizeof(tyDownloadHead) = = par & &
  75. ~DOWNLOAD_HEAD_FLAG = = ((tyDownloadHead *)ptr)- >dwStructFLagInverse & &
  76. CalculateCRC((DWORD *)ptr, sizeof(tyDownloadHead) - 4, 0) = = ((tyDownloadHead *)ptr)- >dwStructCRC)
  77. {
  78. tBoot.dwRecFileLen = ((tyDownloadHead *)ptr)- >dwFileLen;
  79. tBoot.dwDownloadLen = 0;
  80. tBoot.dwFileCRC = ((tyDownloadHead *)ptr)- >dwFileCRC;
  81. tBoot.bBootState = BOOT_STATE_DOWNLOADING;
  82. }
  83. }
  84. break;
  85. case BOOT_STATE_DOWNLOADING:
  86. socket_pc.recvLen = par;
  87. memcpy(socket_pc.recvBuf, ptr, socket_pc.recvLen);
  88. socket_pc.recvFlag = 1;
  89. socket_pc.sendLen = socket_pc.recvLen;
  90. memcpy(socket_pc.sendBuf, ptr, socket_pc.sendLen);
  91. socket_pc.sendFlag = 1;
  92. STMFLASH_ Write(DOWNLOAD_ADDR + tBoot.dwDownloadLen, (DWORD *)socket_pc.recvBuf, socket_pc.recvLen / 4);
  93. tBoot.dwDownloadLen + = socket_pc.recvLen;
  94. if (tBoot.dwDownloadLen >= tBoot.dwRecFileLen)
  95. {
  96. tBoot.bBootState = BOOT_STATE_IDL;
  97. }
  98. break;
  99. default:
  100. break;
  101. }
  102. }
  103. break;
  104. }
  105. return ( 0);
  106. }
  107. void TCPnetTest(void)
  108. {
  109. static U 8 socket_pc_recvBuf[ 256];
  110. static U 8 socket_pc_sendBuf[ 256];
  111. static U 8 socket_debug_recvBuf[ 256];
  112. static U 8 socket_debug_sendBuf[ 256];
  113. / /创建TCP Socket并创建监听
  114. socket_pc.port = 5198;
  115. socket_pc.recvBuf = socket_pc_recvBuf;
  116. socket_pc.sendBuf = socket_pc_sendBuf;
  117. socket_pc.id = tcp_ get_socket(TCP_ TYPE_SERVER | TCP_ TYPE_KEEP_ALIVE, 0, 10, socket_callback);
  118. if(socket_pc.id! = 0)
  119. {
  120. tcp_listen(socket_pc.id,socket_pc.port);
  121. }
  122. / /创建TCP Socket并创建监听
  123. socket_debug.port = 5199;
  124. socket_debug.recvBuf = socket_debug_recvBuf;
  125. socket_debug.sendBuf = socket_debug_sendBuf;
  126. socket_debug.id = tcp_ get_socket(TCP_ TYPE_SERVER | TCP_ TYPE_KEEP_ALIVE, 0, 10, socket_callback);
  127. if(socket_debug.id ! = 0)
  128. {
  129. tcp_listen(socket_debug.id,socket_debug.port);
  130. }
  131. while ( 1)
  132. {
  133. main_TcpNet(); / * RL-TCPnet处理函数 * /
  134. send_poll( &socket_pc);
  135. send_poll( &socket_debug);
  136. os_dly_wait ( 2);
  137. }
  138. }
  139. void send_poll(socketNature_t *socket){
  140. switch (tcp_ get_state(socket- >id)) / * 用于网线插拔的处理 * /
  141. {
  142. case TCP_STATE_ FREE:
  143. case TCP_STATE_CLOSED:
  144. tcp_listen (socket- >id,socket- >port); / /网络断开边接后,开始监听
  145. return;
  146. case TCP_STATE_LISTEN: / /处于监听时不发送
  147. default:
  148. socket- >sendFlag = 0;
  149. return;
  150. case TCP_STATE_CONNECT: / /处于连接状态
  151. break;
  152. }
  153. if(socket- >sendFlag)
  154. {
  155. U 8 *sendbuf;
  156. S 32 iCount;
  157. U 16 maxlen;
  158. socket- >sendFlag = 0;
  159. iCount = socket- >sendLen;
  160. do{
  161. main_TcpNet();
  162. if (tcp_check_ send(socket- >id) = = __ TRUE)
  163. {
  164. maxlen = tcp_max_dsize (socket- >id);
  165. iCount - = maxlen;
  166. if(iCount < 0)
  167. {
  168. maxlen = iCount + maxlen; / * 这么计算没问题的 * /
  169. }
  170. sendbuf = tcp_ get_buf(maxlen);
  171. memcpy(sendbuf,socket- >sendBuf,maxlen);
  172. tcp_ send (socket- >id, sendbuf, maxlen); / * 测试发现只能使用获取的内存 * /
  173. }
  174. }while(iCount > 0);
  175. }
  176. }
AI助手

(3)bootloader.h


 
 
  1. #ifndef BOOT_LOADER_H_
  2. #define BOOT_LOADER_H_
  3. #include "includes.h"
  4. #include "includeAll.H"
  5. #include "bsp.h"
  6. #define BOOT_ADDR 0x8000000 //bootLoader 128K
  7. #define DOWNLOAD_ADDR 0x8020000 //固件 384K
  8. #define APP_ADDR 0x8080000 //应用程序区 384K
  9. #define RESERVED_ADDR 0x80E0000 //预留空间 128K
  10. #define TRUE 1
  11. #define FALSE 0
  12. #define FUN_SUC 0
  13. #define FUN_FAIL 1
  14. #define BYTE unsigned char
  15. #define WORD unsigned short
  16. #define DWORD unsigned int
  17. #define DOWNLOAD_HEAD_FLAG 0x55591012
  18. #define DEBUG_HEAD_FLAG 0x55591013
  19. #define BOOT_STATE_DISCONNECT 0
  20. #define BOOT_STATE_IDL 1
  21. #define BOOT_STATE_DEBUG 2
  22. #define BOOT_STATE_DOWNLOADING 3
  23. typedef struct
  24. {
  25. DWORD dwStructFLag; //识别头
  26. DWORD dwStructFLagInverse; //识别头取反
  27. DWORD dwStructCRC; //结构体校验
  28. }tyDebugHead;
  29. typedef struct
  30. {
  31. DWORD dwStructFLag; //识别头
  32. DWORD dwStructFLagInverse; //识别头反码
  33. BYTE bFileType; //文件类型,0:APP程序,1:BootLoader程序
  34. BYTE bReserve[ 3]; //预留,四字节对齐
  35. DWORD dwFileLen; //下载文件大小
  36. DWORD dwFileCRC; //文件数据校验
  37. DWORD dwStructCRC; //结构体校验
  38. }tyDownloadHead;
  39. typedef struct
  40. {
  41. BYTE bBootState; //连接状态
  42. BYTE bIsNeedSelCmd;
  43. BYTE bSelCmdNum;
  44. DWORD dwDownloadLen;
  45. DWORD dwRecFileLen;
  46. DWORD dwFileCRC;
  47. }tyBoot;
  48. extern tyBoot tBoot;
  49. void Boot_Init(void); //初始化
  50. extern BYTE Boot_Debug(void); //进入Debug模式
  51. extern BYTE Boot_Download(void); //进入下载模式
  52. extern void Boot_LoadApp(DWORD dwAddr); //跳转到APP
  53. extern DWORD CalculateCRC(DWORD *pdwDataBuff, DWORD dwByteLen, DWORD dwOrigXorValue); //计算CRC
  54. #endif
AI助手

(4)bootloader.c


 
 
  1. #include "bootLoader.h"
  2. tyBoot tBoot;
  3. void Boot_Init(void)
  4. {
  5. tBoot.bIsNeedSelCmd = FALSE;
  6. tBoot.bSelCmdNum = 0xFF;
  7. tBoot.dwDownloadLen = 0;
  8. tBoot.dwRecFileLen = 0;
  9. tBoot.bBootState = BOOT_STATE_DISCONNECT;
  10. }
  11. void Debug_Print(char *str)
  12. {
  13. strcpy(socket_debug.sendBuf, str);
  14. socket_debug.sendLen = strlen(socket_debug.sendBuf);
  15. socket_debug.sendFlag = 1;
  16. while ( 1 = = socket_debug.sendFlag)
  17. {
  18. os_dly_wait( 2);
  19. }
  20. }
  21. BYTE Boot_Download(void)
  22. {
  23. char bBuf[ 50] = { 0 };
  24. char overtimeCnt = 0;
  25. Debug_Print( "\r\nDownload Start ...... ");
  26. while (tBoot.dwDownloadLen < tBoot.dwRecFileLen)
  27. {
  28. os_dly_wait( 200);
  29. sprintf(bBuf, "\r\nDownloading Download/Total %d/%d ", tBoot.dwDownloadLen, tBoot.dwRecFileLen);
  30. Debug_Print(bBuf);
  31. overtimeCnt + +;
  32. if(overtimeCnt > 50) / /下载超时
  33. {
  34. Debug_Print( "\r\nDownload Overtime");
  35. return FUN_FAIL;
  36. }
  37. }
  38. Debug_Print( "\r\nDownload Success, Checking File CRC ...... ");
  39. if(CalculateCRC((DWORD *)DOWNLOAD_ADDR, tBoot.dwRecFileLen, 0) ! = tBoot.dwFileCRC)
  40. {
  41. Debug_Print( "\r\nFile CRC Error");
  42. return FUN_FAIL;
  43. }
  44. Debug_Print( "\r\nFile CRC Pass");
  45. sprintf(bBuf, "\r\nPrograming to App Area 0x%8X ...... ", APP_ADDR);
  46. Debug_Print(bBuf);
  47. / /从Download区搬移程序到App区
  48. STMFLASH_ Write(APP_ADDR, (u 32 *)DOWNLOAD_ADDR, (APP_ADDR - DOWNLOAD_ADDR) / 4);
  49. Debug_Print( "\r\nProgram Success");
  50. / /break;
  51. Debug_Print( "\r\nJump to APP Code ");
  52. Boot_LoadApp(APP_ADDR);
  53. return FUN_SUC;
  54. }
  55. BYTE Boot_Debug(void)
  56. {
  57. char bBuf[ 50] = { 0 };
  58. char i;
  59. / /打印地址列表
  60. Debug_Print( "\r\n******* BootLoader *******"
  61. "\r\nBOOT_ADDR : 0x8000000 - 0x8020000"
  62. "\r\nDOWNLOAD_ADDR: 0x8020000 - 0x8080000"
  63. "\r\nAPP_ADDR : 0x8080000 - 0x80E0000"
  64. "\r\nRESERVED_ADDR: 0x80E0000 - 0x8100000");
  65. / /等待激活BootLoader命令
  66. / / for (i = 3; i > 0; i--)
  67. / / {
  68. / / sprintf(bBuf, "\r\nPress any key to stop: %d", i);
  69. / / Debug_Print(bBuf);
  70. / / os_dly_wait( 1000);
  71. / / if (socket_debug.recvFlag)
  72. / / {
  73. / / socket_debug.recvFlag = 0;
  74. / / tBoot.bIsNeedSelCmd = TRUE;
  75. / / break;
  76. / / }
  77. / / }
  78. tBoot.bIsNeedSelCmd = TRUE;
  79. / /跳转到App执行或者选择BootLoader命令
  80. if ( TRUE ! = tBoot.bIsNeedSelCmd)
  81. {
  82. Debug_Print( "\r\nJump to APP Code ");
  83. Boot_LoadApp(APP_ADDR);
  84. }
  85. else
  86. {
  87. Debug_Print( "\r\n1 -> Download App\r\n2 -> Run App");
  88. / /等待用户输入有效的序号
  89. while (( 1 ! = tBoot.bSelCmdNum) & & ( 2 ! = tBoot.bSelCmdNum))
  90. {
  91. os_dly_wait( 100);
  92. if (socket_debug.recvFlag)
  93. {
  94. socket_debug.recvFlag = 0;
  95. tBoot.bSelCmdNum = *socket_debug.recvBuf;
  96. }
  97. }
  98. / /根据选择的序号处理命令
  99. switch (tBoot.bSelCmdNum)
  100. {
  101. case 1:
  102. tBoot.bBootState = BOOT_STATE_IDL;
  103. break;
  104. case 2:
  105. Debug_Print( "\r\nJump to APP Code ");
  106. Boot_LoadApp(APP_ADDR);
  107. break;
  108. default:
  109. break;
  110. }
  111. }
  112. return FUN_SUC;
  113. }
  114. void( *Boot_Jump 2App)();
  115. void Boot_LoadApp(DWORD dwAddr)
  116. {
  117. BYTE i;
  118. / /检查栈顶地址是否合法
  119. if ((( *(vu 32 *)dwAddr) & 0x 2FFE 0000) = = 0x 20000000)
  120. {
  121. / /设置跳转地址
  122. Boot_Jump 2App = (void( *)()) *(vu 32 *)(dwAddr + 4);
  123. / /初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
  124. __ set_MSP( *(vu 32 *)dwAddr);
  125. / /关闭所有中断
  126. for (i = 0; i < 8; i + +)
  127. {
  128. NVIC- >ICER[i] = 0xFFFFFFFF;
  129. NVIC- >ICPR[i] = 0xFFFFFFFF;
  130. }
  131. / /跳转到APP Code
  132. Boot_Jump 2App();
  133. / /跳转之前用死循环卡住
  134. while ( 1);
  135. }
  136. }
  137. DWORD CalculateCRC(DWORD *pdwDataBuff, DWORD dwByteLen, DWORD dwOrigXorValue)
  138. {
  139. DWORD i;
  140. DWORD dwXorResult;
  141. dwXorResult = dwOrigXorValue;
  142. for (i = 0; i < (dwByteLen / sizeof(DWORD)); i + +)
  143. {
  144. dwXorResult ^ = pdwDataBuff[i];
  145. }
  146. return dwXorResult;
  147. }
AI助手

五、客户端实现

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


 
 
  1. from socket import *
  2. import os
  3. import ctypes
  4. class DownloadTool():
  5. def __init__( self):
  6. pass
  7. def Connect( self):
  8. self.tcpHost = 'localhost'
  9. self.tcpPort = 5198
  10. self.tcpRecBufSize = 2048
  11. self.tcpAddr = ( "192.168.1.41", self.tcpPort)
  12. self.tcpSock = socket(AF_INET, SOCK_STREAM)
  13. self.tcpSock.connect( self.tcpAddr)
  14. def CalculateFileSizeAndCRC( self, filePath):
  15. file = open(filePath, "rb")
  16. size = os.path.getsize(filePath)
  17. print( "Bin Size = %d" % ( size))
  18. buf = file. read( 4)
  19. crc = 0
  20. while len(buf) > 0:
  21. crc = crc ^ (int. from_bytes(buf, byteorder = 'little', signed = False))
  22. buf = file. read( 4)
  23. file. close()
  24. return size, crc
  25. def DownloadFile( self):
  26. #传输下载头
  27. fileLen, fileCRC = self.CalculateFileSizeAndCRC( "MC-IOV3248.bin")
  28. headInfo = []
  29. headInfo.append(ctypes.c_uint 32( 0x 55591012))
  30. headInfo.append(ctypes.c_uint
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_38960013

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值