NXP

【MPC5744P】Bootloader上位机开发(CAN通讯)

2019-07-12 13:20发布

上位机源码范例直接下载:https://download.csdn.net/download/u010875635/10882176 此处上位机采用C#编写,界面框架为WPF。 为保证烧录过程不出现错误,上下位机采用一问一答模式,上位机发送一帧数据后,下位机接收处理完毕再回馈给上位机,上位机再决定下一步动作。 核心代码逻辑部分,将命令分成EntryBootloader, Reset, Data, DataEnd, CheckBootloader, Erase, ERR几个部分。 烧录时会先检查是否处于Bootloader,,然后执行擦写操作(擦写地址范围由上位机发送),再发送数据(地址+数据为一帧),最后发送结束命令。 注意,由于CAN长度为8字节,而32位地址已经占用4字节,Flash地址从0x800000开始,所以所有地址减去0x800000,3字节即可,首字节作为数据类型。 示例代码中,无论是进入用户App还是Bootloader都会通过CAN回复当前状态,Bootloader中蓝 {MOD}LED低频1s闪烁,烧录时红 {MOD}LED快速闪烁,用户App中彩灯闪烁,Bootloader中4s未接收到命令会进入用户App中,若监测不到用户App存在,会再次复位到Bootloader中。 注意,本实例中使用的是周立功的USBCAN,理论上支持所有致远的CAN设备,实际试验的是USBCAN-E-mini,若是有其它的CAN设备,更换底层的CAN驱动代码即可,程序没有直接调用ZlgCAN,有中转类,方便替换(主要是初始化函数、波特率设置、发送、接收函数等)。     #region 连接断开 /// /// 连接CAN /// bool Connect() { if (m_BOpen) return true; MotorControl1CmdForDevelop.Scm_DevelopCanConfig scm_DevCanConfig = new MotorControl1CmdForDevelop.Scm_DevelopCanConfig(); scm_DevCanConfig.canType = (CANDevices.ZlgCAN.ZlgCanType)Enum.Parse(typeof(CANDevices.ZlgCAN.ZlgCanType), cbCanType.SelectedItem.ToString()); scm_DevCanConfig.devIndex = (uint)cbDeviceIndex.SelectedIndex; scm_DevCanConfig.canIndexs = m_CanIndexs; scm_DevCanConfig.baudrates = m_Baudrates; scm_DevCanConfig.idList = null; scm_DevCanConfig.sheildRule = new bool[] { false }; MotorControl1CmdForDevelop.Ecm_CanInitialErrorTypes result = m_Mtcl1DevelopCmd.Connect(scm_DevCanConfig);//设置参数 if (result != MotorControl1CmdForDevelop.Ecm_CanInitialErrorTypes.OK) { return false; } m_BOpen = true; Connect_UIChange(true); return true; } /// /// 断开CAN /// bool DisConnect() { if (!m_BOpen) return true; if (!m_Mtcl1DevelopCmd.DisConnect()) return false; m_BOpen = false; Connect_UIChange(false); return true; } /// /// 连接串口 /// private void btnConnectCan_Click(object sender, RoutedEventArgs e) { if (m_BOpen) { if (!DisConnect()) { Controls.Windows.UserMessageBox userMsg = new Windows.UserMessageBox(m_WinLocation, m_WinSize, "CAN异常", "关闭失败!"); userMsg.ShowDialog(); } } else { if (!Connect()) { Controls.Windows.UserMessageBox userMsg = new Windows.UserMessageBox(m_WinLocation, m_WinSize, "CAN异常", "打开失败!"); userMsg.ShowDialog(); } } } #endregion #region 数据收发 #region 发送数据 /// /// 开始烧录 /// private void btnStartBurn_Click(object sender, RoutedEventArgs e) { BurnHex(); } Thread m_SendThread; /// /// 开始烧录、停止烧录 /// private void BurnHex() { if (m_IsBurnning) { m_AutoSendTimer.Stop(); m_IsBurnning = false; RunBurn_UIChange(m_IsBurnning); if (m_SendThread != null) m_SendThread.Abort(); } else { m_SendThread = new Thread(() => { DateTime startTime; m_InBootloader = m_McuFlashIsErased = false; //检查是否处于Bootloader中 TextBlockAddMsg("检查是否处于Bootloader中", System.Windows.Media.Brushes.Black); m_Mtcl1DevelopCmd.SendNormalBytes(0, m_CanDataParse.g_CheckBootloaderCmd); startTime = DateTime.Now; //开始计时 while (!m_InBootloader) { uint count = 0; while (count < 100000) count++; if (DateTime.Now.Subtract(startTime).TotalSeconds > 5) //是否超过5s没有接收到 break; } if (!m_InBootloader) { TextBlockAddMsg("请先进入bootloader模式", System.Windows.Media.Brushes.Red, 1); return; } TextBlockAddMsg("已处于Bootloader中!", System.Windows.Media.Brushes.Blue); TextBlockAddMsg("检查是否已擦除Flash", System.Windows.Media.Brushes.Black); m_Mtcl1DevelopCmd.SendNormalBytes(0, m_CanDataParse.CanErasePackage(m_AllAddrData.AddrDataCollection[0].RealAddr, m_AllAddrData.AddrDataCollection[m_AllAddrData.AddrDataCollection.Count-1].RealAddr)); startTime = DateTime.Now; //开始计时 while (!m_McuFlashIsErased) { uint count = 0; while (count < 100000) count++; if (DateTime.Now.Subtract(startTime).TotalSeconds > 10) //是否超过10s没有接收到 break; } if (!m_McuFlashIsErased) { TextBlockAddMsg("擦除超时", System.Windows.Media.Brushes.Red, 1); return; } string ts = DateTime.Now.Subtract(startTime).TotalSeconds.ToString(); TextBlockAddMsg("擦除完毕!,总耗时:"+ts+"s", System.Windows.Media.Brushes.Blue); TextBlockAddMsg("开始发送数据,总指令数: "+m_AllAddrData.AddrDataCollection.Count.ToString() , System.Windows.Media.Brushes.Black); SendData(); //发送数据 }); m_SendThread.Start(); //m_AutoSendTimer.Start(); m_IsBurnning = true; RunBurn_UIChange(m_IsBurnning); } } /// /// 发送等待接收 /// /// /// 发送数据 /// private void SendData() { m_SendDataCount = 0; m_ReceiveDataCount = 0; DateTime dataSartTime = DateTime.Now; //开始计时 while (m_SendDataCount < m_AllAddrData.AddrDataCollection.Count) { //m_SendHasReponse = false; byte[] data = m_CanDataParse.CanDataPackage(m_AllAddrData.AddrDataCollection[m_SendDataCount]); //地址会减去0x800000 //TextBlockAddMsg("SendData:" + BitConverter.ToString(data), System.Windows.Media.Brushes.DarkOrange); TextBlockChangeMsg("发送进度", "已发送指令: " + (m_SendDataCount+1).ToString() + " ", System.Windows.Media.Brushes.Gray); m_Mtcl1DevelopCmd.SendNormalBytes(0, data); //实际发送 //m_CanDataParse.UserFlash_DataParseAddrData(data, (byte)data.Length); //测试 m_SendDataCount++; double percent = m_SendDataCount * 1.0 / m_AllAddrData.AddrDataCollection.Count * 100; ProcessUpdate(percent); SendDataCheck_Wait(ref m_SendDataCount, ref m_ReceiveDataCount, 100); int tryAgainCount = 0; while (m_SendDataCount != m_ReceiveDataCount && tryAgainCount<3) //未发送成功,尝试3次 { m_Mtcl1DevelopCmd.SendNormalBytes(0, data); //再次发送 SendDataCheck_Wait(ref m_SendDataCount, ref m_ReceiveDataCount, 100); tryAgainCount++; } if (m_SendDataCount != m_ReceiveDataCount) //发送中断 { TextBlockAddMsg("发送中断: ", System.Windows.Media.Brushes.Red); return; } } SendDataCheck_Wait(ref m_SendDataCount, ref m_ReceiveDataCount, 300); //发送完毕,停止发送 TimeSpan ts = DateTime.Now.Subtract(dataSartTime); TextBlockAddMsg("发送数据结束,耗时: " + ts.TotalSeconds.ToString() + "s", System.Windows.Media.Brushes.Black); m_Mtcl1DevelopCmd.SendNormalBytes(0, m_CanDataParse.g_DataEndCmd); //数据结束命令 m_SendDataCount++; SendDataCheck_Wait(ref m_SendDataCount, ref m_ReceiveDataCount, 300); BurnHex(); //m_CanDataParse.UserFlash_EndCheck(); //测试 } #endregion #region 接收数据 /// /// 接收数据,回调函数 /// private void ReceiveData(uint canIndex, byte[] datas) { //判断回馈类型 ClassBootloaderForCAN.CmdType cmdType = m_CanDataParse.ReceiveDataParse(ref datas); //TextBlockAddMsg("ReceiveData:"+BitConverter.ToString(datas), System.Windows.Media.Brushes.DarkOrange); switch (cmdType) { case ClassBootloaderForCAN.CmdType.EntryBootloader: m_InBootloader = true; BootloaderIndicator(true); TextBlockAddMsg("成功进入Bootloader,"+ datas[1].ToString()+"s内无操作进入用户程序!", System.Windows.Media.Brushes.Blue); break; case ClassBootloaderForCAN.CmdType.Reset: m_InBootloader = false; BootloaderIndicator(false); TextBlockAddMsg("进入用户程序!", System.Windows.Media.Brushes.Blue); break; case ClassBootloaderForCAN.CmdType.Data: m_ReceiveDataCount++; //m_SendHasReponse = true; TextBlockChangeMsg("刷写进度","已刷写指令: "+m_ReceiveDataCount.ToString()+" " , System.Windows.Media.Brushes.Gray); break; case ClassBootloaderForCAN.CmdType.DataEnd: //m_SendHasReponse = true; m_ReceiveDataCount++; TextBlockAddMsg("用户程序刷新完毕!", System.Windows.Media.Brushes.Blue); break; case ClassBootloaderForCAN.CmdType.CheckBootloader: m_InBootloader = true; break; case ClassBootloaderForCAN.CmdType.Earse: m_McuFlashIsErased = true; break; case ClassBootloaderForCAN.CmdType.Other: TextBlockAddMsg("Receive Other: " + BitConverter.ToString(datas), System.Windows.Media.Brushes.DarkOrange); break; default: break; } } #endregion