Compare commits

..

No commits in common. "26091028291256c323748aeb5de1433fb154dcdf" and "2de059e0cc787e9423015a9616b059d18d20e8d0" have entirely different histories.

10 changed files with 48 additions and 134 deletions

Binary file not shown.

View File

@ -12,9 +12,10 @@ namespace BootloaderGUI
public const byte ERASE = 0x01; public const byte ERASE = 0x01;
public const byte RECEIVE = 0x02; public const byte RECEIVE = 0x02;
public const byte WRITE = 0x03; public const byte WRITE = 0x03;
public const byte JUMP = 0x04; public const byte VERIFY = 0x04;
public const byte RESET = 0x05; public const byte JUMP = 0x05;
public const byte GO_TO_BOOT = 0x06; public const byte RESET = 0x06;
public const byte GO_TO_BOOT = 0x07;
public const int PAGE_SIZE = 2048; public const int PAGE_SIZE = 2048;
public const int CAN_ID_BOOTLOADER = 0x123; public const int CAN_ID_BOOTLOADER = 0x123;

View File

@ -1,9 +1,6 @@
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
{ {
@ -11,11 +8,7 @@ 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;
@ -320,95 +313,44 @@ namespace BootloaderGUI
public bool WaitForResponse(uint timeoutMs, out UInt16 error) public bool WaitForResponse(uint timeoutMs, out UInt16 error)
{ {
error = 0; error = 0;
waitingForResponse = true; // включаем режим ожидания
DateTime start = DateTime.Now; // 1) Очистим RX буфер у устройства
while ((DateTime.Now - start).TotalMilliseconds < timeoutMs) try
{ {
if (rxQueue.TryDequeue(out var msg)) // SLCAN_PURGE_RX_CLEAR определён в заголовке
{ SlCan_DevicePurge(hDevice, SLCAN_PURGE_RX_CLEAR);
byte[] response = msg.Data; }
catch
{
// если purge не доступен/ошибка — можно продолжить, но логировать
}
waitingForResponse = false; // мы получили ответ — отключаем режим // 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;
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);
} }
waitingForResponse = false; // таймаут error = 0xFFFF; // таймаут / ничего не пришло
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)

View File

@ -74,12 +74,13 @@ namespace BootloaderGUI
top += numPage.Height + gap; top += numPage.Height + gap;
btnErase = new Button() { Top = top, Left = left, Width = wBtn, Text = "ERASE Firmware" }; btnErase = new Button() { Top = top, Left = left, Width = wBtn, Text = "ERASE Firmware" };
Tab.Controls.AddRange(new Control[] { btnErase, }); btnReceive = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "SEND Page" };
Tab.Controls.AddRange(new Control[] { btnErase, btnReceive });
top += hBtn + gap; top += hBtn + gap;
btnReceive = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "SEND Page" };
btnWrite = new Button() { Top = top, Left = left, Width = wBtn, Text = "WRITE Page" }; btnWrite = new Button() { Top = top, Left = left, Width = wBtn, Text = "WRITE Page" };
Tab.Controls.AddRange(new Control[] { btnReceive, btnWrite }); btnVerify = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "VERIFY Firmware" };
Tab.Controls.AddRange(new Control[] { btnWrite, btnVerify });
top += hBtn + gap; top += hBtn + gap;
btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP to App" }; btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP to App" };
@ -91,6 +92,7 @@ namespace BootloaderGUI
btnErase.Click += (s, e) => sendCmdSync(BootloaderCommands.ERASE); btnErase.Click += (s, e) => sendCmdSync(BootloaderCommands.ERASE);
btnReceive.Click += (s, e) => sendPage((int)numPage.Value); btnReceive.Click += (s, e) => sendPage((int)numPage.Value);
btnWrite.Click += (s, e) => sendCmdSync(BootloaderCommands.WRITE); btnWrite.Click += (s, e) => sendCmdSync(BootloaderCommands.WRITE);
btnVerify.Click += (s, e) => sendCmdSync(BootloaderCommands.VERIFY);
btnJump.Click += (s, e) => sendCmdSync(BootloaderCommands.JUMP); btnJump.Click += (s, e) => sendCmdSync(BootloaderCommands.JUMP);
btnReset.Click += (s, e) => sendCmdSync(BootloaderCommands.RESET); btnReset.Click += (s, e) => sendCmdSync(BootloaderCommands.RESET);
btnGoBoot.Click += (s, e) => sendCmdSync(BootloaderCommands.GO_TO_BOOT); btnGoBoot.Click += (s, e) => sendCmdSync(BootloaderCommands.GO_TO_BOOT);

View File

@ -112,18 +112,9 @@ 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";
}
}; };
@ -153,20 +144,11 @@ 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();
} }
@ -240,7 +222,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;
@ -321,7 +303,6 @@ namespace BootloaderGUI
{ {
UpdateStatusOnUI("Error during RECEIVE: " + ex.Message); UpdateStatusOnUI("Error during RECEIVE: " + ex.Message);
} }
} }
private bool SendCmdSync(byte cmd) private bool SendCmdSync(byte cmd)
@ -334,16 +315,12 @@ namespace BootloaderGUI
if (!can.SendCmd(cmd, out UInt16 error)) if (!can.SendCmd(cmd, out UInt16 error))
{ {
if ((error == 0xFFFF) && cmd != (BootloaderCommands.RESET) && (cmd != BootloaderCommands.JUMP)) if (error == 0xFFFF)
{
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);
return false; else
}
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");

View File

@ -58,14 +58,7 @@ 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) => btnGoBoot.Click += (s, e) => sendCmdSync(BootloaderCommands.GO_TO_BOOT);
{
if(sendCmdSync(BootloaderCommands.GO_TO_BOOT))
UpdateStatus("You're in Bootloader");
else
UpdateStatus("Failed: Jump to Bootloader");
};
top += hBtn + gap; top += hBtn + gap;
@ -73,21 +66,13 @@ 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) =>
{ {
if(sendCmdSync(BootloaderCommands.JUMP)) UpdateStatus("Jumping to app...");
UpdateStatus("Jumping to app..."); sendCmdSync(BootloaderCommands.JUMP);
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) => btnReset.Click += (s, e) => sendCmdSync(BootloaderCommands.RESET);
{
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 });
} }
@ -113,17 +98,24 @@ 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("Verifying...");
await Task.Delay(5); // небольшая пауза для UI
if (!sendCmdSync(BootloaderCommands.VERIFY))
{
UpdateStatus("Verify failed");
return;
} }
UpdateStatus("Jumping to app..."); UpdateStatus("Jumping to app...");