diff --git a/botterm/.vs/botterm/v16/.suo b/botterm/.vs/botterm/v16/.suo index f5ecd22..f6a0c24 100644 Binary files a/botterm/.vs/botterm/v16/.suo and b/botterm/.vs/botterm/v16/.suo differ diff --git a/botterm/botterm/CanInterface.cs b/botterm/botterm/CanInterface.cs index 3e66893..c76873d 100644 --- a/botterm/botterm/CanInterface.cs +++ b/botterm/botterm/CanInterface.cs @@ -21,6 +21,11 @@ namespace BootloaderGUI public const uint SLCAN_MODE_NORMAL = 0x01; 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)] private struct SLCAN_MESSAGE { @@ -58,6 +63,19 @@ namespace BootloaderGUI [DllImport(DLL_NAME, CallingConvention = CallingConvention.StdCall)] 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)] 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 = ""; @@ -139,17 +157,9 @@ namespace BootloaderGUI hDevice = dev; - // Включаем приём сообщений SlCan_DeviceEnaRec(hDevice, 1); - // Получаем состояние шины (если у тебя есть функция SlCan_DeviceGetState) - SLCAN_STATE state; - if (!SlCan_DeviceGetState(hDevice, out state)) - { - statusText = "Failed to read bus state"; - } - - // Перевести устройство в конфиг-режим + // CONFIG mode if (!SlCan_DeviceSetMode(hDevice, SLCAN_MODE_CONFIG)) { MessageBox.Show("Failed to set CAN mode", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); @@ -157,16 +167,16 @@ namespace BootloaderGUI return false; } + // Выбранный битрейт SLCAN_BITRATE br = new SLCAN_BITRATE { - BRP = SLCAN_BR_CIA_250K, + BRP = bitrateCode, TSEG1 = 0, TSEG2 = 0, SJW = 0, SAM = 0 }; - // Установить скорость CAN if (!SlCan_DeviceSetBitRate(hDevice, ref br)) { MessageBox.Show("Failed to set CAN speed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); @@ -174,7 +184,6 @@ namespace BootloaderGUI return false; } - // Перевести устройство в нормальный режим if (!SlCan_DeviceSetMode(hDevice, SLCAN_MODE_NORMAL)) { MessageBox.Show("Failed to set CAN mode", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); @@ -182,14 +191,68 @@ namespace BootloaderGUI return false; } - statusText = $"Connected to device 0 (devices: {count})"; + statusText = $"Connected to device 0 (devices: {count}) at {bitrateCode}"; return true; } - - public bool SendCmd(byte cmd) + public static class CanSpeedDialog { + 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; + + // Формируем сообщение SLCAN_MESSAGE msg = new SLCAN_MESSAGE { Info = 0, @@ -199,9 +262,26 @@ namespace BootloaderGUI }; 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) { if (hDevice == IntPtr.Zero) return false; @@ -222,10 +302,55 @@ namespace BootloaderGUI if (!SlCan_DeviceWriteMessages(hDevice, new SLCAN_MESSAGE[] { msg }, 1, out _)) return false; + + } 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() { if (hDevice != IntPtr.Zero) diff --git a/botterm/botterm/LowLevelBoot.cs b/botterm/botterm/LowLevelBoot.cs index 3c0892b..7b66c82 100644 --- a/botterm/botterm/LowLevelBoot.cs +++ b/botterm/botterm/LowLevelBoot.cs @@ -11,7 +11,7 @@ namespace BootloaderGUI public TabPage Tab { get; private set; } private Label lblStatus; - private Button btnSelect, btnErase, btnReceive, btnWrite, btnVerify, btnJump, btnConnect; + private Button btnErase, btnReceive, btnWrite, btnVerify, btnJump; private Button btnReset, btnGoBoot; private NumericUpDown numPage; private ProgressBar progressBar; @@ -22,20 +22,20 @@ namespace BootloaderGUI // Сюда нужно передавать делегаты или интерфейс для CAN private Func readFirmware; private Func sendCmdSync; - private Action sendReceivePage; + private Action sendPage; public LowLevelBootloaderTab( Func readFirmwareDelegate, Func sendCmdSyncDelegate, - Action sendReceivePageDelegate, + Action sendPageDelegate, Label statusLabel, - Action connectCANDelegate) + ProgressBar progrBar) { readFirmware = readFirmwareDelegate; sendCmdSync = sendCmdSyncDelegate; - sendReceivePage = sendReceivePageDelegate; + sendPage = sendPageDelegate; lblStatus = statusLabel; - connectAction = connectCANDelegate; + progressBar = progrBar; InitializeUI(); } @@ -49,73 +49,67 @@ namespace BootloaderGUI int hBtn = 30; 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; - btnSelect = new Button() { Top = top, Left = left, Width = wBtn, Text = "Select Firmware" }; - btnConnect = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "Connect CAN" }; - Tab.Controls.AddRange(new Control[] { btnSelect, btnConnect }); - - top += hBtn + gap; - numPage = new NumericUpDown() { Top = top, Left = left, Width = 120, Minimum = 0, Maximum = 0 }; + // NumericUpDown для выбора страницы + numPage = new NumericUpDown() + { + Top = top, + Left = left + lblPage.Width, // немного справа от подписи + Width = 120, + Minimum = 0, + Maximum = 0 + }; Tab.Controls.Add(numPage); + Tab.Controls.Add(lblPage); + top += numPage.Height + gap; - btnErase = new Button() { Top = top, Left = left, Width = wBtn, Text = "ERASE" }; - btnReceive = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "RECEIVE Page" }; + 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 }); top += hBtn + gap; - btnWrite = new Button() { Top = top, Left = left, Width = wBtn, Text = "WRITE" }; - btnVerify = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "VERIFY" }; + 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 }); top += hBtn + gap; - btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP" }; - 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 = "GO TO BOOT" }; + btnJump = new Button() { Top = top, Left = left, Width = wBtn, Text = "JUMP to App" }; + btnGoBoot = new Button() { Top = top + hBtn + gap, Left = left, Width = wBtn, Text = "JUMP to Boot" }; + btnReset = new Button() { Top = top, Left = left + wBtn + gap, Width = wBtn, Text = "RESET MCU" }; 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); - btnReceive.Click += (s, e) => sendReceivePage((int)numPage.Value); + 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); - } - - // Методы обновления UI из MainForm - public void UpdateStatus(string text) + public void SetMaxPage(int max) { - if (Tab.InvokeRequired) - Tab.Invoke(new Action(() => lblStatus.Text = text)); + if (numPage.InvokeRequired) + { + numPage.Invoke(new Action(() => numPage.Maximum = max)); + } 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; - } + } } diff --git a/botterm/botterm/Program.cs b/botterm/botterm/Program.cs index 1323527..69e265c 100644 --- a/botterm/botterm/Program.cs +++ b/botterm/botterm/Program.cs @@ -21,10 +21,11 @@ namespace BootloaderGUI public class MainForm : Form { // UI + private BootloaderTab regTab; private LowLevelBootloaderTab llTab; private Label lblStatus; private ProgressBar progressBar; - + private Button btnSelect, btnConnect; // CAN SETTINGS private CanInterface can; @@ -41,14 +42,24 @@ namespace BootloaderGUI InitializeUI(); can = new CanInterface(); + + regTab = new BootloaderTab( + readFirmwareDelegate: ReadFirmware, + sendCmdSyncDelegate: SendCmdSync, + sendPageDelegate: SendPage, + progrBar: progressBar + ); + tabControl.TabPages.Add(regTab.Tab); + llTab = new LowLevelBootloaderTab( readFirmwareDelegate: ReadFirmware, sendCmdSyncDelegate: SendCmdSync, - sendReceivePageDelegate: SendReceivePage, + sendPageDelegate: SendPage, statusLabel: lblStatus, - connectCANDelegate: ConnectCan + progrBar: progressBar ); tabControl.TabPages.Add(llTab.Tab); + } @@ -71,20 +82,59 @@ namespace BootloaderGUI }; 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() { - Top = 40, + Top = btnSelect.Bottom + 10, Left = 10, Width = 480, - Height = 300 // уменьшаем, чтобы помещался ProgressBar снизу + Height = 260 }; this.Controls.Add(tabControl); - // создаём ProgressBar под вкладками + // ProgressBar под вкладками progressBar = new ProgressBar() { - Top = tabControl.Bottom + 10, // сразу под TabControl + Top = tabControl.Bottom + 10, Left = 10, Width = 480, Height = 22, @@ -108,17 +158,45 @@ namespace BootloaderGUI private void ConnectCan() { - if (can.ConnectFirstDevice(out string status)) + using (var dlg = new Form()) { - lblStatus.Text = status; - } - else - { - MessageBox.Show(status, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - lblStatus.Text = status; + 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; + else + { + MessageBox.Show(status, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + lblStatus.Text = status; + } + } } } + private int ReadFirmware() { int page_max = 0; @@ -140,8 +218,11 @@ namespace BootloaderGUI } - private void SendReceivePage(int pageNum) + private void SendPage(int pageNum) { + uint timeout = 1000u; + UInt16 error = 0; + try { if (fwData == null) return; @@ -158,12 +239,13 @@ namespace BootloaderGUI } // CRC32 (можно оставить заглушкой, как у тебя) - uint crc = 123; - byte[] crcBE = new byte[4]; - crcBE[0] = (byte)((crc >> 24) & 0xFF); - crcBE[1] = (byte)((crc >> 16) & 0xFF); - crcBE[2] = (byte)((crc >> 8) & 0xFF); - crcBE[3] = (byte)(crc & 0xFF); + uint crc = CRC32Calculator.Compute(page); + byte[] crcBE = new byte[4] { + (byte)((crc >> 24) & 0xFF), + (byte)((crc >> 16) & 0xFF), + (byte)((crc >> 8) & 0xFF), + (byte)(crc & 0xFF) + }; byte[] full = new byte[BootloaderCommands.PAGE_SIZE + 4]; Array.Copy(page, 0, full, 0, BootloaderCommands.PAGE_SIZE); @@ -201,7 +283,21 @@ namespace BootloaderGUI } UpdateProgressOnUI(100); - UpdateStatusOnUI($"Page {pageNum} sent (RECEIVE)"); + + + if(can.WaitForResponse(timeout, out error)) + { + 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) { @@ -211,17 +307,28 @@ namespace BootloaderGUI 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; } - 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; } + private void UpdateStatusOnUI(string text) { 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; + } + } + + } + + diff --git a/botterm/botterm/UserFriendlyBoot.cs b/botterm/botterm/UserFriendlyBoot.cs new file mode 100644 index 0000000..b8e966c --- /dev/null +++ b/botterm/botterm/UserFriendlyBoot.cs @@ -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 readFirmware; + private Func sendCmdSync; + private Action sendPage; + private int totalPages; + public BootloaderTab( + Func readFirmwareDelegate, + Func sendCmdSyncDelegate, + Action 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; + } + } +} diff --git a/botterm/botterm/bin/Debug/botterm.exe b/botterm/botterm/bin/Debug/botterm.exe index cbcbe8d..29cf30c 100644 Binary files a/botterm/botterm/bin/Debug/botterm.exe and b/botterm/botterm/bin/Debug/botterm.exe differ diff --git a/botterm/botterm/bin/Debug/botterm.pdb b/botterm/botterm/bin/Debug/botterm.pdb index 941cf8a..5902e85 100644 Binary files a/botterm/botterm/bin/Debug/botterm.pdb and b/botterm/botterm/bin/Debug/botterm.pdb differ diff --git a/botterm/botterm/botterm.csproj b/botterm/botterm/botterm.csproj index 1f4608f..9d9e726 100644 --- a/botterm/botterm/botterm.csproj +++ b/botterm/botterm/botterm.csproj @@ -54,6 +54,7 @@ + ResXFileCodeGenerator Resources.Designer.cs diff --git a/botterm/botterm/obj/Debug/botterm.csproj.AssemblyReference.cache b/botterm/botterm/obj/Debug/botterm.csproj.AssemblyReference.cache index f5e894a..1228a24 100644 Binary files a/botterm/botterm/obj/Debug/botterm.csproj.AssemblyReference.cache and b/botterm/botterm/obj/Debug/botterm.csproj.AssemblyReference.cache differ diff --git a/botterm/botterm/obj/Debug/botterm.csproj.CoreCompileInputs.cache b/botterm/botterm/obj/Debug/botterm.csproj.CoreCompileInputs.cache index 1e128e0..e30f43b 100644 --- a/botterm/botterm/obj/Debug/botterm.csproj.CoreCompileInputs.cache +++ b/botterm/botterm/obj/Debug/botterm.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -bc27571c42487534b7297bfdef2c705a3d0dd908 +6e4f7064cd453f33ddf5825a7ce97a1c149aef21 diff --git a/botterm/botterm/obj/Debug/botterm.exe b/botterm/botterm/obj/Debug/botterm.exe index cbcbe8d..29cf30c 100644 Binary files a/botterm/botterm/obj/Debug/botterm.exe and b/botterm/botterm/obj/Debug/botterm.exe differ diff --git a/botterm/botterm/obj/Debug/botterm.pdb b/botterm/botterm/obj/Debug/botterm.pdb index 941cf8a..5902e85 100644 Binary files a/botterm/botterm/obj/Debug/botterm.pdb and b/botterm/botterm/obj/Debug/botterm.pdb differ