升级思路:
当升到升级指令时, 将其他不相关的任务杀掉,将can的波特率降到50k(上位机也降到50k), 将收到的数据放在flash的后端,当CRC校验正确,进行搬运,最后升级完成。
1.1 升级指令的帧格式
帧格式
数据格式 字符 说明
帧头 0XAA 帧开始的标志
地址 0XFF 广播地址
数据长度 0X0B 命令+数据域
命令 0X7C
数据域 BYTE0:板类型
0X1:主控板
0X2:列控板
0X3:节控板
0X4:层控板
BYTE1:更新方式
0X1:单板更新
0X2:全部更新
BYTE2:主控板地址
BYTE3:列控板地址
BYTE4:节控板地址
BYTE5:层控板地址
BYTE6:程序占用CAN帧总数量低字节
BYTE7:程序占用CAN帧总数量高字节
BYTE8:程序CRC16低字节
BYTE9:程序CRC16高字节
校验 地址、数据长度、命令、数据域的异或
回复帧
数据格式 字符 说明
帧头 0XAA 帧开始的标志
地址 列控地址
数据长度 0X2
命令 0X7C 取自命令帧
数据域 0 ——- 收到升级指令,并回复
1 ——接收上位机的帧数据完毕,校验和正确
2 — 接收上位机的帧数据完毕,校验和错误
3 — 升级命令完成
校验 地址、数据长度、命令、数据域桉位异或
1.2 接收到升级指令的处理函数:
```
static void BroadCast_MasterCommand_UpdateProgram(unsigned char *p_Can1OneFrameBuff)
{
gt_CanUpdateFLash.Type =p_Can1OneFrameBuff[4] ;
gt_CanUpdateFLash.Mode = p_Can1OneFrameBuff[5] ;
gt_CanUpdateFLash.Main_Borad_Addr= p_Can1OneFrameBuff[6] ;
gt_CanUpdateFLash.Line_Board_Addr= p_Can1OneFrameBuff[7] ;
gt_CanUpdateFLash.Jie_Board_Addr = p_Can1OneFrameBuff[8] ;
gt_CanUpdateFLash.Layer_Board_Addr= p_Can1OneFrameBuff[9] ;
gt_CanUpdateFLash.Frm_Num_low = p_Can1OneFrameBuff[10] ;
gt_CanUpdateFLash.Frm_Num_high = p_Can1OneFrameBuff[11] ;
gt_CanUpdateFLash.CRC_16_low = p_Can1OneFrameBuff[12] ;
gt_CanUpdateFLash.CRC_16_high = p_Can1OneFrameBuff[13] ;
memcpy(& gt_CanUpdateFLashForMove.Type, >_CanUpdateFLash.Type,sizeof(gt_CanUpdateFLash));
这里要注意: gt_CanUpdateFLashForMove 定义的位置 Flash_Update_Struct gt_CanUpdateFLashForMove __attribute__((at(0X2000FC80)))={0} ;
编译器中设定的程序的运行范围为: 0x20000000 -----0 0x2000FC00
因为编译器编译程序时,全局变量编译完后 是以地址的方式存储在flash中,当调用这个变量时,从指定地址存取数, 当程序变化大时,全局变量的地址就变了,所以此处需将这个全局变量结构体地址 编译为固定的。(这样app app1 app2 等 调用这个内存中这个全局变量地址就一致了。)
if(gt_CanUpdateFLash.Type == 0x02) // 列控板
{
if(gt_CanUpdateFLash.Mode == 0x01) //单板更新
{
if (gt_CanUpdateFLash.Line_Board_Addr == gt_Can1Operation.t_Can1Addr.Can1Addr)
{
gt_ParameterVal.UpprogrameFlag =1 ; // 平时为0 ,在motor 任务中, 当要升级时,只进行喂狗的操作,不进行其他的操作
gt_CanUpdateFLash.Flash_Update_Frm_Flag = 2 ;// 更新列控的标志位, 中断函数走这个分支
// 回复上位机
unsigned char coladdr=gt_Can1Operation.t_Can1Addr.Can1Addr ;
unsigned char temp=0 ;//0 ------- 收到升级指令,并回复
gt_Can1Operation.Can1TxDataByProtocal(coladdr,2,MasterCommand_UpdateProgram, &temp);
printf("Single borad will be Updated \r\n");
CAN1_UpprogrameConfiguration(); //将can的波特率更改 成50k
// 擦除 后面的flash 数据,用于存储接收的新can帧数据
int pages,i=0;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); //清除标志位
for(i= (FLASH2_BASE-0x8000000)/2048 ; i<= (FLASH2_BASE-0x8000000)/2048+40 ; i++) // 40 *2 =80k(应用程序的大小不能超过40k)
{
if(FLASH_ErasePage(0x8000000+2048*i) != FLASH_COMPLETE)//擦除需要的页
{
}
}
FLASH_Lock();
DelOthersTask(); //升级过程中,除了喂狗外,其他线程都杀掉
}else
{
gt_ParameterVal.UpprogrameFlag =1 ; // 平时为0 ,在motor 任务中, 当要升级时,只进行喂狗的操作,不进行其他的操作
DelOthersTask(); //删除其他任务,防止干扰can总线
}
}
else if (gt_CanUpdateFLash.Mode == 0x02) // 全部更新
{
/*
// 存储升级参数
FLASH_Unlock(); //解锁FLASH编程擦除控制器
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); //清除标志位
FLASH_ErasePage(FLASH_START_ADDR); //擦除指定地址页
FLASH_ProgramHalfWord(UpdateProgramParameAddr , gt_CanUpdateFLash.Type );
FLASH_ProgramHalfWord(UpdateProgramParameAddr+2 ,gt_CanUpdateFLash.Mode);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+4 ,gt_CanUpdateFLash.Main_Borad_Addr);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+6 ,gt_CanUpdateFLash.Line_Board_Addr);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+8 ,gt_CanUpdateFLash.Jie_Board_Addr);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+10 ,gt_CanUpdateFLash.Layer_Board_Addr);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+12 ,gt_CanUpdateFLash.Frm_Num_low );
FLASH_ProgramHalfWord(UpdateProgramParameAddr+14 ,gt_CanUpdateFLash.Frm_Num_high);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+16 ,gt_CanUpdateFLash.Mode);
FLASH_ProgramHalfWord(UpdateProgramParameAddr+18 ,gt_CanUpdateFLash.CRC_16_low );
FLASH_ProgramHalfWord(UpdateProgramParameAddr+20 ,gt_CanUpdateFLash.CRC_16_high);
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); //清除标志位
FLASH_Lock(); //锁定FLASH编程擦除控制器
*/
gt_ParameterVal.UpprogrameFlag =1 ; // 平时为0 ,在motor 任务中, 当要升级时,只进行喂狗的操作,不进行其他的操作
gt_CanUpdateFLash.Flash_Update_Frm_Flag = 2 ;// 更新列控的标志位, 中断函数走这个分支
// ReadUpdateProgramParameData();// 读参数 并打印,供升级程序 参照对比
// 回复上位机
unsigned char coladdr=gt_Can1Operation.t_Can1Addr.Can1Addr ;
unsigned char temp=0 ;//0 ------- 收到升级指令,并回复
gt_Can1Operation.Can1TxDataByProtocal(coladdr,2,MasterCommand_UpdateProgram, &temp);
printf("All boards will be Updated \r\n");
CAN1_UpprogrameConfiguration(); //将can的波特率更改 成50k
// 擦除 后面的flash 数据,用于存储接收的新can帧数据
int pages,i=0;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); //清除标志位
for(i= (FLASH2_BASE-0x8000000)/2048 ; i<= (FLASH2_BASE-0x8000000)/2048+40 ; i++) // 40 *2 =80k(应用程序的大小不能超过40k)
{
if(FLASH_ErasePage(0x8000000+2048*i) != FLASH_COMPLETE)//擦除需要的页
{
//return;
}
}
FLASH_Lock();
// jump_iap(); //跳转到 升级程序
DelOthersTask(); //升级过程中,除了喂狗外,其他线程都杀掉
}
}
}
1.3 中断函数:
void CAN1_RX0_IRQHandler(void)
{
if(gt_CanUpdateFLash.Flash_Update_Frm_Flag==0)
{
CanRxMsg RxMessage;
if(SET == CAN_GetITStatus(CAN1,CAN_IT_FF0))
{
CAN_ClearITPendingBit(CAN1,CAN_IT_FF0);
}
else if(SET == CAN_GetITStatus(CAN1,CAN_IT_FOV0))
{
CAN_ClearITPendingBit(CAN1,CAN_IT_FOV0);
}
else
{
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
}
// debug_can1("can[0]=%x,can[1]=%x,can[2]=%x,can[3]=%x,can[4]=%x,can[5]=%x,can[6]=%x,can[7]=%x \r\n" ,\
// RxMessage.Data[0],RxMessage.Data[1],RxMessage.Data[2],RxMessage.Data[3],RxMessage.Data[4], \
// RxMessage.Data[5],RxMessage.Data[6],RxMessage.Data[7]);
int putdatacount = RxMessage.DLC ;
int i;
for(i=0;i<putdatacount; i++) // 将数据放入环形区,并发送信号给 can1parasedata thread
{
gt_Can1Operation.Can1PutData(RxMessage.Data[ i]);
}
osSemaphoreRelease(sem_can1_parase);
}
if(gt_CanUpdateFLash.Flash_Update_Frm_Flag==2) // 更新列控标志位
{
CanRxMsg RxMessage2;
if(SET == CAN_GetITStatus(CAN1,CAN_IT_FF0))
{
CAN_ClearITPendingBit(CAN1,CAN_IT_FF0);
}
else if(SET == CAN_GetITStatus(CAN1,CAN_IT_FOV0))
{
CAN_ClearITPendingBit(CAN1,CAN_IT_FOV0);
}
else
{
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage2);
}
// 收到的帧数据 写到后面的flash中
u32 temp1, temp2;
temp1=RxMessage2.Data[3]<<24|RxMessage2.Data[2]<<16|RxMessage2.Data[1]<<8|RxMessage2.Data[0];
temp2=RxMessage2.Data[7]<<24|RxMessage2.Data[6]<<16|RxMessage2.Data[5]<<8|RxMessage2.Data[4];
FLASH_Unlock();
FLASH_ProgramWord(FLASH2_BASE+Flash_Update_Frm_Num*8,temp1);
FLASH_ProgramWord(FLASH2_BASE+Flash_Update_Frm_Num*8+4,temp2);
FLASH_Lock();
Flash_Update_Frm_Num ++;
if(Flash_Update_Frm_Num == ( (gt_CanUpdateFLash.Frm_Num_high<<8)|gt_CanUpdateFLash.Frm_Num_low ))//数据帧接收完毕
{
// 计算Crc16
int i=0 ;
unsigned int CrcVal=0 ;
CrcVal= modbusRtuCrc16((u8 *)(FLASH2_BASE) , Flash_Update_Frm_Num*8 );
printf("Frame Len is %d,Crc val is %d \r\n" , Flash_Update_Frm_Num,CrcVal);
unsigned int TmpCrc =(gt_CanUpdateFLash.CRC_16_high<< 8) | gt_CanUpdateFLash.CRC_16_low ;
if(TmpCrc == CrcVal)
{
// 回复上位机,并打印
unsigned char returnval = 1 ;// 1 -----接收上位机的帧数据完毕,校验和正确 // 2 -----接收上位机的帧数据完毕,校验和错误
gt_Can1Operation.Can1TxDataByProtocal(gt_Can1Operation.t_Can1Addr.Can1Addr,2,MasterCommand_UpdateProgram,&returnval);
printf("rcv data complete\r\n");
printf("Crc is Right And Update Programe \r\n");
Flash1_Program();
}else if (TmpCrc != CrcVal)
{
unsigned char returnval =2 ;// 1 -----接收上位机的帧数据完毕,校验和正确 // 2 -----接收上位机的帧数据完毕,校验和错误
gt_Can1Operation.Can1TxDataByProtocal(gt_Can1Operation.t_Can1Addr.Can1Addr,2,MasterCommand_UpdateProgram,&returnval);
printf("Crc is Wrong\r\n");
// 复位
SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |///软件复位
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk );
}
}
}
}
1.4 内部flash的分区:
#define FLASH_START_ADDR 0x8000000+1024*86 //所用芯片flash一页为2k ,共128页, (这2k空间用于存参数 100 -101)
#define UpdateProgramParameAddr 0x8000000 + 1024 *88 // 用于存储升级程序的参数, 现好像没有用 (原打算用于secondbootloader方式进行升级 )
//#define StorageZhanWeiDataPage1_Addr 0x8000000 + 1024 *104 // 这一页实际没有用
#define FLASH_KWZY_INFO_ADDR 0x8000000+1024*90 //保存库位占用信息,用于上电后显示
#define FLASH2_BASE 0x8000000+1024*92 m32 flash操作函数的 地址为 __at_0x8014000 即80k 的空间
更新程序函数的地址 __at_0x8015000 即84k 的空间
所以编译出的程序大小为84k 应用程序的大小 必须在80k 以内
1.5 上位机代码
private void DoWork()
{
binContent = File.ReadAllBytes(SelectFileName);
Int32 length = binContent.Length;
Int32 framelen = length / 8;
Int32 count = 0;
string path = @"d:/sendbin.txt";
if (File.Exists(path))
{
File.Delete(path);
}
FileStream file_stream = new FileStream("d:/sendbin.txt", FileMode.OpenOrCreate , FileAccess.ReadWrite);
BinaryWriter write_file_stream = new BinaryWriter(file_stream);
if ((coloumCanIdAddr == 1) || (coloumCanIdAddr == 2) || (coloumCanIdAddr == 3) || (coloumCanIdAddr == 4) || (coloumCanIdAddr == 5) || (coloumCanIdAddr == 6) || (coloumCanIdAddr == 7)
|| (coloumCanIdAddr == 8) || (coloumCanIdAddr == 9) || (coloumCanIdAddr == 10) || (coloumCanIdAddr == 11) || (coloumCanIdAddr == 12) || (coloumCanIdAddr == 13) || (coloumCanIdAddr == 14)
|| (coloumCanIdAddr == 15) || (coloumCanIdAddr == 16))
{
// 发送列控板 升级指令
byte[] UpdateProgram = new byte[15];
UpdateProgram[0] = 0xaa;
UpdateProgram[1] = 0xff;
UpdateProgram[2] = 0x0b; // 数据长度
UpdateProgram[3] = 0x7c; // 命令
UpdateProgram[4] = 0x02; // 板子类型 列控板
UpdateProgram[5] = 0x01; // 01单板更新 全部更新 02
UpdateProgram[6] = 0x00; // 主控板地址
UpdateProgram[7] = coloumCanIdAddr;// 列控板地址
UpdateProgram[8] = 0; //节控板地址
UpdateProgram[9] = 0; //层控板地址
UpdateProgram[10] = (byte)((framelen) & 0xff);// 程序can帧总量 低字节
UpdateProgram[11] = (byte)(((framelen) & 0xff00) >> 8);// 程序Can帧总量 高字节
UpdateProgram[12] = (byte)(modbusRtuCrc16(binContent, (uint)length) & 0xff); // 程序CRC16 低字节
UpdateProgram[13] = (byte)((modbusRtuCrc16(binContent, (uint)length) >> 8) & 0xff);// 程序CRC16 高字节
UpdateProgram[14] = CalXol_Cal(UpdateProgram); // 亦或
MessageBox.Show("帧长 (十进制)" + framelen.ToString() + ",Crc (十进制 )" + modbusRtuCrc16(binContent, (uint)length).ToString());
CanSenFunc(0x00, UpdateProgram, 8);// 发送帧的前8位数据
byte[] can_2_buffer = new byte[7]; // 后面7位数据
can_2_buffer[0] = UpdateProgram[8];
can_2_buffer[1] = UpdateProgram[9];
can_2_buffer[2] = UpdateProgram[10];
can_2_buffer[3] = UpdateProgram[11];
can_2_buffer[4] = UpdateProgram[12];
can_2_buffer[5] = UpdateProgram[13];
can_2_buffer[6] = UpdateProgram[14];
CanSenFunc(0x00, can_2_buffer, 7);// 发送帧的后7位数据
}
else if (coloumCanIdAddr == 0xff)
{
// 发送列控板 升级指令
byte[] UpdateProgram = new byte[15];
UpdateProgram[0] = 0xaa;
UpdateProgram[1] = 0xff;
UpdateProgram[2] = 0x0b; // 数据长度
UpdateProgram[3] = 0x7c; // 命令
UpdateProgram[4] = 0x02; // 板子类型 列控板
UpdateProgram[5] = 0x02; // 01单板更新 全部更新 02
UpdateProgram[6] = 0x00; // 主控板地址
UpdateProgram[7] = coloumCanIdAddr;// 列控板地址
UpdateProgram[8] = 0; //节控板地址
UpdateProgram[9] = 0; //层控板地址
UpdateProgram[10] = (byte)((framelen) & 0xff);// 程序can帧总量 低字节
UpdateProgram[11] = (byte)(((framelen) & 0xff00) >> 8);// 程序Can帧总量 高字节
UpdateProgram[12] = (byte)(modbusRtuCrc16(binContent, (uint)length) & 0xff); // 程序CRC16 低字节
UpdateProgram[13] = (byte)((modbusRtuCrc16(binContent, (uint)length) >> 8) & 0xff);// 程序CRC16 高字节
UpdateProgram[14] = CalXol_Cal(UpdateProgram); // 亦或
MessageBox.Show("帧长 (十进制)" + framelen.ToString() + ",Crc (十进制 )" + modbusRtuCrc16(binContent, (uint)length).ToString());
CanSenFunc(0x00, UpdateProgram, 8);// 发送帧的 前8位数据
byte[] can_2_buffer = new byte[7]; // 后面7位数据
can_2_buffer[0] = UpdateProgram[8];
can_2_buffer[1] = UpdateProgram[9];
can_2_buffer[2] = UpdateProgram[10];
can_2_buffer[3] = UpdateProgram[11];
can_2_buffer[4] = UpdateProgram[12];
can_2_buffer[5] = UpdateProgram[13];
can_2_buffer[6] = UpdateProgram[14];
CanSenFunc(0x00, can_2_buffer, 7);// 发送帧的后7位数据
}
else if (coloumCanIdAddr == 100)
{
return;
}
Thread.Sleep(5000);
UpprogrameConfigureCan(); //将can的波特率初始化成 50k
Thread.Sleep(200);
Int32 line = -1;
for (count = 0; count <= length; count++)
{
if (count%8 == 0)
{
line++;
System.Threading.Thread.Sleep(30);// 每can 帧延时时间
byte[] tempcanbuffer = new byte[8];
if (line == 0) //count=0 ,此时不发送数据
{
}else if (line == 1) //count =8 ,发送第1帧
{
tempcanbuffer[0] = binContent[0];
tempcanbuffer[1] = binContent[1];
tempcanbuffer[2] = binContent[2];
tempcanbuffer[3] = binContent[3];
tempcanbuffer[4] = binContent[4];
tempcanbuffer[5] = binContent[5];
tempcanbuffer[6] = binContent[6];
tempcanbuffer[7] = binContent[7];
CanSenFunc(0x00, tempcanbuffer, 8);// 发送帧的后7位数据
}
else if (line >1 ) // count=16\24\32\40.......
{
// can帧赋值
tempcanbuffer[0] = binContent[(line - 1) * 8 + 0];
tempcanbuffer[1] = binContent[(line - 1) * 8 + 1];
tempcanbuffer[2] = binContent[(line - 1) * 8 + 2];
tempcanbuffer[3] = binContent[(line - 1) * 8 + 3];
tempcanbuffer[4] = binContent[(line - 1) * 8 + 4];
tempcanbuffer[5] = binContent[(line - 1) * 8 + 5];
tempcanbuffer[6] = binContent[(line - 1) * 8 + 6];
tempcanbuffer[7] = binContent[(line - 1) * 8 + 7];
CanSenFunc(0x00, tempcanbuffer, 8);// 发送帧的后7位数据
// 发送8位数据
string temp_string = string.Format(" {0:x},{1:x},{2:x},{3:x},{4:x},{5:x},{6:x},{7:x} \r\n", binContent[(line - 1) * 8 + 0],
binContent[(line - 1) * 8 + 1], binContent[(line - 1) * 8 + 2], binContent[(line - 1) * 8 + 3], binContent[(line - 1) * 8 + 4],
binContent[(line - 1) * 8 + 5], binContent[(line - 1) * 8 + 6], binContent[(line - 1) * 8 + 7], System.Globalization.NumberStyles.HexNumber);
write_file_stream.Write(temp_string);
/*
print_textbox.Dispatcher.BeginInvoke(new ThreadStart( delegate()
{
// print_textbox.Text += temp_string; // 注意最后将这段代码屏蔽掉了,否则当程序发送到一定量后,出现发送的can帧不正确的现象, 即后面的帧会经常有重复的现象。(在发送过程中,经常清文本框中的内容,发送的数据就正确。)
} ));
*/
double progressval = (double)count / (double)length * 100;// 更新进度条
Colboard_progressbar.Dispatcher.BeginInvoke(new ThreadStart(delegate() { Colboard_progressbar.Value = progressval; }));
}
}
}
update_button.Dispatcher.BeginInvoke(new ThreadStart(delegate()
{
update_button.IsEnabled = true;
}));
write_file_stream.Close();
System.Threading.Thread.Sleep(2000); //用于接收 嵌入式的回帧 ,接收完成后,再关闭
readDataTimer.Stop();
}