原创 pic18f66J60 网络MCU的 网络升级应用程序.

2018-6-20 09:12 1777 17 17 分类: MCU/ 嵌入式 文集: 网络

此文的MCU 可以移植的PIC18F6XXX 网络芯片全系列。 

在网络单片机中,网络协议栈占很大的一块空间,一般不需要改,通常也不知道怎么改。 都是厂家提供的。 
但是应用程序部分,是经常需要改动。 本文就是说明怎么从网络上下载并覆盖应用程序,而不改动协议栈。

以PIC18F66J60 ,MPLAB IDE V8.63 为例: 

    如果需要升级,一般必须把FLASH中的程序 分成2部分,其中一部分是应用程序部分。
具体分的步骤:
1 在18F66J60.LKR 文件中 ,把每一段在什么位置  划分清楚。

CODEPAGE   NAME=vectors     START=0x0            END=0x29           PROTECTED
CODEPAGE   NAME=page       START=0x2A           END=0xCFFF       //用户存储空间
CODEPAGE   NAME=bootldrom   START=0XD000         END=0XEFFF          PROTECTED    //有8K的应用程序升级空间
CODEPAGE   NAME=AppConfigFlash  START=0XF000         END=0XF400          PROTECTED   // 用于保存IP 地址和MAC地址一些配置字
CODEPAGE   NAME=page2 START=0XF401          END=0XFFF7     //用户存储空间
CODEPAGE   NAME=config     START=0xFFF8         END=0xFFFD         PROTECTED
CODEPAGE   NAME=devid      START=0x3FFFFE       END=0x3FFFFF       PROTECTED


从上面可以看出,bootldrom  区域就是我们要改写和升级的区域。 我们要把应用程序全部写入这个区域。 
 整个的LKR文件比较大,如果有人要请给我留言。 

在主程序里要定义这个区域:
  main()
{
init();
while(1)
{
tcpip_task();

#pragma code BOOTROM
app_task();
#pragma code
}
}

这个app_task();  编译器会把这段程序 固定在 bootldrom  区域 。 方便我们要擦写这段应用程序  


PIC18F66J60 的FLASH 擦写和别的FLASH 擦写一样, 一擦除就1个块,即1K字节.哪怕是改动其中的一个字节。
在LKR文件中,可以看到 ,为升级程序 保留了START=0XD000         END=0XEFFF    8K的应用程序升级空间。
如果你的程序量不够,请自行扩展。 

写好程序,编译,烧写进MCU 正常运行。 
如果想修改应用程序。  只需要改动app_task();  编译之后,打开HEX 文件和原来的HEX文件比较,就会发现只有改动的程序部分,HEX文件发生了变化。

怎么替换应用程序呢?

1    PIC18F66J60 是带网络口的, 服务器发送 升级命令包 给MCU;    当MCU 接收到这个命令包后,开始初始化并擦除应用程序的区域。
    擦除需要几秒的时间。

2   然后MCU 陆续的接收到数据包,   并依次写入自己的FLASH。
    我用的是TCP连接,因此一个包的大小固定在512个字节。

3  如果2秒 没有接收到服务器的数据包。 MCU退出写程序,并重启。 


这里面涉及几个程序比较重要:
1  擦FLASH 的程序。   
2 根据HEX文件 来写FLASH的程序。
 
HEX文件的格式是公开的,百度上有详细的介绍。 

官网上的BOOTLOAD 程序是全部改写,和这个比,烧写时间更长点。
因为只是烧写其中的一部分程序,任何人拿到程序也没有什么用。自带加密功能。 

 
以下代码是MICROCHIP 提供。 是对HEX 解码过程。 我认为是最重要的一段程序。
/******************************************************************************
* Function:        static CHAR DecodeHex(BYTE *vHexData, WORD *wHexDataLen, BYTE *vMemoryData, DWORD *dwAddress, BOOL bInitialize)
*
* PreCondition:    None
*
* Input:           *vHexData: Pointer to Intel HEX formatted data to read
* *wHexDataLen: Pointer to a WORD containing the number of wHexData bytes that are available
* *vMemoryData: Pointer to RAM bytes were binary decoded data will be written.  There must be exactly FLASH_WRITE_SIZE bytes available at this address.
* bInitialize: TRUE for very first call, FALSE for all subsequent calls of the same .hex file.
*
* Output:          wHexDataLen: WORD containing the number of wHexData bytes that were processed from the *vHexData stream
* vMemoryData: RAM bytes containing binary data decoded from the hex stream
* dwAddress: DWORD containing the base memory address where you should write the vMemoryData array
* CHAR: < 0 on error (checksum error, illegal data in hex file, etc)
*  0 on success and need more data
*  > 0 on success and end of file record found
*
* Side Effects:    None
*
* Overview:        Converts an Intel HEX file stream into a stream of binary 
* data to be programming into Flash memory.
*
* Note:            None
*****************************************************************************/
static CHAR DecodeHex(BYTE *vHexData, WORD *wHexDataLen, BYTE *vMemoryData, DWORD *dwAddress, BOOL bInitialize)
{
static enum
{
GetColon = 0,
GetRecordLength1,
GetRecordLength0,
GetAddress3,
GetAddress2,
GetAddress1,
GetAddress0,
GetRecordType1,
GetRecordType0,
GetDataBytes1,
GetDataBytes0,
GetChecksum1,
GetChecksum0
} smRecordParser;
static BYTE vRecordByte;
static BYTE vRecordLen;
static WORD_VAL wvVolatileAddress;
static WORD_VAL wvLatchedAddress;
static DWORD_VAL dwvVolatileExtAddress;
static DWORD_VAL dwvLatchedExtAddress;
static DWORD dwTemp;
static BYTE vRecordType;
static BYTE vChecksum;

BYTE vEncodedByte; 
BYTE vDecodedByte;
WORD w;
WORD wHexLen;
BOOL bByteWritten;

// Reset state machine if this is a first time
if(bInitialize)
{
wvVolatileAddress.Val = 0;
wvLatchedAddress.Val = 0;
dwvVolatileExtAddress.Val = 0;
dwvLatchedExtAddress.Val = 0;
smRecordParser = GetColon;
return DECODE_SUCCESS_NEED_MORE_HEXDATA;
}

bByteWritten = FALSE;
wHexLen = *wHexDataLen;
*wHexDataLen = 0;

while(wHexLen)
{
wHexLen--;
vEncodedByte = vHexData[*wHexDataLen];
*wHexDataLen += 1;
vDecodedByte = HexToBin(vEncodedByte);
switch(smRecordParser)
{
case GetColon:
// Skip whitespace
if(vEncodedByte == '\r')
break;
if(vEncodedByte == '\n')
break;
if(vEncodedByte == ' ')
break;

// Make sure this character is a colon
if(vEncodedByte != ':')
return DECODE_ERROR_INVALID_FORMAT;

smRecordParser = GetRecordLength1;
break;
case GetRecordLength1:
vRecordLen = vDecodedByte<<4;
smRecordParser = GetRecordLength0;
break;
case GetRecordLength0:
vRecordLen |= vDecodedByte;
vChecksum = vRecordLen;
smRecordParser = GetAddress3;
break;
case GetAddress3:
wvVolatileAddress.Val = 0;
wvVolatileAddress.v[1] = vDecodedByte<<4;
smRecordParser = GetAddress2;
break;
case GetAddress2:
wvVolatileAddress.v[1] |= vDecodedByte;
vChecksum += wvVolatileAddress.v[1];
smRecordParser = GetAddress1;
break;
case GetAddress1:
wvVolatileAddress.v[0] = vDecodedByte<<4;
smRecordParser = GetAddress0;
break;
case GetAddress0:
wvVolatileAddress.v[0] |= vDecodedByte;
vChecksum += wvVolatileAddress.v[0];
smRecordParser = GetRecordType1;
wvLatchedAddress.Val = wvVolatileAddress.Val;

// Make sure we haven't moved out of this flash write page
if(bByteWritten)
{
dwTemp = dwvLatchedExtAddress.Val + wvLatchedAddress.Val;
if((dwTemp >= *dwAddress + FLASH_WRITE_SIZE) || (dwTemp < *dwAddress))
return DECODE_SUCCESS_NEW_ADDRESS;
}
break;
case GetRecordType1:
vRecordType = vDecodedByte<<4;
smRecordParser = GetRecordType0;
break;
case GetRecordType0:
vRecordType |= vDecodedByte;
vChecksum += vRecordType;
if(vRecordType == 0x01u)  // End of records marker found
{
smRecordParser = GetChecksum1;
return DECODE_SUCCESS_EOF;
}
else if(vRecordType == 0x02u || vRecordType == 0x04u)     // Extended Segment Address Record or Extended Linear Address Record
{
if(vRecordLen != 2u)
return DECODE_ERROR_INVALID_FORMAT;
}
else if(vRecordType != 0x00u) // Unrecognized record type found
{
return DECODE_ERROR_INVALID_FORMAT;
}
smRecordParser = vRecordLen ? GetDataBytes1 : GetChecksum1;
break;
case GetDataBytes1:
vRecordByte = vDecodedByte<<4;
smRecordParser = GetDataBytes0;
break;
case GetDataBytes0:
vRecordLen--;
smRecordParser = GetDataBytes1;
vRecordByte |= vDecodedByte;
vChecksum += vRecordByte;
if(vRecordType == 0x00u)
{
if(!bByteWritten)
{
dwTemp = dwvLatchedExtAddress.Val + wvLatchedAddress.Val;
*dwAddress = dwTemp & ~(FLASH_WRITE_SIZE-1ul);
bByteWritten = TRUE;
}
vMemoryData[wvLatchedAddress.v[0] & (FLASH_WRITE_SIZE-1)] = vRecordByte;
wvLatchedAddress.Val++;
}
else if(vRecordType == 0x02u)
{
// See if this is the high byte or low byte
if(vRecordLen)
{
dwvVolatileExtAddress.v[3] = 0x00;
dwvVolatileExtAddress.v[2] = vRecordByte>>4;
dwvVolatileExtAddress.v[1] = vRecordByte<<4;
}
else
{
dwvVolatileExtAddress.v[1] |= vRecordByte>>4;
dwvVolatileExtAddress.v[0] = vRecordByte<<4;
dwvLatchedExtAddress.Val = dwvVolatileExtAddress.Val;
}
}
else if(vRecordType == 0x04u)
{
// See if this is the high byte or low byte
if(vRecordLen)
{
dwvVolatileExtAddress.v[3] = vRecordByte;
dwvVolatileExtAddress.v[1] = 0x00;
dwvVolatileExtAddress.v[0] = 0x00;
}
else
{
dwvVolatileExtAddress.v[2] = vRecordByte;
dwvLatchedExtAddress.Val = dwvVolatileExtAddress.Val;
}
}

if(vRecordLen == 0u)
smRecordParser = GetChecksum1;

// Make sure we haven't moved out of this flash write page
if(bByteWritten)
{
dwTemp = dwvLatchedExtAddress.Val + wvLatchedAddress.Val;
if((dwTemp >= *dwAddress + FLASH_WRITE_SIZE) || (dwTemp < *dwAddress))
return DECODE_SUCCESS_NEW_ADDRESS;
}

break;
case GetChecksum1:
vRecordByte = vDecodedByte<<4;
smRecordParser = GetChecksum0;
break;
case GetChecksum0:
vRecordByte |= vDecodedByte;
if(vRecordByte != (~vChecksum + 1))
return DECODE_ERROR_CHECKSUM;
smRecordParser = GetColon;
break;
}
}

return DECODE_SUCCESS_NEED_MORE_HEXDATA;
}


怎样制作升级包呢?
这要用到Uedit.    用Uedit  打开HEX文件。    可以清楚的看到程序的定位。
       HEX 文件是带烧写位置。
      把0XD000到0XEFFF 之外的数据删除就可以了。  这样就得到了一个升级包。
这个升级包只是一个程序,还需要加个256字节的头文件。

这样程序在下载时,先把第一个256字节 发出去。然后软件等等10秒后,以设定的时间间隔(默认是100MS) 陆续发送其他的数据。
MCU在接收到第一个数据包后,有10秒的时间可以擦除应用程序的块。  
10秒后,接收到数据,然后写进去就快了,100MS足够。

这只是个简单的通讯。 应用在PC 和MCU 直接 网络通讯的基础。 没有应答。 MCU 只接收数据,不回应信息。

这个软件是自己写的,有需要的请到WWW.QDYKKJ.COM 上下载,或给我留言。




    















PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
17
关闭 站长推荐上一条 /3 下一条