历时5个工作日,总算把以太网远程远程固件升级功能完成,期间踩了不少的坑。最初设计的时候参照stm32官方1.26.1标准固件HAL库,新建一个工程,把官方对应的驱动移植到新的工程底下。
因为使用以太网方式BootLoader,由于以太网占用内存及ROM空间比较大,故从0x08000000—0x08020000处128KROM空间分配给BootLoader程序,从0x08020000—0x080FFFFF处896KROM空间分配给用户程序。
分配空间,下载BootLoader程序到主板上,使用http登陆到下载程序界面,点击下载,然后复位,结果用户程序未执行,未能成功。
使用烧录器烧录用户程序到0x08020000处,使用BootLoader引导对比测试,发现跳转以后用户程序正常运行。说明跳转过程没有问题,BootLoader程序跳转正常。通过汇编单步调试,定位到跳转到HardFault_Handler错误中断前代码地址0x080209C0处。同时在同一地址处,比较BootLoader烧录代码与烧录器烧录代码差异,发现在0x080209DA地址处发生了代码差异。故此现象由于FLASH存储的代码差异引起,可能原因如下:
- BootLoader程序接收http传输bin文件发生了错误
- BootLoader程序写FLASH发生了错误
- 编译器生成bin文件发生了错误
最容易排查的是http传输问题。测试使用http接收数据转发网络日志存储,通过存储发现,发送6000多字节,实际接收2000多个字节,判定接收异常。
同时对比使用http调试助手模拟http服务器,测试发现,模拟服务器能够接收完整的数据,判定web客户端发送正常。
同步通过日志打印,最终发现,官方的例程驱动移植过来,连续接收网络数据存在网络丢包现象,故官方的网络驱动接收存在bug,然后重新设计网络驱动程序。最终完美完成远程web方式固件升级。关键代码如下:
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "fsdata.h"
#include "fsdata.c"
#include "main.h"
#include "flash_if.h"
#include <string.h>
#include <stdio.h>
struct fs_file {
char *data;
int len;
};
struct http_state
{
char *file;
u32_t left;
};
typedef enum
{
LoginPage = 0,
FileUploadPage,
UploadDonePage,
ResetDonePage
}htmlpageState;
htmlpageState htmlpage;
static const char http_crnl_2[4] =
/* "\r\n--" */
{0xd, 0xa,0x2d,0x2d};
static const char octet_stream[14] =
/* "octet-stream" */
{0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,0x0d, };
static const char Content_Length[17] =
/* Content Length */
{0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67,0x74, 0x68, 0x3a, 0x20, };
static __IO uint32_t DataFlag=0;
static __IO uint32_t size =0;
__IO uint32_t FlashWriteAddress;
static uint32_t TotalReceived=0;
static char LeftBytesTab[4];
static uint8_t LeftBytes=0;
static __IO uint8_t resetpage=0;
static uint32_t ContentLengthOffset =0,BrowserFlag=0;
static __IO uint32_t TotalData=0, checklogin=0;
void IAP_HTTP_writedata(char * ptr, uint32_t len);
#if LWIP_TCP
static struct tcp_pcb *tcp_echoserver_pcb;
/* ECHO protocol states */
enum tcp_echoserver_states
{
ES_NONE = 0,
ES_ACCEPTED,
ES_RECEIVED,
ES_CLOSING
};
/* structure for maintaing connection infos to be passed as argument
to LwIP callbacks*/
struct tcp_echoserver_struct
{
u8_t state; /* current connection state */
u8_t retries;
struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */
struct pbuf *p; /* pointer on the received/to be transmitted pbuf */
};
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static void tcp_echoserver_error(void *arg, err_t err);
static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb);
static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);
static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);
static int fs_open(char *name, struct fs_file *file)
{
struct fsdata_file_noconst *f;
for (f = (struct fsdata_file_noconst *)FS_ROOT; f != NULL; f = (struct fsdata_file_noconst *)f->next)
{
if (!strcmp(name, f->name))
{
file->data = f->data;
file->len = f->len;
return 1;
}
}
return 0;
}
static uint32_t Parse_Content_Length(char *data, uint32_t len)
{
uint32_t i=0,size=0, S=1;
int32_t j=0;
char sizestring[6], *ptr;
ContentLengthOffset =0;
/* find Content-Length data in packet buffer */
for (i=0;i<len;i++)
{
if (strncmp ((char*)(data+i), Content_Length, 16)==0)
{
ContentLengthOffset = i+16;
break;
}
}
/* read Content-Length value */
if (ContentLengthOffset)
{
i=0;
ptr = (char*)(data + ContentLengthOffset);
while(*(ptr+i)!=0x0d)
{
sizestring[i] = *(ptr+i);
i++;
ContentLengthOffset++;
}
if (i>0)
{
/* transform string data into numeric format */
for(j=i-1;j>=0;j--)
{
size += (sizestring[j]-'0')*S;
S=S*10;
}
}
}
return size;
}
void tcp_echoserver_init(void)
{
err_t err;
tcp_echoserver_pcb = tcp_new(); /* create new tcp pcb */
if (tcp_echoserver_pcb != NULL)
{
err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 80); /* bind echo_pcb to port 7 (ECHO protocol) */
if (err == ERR_OK)
{
tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb); /* start tcp listening for echo_pcb */
tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept); /* initialize LwIP tcp_accept callback function */
}
else
{
memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb); /* deallocate the pcb */
}
}
}
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
err_t ret_err;
struct tcp_echoserver_struct *es;
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
tcp_setprio(newpcb, TCP_PRIO_MIN); /* set priority for the newly accepted tcp connection newpcb */
/* allocate structure es to maintain tcp connection informations */
es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));
if (es != NULL)
{
es->state = ES_ACCEPTED;
es->pcb = newpcb;
es->retries = 0;
es->p = NULL;
tcp_arg(newpcb, es); /* pass newly allocated es structure as argument to newpcb */
tcp_recv(newpcb, tcp_echoserver_recv); /* initialize lwip tcp_recv callback function for newpcb */
tcp_err(newpcb, tcp_echoserver_error); /* initialize lwip tcp_err callback function for newpcb */
tcp_poll(newpcb, tcp_echoserver_poll, 0); /* initialize lwip tcp_poll callback function for newpcb */
ret_err = ERR_OK;
}
else
{
tcp_echoserver_connection_close(newpcb, es); /* close tcp connection */
ret_err = ERR_MEM; /* return memory error */
}
return ret_err;
}
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
struct tcp_echoserver_struct *es;
err_t ret_err;
struct pbuf *ptr;
LWIP_ASSERT("arg != NULL",arg != NULL);
es = (struct tcp_echoserver_struct *)arg;
/* if we receive an empty tcp frame from client => close connection */
if (p == NULL)
{
es->state = ES_CLOSING; /* remote host closed connection */
if(es->p == NULL)
{
tcp_echoserver_connection_close(tpcb, es); /* we're done sending, close connection */
}
else
{ /* we're not done yet */
tcp_sent(tpcb, tcp_echoserver_sent); /* acknowledge received packet */
tcp_echoserver_send(tpcb, es); /* send remaining data*/
}
ret_err = ERR_OK;
}
/* else : a non empty frame was received from client but for some reason err != ERR_OK */
else if(err != ERR_OK)
{
if (p != NULL) /* free received pbuf*/
{
es->p = NULL;
pbuf_free(p);
}
ret_err = err;
}
else if(es->state == ES_ACCEPTED)
{
es->state = ES_RECEIVED; /* first data chunk in p->payload */
es->p = p; /* store reference to incoming pbuf (chain) */
tcp_sent(tpcb, tcp_echoserver_sent); /* initialize LwIP tcp_sent callback function */
tcp_echoserver_send(tpcb, es); /* send back the received data (echo) */
ret_err = ERR_OK;
}
else if (es->state == ES_RECEIVED)
{
/* more data received from client and previous data has been already sent*/
if(es->p == NULL)
{
es->p = p;
tcp_echoserver_send(tpcb, es); /* send back received data */
}
else
{
ptr = es->p; /* chain pbufs to the end of what we recv'ed previously */
pbuf_chain(ptr,p);
}
ret_err = ERR_OK;
}
else if(es->state == ES_CLOSING)
{
tcp_recved(tpcb, p->tot_len); /* odd case, remote side closing twice, trash data */
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
else
{
tcp_recved(tpcb, p->tot_len); /* unkown es->state, trash data */
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
return ret_err;
}
static void tcp_echoserver_error(void *arg, err_t err)
{
struct tcp_echoserver_struct *es;
LWIP_UNUSED_ARG(err);
es = (struct tcp_echoserver_struct *)arg;
if (es != NULL)
{
mem_free(es); /* free es structure */
}
}
static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb)
{
err_t ret_err;
struct tcp_echoserver_struct *es;
es = (struct tcp_echoserver_struct *)arg;
if (es != NULL)
{
if (es->p != NULL)
{
tcp_sent(tpcb, tcp_echoserver_sent);
tcp_echoserver_send(tpcb, es); /* there is a remaining pbuf (chain) , try to send data */
}
else
{
if(es->state == ES_CLOSING) /* no remaining pbuf (chain) */
{
tcp_echoserver_connection_close(tpcb, es); /* close tcp connection */
}
}
ret_err = ERR_OK;
}
else
{
tcp_abort(tpcb); /* nothing to be done */
ret_err = ERR_ABRT;
}
return ret_err;
}
static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
struct tcp_echoserver_struct *es;
LWIP_UNUSED_ARG(len);
es = (struct tcp_echoserver_struct *)arg;
es->retries = 0;
if(es->p != NULL)
{
/* still got pbufs to send */
tcp_sent(tpcb, tcp_echoserver_sent);
tcp_echoserver_send(tpcb, es);
}
else
{
/* if no more data to send and client closed connection*/
if(es->state == ES_CLOSING)
tcp_echoserver_connection_close(tpcb, es);
}
return ERR_OK;
}
err_t TcpSend(unsigned char *pData,int len);
void TcpPrintf(const char *format, ...);
u8_t g_recvBuff[1500];
unsigned int g_len = 0;
unsigned int g_bootBytes=0;
unsigned char g_recvData[10240];
void WebAnalysis(char *data,int len,struct tcp_pcb *tpcb)
{
uint32_t DataOffset, FilenameOffset;
char *ptr,filename[40], login[LOGIN_SIZE+1];
struct fs_file file = {0, 0};
struct http_state *hs;
struct pbuf *ptrData;
int i;
if (strncmp(data, "GET / HTTP", 10) == 0) //首页登陆界面
{
htmlpage = LoginPage;
fs_open("/index.html", &file);
tcp_write(tpcb, file.data, file.len, 1);
}
else if ((strncmp(data, "GET /resetmcu.cgi", 17) ==0)&&(htmlpage == UploadDonePage)) //复位界面
{
htmlpage = ResetDonePage;
fs_open("/reset.html", &file);
tcp_write(tpcb, file.data, file.len, 1);
resetpage = 1;
}
else if ((strncmp(data, "POST /checklogin.cgi",20)==0)&&(htmlpage== LoginPage))
{
ptr = strstr(data,"username=");
if(ptr != NULL)
{
sprintf((char *)login,"username=%s&password=%s",USERID,PASSWORD);
if (strncmp(ptr, (char *)login ,LOGIN_SIZE)==0)
{
htmlpage = FileUploadPage;
fs_open("/upload.html", &file);
}
else
{
htmlpage = LoginPage;
fs_open("/index.html", &file);
}
tcp_write(tpcb, file.data, file.len, 1);
}
}
else if (strncmp(data, "POST /upload.cgi",16)==0||DataFlag >=1)
{
DataOffset =0;
if (DataFlag ==0)
{
BrowserFlag=0;
TotalReceived =0;
size = Parse_Content_Length(data, len);
ptr = strstr(data,octet_stream);
if(ptr != NULL)
{
DataOffset = ptr - data + 16;
}
if (DataOffset==0)
{
DataFlag++;
BrowserFlag = 1;
return;
}
else
{
TotalReceived = len - (ContentLengthOffset + 4);
}
}
if (((DataFlag ==1)&&(BrowserFlag==1)) || ((DataFlag ==0)&&(BrowserFlag==0)))
{
if ((DataFlag ==0)&&(BrowserFlag==0))
{
DataFlag++;
}
else if ((DataFlag ==1)&&(BrowserFlag==1))
{
ptr = strstr(data,octet_stream);
if(ptr != NULL)
{
DataOffset = ptr - data + 16;
}
TotalReceived+=len;
DataFlag++;
}
FilenameOffset = 0;
ptr = strstr(data,"filename=");
if(ptr != NULL)
{
FilenameOffset = ptr - data + 16;
}
i =0;
if (FilenameOffset)
{
while((*(data+FilenameOffset + i)!=0x22 )&&(i < 40))
{
filename[i] = *(data+FilenameOffset + i);
i++;
}
filename[i] = 0x0;
}
if (i==0)
{
htmlpage = FileUploadPage;
fs_open("/upload.html", &file);
tcp_write(tpcb, file.data, file.len, 1);
DataFlag=0;
return;
}
TotalData =0;
FLASH_If_Init();
FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
FlashWriteAddress = USER_FLASH_FIRST_PAGE_ADDRESS;
// TcpPrintf(" State: Programming...\n");
g_bootBytes = 0;
}
else
{
TotalReceived +=len;
}
ptr = (char*)(data + DataOffset);
len-= DataOffset;
TotalData +=len;
if (TotalReceived == size)
{
i=4;
while (strncmp ((char*)(data+ len -i),http_crnl_2 , 4) && (len -i > 0))
{
i++;
}
len-=i;
TotalData-=i;
if (len)
{
memcpy(g_recvData+g_bootBytes,ptr, len);
g_bootBytes += len;
IAP_HTTP_writedata(ptr,len);
}
DataFlag=0;
// TcpPrintf(" State: Prog Finished %d\r\n",g_bootBytes);
htmlpage = UploadDonePage;
fs_open("/uploaddone.html", &file);
tcp_write(tpcb, file.data, file.len, 1);
}
else
{
if(len)
{
memcpy(g_recvData+g_bootBytes,ptr, len);
g_bootBytes += len;
IAP_HTTP_writedata(ptr,len);
}
}
}
else
{
// close_conn(pcb, hs);
}
}
static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
{
struct pbuf *ptr;
err_t wr_err = ERR_OK;
u16_t plen;
u8_t freed;
char *data;
int len;
plen = ptr->len;
len = 0;
memset(g_recvBuff,0,sizeof(g_recvBuff));
while ((wr_err == ERR_OK) && (es->p != NULL) && (es->p->len <= tcp_sndbuf(tpcb)))
{
g_len += ptr->len;
ptr = es->p; /* get pointer on pbuf from es structure */
if(ptr->len > (1500-len))
{
data = (char *)&g_recvBuff[0];
WebAnalysis(data,len,tpcb);
memset(g_recvBuff,0,sizeof(g_recvBuff));
len = 0;
}
memcpy(g_recvBuff+len,ptr->payload, ptr->len);
len += ptr->len;
es->p = ptr->next; /* continue with next pbuf in chain (if any) */
if(es->p != NULL)
{
pbuf_ref(es->p); /* increment reference count for es->p */
}
do /* chop first pbuf from chain */
{
freed = pbuf_free(ptr); /* try hard to free pbuf */
}
while(freed == 0);
tcp_recved(tpcb, plen); /* we can read more data now */
}
data = (char *)&g_recvBuff[0];
WebAnalysis(data,len,tpcb);
}
static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
{
/* remove all callbacks */
tcp_arg(tpcb, NULL);
tcp_sent(tpcb, NULL);
tcp_recv(tpcb, NULL);
tcp_err(tpcb, NULL);
tcp_poll(tpcb, NULL, 10);
/* delete es structure */
if (es != NULL)
{
mem_free(es);
}
/* close tcp connection */
tcp_close(tpcb);
}
void IAP_HTTP_writedata(char * ptr, uint32_t len)
{
uint32_t count, i=0, j=0;
/* check if any left bytes from previous packet transfer*/
/* if it is the case do a concat with new data to create a 32-bit word */
if (LeftBytes)
{
while(LeftBytes<=3)
{
if(len>(j+1))
{
LeftBytesTab[LeftBytes++] = *(ptr+j);
}
else
{
LeftBytesTab[LeftBytes++] = 0xFF;
}
j++;
}
FLASH_If_Write(&FlashWriteAddress, (uint32_t*)(LeftBytesTab),1);
LeftBytes =0;
/* update data pointer */
ptr = (char*)(ptr+j);
len = len -j;
}
/* write received bytes into flash */
count = len/4;
/* check if remaining bytes < 4 */
i= len%4;
if (i>0)
{
if (TotalReceived != size)
{
/* store bytes in LeftBytesTab */
LeftBytes=0;
for(;i>0;i--)
LeftBytesTab[LeftBytes++] = *(char*)(ptr+ len-i);
}
else count++;
}
FLASH_If_Write(&FlashWriteAddress, (uint32_t*)ptr ,count);
}
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/