stm32 can在线升级例子

升级思路:
当升到升级指令时, 将其他不相关的任务杀掉,将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, &gt_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();

        }

猜你喜欢

转载自blog.csdn.net/tiger15605353603/article/details/81352332