работает но не стабильно
This commit is contained in:
parent
614b328a58
commit
2609102829
Binary file not shown.
@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace BootloaderGUI
|
namespace BootloaderGUI
|
||||||
{
|
{
|
||||||
@ -8,7 +11,11 @@ namespace BootloaderGUI
|
|||||||
{
|
{
|
||||||
private const string DLL_NAME = "slcan.dll";
|
private const string DLL_NAME = "slcan.dll";
|
||||||
public IntPtr hDevice { get; private set; } = IntPtr.Zero;
|
public IntPtr hDevice { get; private set; } = IntPtr.Zero;
|
||||||
|
public event Action<UInt16> ErrorReceived; // событие для ошибок
|
||||||
|
private CancellationTokenSource rxCts;
|
||||||
|
private ConcurrentQueue<SLCAN_MESSAGE> rxQueue = new ConcurrentQueue<SLCAN_MESSAGE>();
|
||||||
private bool slcanLoaded = false;
|
private bool slcanLoaded = false;
|
||||||
|
private bool waitingForResponse = false;
|
||||||
|
|
||||||
|
|
||||||
public const ushort SLCAN_BR_CIA_1000K = 0x8000;
|
public const ushort SLCAN_BR_CIA_1000K = 0x8000;
|
||||||
@ -313,44 +320,95 @@ namespace BootloaderGUI
|
|||||||
public bool WaitForResponse(uint timeoutMs, out UInt16 error)
|
public bool WaitForResponse(uint timeoutMs, out UInt16 error)
|
||||||
{
|
{
|
||||||
error = 0;
|
error = 0;
|
||||||
|
waitingForResponse = true; // включаем режим ожидания
|
||||||
|
|
||||||
// 1) Очистим RX буфер у устройства
|
DateTime start = DateTime.Now;
|
||||||
try
|
while ((DateTime.Now - start).TotalMilliseconds < timeoutMs)
|
||||||
{
|
{
|
||||||
// SLCAN_PURGE_RX_CLEAR определён в заголовке
|
if (rxQueue.TryDequeue(out var msg))
|
||||||
SlCan_DevicePurge(hDevice, SLCAN_PURGE_RX_CLEAR);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// если purge не доступен/ошибка — можно продолжить, но логировать
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Ждём один свежий пакет (блокирующе, с таймаутом)
|
|
||||||
SLCAN_MESSAGE[] buffer = new SLCAN_MESSAGE[1];
|
|
||||||
buffer[0].Data = new byte[8];
|
|
||||||
|
|
||||||
if (SlCan_DeviceReadMessages(hDevice, timeoutMs, buffer, 1, out uint read))
|
|
||||||
{
|
|
||||||
if (read > 0)
|
|
||||||
{
|
{
|
||||||
byte[] response = buffer[0].Data;
|
byte[] response = msg.Data;
|
||||||
|
|
||||||
|
waitingForResponse = false; // мы получили ответ — отключаем режим
|
||||||
if (response[0] == 0)
|
if (response[0] == 0)
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error = (UInt16)((response[1] << 8) | response[2]);
|
error = (UInt16)((response[1] << 8) | response[2]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Thread.Sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = 0xFFFF; // таймаут / ничего не пришло
|
waitingForResponse = false; // таймаут
|
||||||
|
error = 0xFFFF;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Запуск фонового приёма сообщений только с ID=123.
|
||||||
|
/// </summary>
|
||||||
|
public void StartBackgroundReceive()
|
||||||
|
{
|
||||||
|
rxCts = new CancellationTokenSource();
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
SLCAN_MESSAGE[] buffer = new SLCAN_MESSAGE[1];
|
||||||
|
buffer[0].Data = new byte[8];
|
||||||
|
|
||||||
|
while (!rxCts.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (SlCan_DeviceReadMessages(hDevice, 200, buffer, 1, out uint read) && read > 0)
|
||||||
|
{
|
||||||
|
if (buffer[0].ID == 123)
|
||||||
|
{
|
||||||
|
if (waitingForResponse)
|
||||||
|
{
|
||||||
|
// Кладём пакет в очередь для WaitForResponse
|
||||||
|
rxQueue.Enqueue(buffer[0]);
|
||||||
|
}
|
||||||
|
// Проверяем, ошибка ли это
|
||||||
|
byte[] resp = buffer[0].Data;
|
||||||
|
if (resp[0] != 0) // ошибка
|
||||||
|
{
|
||||||
|
UInt16 err = (UInt16)((resp[1] << 8) | resp[2]);
|
||||||
|
ErrorReceived?.Invoke(err); // вызываем событие
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, rxCts.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Остановка фонового приёма.
|
||||||
|
/// </summary>
|
||||||
|
public void StopBackgroundReceive()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rxCts?.Cancel();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Disconnect()
|
public void Disconnect()
|
||||||
{
|
{
|
||||||
if (hDevice != IntPtr.Zero)
|
if (hDevice != IntPtr.Zero)
|
||||||
|
@ -112,9 +112,18 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
string status;
|
string status;
|
||||||
if (can.ConnectFirstDevice(bitrate.Value, out status))
|
if (can.ConnectFirstDevice(bitrate.Value, out status))
|
||||||
|
{
|
||||||
lblStatus.Text = status;
|
lblStatus.Text = status;
|
||||||
|
|
||||||
|
// запуск фонового приёма
|
||||||
|
can.ErrorReceived -= Can_ErrorReceived; // безопасно удалить старую подписку
|
||||||
|
can.ErrorReceived += Can_ErrorReceived;
|
||||||
|
can.StartBackgroundReceive();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
lblStatus.Text = "Connect failed";
|
lblStatus.Text = "Connect failed";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -144,11 +153,20 @@ namespace BootloaderGUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void Can_ErrorReceived(UInt16 err)
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
lblStatus.Text = $"Error received: 0x{err:X4}";
|
||||||
|
MessageBox.Show($"Error received from device: 0x{err:X4}", "CAN Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
can?.StopBackgroundReceive();
|
||||||
can?.Disconnect();
|
can?.Disconnect();
|
||||||
can?.Free();
|
can?.Free();
|
||||||
}
|
}
|
||||||
@ -222,7 +240,7 @@ namespace BootloaderGUI
|
|||||||
{
|
{
|
||||||
uint timeout = 1000u;
|
uint timeout = 1000u;
|
||||||
UInt16 error = 0;
|
UInt16 error = 0;
|
||||||
|
Thread.Sleep(10);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (fwData == null) return;
|
if (fwData == null) return;
|
||||||
@ -303,6 +321,7 @@ namespace BootloaderGUI
|
|||||||
{
|
{
|
||||||
UpdateStatusOnUI("Error during RECEIVE: " + ex.Message);
|
UpdateStatusOnUI("Error during RECEIVE: " + ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SendCmdSync(byte cmd)
|
private bool SendCmdSync(byte cmd)
|
||||||
@ -315,12 +334,16 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
if (!can.SendCmd(cmd, out UInt16 error))
|
if (!can.SendCmd(cmd, out UInt16 error))
|
||||||
{
|
{
|
||||||
if (error == 0xFFFF)
|
if ((error == 0xFFFF) && cmd != (BootloaderCommands.RESET) && (cmd != BootloaderCommands.JUMP))
|
||||||
|
{
|
||||||
MessageBox.Show($"Command 0x{cmd:X2} timed out", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBox.Show($"Command 0x{cmd:X2} timed out", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
else
|
return false;
|
||||||
|
}
|
||||||
|
else if ((error != 0xFFFF) && (error != 0))
|
||||||
|
{
|
||||||
MessageBox.Show($"Command 0x{cmd:X2} failed, error code: 0x{error:X4}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBox.Show($"Command 0x{cmd:X2} failed, error code: 0x{error:X4}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateStatusOnUI($"Command 0x{cmd:X2} sent successfully");
|
UpdateStatusOnUI($"Command 0x{cmd:X2} sent successfully");
|
||||||
|
@ -58,7 +58,14 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
// Кнопка "Go To Boot"
|
// Кнопка "Go To Boot"
|
||||||
btnGoBoot = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Height = hBtn, Text = "Go To Boot" };
|
btnGoBoot = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Height = hBtn, Text = "Go To Boot" };
|
||||||
btnGoBoot.Click += (s, e) => sendCmdSync(BootloaderCommands.GO_TO_BOOT);
|
btnGoBoot.Click += (s, e) =>
|
||||||
|
{
|
||||||
|
if(sendCmdSync(BootloaderCommands.GO_TO_BOOT))
|
||||||
|
UpdateStatus("You're in Bootloader");
|
||||||
|
else
|
||||||
|
UpdateStatus("Failed: Jump to Bootloader");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
top += hBtn + gap;
|
top += hBtn + gap;
|
||||||
|
|
||||||
@ -66,13 +73,21 @@ namespace BootloaderGUI
|
|||||||
Button btnGoApp = new Button() { Top = top, Left = left, Width = wBtn, Height = hBtn, Text = "Go To App" };
|
Button btnGoApp = new Button() { Top = top, Left = left, Width = wBtn, Height = hBtn, Text = "Go To App" };
|
||||||
btnGoApp.Click += (s, e) =>
|
btnGoApp.Click += (s, e) =>
|
||||||
{
|
{
|
||||||
UpdateStatus("Jumping to app...");
|
if(sendCmdSync(BootloaderCommands.JUMP))
|
||||||
sendCmdSync(BootloaderCommands.JUMP);
|
UpdateStatus("Jumping to app...");
|
||||||
|
else
|
||||||
|
UpdateStatus("Failed: Jump to App");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Кнопка "Reset"
|
// Кнопка "Reset"
|
||||||
btnReset = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Height = hBtn, Text = "Reset" };
|
btnReset = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Height = hBtn, Text = "Reset" };
|
||||||
btnReset.Click += (s, e) => sendCmdSync(BootloaderCommands.RESET);
|
btnReset.Click += (s, e) =>
|
||||||
|
{
|
||||||
|
if (sendCmdSync(BootloaderCommands.RESET))
|
||||||
|
UpdateStatus("Resetting...");
|
||||||
|
else
|
||||||
|
UpdateStatus("Failed: Reset MCU");
|
||||||
|
};
|
||||||
|
|
||||||
Tab.Controls.AddRange(new Control[] { btnFirmware, btnGoBoot, btnGoApp, btnReset });
|
Tab.Controls.AddRange(new Control[] { btnFirmware, btnGoBoot, btnGoApp, btnReset });
|
||||||
}
|
}
|
||||||
@ -98,16 +113,17 @@ namespace BootloaderGUI
|
|||||||
for (int page = 0; page < totalPages; page++)
|
for (int page = 0; page < totalPages; page++)
|
||||||
{
|
{
|
||||||
UpdateStatus($"Sending page {page + 1}/{totalPages}");
|
UpdateStatus($"Sending page {page + 1}/{totalPages}");
|
||||||
|
await Task.Delay(5); // небольшая пауза для UI
|
||||||
sendPage(page);
|
sendPage(page);
|
||||||
|
|
||||||
UpdateStatus($"Writing page {page + 1}/{totalPages}");
|
UpdateStatus($"Writing page {page + 1}/{totalPages}");
|
||||||
|
await Task.Delay(5); // небольшая пауза для UI
|
||||||
if (!sendCmdSync(BootloaderCommands.WRITE))
|
if (!sendCmdSync(BootloaderCommands.WRITE))
|
||||||
{
|
{
|
||||||
UpdateStatus($"Write failed on page {page}");
|
UpdateStatus($"Write failed on page {page}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(5); // небольшая пауза для UI
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateStatus("Jumping to app...");
|
UpdateStatus("Jumping to app...");
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user