сделана вкладка для userfriendly бута, добавлен crc и переделана структура и взаимосвязи програмы
This commit is contained in:
parent
0fb23abb18
commit
2de059e0cc
Binary file not shown.
@ -21,6 +21,11 @@ namespace BootloaderGUI
|
|||||||
public const uint SLCAN_MODE_NORMAL = 0x01;
|
public const uint SLCAN_MODE_NORMAL = 0x01;
|
||||||
public const uint SLCAN_MODE_LISTENONLY = 0x02;
|
public const uint SLCAN_MODE_LISTENONLY = 0x02;
|
||||||
|
|
||||||
|
private const byte SLCAN_PURGE_TX_ABORT = 0x01;
|
||||||
|
private const byte SLCAN_PURGE_RX_ABORT = 0x02;
|
||||||
|
private const byte SLCAN_PURGE_TX_CLEAR = 0x04;
|
||||||
|
private const byte SLCAN_PURGE_RX_CLEAR = 0x08;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
private struct SLCAN_MESSAGE
|
private struct SLCAN_MESSAGE
|
||||||
{
|
{
|
||||||
@ -58,6 +63,19 @@ namespace BootloaderGUI
|
|||||||
[DllImport(DLL_NAME, CallingConvention = CallingConvention.StdCall)]
|
[DllImport(DLL_NAME, CallingConvention = CallingConvention.StdCall)]
|
||||||
private static extern bool SlCan_DeviceEnaRec(IntPtr hDevice, byte bValue);
|
private static extern bool SlCan_DeviceEnaRec(IntPtr hDevice, byte bValue);
|
||||||
|
|
||||||
|
[DllImport(DLL_NAME, CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern bool SlCan_DeviceReadMessages(
|
||||||
|
IntPtr hDevice,
|
||||||
|
uint dwTimeout, // таймаут в мс
|
||||||
|
[Out] SLCAN_MESSAGE[] pMsg, // буфер для сообщений
|
||||||
|
uint dwCount, // размер буфера
|
||||||
|
out uint pdwRead // количество реально прочитанных
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport(DLL_NAME, CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern bool SlCan_DevicePurge(IntPtr hDevice, byte bValue);
|
||||||
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
private struct SLCAN_STATE
|
private struct SLCAN_STATE
|
||||||
{
|
{
|
||||||
@ -104,7 +122,7 @@ namespace BootloaderGUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ConnectFirstDevice(out string statusText)
|
public bool ConnectFirstDevice(ushort bitrateCode, out string statusText)
|
||||||
{
|
{
|
||||||
statusText = "";
|
statusText = "";
|
||||||
|
|
||||||
@ -139,17 +157,9 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
hDevice = dev;
|
hDevice = dev;
|
||||||
|
|
||||||
// Включаем приём сообщений
|
|
||||||
SlCan_DeviceEnaRec(hDevice, 1);
|
SlCan_DeviceEnaRec(hDevice, 1);
|
||||||
|
|
||||||
// Получаем состояние шины (если у тебя есть функция SlCan_DeviceGetState)
|
// CONFIG mode
|
||||||
SLCAN_STATE state;
|
|
||||||
if (!SlCan_DeviceGetState(hDevice, out state))
|
|
||||||
{
|
|
||||||
statusText = "Failed to read bus state";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Перевести устройство в конфиг-режим
|
|
||||||
if (!SlCan_DeviceSetMode(hDevice, SLCAN_MODE_CONFIG))
|
if (!SlCan_DeviceSetMode(hDevice, SLCAN_MODE_CONFIG))
|
||||||
{
|
{
|
||||||
MessageBox.Show("Failed to set CAN mode", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("Failed to set CAN mode", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
@ -157,16 +167,16 @@ namespace BootloaderGUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Выбранный битрейт
|
||||||
SLCAN_BITRATE br = new SLCAN_BITRATE
|
SLCAN_BITRATE br = new SLCAN_BITRATE
|
||||||
{
|
{
|
||||||
BRP = SLCAN_BR_CIA_250K,
|
BRP = bitrateCode,
|
||||||
TSEG1 = 0,
|
TSEG1 = 0,
|
||||||
TSEG2 = 0,
|
TSEG2 = 0,
|
||||||
SJW = 0,
|
SJW = 0,
|
||||||
SAM = 0
|
SAM = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Установить скорость CAN
|
|
||||||
if (!SlCan_DeviceSetBitRate(hDevice, ref br))
|
if (!SlCan_DeviceSetBitRate(hDevice, ref br))
|
||||||
{
|
{
|
||||||
MessageBox.Show("Failed to set CAN speed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("Failed to set CAN speed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
@ -174,7 +184,6 @@ namespace BootloaderGUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Перевести устройство в нормальный режим
|
|
||||||
if (!SlCan_DeviceSetMode(hDevice, SLCAN_MODE_NORMAL))
|
if (!SlCan_DeviceSetMode(hDevice, SLCAN_MODE_NORMAL))
|
||||||
{
|
{
|
||||||
MessageBox.Show("Failed to set CAN mode", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("Failed to set CAN mode", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
@ -182,14 +191,68 @@ namespace BootloaderGUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
statusText = $"Connected to device 0 (devices: {count})";
|
statusText = $"Connected to device 0 (devices: {count}) at {bitrateCode}";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CanSpeedDialog
|
||||||
public bool SendCmd(byte cmd)
|
|
||||||
{
|
{
|
||||||
|
public static ushort? ShowDialog()
|
||||||
|
{
|
||||||
|
Form dlg = new Form()
|
||||||
|
{
|
||||||
|
Text = "Select CAN Speed",
|
||||||
|
Width = 250,
|
||||||
|
Height = 150,
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||||
|
StartPosition = FormStartPosition.CenterParent,
|
||||||
|
MaximizeBox = false,
|
||||||
|
MinimizeBox = false
|
||||||
|
};
|
||||||
|
|
||||||
|
ComboBox combo = new ComboBox()
|
||||||
|
{
|
||||||
|
Left = 10,
|
||||||
|
Top = 10,
|
||||||
|
Width = 200,
|
||||||
|
DropDownStyle = ComboBoxStyle.DropDownList
|
||||||
|
};
|
||||||
|
|
||||||
|
// Заполняем скорости
|
||||||
|
combo.Items.Add(new { Text = "1000K", Value = CanInterface.SLCAN_BR_CIA_1000K });
|
||||||
|
combo.Items.Add(new { Text = "800K", Value = CanInterface.SLCAN_BR_CIA_800K });
|
||||||
|
combo.Items.Add(new { Text = "500K", Value = CanInterface.SLCAN_BR_CIA_500K });
|
||||||
|
combo.Items.Add(new { Text = "250K", Value = CanInterface.SLCAN_BR_CIA_250K });
|
||||||
|
combo.Items.Add(new { Text = "125K", Value = CanInterface.SLCAN_BR_CIA_125K });
|
||||||
|
combo.DisplayMember = "Text";
|
||||||
|
combo.SelectedIndex = 3; // по умолчанию 250K
|
||||||
|
|
||||||
|
Button ok = new Button() { Text = "OK", Left = 10, Top = 50, Width = 80, DialogResult = DialogResult.OK };
|
||||||
|
Button cancel = new Button() { Text = "Cancel", Left = 100, Top = 50, Width = 80, DialogResult = DialogResult.Cancel };
|
||||||
|
|
||||||
|
dlg.Controls.Add(combo);
|
||||||
|
dlg.Controls.Add(ok);
|
||||||
|
dlg.Controls.Add(cancel);
|
||||||
|
dlg.AcceptButton = ok;
|
||||||
|
dlg.CancelButton = cancel;
|
||||||
|
|
||||||
|
if (dlg.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
dynamic selected = combo.SelectedItem;
|
||||||
|
return (ushort)selected.Value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public bool SendCmd(byte cmd, out UInt16 error)
|
||||||
|
{
|
||||||
|
error = 0;
|
||||||
if (hDevice == IntPtr.Zero) return false;
|
if (hDevice == IntPtr.Zero) return false;
|
||||||
|
|
||||||
|
// Формируем сообщение
|
||||||
SLCAN_MESSAGE msg = new SLCAN_MESSAGE
|
SLCAN_MESSAGE msg = new SLCAN_MESSAGE
|
||||||
{
|
{
|
||||||
Info = 0,
|
Info = 0,
|
||||||
@ -199,8 +262,25 @@ namespace BootloaderGUI
|
|||||||
};
|
};
|
||||||
msg.Data[0] = cmd;
|
msg.Data[0] = cmd;
|
||||||
|
|
||||||
return SlCan_DeviceWriteMessages(hDevice, new SLCAN_MESSAGE[] { msg }, 1, out _);
|
if (!SlCan_DeviceWriteMessages(hDevice, new SLCAN_MESSAGE[] { msg }, 1, out _))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Таймаут: 10с для ERASE, 1с для остальных
|
||||||
|
uint timeout = (cmd == BootloaderCommands.ERASE) ? 10000u : 1000u;
|
||||||
|
|
||||||
|
if(cmd != BootloaderCommands.JUMP)
|
||||||
|
{
|
||||||
|
return WaitForResponse(timeout, out error);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public bool SendData(byte[] data)
|
public bool SendData(byte[] data)
|
||||||
{
|
{
|
||||||
@ -222,10 +302,55 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
if (!SlCan_DeviceWriteMessages(hDevice, new SLCAN_MESSAGE[] { msg }, 1, out _))
|
if (!SlCan_DeviceWriteMessages(hDevice, new SLCAN_MESSAGE[] { msg }, 1, out _))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public bool WaitForResponse(uint timeoutMs, out UInt16 error)
|
||||||
|
{
|
||||||
|
error = 0;
|
||||||
|
|
||||||
|
// 1) Очистим RX буфер у устройства
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
byte[] response = buffer[0].Data;
|
||||||
|
if (response[0] == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = (UInt16)((response[1] << 8) | response[2]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = 0xFFFF; // таймаут / ничего не пришло
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Disconnect()
|
public void Disconnect()
|
||||||
{
|
{
|
||||||
if (hDevice != IntPtr.Zero)
|
if (hDevice != IntPtr.Zero)
|
||||||
|
@ -11,7 +11,7 @@ namespace BootloaderGUI
|
|||||||
public TabPage Tab { get; private set; }
|
public TabPage Tab { get; private set; }
|
||||||
|
|
||||||
private Label lblStatus;
|
private Label lblStatus;
|
||||||
private Button btnSelect, btnErase, btnReceive, btnWrite, btnVerify, btnJump, btnConnect;
|
private Button btnErase, btnReceive, btnWrite, btnVerify, btnJump;
|
||||||
private Button btnReset, btnGoBoot;
|
private Button btnReset, btnGoBoot;
|
||||||
private NumericUpDown numPage;
|
private NumericUpDown numPage;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
@ -22,20 +22,20 @@ namespace BootloaderGUI
|
|||||||
// Сюда нужно передавать делегаты или интерфейс для CAN
|
// Сюда нужно передавать делегаты или интерфейс для CAN
|
||||||
private Func<int> readFirmware;
|
private Func<int> readFirmware;
|
||||||
private Func<byte, bool> sendCmdSync;
|
private Func<byte, bool> sendCmdSync;
|
||||||
private Action<int> sendReceivePage;
|
private Action<int> sendPage;
|
||||||
|
|
||||||
public LowLevelBootloaderTab(
|
public LowLevelBootloaderTab(
|
||||||
Func<int> readFirmwareDelegate,
|
Func<int> readFirmwareDelegate,
|
||||||
Func<byte, bool> sendCmdSyncDelegate,
|
Func<byte, bool> sendCmdSyncDelegate,
|
||||||
Action<int> sendReceivePageDelegate,
|
Action<int> sendPageDelegate,
|
||||||
Label statusLabel,
|
Label statusLabel,
|
||||||
Action connectCANDelegate)
|
ProgressBar progrBar)
|
||||||
{
|
{
|
||||||
readFirmware = readFirmwareDelegate;
|
readFirmware = readFirmwareDelegate;
|
||||||
sendCmdSync = sendCmdSyncDelegate;
|
sendCmdSync = sendCmdSyncDelegate;
|
||||||
sendReceivePage = sendReceivePageDelegate;
|
sendPage = sendPageDelegate;
|
||||||
lblStatus = statusLabel;
|
lblStatus = statusLabel;
|
||||||
connectAction = connectCANDelegate;
|
progressBar = progrBar;
|
||||||
InitializeUI();
|
InitializeUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,73 +49,67 @@ namespace BootloaderGUI
|
|||||||
int hBtn = 30;
|
int hBtn = 30;
|
||||||
int gap = 10;
|
int gap = 10;
|
||||||
|
|
||||||
lblStatus = new Label() { Top = top, Left = left, Width = 520, Text = "Select firmware file" };
|
// Подпись для выбора страницы прошивки
|
||||||
Tab.Controls.Add(lblStatus);
|
Label lblPage = new Label()
|
||||||
|
{
|
||||||
|
Top = top,
|
||||||
|
Left = left,
|
||||||
|
Width = 130,
|
||||||
|
Height = 20,
|
||||||
|
Text = "Page of firmware to send"
|
||||||
|
};
|
||||||
|
|
||||||
top += 30;
|
// NumericUpDown для выбора страницы
|
||||||
btnSelect = new Button() { Top = top, Left = left, Width = wBtn, Text = "Select Firmware" };
|
numPage = new NumericUpDown()
|
||||||
btnConnect = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "Connect CAN" };
|
{
|
||||||
Tab.Controls.AddRange(new Control[] { btnSelect, btnConnect });
|
Top = top,
|
||||||
|
Left = left + lblPage.Width, // немного справа от подписи
|
||||||
top += hBtn + gap;
|
Width = 120,
|
||||||
numPage = new NumericUpDown() { Top = top, Left = left, Width = 120, Minimum = 0, Maximum = 0 };
|
Minimum = 0,
|
||||||
|
Maximum = 0
|
||||||
|
};
|
||||||
Tab.Controls.Add(numPage);
|
Tab.Controls.Add(numPage);
|
||||||
|
Tab.Controls.Add(lblPage);
|
||||||
|
|
||||||
|
|
||||||
top += numPage.Height + gap;
|
top += numPage.Height + gap;
|
||||||
btnErase = new Button() { Top = top, Left = left, Width = wBtn, Text = "ERASE" };
|
btnErase = new Button() { Top = top, Left = left, Width = wBtn, Text = "ERASE Firmware" };
|
||||||
btnReceive = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "RECEIVE Page" };
|
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, btnReceive });
|
||||||
|
|
||||||
top += hBtn + gap;
|
top += hBtn + gap;
|
||||||
btnWrite = new Button() { Top = top, Left = left, Width = wBtn, Text = "WRITE" };
|
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" };
|
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[] { btnWrite, btnVerify });
|
||||||
|
|
||||||
top += hBtn + gap;
|
top += hBtn + gap;
|
||||||
btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP" };
|
btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP to App" };
|
||||||
btnReset = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "RESET" };
|
btnGoBoot = new Button() { Top = top + hBtn + gap, Left = left, Width = wBtn, Text = "JUMP to Boot" };
|
||||||
btnGoBoot = new Button() { Top = top + hBtn + gap, Left = left, Width = wBtn, Text = "GO TO BOOT" };
|
btnReset = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "RESET MCU" };
|
||||||
Tab.Controls.AddRange(new Control[] { btnJump, btnReset, btnGoBoot });
|
Tab.Controls.AddRange(new Control[] { btnJump, btnReset, btnGoBoot });
|
||||||
|
|
||||||
top += 2 * hBtn + 2 * gap;
|
|
||||||
progressBar = new ProgressBar() { Top = top, Left = left, Width = 520, Height = 22 };
|
|
||||||
Tab.Controls.Add(progressBar);
|
|
||||||
|
|
||||||
// события
|
// события
|
||||||
btnSelect.Click += (s, e) =>
|
|
||||||
{
|
|
||||||
int pageMax = readFirmware();
|
|
||||||
numPage.Maximum = pageMax;
|
|
||||||
};
|
|
||||||
btnConnect.Click += (s, e) => connectAction?.Invoke();
|
|
||||||
btnErase.Click += (s, e) => sendCmdSync(BootloaderCommands.ERASE);
|
btnErase.Click += (s, e) => sendCmdSync(BootloaderCommands.ERASE);
|
||||||
btnReceive.Click += (s, e) => sendReceivePage((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);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetMaxPage(int max)
|
||||||
// Методы обновления UI из MainForm
|
|
||||||
public void UpdateStatus(string text)
|
|
||||||
{
|
{
|
||||||
if (Tab.InvokeRequired)
|
if (numPage.InvokeRequired)
|
||||||
Tab.Invoke(new Action(() => lblStatus.Text = text));
|
{
|
||||||
|
numPage.Invoke(new Action(() => numPage.Maximum = max));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
lblStatus.Text = text;
|
{
|
||||||
|
numPage.Maximum = max;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateProgress(int percent)
|
|
||||||
{
|
|
||||||
if (percent < 0) percent = 0;
|
|
||||||
if (percent > 100) percent = 100;
|
|
||||||
if (Tab.InvokeRequired)
|
|
||||||
Tab.Invoke(new Action(() => progressBar.Value = percent));
|
|
||||||
else
|
|
||||||
progressBar.Value = percent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,11 @@ namespace BootloaderGUI
|
|||||||
public class MainForm : Form
|
public class MainForm : Form
|
||||||
{
|
{
|
||||||
// UI
|
// UI
|
||||||
|
private BootloaderTab regTab;
|
||||||
private LowLevelBootloaderTab llTab;
|
private LowLevelBootloaderTab llTab;
|
||||||
private Label lblStatus;
|
private Label lblStatus;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
private Button btnSelect, btnConnect;
|
||||||
|
|
||||||
// CAN SETTINGS
|
// CAN SETTINGS
|
||||||
private CanInterface can;
|
private CanInterface can;
|
||||||
@ -41,14 +42,24 @@ namespace BootloaderGUI
|
|||||||
InitializeUI();
|
InitializeUI();
|
||||||
can = new CanInterface();
|
can = new CanInterface();
|
||||||
|
|
||||||
|
|
||||||
|
regTab = new BootloaderTab(
|
||||||
|
readFirmwareDelegate: ReadFirmware,
|
||||||
|
sendCmdSyncDelegate: SendCmdSync,
|
||||||
|
sendPageDelegate: SendPage,
|
||||||
|
progrBar: progressBar
|
||||||
|
);
|
||||||
|
tabControl.TabPages.Add(regTab.Tab);
|
||||||
|
|
||||||
llTab = new LowLevelBootloaderTab(
|
llTab = new LowLevelBootloaderTab(
|
||||||
readFirmwareDelegate: ReadFirmware,
|
readFirmwareDelegate: ReadFirmware,
|
||||||
sendCmdSyncDelegate: SendCmdSync,
|
sendCmdSyncDelegate: SendCmdSync,
|
||||||
sendReceivePageDelegate: SendReceivePage,
|
sendPageDelegate: SendPage,
|
||||||
statusLabel: lblStatus,
|
statusLabel: lblStatus,
|
||||||
connectCANDelegate: ConnectCan
|
progrBar: progressBar
|
||||||
);
|
);
|
||||||
tabControl.TabPages.Add(llTab.Tab);
|
tabControl.TabPages.Add(llTab.Tab);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -71,20 +82,59 @@ namespace BootloaderGUI
|
|||||||
};
|
};
|
||||||
this.Controls.Add(lblStatus);
|
this.Controls.Add(lblStatus);
|
||||||
|
|
||||||
// создаём TabControl
|
// Кнопки Select Firmware и Connect CAN
|
||||||
|
btnSelect = new Button()
|
||||||
|
{
|
||||||
|
Top = lblStatus.Bottom + 10,
|
||||||
|
Left = 10,
|
||||||
|
Width = 200,
|
||||||
|
Text = "Select Firmware"
|
||||||
|
};
|
||||||
|
btnSelect.Click += (s, e) =>
|
||||||
|
{
|
||||||
|
int pageMax = ReadFirmware();
|
||||||
|
UpdateStatusOnUI($"File loaded. Total pages: {pageMax + 1}");
|
||||||
|
llTab?.SetMaxPage(pageMax); // <-- обновляем numPage
|
||||||
|
regTab?.SetMaxPage(pageMax); // <-- обновляем numPage
|
||||||
|
};
|
||||||
|
|
||||||
|
btnConnect = new Button()
|
||||||
|
{
|
||||||
|
Top = lblStatus.Bottom + 10,
|
||||||
|
Left = btnSelect.Right + 10,
|
||||||
|
Width = 200,
|
||||||
|
Text = "Connect CAN"
|
||||||
|
};
|
||||||
|
btnConnect.Click += (s, e) =>
|
||||||
|
{
|
||||||
|
ushort? bitrate = CanInterface.CanSpeedDialog.ShowDialog();
|
||||||
|
if (bitrate == null) return;
|
||||||
|
|
||||||
|
string status;
|
||||||
|
if (can.ConnectFirstDevice(bitrate.Value, out status))
|
||||||
|
lblStatus.Text = status;
|
||||||
|
else
|
||||||
|
lblStatus.Text = "Connect failed";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.Controls.Add(btnSelect);
|
||||||
|
this.Controls.Add(btnConnect);
|
||||||
|
|
||||||
|
// создаём TabControl ниже кнопок
|
||||||
tabControl = new TabControl()
|
tabControl = new TabControl()
|
||||||
{
|
{
|
||||||
Top = 40,
|
Top = btnSelect.Bottom + 10,
|
||||||
Left = 10,
|
Left = 10,
|
||||||
Width = 480,
|
Width = 480,
|
||||||
Height = 300 // уменьшаем, чтобы помещался ProgressBar снизу
|
Height = 260
|
||||||
};
|
};
|
||||||
this.Controls.Add(tabControl);
|
this.Controls.Add(tabControl);
|
||||||
|
|
||||||
// создаём ProgressBar под вкладками
|
// ProgressBar под вкладками
|
||||||
progressBar = new ProgressBar()
|
progressBar = new ProgressBar()
|
||||||
{
|
{
|
||||||
Top = tabControl.Bottom + 10, // сразу под TabControl
|
Top = tabControl.Bottom + 10,
|
||||||
Left = 10,
|
Left = 10,
|
||||||
Width = 480,
|
Width = 480,
|
||||||
Height = 22,
|
Height = 22,
|
||||||
@ -108,16 +158,44 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
private void ConnectCan()
|
private void ConnectCan()
|
||||||
{
|
{
|
||||||
if (can.ConnectFirstDevice(out string status))
|
using (var dlg = new Form())
|
||||||
{
|
{
|
||||||
|
dlg.Text = "Select CAN Speed";
|
||||||
|
dlg.Width = 300;
|
||||||
|
dlg.Height = 200;
|
||||||
|
var combo = new ComboBox() { Left = 20, Top = 20, Width = 200 };
|
||||||
|
combo.Items.AddRange(new object[]
|
||||||
|
{
|
||||||
|
new { Text="1000K", Value=CanInterface.SLCAN_BR_CIA_1000K },
|
||||||
|
new { Text="800K", Value=CanInterface.SLCAN_BR_CIA_800K },
|
||||||
|
new { Text="500K", Value=CanInterface.SLCAN_BR_CIA_500K },
|
||||||
|
new { Text="250K", Value=CanInterface.SLCAN_BR_CIA_250K },
|
||||||
|
new { Text="125K", Value=CanInterface.SLCAN_BR_CIA_125K },
|
||||||
|
});
|
||||||
|
combo.DisplayMember = "Text";
|
||||||
|
combo.ValueMember = "Value";
|
||||||
|
combo.SelectedIndex = 3; // по умолчанию 250K
|
||||||
|
|
||||||
|
var ok = new Button() { Text = "OK", Left = 20, Top = 60, DialogResult = DialogResult.OK };
|
||||||
|
dlg.Controls.Add(combo);
|
||||||
|
dlg.Controls.Add(ok);
|
||||||
|
dlg.AcceptButton = ok;
|
||||||
|
|
||||||
|
if (dlg.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
ushort bitrate = (ushort)((dynamic)combo.SelectedItem).Value;
|
||||||
|
|
||||||
|
if (can.ConnectFirstDevice(bitrate, out string status))
|
||||||
lblStatus.Text = status;
|
lblStatus.Text = status;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MessageBox.Show(status, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(status, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
lblStatus.Text = status;
|
lblStatus.Text = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private int ReadFirmware()
|
private int ReadFirmware()
|
||||||
{
|
{
|
||||||
@ -140,8 +218,11 @@ namespace BootloaderGUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void SendReceivePage(int pageNum)
|
private void SendPage(int pageNum)
|
||||||
{
|
{
|
||||||
|
uint timeout = 1000u;
|
||||||
|
UInt16 error = 0;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (fwData == null) return;
|
if (fwData == null) return;
|
||||||
@ -158,12 +239,13 @@ namespace BootloaderGUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRC32 (можно оставить заглушкой, как у тебя)
|
// CRC32 (можно оставить заглушкой, как у тебя)
|
||||||
uint crc = 123;
|
uint crc = CRC32Calculator.Compute(page);
|
||||||
byte[] crcBE = new byte[4];
|
byte[] crcBE = new byte[4] {
|
||||||
crcBE[0] = (byte)((crc >> 24) & 0xFF);
|
(byte)((crc >> 24) & 0xFF),
|
||||||
crcBE[1] = (byte)((crc >> 16) & 0xFF);
|
(byte)((crc >> 16) & 0xFF),
|
||||||
crcBE[2] = (byte)((crc >> 8) & 0xFF);
|
(byte)((crc >> 8) & 0xFF),
|
||||||
crcBE[3] = (byte)(crc & 0xFF);
|
(byte)(crc & 0xFF)
|
||||||
|
};
|
||||||
|
|
||||||
byte[] full = new byte[BootloaderCommands.PAGE_SIZE + 4];
|
byte[] full = new byte[BootloaderCommands.PAGE_SIZE + 4];
|
||||||
Array.Copy(page, 0, full, 0, BootloaderCommands.PAGE_SIZE);
|
Array.Copy(page, 0, full, 0, BootloaderCommands.PAGE_SIZE);
|
||||||
@ -201,7 +283,21 @@ namespace BootloaderGUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpdateProgressOnUI(100);
|
UpdateProgressOnUI(100);
|
||||||
|
|
||||||
|
|
||||||
|
if(can.WaitForResponse(timeout, out error))
|
||||||
|
{
|
||||||
UpdateStatusOnUI($"Page {pageNum} sent (RECEIVE)");
|
UpdateStatusOnUI($"Page {pageNum} sent (RECEIVE)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (error == 0xFFFF)
|
||||||
|
MessageBox.Show($"Error during sending: timed out", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
else
|
||||||
|
MessageBox.Show($"Error during sending, error code: 0x{error:X4}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -211,17 +307,28 @@ namespace BootloaderGUI
|
|||||||
|
|
||||||
private bool SendCmdSync(byte cmd)
|
private bool SendCmdSync(byte cmd)
|
||||||
{
|
{
|
||||||
if (can == null || !can.SendCmd(cmd))
|
if (can == null)
|
||||||
{
|
{
|
||||||
MessageBox.Show("CAN device not connected or send failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBox.Show("CAN device not connected", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateStatusOnUI($"Command 0x{cmd:X2} sent");
|
if (!can.SendCmd(cmd, out UInt16 error))
|
||||||
|
{
|
||||||
|
if (error == 0xFFFF)
|
||||||
|
MessageBox.Show($"Command 0x{cmd:X2} timed out", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
else
|
||||||
|
MessageBox.Show($"Command 0x{cmd:X2} failed, error code: 0x{error:X4}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatusOnUI($"Command 0x{cmd:X2} sent successfully");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void UpdateStatusOnUI(string text)
|
private void UpdateStatusOnUI(string text)
|
||||||
{
|
{
|
||||||
if (this.InvokeRequired)
|
if (this.InvokeRequired)
|
||||||
@ -242,4 +349,32 @@ namespace BootloaderGUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CRC32Calculator
|
||||||
|
{
|
||||||
|
private const uint Polynomial = 0x04C11DB7;
|
||||||
|
|
||||||
|
public static uint Compute(byte[] data)
|
||||||
|
{
|
||||||
|
uint crc = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
foreach (byte b in data)
|
||||||
|
{
|
||||||
|
crc ^= (uint)(b << 24);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if ((crc & 0x80000000) != 0)
|
||||||
|
crc = (crc << 1) ^ Polynomial;
|
||||||
|
else
|
||||||
|
crc <<= 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc ^ 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
140
botterm/botterm/UserFriendlyBoot.cs
Normal file
140
botterm/botterm/UserFriendlyBoot.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace BootloaderGUI
|
||||||
|
{
|
||||||
|
public class BootloaderTab
|
||||||
|
{
|
||||||
|
public TabPage Tab { get; private set; }
|
||||||
|
|
||||||
|
private Label lblStatus; // Локальный label для статуса
|
||||||
|
private Button btnFirmware, btnGoBoot, btnReset;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
|
private Func<int> readFirmware;
|
||||||
|
private Func<byte, bool> sendCmdSync;
|
||||||
|
private Action<int> sendPage;
|
||||||
|
private int totalPages;
|
||||||
|
public BootloaderTab(
|
||||||
|
Func<int> readFirmwareDelegate,
|
||||||
|
Func<byte, bool> sendCmdSyncDelegate,
|
||||||
|
Action<int> sendPageDelegate,
|
||||||
|
ProgressBar progrBar) // убрали внешний statusLabel
|
||||||
|
{
|
||||||
|
readFirmware = readFirmwareDelegate;
|
||||||
|
sendCmdSync = sendCmdSyncDelegate;
|
||||||
|
sendPage = sendPageDelegate;
|
||||||
|
progressBar = progrBar;
|
||||||
|
InitializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeUI()
|
||||||
|
{
|
||||||
|
Tab = new TabPage("Bootloader");
|
||||||
|
|
||||||
|
int left = 10;
|
||||||
|
int top = 10;
|
||||||
|
int wBtn = 200;
|
||||||
|
int hBtn = 30;
|
||||||
|
int gap = 10;
|
||||||
|
|
||||||
|
// Создаём локальный label для статуса
|
||||||
|
lblStatus = new Label()
|
||||||
|
{
|
||||||
|
Top = top,
|
||||||
|
Left = left,
|
||||||
|
Width = 400,
|
||||||
|
Height = 25,
|
||||||
|
Text = "Ready"
|
||||||
|
};
|
||||||
|
Tab.Controls.Add(lblStatus);
|
||||||
|
|
||||||
|
top += lblStatus.Height + gap;
|
||||||
|
|
||||||
|
// Кнопка "Firmware" - автоматическая прошивка
|
||||||
|
btnFirmware = new Button() { Top = top, Left = left, Width = wBtn, Height = hBtn, Text = "Firmware" };
|
||||||
|
btnFirmware.Click += async (s, e) => await FlashFirmwareAsync();
|
||||||
|
|
||||||
|
// Кнопка "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);
|
||||||
|
|
||||||
|
top += hBtn + gap;
|
||||||
|
|
||||||
|
// Кнопка "Go To App"
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Кнопка "Reset"
|
||||||
|
btnReset = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Height = hBtn, Text = "Reset" };
|
||||||
|
btnReset.Click += (s, e) => sendCmdSync(BootloaderCommands.RESET);
|
||||||
|
|
||||||
|
Tab.Controls.AddRange(new Control[] { btnFirmware, btnGoBoot, btnGoApp, btnReset });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task FlashFirmwareAsync()
|
||||||
|
{
|
||||||
|
if(totalPages <= 0)
|
||||||
|
{
|
||||||
|
UpdateStatus("No Firmware Selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatus("Erasing...");
|
||||||
|
await Task.Delay(5); // небольшая пауза для UI
|
||||||
|
if (!sendCmdSync(BootloaderCommands.ERASE))
|
||||||
|
{
|
||||||
|
UpdateStatus("Erase failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(5); // небольшая пауза для UI
|
||||||
|
for (int page = 0; page < totalPages; page++)
|
||||||
|
{
|
||||||
|
UpdateStatus($"Sending page {page + 1}/{totalPages}");
|
||||||
|
sendPage(page);
|
||||||
|
|
||||||
|
UpdateStatus($"Writing page {page + 1}/{totalPages}");
|
||||||
|
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...");
|
||||||
|
await Task.Delay(5); // небольшая пауза для UI
|
||||||
|
sendCmdSync(BootloaderCommands.JUMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Локальный метод для обновления статуса
|
||||||
|
private void UpdateStatus(string text)
|
||||||
|
{
|
||||||
|
if (lblStatus.InvokeRequired)
|
||||||
|
lblStatus.Invoke(new Action(() => lblStatus.Text = text));
|
||||||
|
else
|
||||||
|
lblStatus.Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMaxPage(int max)
|
||||||
|
{
|
||||||
|
totalPages = max + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
@ -54,6 +54,7 @@
|
|||||||
<Compile Include="LowLevelBoot.cs" />
|
<Compile Include="LowLevelBoot.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="UserFriendlyBoot.cs" />
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
Binary file not shown.
@ -1 +1 @@
|
|||||||
bc27571c42487534b7297bfdef2c705a3d0dd908
|
6e4f7064cd453f33ddf5825a7ce97a1c149aef21
|
||||||
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user