作者:richardL
摘自:佐格
应佐格公司FFT先生邀请,谈谈在F149的TCP/IP功能上的增强,实际上我接触430的时间很短(FFT,moom知道,只有半年多),只是天天跟CaptureCIS+Allegro,IAR Compiler, CodeWright, VisualC++, cvs, gcc打交道,方方面面都要做(谁叫我在小公司打工呢),上手快些而已.就如FFT所说,希望在不触及(或自己不觉得影响到)商业/工业秘密的时候,各位同行多多交流交流,互促进步. 程序以TI网站上原码为最初原型(其实当初我最早想移植uIP的,后来看到时间紧,放弃了)
1.文件结构调整
当我一看到TI的范例,就对其结构不已为然,通篇项目只有一个C,其他都是关联近来的,这完全不时一个标准C/C++程序员的风格嘛,对功能扩展,错误跟踪分析不利,所以我第一件事就改了文件结构划分.
使各个C文件单独编译,最后连接,其他应用程序当然是单独的文件了, 免得在调试和分析的时候在整个项目到处乱翻编量和代码实现.
另外由于Socket的应用显然包括网络的两端,因此单独列出一两个H文件,方便和其他系统C的编译连接(如port定义,各个socket命令参数,格式等),由于平台的相关性,强烈建议采用uIP等的数据类型定义方式,以免以后与32位系统socket联调时麻烦.
2.FLASH区域的详细划分:
主要代码区
应用数据区
系统升级代码及永久数据区(永久数据包括MAC地址等,注意IP地址并不在此内,其实IP,Gateway,等参数随时可能改变)
网络信息区(IP地址,SubNetMask,别名等等)
系统版本区(软硬件版本信息,只有系统在线自升级才修改,保留信息结构的一致性,要有扩充余地,方便高端程序对该设备基本软硬件信息的访问)
中断向量区(系统固定,但软件在线升级时要更改, 因为很可能你的产品安装后要在线升级软件版本,而你的新的软件版本改变了中断方式和中断数量!!!)
3.编译器连接器的设定
配合FLASH的划分,设定相关的连接参数: 我的如下XXXX代表应用层)
-cMSP430
-Z(DATA)UDATA0,IDATA0,ECSTR=0200-09FF
-Z(DATA)CSTACK#0200-0A00 //keep 300b+(12c) for stack!!! (check map file)
-Z(CODE)INFO=1000-10FF
// Main memory (FLASH)
//map of msp430f149
//1000-107f Segment B (system mode) (128 bytes) Segment B
//1080-10ff Segment A (node-net info) (128 bytes) Segment A
//1100-11ff NOT USED ..............!!!!!!!!!!.................. (256 bytes) Segment 119# //special for F149!!! see slas272d.pdf P12
//1200-91ff xxxx-zone (32k bytes) Segment 55# - 118#
//9200-f9ff general code,const -- changed under system upgraded (26k bytes) Segment 3# - 54# (512B/seg)
//fa00-fbff chip info --maybe changed under system upgraded (512 bytes) Segment 2#
//fc00-fdff mac address --never change!!! (512 bytes) Segment 1#
//fe00-ffff interrupt vectors -- maybe changed (512 bytes) Segment 0# -w/Interrupt Vectors
-Z(CODE)SYS_MODE_TABLE=1000-107F
-Z(CODE)NODE_INFO_TABLE=1080-10FF
-Z(CODE)xxxx_ZONE=1200-91FF
-Z(CODE)CODE,CONST,CSTR,CDATA0,CCSTR=9200-f9ff
-Z(CODE)CHIP_INFO=fa00-fbff
-Z(CODE)MAC_TABLE=fc00-Fdff
-Z(CODE)INTVEC=FFE0-FFFF
4.变量和代码的优化
TCP/IP协议中用了很多全局/局部变量,我更改了一些简单变量为结构模式,有些直接放在响应的FLASH区,从而提高代码的效率和节约了内存.有些函数的参数尽量减少到2个以内(或分开列出),而有些函数根本不必要单独列出,用宏来代替就可以了(效率),以下范例:
a. 合并两个重复的代码:
//#define SwapBytes(Data) ((u16_t)((Data >> 8) | (Data << 8)))
#define SWAPB(Word) ((u16_t)((Word) << 8) | ((Word) >> 8))
b. TxTCPBuffer 和 RxTCPBuffer 的直接访问(不用多次拷贝----浪费内寸和时间!!!)
extern u8_t RxTCPBuffer[MSP_MAX_TCP_RX_DATA_SIZE]; // space for incoming TCP-data
//pointer of RX_BUFFER
#define TCP_REQ_PTR_KEY ((u8_t *)TCP_RX_BUF)
#define TCP_REQ_PTR_ORDER ((u8_t *)TCP_REQ_PTR_KEY +sizeof(u32_t))
#define TCP_REQ_PTR_LEN ((u8_t *)TCP_REQ_PTR_ORDER +sizeof(u16_t))
#define TCP_REQ_PTR_DATA ((u8_t *)TCP_REQ_PTR_LEN +sizeof(u16_t))
#define TCP_REQ_KEY ((u32_t)*((u32_t *)TCP_REQ_PTR_KEY))
#define TCP_REQ_ORDER ((u16_t)*((u16_t *)TCP_REQ_PTR_ORDER))
#define TCP_REQ_LEN ((u16_t)*((u16_t *)TCP_REQ_PTR_LEN))
#define TCP_REQ_DATA_U8 ((u8_t)*((u8_t *)TCP_REQ_PTR_DATA))
#define TCP_REQ_DATA_U16 ((u16_t)*((u16_t *)TCP_REQ_PTR_DATA))
#define TCP_REQ_DATA_U32 ((u32_t)*((u32_t *)TCP_REQ_PTR_DATA))
#define TCP_REQ_DATA_U16A TCP_REQ_DATA_U16 //first 16bit
#define TCP_REQ_DATA_U16B ((u16_t)*((u16_t *)(TCP_REQ_PTR_DATA+2)))
c. 定义通用head file
#ifdef PLATFORM_MSP //special for MSP 16bit
typedef unsigned char u8_t;
typedef signed char s8_t;
typedef unsigned int u16_t;
typedef signed int s16_t;
typedef unsigned long u32_t;
typedef signed long s32_t;
#else //Win32 Linux Unix ---Client 32bit-platform
typedef unsigned char u8_t;
typedef signed char s8_t;
typedef unsigned short u16_t;
typedef signed short s16_t;
typedef unsigned int u32_t;
typedef signed int s32_t;
#endif
//packet id define for client/MSP
#define PKTID_GENERAL 'G0'
#define PKTID_CHANGE_MODE (PKTID_GENERAL+0 ) //change mode between (XXXXserver/ SYSburnserver/ HTTPserver)
#define PKTID_DUMP_FLASH (PKTID_GENERAL+1 ) //download ANY datas from any positon in flash-mem Data-Zone
....
typedef struct struReqHead_XXX{
u32_t key; // key for security
u16_t order; // same as order_code (see above) = PKTID_...
u16_t len;
}struReqHead;
typedef struct struNetInfo_XXX{
u16_t MyIP[2];
u16_t MasterIP[2];
u16_t SubnetMask[2];
u16_t GatewayIP[2];
}struNetInfo;
//for protocal_support;
#define PROTOCAL_SUPPORT 0x0001
#define PROTOCOL_ICMP (PROTOCAL_SUPPORT<<0)
#define PROTOCOL_BROADCAST (PROTOCAL_SUPPORT<<1)
#define PROTOCAL_FULL_SUPPORT (PROTOCOL_ICMP|PROTOCOL_BROADCAST)
#define TCP_PORT_XXX 4112
#define TCP_PORT_MASTER 5001
d. 几个优化:
#define SendFrame1() {CopyToFrame8900(&TxFrame1, TxFrame1Size);}
#define SendFrame2() {CopyToFrame8900(&TxFrame2, TxFrame2Size);}
#define TCPRestartTimer() {TCPTimer = 0;}
#define TCPStopTimer() {TCPFlags &= ~TCP_TIMER_RUNNING;}
将CalcChecksum()划分成两个, 内部代码做简单优化(在C-spy中看看汇编就知道了)
u16_t CalcChecksum(...)
{
最开始
u32_t Sum = 0;
...
最后
Sum = (Sum >> 16) + (Sum & 0xffff); // add hi 16 to low 16
Sum += (Sum >> 16); // add carry
return ~Sum;
}
5. BROADCAST功能支持的扩展
6. UDP的扩展(很容易的,见:http://www.faqs.org/rfcs/rfc768.html)
7. 系统在线升级:
基本流程(直到旧系统擦除前,随时可以Cancel)
a.模式切换(权限验证)
b.代码dump(由于接收缓冲区小,这是最耗时间的地方,目前12K的新系统代码dump所需时间大概15秒)
c.代码验证
d.全部FLASH擦除(除了系统升级代码区和dump区).
e.代码解析和复制
f.reboot. #define REBOOT {WDTCTL=0x1000;} //(PUC) is generated see slau049.pdf p146
注:在d和e运行其间如果掉电的话,就只有到现场去取下设备来JXXX/BSL编程了.
8. 其他
a. 转换程序:将msp430.txt格式的代码文件转为适合在线系统升级的数据包(编码前---保护版权哦)
int main(int argc, char* argv[])
{
FILE *fin,*fout;
char fout_name[80];
struXXXBlock block;
int version;
char line[80];
char line_bin[40];
char addr_str[5];
addr_str[4]=0;
printf("\n\t%s (%s)\n\t\t%s %s\n\n",__PRODUCT__,__PRODUCTVERSION__,__COMPANY__,__COPYRIGHT__);
if (argc<=2) {
printf("\tformat error! usage: CCC input_file_name version\n\texample: CCC xxx.d43 101\n";
return -1;
}
if( (fin= fopen( argv[1], "rt" )) == NULL )
{
printf( "The file %s was not opened\n",argv[1]);
return -1;
}
if ((version=atoi(argv[2]))==0)
{
printf( "\tversion should not be ZERO!\n";
return -1;
}
sprintf(fout_name,"XXX%04d.sys",version);
printf("\tinput: \t%s\n\toutput: \t%s\n",argv[1],fout_name);
int linecnt=0;
block.len=0;
block.addr=0;
while ( fgets( line, 100, fin) != NULL)
{
if (line[0]=='@')
{
if (block.len>0) write_block(&block);
memcpy(addr_str,&line[1],4);//get addr
block.addr=hex2uint(addr_str);
block.len=0;
}
else if (line[0]=='q')
{
if (block.len>0) write_block(&block);
break; //end of code
}
else
{
int len=asc2char(&line[0],&line_bin[0]);
if ((block.len+len)>=MAX_BLOCK_LEN)
{
int num=MAX_BLOCK_LEN-block.len;
memcpy(&block.buff[block.len],&line_bin[0],num);
block.len=MAX_BLOCK_LEN;
write_block(&block);
block.len=len-num;
memcpy(&block.buff[0],&line_bin[num-1],block.len);
block.addr+=MAX_BLOCK_LEN;
}
else
{
memcpy(&block.buff[block.len],&line_bin[0],len);
block.len+=len;
}
}
}
//write end block (addr=len=0)
block.len=0;
block.addr=0;
write_block(&block);
u16_t checksum=CalcChecksum(&TotalBlock[0],TotalBlock_Len);
printf("\tlenght: \t%d bytes\n\tchecksum: \t%04x\n",TotalBlock_Len+2,checksum);
memcpy(&TotalBlock[TotalBlock_Len],&checksum,2);
TotalBlock_Len+=2;
fclose(fin);
//save to file
if ((fout=fopen(fout_name,"wb") ==NULL)
{
printf( "The file %s can not be created\n",fout_name);
return -1;
}
fwrite(&TotalBlock[0],TotalBlock_Len,1,fout);
fflush(fout);
fclose(fout);
printf("\tdone.(OK)\n",argv[1],fout_name);
return 0;
}
b. 升级程序:将新版文件通过socket通讯来在线升级(编码前---保护版权哦)
BOOL CSystemBurner::OnSocketReceive(int iErrorCode)
{
try
{
if (iErrorCode)
{
printf("SocketReceive error\n";Close();return FALSE;
}
char cBuffer[1024];
int iRecv=Receive(cBuffer,sizeof(cBuffer));
struAnswerHead head;
memcpy((unsigned char *)&head,cBuffer,sizeof(struAnswerHead));
if (iRecv!=(head.len+sizeof(struAnswerHead)))
{ printf("len is not matched.\n";
return FALSE;
}
char TxBuffer[128];
int len;
struct struDumpBlk_XXX{
union {
char full[SMALL_BLK_SIZE];
struct {
u16_t offset;
char code[SMALL_BLK_SIZE-2];
}S;
}U;
} dumpBlk;
u16_t RxData;
switch (head.id)
{
case PKTID_CHANGE_MODE:
m_status=CSystemBurner:UMP;
len=CreatePacketOrder(TxBuffer, PKTID_SYS_DUMP, NULL,0);
Send(TxBuffer,len);
printf("change mode OK.\nstart dumping....% 3d/% 3d",m_small_blk_cnt+1,m_small_blk_num);
m_small_blk_cnt++;
m_CodeOffset+=SMALL_BLK_SIZE-2;
break;
//keep dump or do next step-checksum
case PKTID_SYS_DUMP:
if (m_small_blk_cnt>=m_small_blk_num) { //finish dump
printf("\ndump done(OK).\n";
m_status=CSystemBurner::CHECKSUM;
len=CreatePacketOrder(TxBuffer, PKTID_SYS_CHECKSUM, NULL,0);
Send(TxBuffer,len);
printf("do check ...\t";
}
else {
dumpBlk.U.S.offset=m_small_blk_cnt;
memcpy(dumpBlk.U.S.code,m_pCode+m_CodeOffset,SMALL_BLK_SIZE-2);
m_status=CSystemBurner:UMP;
len=CreatePacketOrder(TxBuffer, PKTID_SYS_DUMP, dumpBlk.U.full,SMALL_BLK_SIZE);
Send(TxBuffer,len);
printf("\b\b\b\b\b\b\b% 3d/% 3d",m_small_blk_cnt+1,m_small_blk_num);
m_small_blk_cnt++;
m_CodeOffset+=SMALL_BLK_SIZE-2;
}
break;
//checksum correct then do BURN
case PKTID_SYS_CHECKSUM:
memcpy((unsigned char *)&RxData,cBuffer+sizeof(struAnswerHead),sizeof(u16_t));
printf("checksum OK. ----- value=%04x\n",RxData);
m_status=CSystemBurner::BURN;
len=CreatePacketOrder(TxBuffer, PKTID_SYS_BURN, NULL,0);
Send(TxBuffer,len);
printf("do burn ...\t";
break;
//last receive before burning!
case PKTID_SYS_BURN:
m_status=CSystemBurner::BURN_CONFIRMED;
printf("received PKTID_SYS_BURN.\n";
break;
//cancel command be confirmed.
case PKTID_SYS_CANCEL:
m_status=CSystemBurner::CANCEL_CONFIRMED;
break;
default:
printf("unknown echo: %08x\n",head.id);
break;
}
return TRUE;
}
ERROR_HANDLER_RETURN("OnSocketReceive",TRUE)
}