采用TCP协议实现ethernet bootloader


TCP/IP Stack

Microchip TCP/IP Stack是免费的,广泛应用于PIC单片机中。由于有远程更新程序的需求,我决定开发基于TCP协议的ethernet bootloader, 主要使用了Microchip TCP/IP Stack的TCP模块。最终我开发出来的ethernet bootloader 在PIC18F97J60上验证通过。整个实现上分两部分,一部分是单片机端的基于TCP协议的bootloader程序,我将其命名为PhnBoot_v2.0, 另外一部分是PC端同样基于TCP协议于单片机互动的通信程序,我将其命名为PhnLoader_v2.0。我还定义了PhnBoot_v2.0和PhnLoader_v2.0之间传输数据的通信协定。下面将细说我是如何实现的。

通信协定

单片机端PhnBoot_v2.0和PC端PhnLoader_v2.0之间的通信数据包采用以下协定

<STX><CMD><ADDRL><ADDRH><ADDRU><LEN><DATA>...<DATA><ETX>

定义如下:

STX - Start of packet indicator

ETX - End of packet indicator

LEN - The length of true data

DATA - General data 16 bytes, only first LEN of datas are true

CMD - Base command

ADDR - Address up to 24 bits ( ADDRL , ADDRH , ADDRH)

具体有以下Base command:

RD-VER: 0x00 -- Read Version Information (最终版本删除了此命令)

RD_MEM: 0x01 -- Read Program Memory (最终版本删除了此命令)

ER_MEM: 0x03 -- Erase Program Memory

WR_MEM: 0x02 -- Write Program Memory

WR_CFG: 0x04 -- Write Configuration Registers

PhnLoader_v2.0 功能

定义好了通讯协定, 接着就按照协定去实现PhnLoader_v2.0。 PhnLoader_v2.0的具体功能包括选择IP地址,端口和协议类型, 目前只支持TCP协议, 建立TCP服务器,加载应用程序Hex文件,Parse 应用程序的Hex文件,一行一行解读Hex文件,一旦收到连接请求,建立TCP连接,一旦收到应用程序跟新请求,立刻按照通讯协定采用TCP协议发送Hex记录到单片机,接收单片机发送回来的Response,发送完毕后断开TCP连接,发送期间出现问题就立马结束发送。

PhnLoader_v2.0 主要代码段

PhnLoader_v2.0是用C#实现的,是我在利用空余时间自学C#后写的,上面提到的功能都实现了。

View Code

PhnLoader_v2.0 用户界面

 

PhnBoot_v2.0 功能

在PhnLoader_v2.0完成后,接着就是完成PhnBoot_v2.0。 PhnBoot_v2.0主要功能就是使用Microchip的TCP/IP Stack建立TCP Client,发送连接请求,建立连接后发送更新应用程序请求,接收PhnLoader_v2.0传送过来的Hex记录。解读Hex记录中的启始位,命名,地址,数据和结束位,将数据烧录到指定的程序存储器的位置上,然后通过ethernet返回Response消息给PC端PhnLoader_v2.0。

PhnBoot_v2.0 位置

PhnBoot_v2.0放置在程序存储器的头部,大小为0x4C00程序字。

 

Interrupt Vector Remap

由于PhnBoot_v2.0位于程序存储器的头部,需要对Interrupt Vector进行remap. 代码如下。

  1. #define APP_START 0x4C00 
  2.  
  3. #define REMAPPED_APP_HIGH_INTERRUPT_VECTOR 0x4C08 
  4.  
  5. #define REMAPPED_APP_LOW_INTERRUPT_VECTOR 0x4C18 
  6.  
  7. #pragma code low_vector_section=0x018 
  8.  
  9. void low_vector (void
  10.  
  11.  
  12. _asm 
  13.  
  14. goto REMAPPED_APP_LOW_INTERRUPT_VECTOR 
  15.  
  16. _endasm 
  17.  
  18.  
  19. #pragma code high_vector_section=0x08 
  20.  
  21. void high_vector (void
  22.  
  23.  
  24. _asm 
  25.  
  26. goto REMAPPED_APP_HIGH_INTERRUPT_VECTOR 
  27.  
  28. _endasm 
  29.  

PhnBoot_v2.0 主要代码段

PhnBoot_v2.0 是用C语言写的,Microchip 8-bit C Compiler--MCC18编译的。

  1. switch (GenState) 
  2.  
  3.  
  4. case SM_HOME: 
  5.  
  6. ARPResolve(&Server.IPAddr); 
  7.  
  8. if (ARPIsResolved(&Server.IPAddr,&Server.MACAddr)) 
  9.  
  10.  
  11. #ifdef STACK_USE_UDP 
  12.  
  13. MySock = UDPOpen(ClientPort,&Server,ServerPort); 
  14.  
  15. #endif 
  16.  
  17. #ifdef STACK_USE_TCP 
  18.  
  19. MySock = TCPOpen((DWORD)&Server, TCP_OPEN_NODE_INFO, ServerPort, 0); 
  20.  
  21. #endif 
  22.  
  23. if (MySock != INVALID_SOCKET) 
  24.  
  25.  
  26. tick = 0x4000; 
  27.  
  28. delay = BOOT_TIMEOUT; 
  29.  
  30. GenState++; 
  31.  
  32.  
  33.  
  34. else 
  35.  
  36.  
  37. tick--; 
  38.  
  39. if (tick==0) 
  40.  
  41.  
  42. tick = 0x4000; 
  43.  
  44. if (delay == 0) 
  45.  
  46.  
  47. delay = BOOT_TIMEOUT; 
  48.  
  49. GenState = SM_CLOSE; 
  50.  
  51.  
  52. delay--; 
  53.  
  54.  
  55.  
  56. break
  57.  
  58. case SM_READY: 
  59.  
  60. #ifdef STACK_USE_UDP 
  61.  
  62. if (UDPIsPutReady(MySock) > BUFFER_MAX) 
  63.  
  64.  
  65. UDPPutString(ok); 
  66.  
  67. UDPFlush(); 
  68.  
  69. GenState++; 
  70.  
  71.  
  72. #endif 
  73.  
  74. #ifdef STACK_USE_TCP 
  75.  
  76. if (TCPIsConnected(MySock)) 
  77.  
  78.  
  79. TCPPutString(MySock,ok); 
  80.  
  81. TCPFlush(MySock); 
  82.  
  83. GenState++; 
  84.  
  85.  
  86. #endif 
  87.  
  88. else 
  89.  
  90.  
  91. tick--; 
  92.  
  93. if (tick==0) 
  94.  
  95.  
  96. tick = 0x4000; 
  97.  
  98. if (delay == 0) 
  99.  
  100.  
  101. delay = BOOT_TIMEOUT; 
  102.  
  103. GenState = SM_CLOSE; 
  104.  
  105.  
  106. delay--; 
  107.  
  108.  
  109.  
  110. break
  111.  
  112. case SM_RESPONSE: 
  113.  
  114. #ifdef STACK_USE_UDP 
  115.  
  116. networkBytes = UDPIsGetReady(MySock); 
  117.  
  118. #endif 
  119.  
  120. #ifdef STACK_USE_TCP 
  121.  
  122. networkBytes = TCPIsGetReady(MySock); 
  123.  
  124. #endif 
  125.  
  126. if (networkBytes >= BUFFER_MAX) 
  127.  
  128.  
  129. #ifdef STACK_USE_UDP 
  130.  
  131. UDPGetArray(line_buffer, BUFFER_MAX); 
  132.  
  133. UDPDiscard(); 
  134.  
  135. #endif 
  136.  
  137. #ifdef STACK_USE_TCP 
  138.  
  139. TCPGetArray(MySock,line_buffer,BUFFER_MAX); 
  140.  
  141. TCPDiscard(MySock); 
  142.  
  143. #endif 
  144.  
  145. if (line_buffer[0] == STX && line_buffer[BUFFER_MAX - 1] == ETX) 
  146.  
  147.  
  148. switch (line_buffer[CMD_INDEX]) 
  149.  
  150.  
  151. case WR_MEM: 
  152.  
  153. EECON1 = PGM_WRITE; 
  154.  
  155. WriteMem(); 
  156.  
  157. break
  158.  
  159. case WR_CFG: 
  160.  
  161. if (!last_block_written&&!CFG_NUM) 
  162.  
  163.  
  164. WriteStart(); 
  165.  
  166. last_block_written = 1; 
  167.  
  168. ResetBlockBuffer(); 
  169.  
  170.  
  171. CFG_NUM++; 
  172.  
  173. EECON1 = CFG_WRITE; 
  174.  
  175. WriteCfg(); 
  176.  
  177. break
  178.  
  179. case ER_MEM: 
  180.  
  181. EECON1 = PGM_ERASE; 
  182.  
  183. EraseMem(); 
  184.  
  185. break
  186.  
  187. case RUN_APP: 
  188.  
  189. if (!last_block_written) 
  190.  
  191.  
  192. WriteStart(); 
  193.  
  194. last_block_written = 1; 
  195.  
  196. ResetBlockBuffer(); 
  197.  
  198.  
  199. GenState++; 
  200.  
  201. default
  202.  
  203. break
  204.  
  205.  
  206. #ifdef STACK_USE_UDP 
  207.  
  208. if (UDPIsPutReady(MySock) >= BUFFER_MAX) 
  209.  
  210.  
  211. UDPPutArray(line_buffer, BUFFER_MAX); 
  212.  
  213. UDPFlush(); 
  214.  
  215.  
  216. #endif 
  217.  
  218. #ifdef STACK_USE_TCP 
  219.  
  220. if (TCPIsPutReady(MySock) >= BUFFER_MAX) 
  221.  
  222.  
  223. TCPPutArray(MySock,line_buffer, BUFFER_MAX); 
  224.  
  225. TCPFlush(MySock); 
  226.  
  227.  
  228. #endif 
  229.  
  230.  
  231.  
  232. else 
  233.  
  234.  
  235. tick--; 
  236.  
  237. if (tick==0) 
  238.  
  239.  
  240. tick = 0x4000; 
  241.  
  242. if (delay == 0) 
  243.  
  244.  
  245. delay = BOOT_TIMEOUT; 
  246.  
  247. GenState = SM_CLOSE; 
  248.  
  249.  
  250. delay--; 
  251.  
  252.  
  253.  
  254. break
  255.  
  256. case SM_CLOSE: 
  257.  
  258. while (!TXSTAbits.TRMT); 
  259.  
  260. TXREG='>'
  261.  
  262. #ifdef STACK_USE_UDP 
  263.  
  264. UDPClose(MySock); 
  265.  
  266. #endif 
  267.  
  268. #ifdef STACK_USE_TCP 
  269.  
  270. TCPDisconnect(MySock); 
  271.  
  272. #endif 
  273.  
  274. MySock = INVALID_SOCKET; 
  275.  
  276. _asm 
  277.  
  278. goto APP_START 
  279.  
  280. _endasm 
  281.  
  282. break
  283.  

如何使用

1. 使用MCC18编译PhnBoot_v2.0,

2. 使用pickit3烧录PhnBoot_v2.0的Hex文件到目标板中。

3. 拔除pickit3烧录器

4. 将目标板与PC的接入同一局域网,并设置PC的IP地址和目标板的IP地址为同一网域,打开PhnLoader_v2.0用户界面,选择IP, 端口,和通信协议。

5. 点击PhnLoader_v2.0用户界面上的“.."按钮加载需要烧录的应用程序Hex文件 (注意:由于PhnBoot_v2.0占用了程序存储器头部0x4C00程序字,所以应用程序编译需要设置Code offset为0x4C00)。

6. 重启目标板,接着立刻在PhnLoader_v2.0界面上点击Download按钮。如果超时未点击Download按钮,目标板会自动跳转到上次烧录的应用程序中去。

7. 烧录完毕,再次重启目标板, 2秒后目标板开始正常运行应用程序。

之后每次更新应用程序,只需重复步骤 4 ~ 7 就可以了。

主要特性

本PIC ethernet bootloader有以下主要特性

1. 使用了Microchip免费的TCP/IP Stack,采用TCP协议。

2. C语言写的,MCC18 编译。

3. 非常容易移植。

4. 支持FLASH烧写, 快速,占用空间小。

5. 不支持EEPROM烧写。

6. 支持CONFIG BITS/IDLOC 烧写。



相关内容