Administrator
发布于 2025-04-01 / 4 阅读
0
0

MCU升级-OTA

1,支持串口和4G模块升级

2,升级流程

参照TRIO-V5的IAP

3, VC-PRJ有关函数和流程

image

1,勾起CPU-IAP checkBox()

2,MCU特殊地址(0xFFFC)写4个字节表示iap开始,返回校验和作为ack

3,上位机MUDP收到ack后给主控发消息WM_LAN_ACK_EP

::SendMessage(hpMsg,WM_LAN_ACK_EP,(WPARAM) iPackLen,(LPARAM)dataBody);//¶ÔGPSVIEWÀàPostÏûÏ¢£¬OK

4,主控消息处理GetLanAckEp,如果当前状态是PROG_S_DISABLE,且4字节是正确校验和返回,烧写按钮(IDC_BUTTON_PROG)有效

5,用户点击烧写按钮,处理完待烧写数据(OnButtonPreBuf)后,主控烧写线程progEprom进入下一个环节PROG_S_READY

6,主控烧写线程progEpromPROG_S_READY,计算好烧写参数后,流转到PROG_S_FILL_BUF环节,分片(128字节)写入mcu-eeprom,进入PROG_S_CHECK_SUM

包括块对齐,即烧写区固定为128数据,尾部填充0xFF

7,主控烧写线程progEpromPROG_S_CHECK_SUM

如果校验成功,如未写完,地址增加返回progEpromPROG_S_READY

如写完,进入最后PROG_S_EPMARK

校验失败,重写此片数据(地址不变),返回progEpromPROG_S_READY

8,主控烧写线程progEpromPROG_S_EPMARK,写8个特殊数据(总校验和4B,长度(总字节数)2B,长度取反2B,以及iap标志0x9525_52BB)到特殊地址(0xFFF4), 进入PROG_S_EPCHECK环节

9,主控烧写线程progEpromPROG_S_EPCHECK,并不处理具体事务,等待GetLanAckEp接收ACK帧,收到后计算校验和sum4k_rcv

(if(state_eprom == PROG_S_EPCHECK) state_eprom = PROG_S_OVER ;)

10,主控烧写线程progEpromPROG_S_OVER,如校验成功,发送mcu复位命令,切换至PROG_S_DISABLE

其余:mcu重启,boot检查是否有iap标志0x9525_52BB,如有,

附录

1,CheckCpuIap

void CParaAnalyseDlg::CheckCpuIap()
{
BYTE cmd[4], buf[128];

cmd[0]= 0xff;
cmd[1]= 0xfc;

cmd[2]= 0;
cmd[3]= 4;

buf[0] = 0x11;
buf[1] = 0x22;
buf[2] = 0x33;
buf[3] = 0x44;

m_UDP.OnSend(NCMD_EPROM_PROG, buf, cmd); //
}

2,Ack处理函数

void CParaAnalyseDlg::GetLanAckEp(WPARAM wParam, LPARAM lParam)
{
BYTE type;
BYTE buf[16];
BYTE ep_ack_check[4]={0x00,0x00,0x00,0xaa};
int i;
ZeroMemory(buf, wParam);
memcpy(buf, (BYTE*) lParam, wParam);

if(wParam==4)//message from main dlg's i/f
{
if(state_eprom == PROG_S_DISABLE)
{
i=memcmp(buf,ep_ack_check,4);
if(i==0)
{
//state_eprom = PROG_S_READY ;
((CButton*)GetDlgItem(IDC_BUTTON_PROG))->EnableWindow(TRUE);
return;
}
}
sum4k_rcv = Merge4B(buf[0],buf[1],buf[2],buf[3]);
if(state_eprom == PROG_S_EPCHECK) state_eprom = PROG_S_OVER ;
else state_eprom = PROG_S_CHECK_SUM ;
TRACE("\r\n Just got 128B sum ack. \r\n");

}
}

3,烧写线程函数progEprom

void CParaAnalyseDlg::progEprom()
{

int i=0,j=0,k=0,m=0,t,sub=0,sec=0;
BYTE buf[12];
UINT32 iLen = 0, sum4k=0, sum_all=0;
float ratio;
WORD waddr,wlen;

while(1)
{

if(state_eprom == PROG_S_DISABLE)
{
Sleep(100);
}
else
{
switch(state_eprom)
{
case PROG_S_READY:
memset(ProgFileBuf,0xff,PROG_FILE_MAX);
memcpy(ProgFileBuf,prog_file_buf,prog_file_len);
iLen = prog_file_len;
i=iLen/FLASH_PAGE_LEN;
j=iLen%FLASH_PAGE_LEN;
if(j) i+=1; //填满 EPROM_PAGE_LEN 块, 不足
i=i*4;//换算成整数个128 PAGE
state_eprom = PROG_S_FILL_BUF ;
k = 0;
m_progressUpdate.SetPos(0);
sum_all = 0 ;
progErr = 0 ;
break;

case PROG_S_FILL_BUF:
{

waddr = k*128;
buf[0] = (BYTE)(waddr >>8);
buf[1] = (BYTE)(waddr );
buf[2] = (BYTE)(0 );
buf[3] = (BYTE)(128 );

m_UDP.OnSend(NCMD_EPROM_PROG, &ProgFileBuf[EPROM_PAGE_LEN*k], buf); //发送4个1K数据到MCU,以填满4096缓冲
// if(m<(PROG_BUFFER_PARTS-1)) Sleep(WAIT_BUF_1K);

sum4k = 0 ;
for(t=0;t<EPROM_PAGE_LEN;t++)
{
sum4k += ProgFileBuf[EPROM_PAGE_LEN*k+t];
}
// sum_all += sum4k ; //总校验和,最后写入EEPROM FLAG区-12字节
//state_prog = PROG_S_WAIT_CHECK ;
state_eprom = PROG_S_DISABLE;
TRACE("\r\n Just wrote 128B to EPROM @0x%X \r\n",waddr);
SetTimer(TIMER_WAIT_EPROM,3000,NULL);
}

break;

case PROG_S_CHECK_SUM:
if(sum4k_rcv == sum4k)
{
if(k<i-1)
{
k++;
state_eprom = PROG_S_FILL_BUF ;
}
else
{
wlen = (k+1)*128;
state_eprom = PROG_S_EPMARK;
}

TRACE("\r\n Check sum OK. \r\n");
ratio=(float)(k+1);
ratio/=i;
ratio*=100;
m_progressUpdate.SetPos((int)ratio);

//--总校验和,最后写入EEPROM FLAG区-12字节--
sum_all += sum4k ; //每次正确校验后再加,否则或写错,造成升级失败 2025-02-17

}
else
{
state_eprom = PROG_S_FILL_BUF ;//校验和失败,重写此128B块
TRACE("\r\n Check sum failure, try again. sum4k_rcv=0x%08X. sumOriginal=0x%08X\r\n",sum4k_rcv,sum4k);

}
break;

case PROG_S_EPMARK:
buf[0] = (BYTE)(sum_all>>24);
buf[1] = (BYTE)(sum_all>>16);
buf[2] = (BYTE)(sum_all>>8);
buf[3] = (BYTE)(sum_all>>0);

buf[4] = (BYTE)(wlen>>8);
buf[5] = (BYTE)(wlen>>0);
buf[6] = (BYTE)(0xff-buf[4]);
buf[7] = (BYTE)(0xff-buf[5]);

buf[8] = (BYTE)(0x95);
buf[9] = (BYTE)(0x25);
buf[10] = (BYTE)(0x52);
buf[11] = (BYTE)(0xbb);

MakeEpFlag(buf);
state_eprom = PROG_S_EPCHECK;
TRACE("\r\n Make eeprom flag for Bootloader... \r\n");
break;

case PROG_S_EPCHECK:
Sleep(300);
break;

case PROG_S_OVER:

if(sum4k_rcv == sum_mark)
{

//-烧写完成,自动重启动,
iap_busy = FALSE ;//告诉UDP可以继续处理状态包2025-2-14

TRACE("\r\n Check sum-all OK, to reset... \r\n");
buf[0]=PROG_TIPS_OVER;
PostMessage(WM_LAN_ACK_IAP,(WPARAM) 2,(LPARAM)buf);//对GPSVIEW类Post消息,OK
Sleep(500);

m_UDP.OnSend(NCMD_RESET, NULL, NULL);

state_eprom = PROG_S_DISABLE;
((CButton)GetDlgItem(IDC_BUTTON_PROG))->EnableWindow(FALSE);

((CButton)GetDlgItem(IDC_BUTTON_PROG))->SetWindowText("--");
}
else
{
state_eprom = PROG_S_EPMARK;
TRACE("\r\n Check sum-all failed. Try again.. \r\n");
}
KillTimer(TIMER_WAIT_EPROM);
ShowEndProg(END_PROG_OK);
break;

default:
Sleep(100);
break;

}//end of switch

}//end of else

}//end of while

}

4,结束处理函数

void CParaAnalyseDlg::MakeEpFlag(BYTE*ibuf)
{
BYTE cmd[4], buf[128];

cmd[0]= 0xff;
cmd[1]= 0xf4; //start of FLAG address

cmd[2]= 0;
cmd[3]= 12;

sum_mark = 0 ;
for(int i=0; i<12;i++)
{
buf[i] = ibuf[i] ;
sum_mark += buf[i] ;

}
// for(i=0; i<16;i++)
// {
// sum_mark += buf[i] ;
//
// }
//

m_UDP.OnSend(NCMD_EPROM_PROG, buf, cmd); //
}

5,烧写文件准备--烧写按钮响应

void CParaAnalyseDlg::OnButtonPreBuf()
{

//--1 启动后的前三关键字节拷贝到烧写缓冲区--
for(int i=0;i<3;i++) prog_file_buf[i]=pre_file_buf[i];

//--2 拷贝对应偏移量+3后面的数据,接着开机三字节放入待烧写区
if(m_checkRcv==FALSE)
{
tlen = iLen - BIN_CPU_OFFSET;//去掉偏移的16KB(cpu) 2024-7-13
for(i=3;i<tlen;i++) prog_file_buf[i] = pre_file_buf[0x4000+i];

}
else
{
tlen = iLen - BIN_RCV_OFFSET;//去掉偏移的4KB(rcv) 2024-7-13
for(i=3;i<tlen;i++) prog_file_buf[i] = pre_file_buf[0x1000+i];

}

6,mcu-BootLoader的处理

#define ADDR_EEIAP_MARK 0xFFF4

u8 eepromIap(void)
{
u32 sum, sum_rd, sum_all,sum_flash;
u16 waddr,addr,len,i,j,k;
u8 buf[128];

Read24c64(ADDR_EEIAP_MARK,buf,12);
// printf("Mark: %02bX %02bX %02bX %02bX -- %02bX %02bX %02bX %02bX %02bX %02bX %02bX %02bX \r\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);

//--1 校验IAP标记
if(buf[8]!=0x95 || buf[9]!=0x25 || buf[10]!=0x52 || buf[11]!=0xbb )
{
//no mark reqyure IAP from eeprom return
return ERROR_IAP_MARK ;
}
// printf("4B Mark ok \r\n");

//--2 校验IAP长度
len = Merge2B(buf[4],buf[5]);
if(len==0) return ERROR_IAP_LEN;

i = Merge2B(buf[6],buf[7]);
if((i+len)!=0xffff) return ERROR_IAP_LENN ;

// printf("Len ok %hX\r\n",len);

//--3 获取IAP数据的校验和
sum_rd = Merge4B(buf[0],buf[1],buf[2],buf[3]);
// printf("sum_rd=%lX \r\n",sum_rd);

//--4 校验eeprom的IAP数据
j=len>>7;//iap数据是128的倍数
//j=len;//上位机软件存的长度就是128的倍数,即写了多少个128的Page
addr=0;
sum=0;
for(i=0;i<j;i++)
{
Read24c64(addr,buf,128);
for(k=0;k<128;k++) sum += buf[k];
addr += 128 ;
}

if(sum!=sum_rd)
{
// printf("Bad sum %lX j=%hu\r\n",sum,j);
return ERROR_IAP_SUM;
}

sum_all = 0 ;
sum_flash = 0;
// printf("SUM ALL=%lX \r\n",sum_all);

//--5 写入数据到MCU-flash
j = len>>9;
/// printf("Prog =%hX j=%hx \r\n",len,j);
waddr = 0 ;//
addr = 0;
for(k=0; k<j; k++) //j个512扇区
{

//--读512出eeprom并计算检验和
Read24c64(addr,gLanRxBuf,512);
sum=0;
for(i=0;i<512;i++) sum += gLanRxBuf[i];
waddr = addr + 0x4000 ;
sum_all += sum ;

//--写入512到flash并回读校验和
iap_erase(waddr);
Delayms(100);

for(i=0;i<512;i++)
{
iap_write(waddr+i,gLanRxBuf[i]);
nopx();
//sendu(gLanRxBuf[i]);
}
//Delayms(100);
sum_rd = 0 ;
for(i=0;i<512;i++)
{

buf[0] = iap_read(waddr+i);
//sendu(buf[0]);
sum_rd += buf[0];
}

//
if(sum == sum_rd)
{
beep(10);
addr += 512 ;
sum_flash += sum ;
}
else beep(300); //失败就一直写
//printf("Prog Flash Addr=%h04X, sum=%lX / rd_sum=%lX \r\n",waddr,sum,sum_rd);

}
// printf("SUM_FLASH=%lX (%lx)\r\n",sum_flash,sum_all);

// Read24c64(ADDR_EEIAP_MARK,buf,12);
//
// printf("Mark: %02bX %02bX %02bX %02bX -- %02bX %02bX %02bX %02bX %02bX %02bX %02bX %02bX \r\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);

for(i=0;i<4;i++) buf[i] = 0x00;
Write24c64(ADDR_EEIAP_MARK+8,buf,4); //擦掉最后四字节,下次就不会再刷新代码了。
printf("Set User\r\n");
// sendu('E'); sendu('E'); sendu('I'); sendu('A'); sendu('P'); sendu('O'); sendu('K');
// sendu(0x0d);sendu(0x0a);
return 0x30 ;

}


评论