Compare commits
2 Commits
2de059e0cc
...
2609102829
Author | SHA1 | Date | |
---|---|---|---|
2609102829 | |||
614b328a58 |
Binary file not shown.
@ -12,10 +12,9 @@ namespace BootloaderGUI
|
||||
public const byte ERASE = 0x01;
|
||||
public const byte RECEIVE = 0x02;
|
||||
public const byte WRITE = 0x03;
|
||||
public const byte VERIFY = 0x04;
|
||||
public const byte JUMP = 0x05;
|
||||
public const byte RESET = 0x06;
|
||||
public const byte GO_TO_BOOT = 0x07;
|
||||
public const byte JUMP = 0x04;
|
||||
public const byte RESET = 0x05;
|
||||
public const byte GO_TO_BOOT = 0x06;
|
||||
|
||||
public const int PAGE_SIZE = 2048;
|
||||
public const int CAN_ID_BOOTLOADER = 0x123;
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace BootloaderGUI
|
||||
{
|
||||
@ -8,7 +11,11 @@ namespace BootloaderGUI
|
||||
{
|
||||
private const string DLL_NAME = "slcan.dll";
|
||||
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 waitingForResponse = false;
|
||||
|
||||
|
||||
public const ushort SLCAN_BR_CIA_1000K = 0x8000;
|
||||
@ -313,44 +320,95 @@ namespace BootloaderGUI
|
||||
public bool WaitForResponse(uint timeoutMs, out UInt16 error)
|
||||
{
|
||||
error = 0;
|
||||
waitingForResponse = true; // включаем режим ожидания
|
||||
|
||||
// 1) Очистим RX буфер у устройства
|
||||
try
|
||||
DateTime start = DateTime.Now;
|
||||
while ((DateTime.Now - start).TotalMilliseconds < timeoutMs)
|
||||
{
|
||||
// SLCAN_PURGE_RX_CLEAR определён в заголовке
|
||||
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)
|
||||
if (rxQueue.TryDequeue(out var msg))
|
||||
{
|
||||
byte[] response = buffer[0].Data;
|
||||
byte[] response = msg.Data;
|
||||
|
||||
waitingForResponse = false; // мы получили ответ — отключаем режим
|
||||
if (response[0] == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = (UInt16)((response[1] << 8) | response[2]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
error = 0xFFFF; // таймаут / ничего не пришло
|
||||
waitingForResponse = false; // таймаут
|
||||
error = 0xFFFF;
|
||||
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()
|
||||
{
|
||||
if (hDevice != IntPtr.Zero)
|
||||
|
@ -74,13 +74,12 @@ namespace BootloaderGUI
|
||||
|
||||
top += numPage.Height + gap;
|
||||
btnErase = new Button() { Top = top, Left = left, Width = wBtn, Text = "ERASE Firmware" };
|
||||
btnReceive = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "SEND Page" };
|
||||
Tab.Controls.AddRange(new Control[] { btnErase, btnReceive });
|
||||
Tab.Controls.AddRange(new Control[] { btnErase, });
|
||||
|
||||
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" };
|
||||
btnVerify = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "VERIFY Firmware" };
|
||||
Tab.Controls.AddRange(new Control[] { btnWrite, btnVerify });
|
||||
Tab.Controls.AddRange(new Control[] { btnReceive, btnWrite });
|
||||
|
||||
top += hBtn + gap;
|
||||
btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP to App" };
|
||||
@ -92,7 +91,6 @@ namespace BootloaderGUI
|
||||
btnErase.Click += (s, e) => sendCmdSync(BootloaderCommands.ERASE);
|
||||
btnReceive.Click += (s, e) => sendPage((int)numPage.Value);
|
||||
btnWrite.Click += (s, e) => sendCmdSync(BootloaderCommands.WRITE);
|
||||
btnVerify.Click += (s, e) => sendCmdSync(BootloaderCommands.VERIFY);
|
||||
btnJump.Click += (s, e) => sendCmdSync(BootloaderCommands.JUMP);
|
||||
btnReset.Click += (s, e) => sendCmdSync(BootloaderCommands.RESET);
|
||||
btnGoBoot.Click += (s, e) => sendCmdSync(BootloaderCommands.GO_TO_BOOT);
|
||||
|
@ -112,9 +112,18 @@ namespace BootloaderGUI
|
||||
|
||||
string status;
|
||||
if (can.ConnectFirstDevice(bitrate.Value, out status))
|
||||
{
|
||||
lblStatus.Text = status;
|
||||
|
||||
// запуск фонового приёма
|
||||
can.ErrorReceived -= Can_ErrorReceived; // безопасно удалить старую подписку
|
||||
can.ErrorReceived += Can_ErrorReceived;
|
||||
can.StartBackgroundReceive();
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
can?.StopBackgroundReceive();
|
||||
can?.Disconnect();
|
||||
can?.Free();
|
||||
}
|
||||
@ -222,7 +240,7 @@ namespace BootloaderGUI
|
||||
{
|
||||
uint timeout = 1000u;
|
||||
UInt16 error = 0;
|
||||
|
||||
Thread.Sleep(10);
|
||||
try
|
||||
{
|
||||
if (fwData == null) return;
|
||||
@ -303,6 +321,7 @@ namespace BootloaderGUI
|
||||
{
|
||||
UpdateStatusOnUI("Error during RECEIVE: " + ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool SendCmdSync(byte cmd)
|
||||
@ -315,12 +334,16 @@ namespace BootloaderGUI
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatusOnUI($"Command 0x{cmd:X2} sent successfully");
|
||||
|
@ -58,7 +58,14 @@ namespace BootloaderGUI
|
||||
|
||||
// Кнопка "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;
|
||||
|
||||
@ -66,13 +73,21 @@ namespace BootloaderGUI
|
||||
Button btnGoApp = new Button() { Top = top, Left = left, Width = wBtn, Height = hBtn, Text = "Go To App" };
|
||||
btnGoApp.Click += (s, e) =>
|
||||
{
|
||||
UpdateStatus("Jumping to app...");
|
||||
sendCmdSync(BootloaderCommands.JUMP);
|
||||
if(sendCmdSync(BootloaderCommands.JUMP))
|
||||
UpdateStatus("Jumping to app...");
|
||||
else
|
||||
UpdateStatus("Failed: Jump to App");
|
||||
};
|
||||
|
||||
// Кнопка "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 });
|
||||
}
|
||||
@ -98,24 +113,17 @@ namespace BootloaderGUI
|
||||
for (int page = 0; page < totalPages; page++)
|
||||
{
|
||||
UpdateStatus($"Sending page {page + 1}/{totalPages}");
|
||||
await Task.Delay(5); // небольшая пауза для UI
|
||||
sendPage(page);
|
||||
|
||||
UpdateStatus($"Writing page {page + 1}/{totalPages}");
|
||||
await Task.Delay(5); // небольшая пауза для UI
|
||||
if (!sendCmdSync(BootloaderCommands.WRITE))
|
||||
{
|
||||
UpdateStatus($"Write failed on page {page}");
|
||||
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...");
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user