上位机源码范例直接下载:
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