适用于 STM32 系列单片机的 USB DFU 上位机程序

一、系统架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    STM32 USB DFU 上位机系统                 │
├─────────────────────────────────────────────────────────────┤
│  设备管理     │  固件处理     │  DFU协议栈   │  用户界面     │
│               │               │              │              │
│  • 设备枚举   │  • 文件解析   │  • 状态机    │  • 主窗口    │
│  • 设备识别   │  • 校验计算   │  • 命令发送  │  • 进度显示  │
│  • 连接管理   │  • 地址映射   │  • 数据传输  │  • 日志输出  │
│  • 热插拔检测 │  • 格式转换   │  • 超时重试  │  • 错误处理  │
└─────────────────────────────────────────────────────────────┘

二、开发环境配置

2.1 开发环境要求

复制代码
操作系统:Windows 7/10/11
开发工具:Visual Studio 2019/2022
.NET版本:.NET Framework 4.7.2 或 .NET 6
USB库:LibUsbDotNet 2.2.29

2.2 项目文件结构

复制代码
STM32_DFU_Tool/
├── Forms/
│   ├── MainForm.cs          # 主窗口
│   ├── DeviceForm.cs       # 设备选择窗口
│   └── LogForm.cs          # 日志窗口
├── DFU/
│   ├── DFUManager.cs       # DFU管理器
│   ├── DFUProtocol.cs      # DFU协议实现
│   ├── DFUParser.cs        # 固件文件解析
│   └── DFUValidator.cs     # 数据校验
├── USB/
│   ├── USBDevice.cs        # USB设备封装
│   ├── USBEnumerator.cs    # USB枚举器
│   └── USBHotplug.cs       # 热插拔检测
├── Utils/
│   ├── Logger.cs           # 日志工具
│   ├── CRC32.cs            # CRC32计算
│   └── HexParser.cs        # HEX文件解析
└── Resources/
    ├── stm32_dfu.inf       # 驱动信息文件
    └── icons/              # 图标资源

三、完整源码实现

3.1 主窗口界面 (MainForm.cs)

csharp 复制代码
using System;
using System.Windows.Forms;
using System.IO;
using LibUsbDotNet;
using LibUsbDotNet.Main;

namespace STM32_DFU_Tool
{
    public partial class MainForm : Form
    {
        private DFUManager dfuManager;
        private USBEnumerator usbEnumerator;
        private bool isUpgrading = false;
        
        public MainForm()
        {
            InitializeComponent();
            InitializeDFU();
        }
        
        private void InitializeDFU()
        {
            dfuManager = new DFUManager();
            usbEnumerator = new USBEnumerator();
            
            // 注册设备变化事件
            usbEnumerator.DeviceAdded += OnDeviceAdded;
            usbEnumerator.DeviceRemoved += OnDeviceRemoved;
            
            // 注册DFU事件
            dfuManager.ProgressChanged += OnProgressChanged;
            dfuManager.StatusChanged += OnStatusChanged;
            dfuManager.ErrorOccurred += OnErrorOccurred;
            dfuManager.UpgradeCompleted += OnUpgradeCompleted;
        }
        
        private void MainForm_Load(object sender, EventArgs e)
        {
            // 初始化设备列表
            RefreshDeviceList();
            
            // 设置默认参数
            txtVendorID.Text = "0x0483";  // STM32默认VID
            txtProductID.Text = "0xDF11"; // STM32 DFU默认PID
            txtAltSetting.Text = "0";
            txtTransferSize.Text = "1024";
            
            // 加载最近文件
            LoadRecentFiles();
        }
        
        private void btnRefresh_Click(object sender, EventArgs e)
        {
            RefreshDeviceList();
        }
        
        private void RefreshDeviceList()
        {
            lstDevices.Items.Clear();
            
            var devices = usbEnumerator.GetDFUDevices();
            foreach (var device in devices)
            {
                ListViewItem item = new ListViewItem(device.DevicePath);
                item.SubItems.Add($"0x{device.VendorID:X4}");
                item.SubItems.Add($"0x{device.ProductID:X4}");
                item.SubItems.Add(device.SerialNumber);
                item.Tag = device;
                lstDevices.Items.Add(item);
            }
            
            lblDeviceCount.Text = $"发现 {devices.Count} 个DFU设备";
        }
        
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "固件文件|*.bin;*.hex;*.dfu|所有文件|*.*";
            dialog.Title = "选择固件文件";
            
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                txtFirmwarePath.Text = dialog.FileName;
                SaveRecentFile(dialog.FileName);
                
                // 解析文件信息
                var fileInfo = DFUParser.ParseFileInfo(dialog.FileName);
                lblFileInfo.Text = $"大小: {fileInfo.Size} 字节, 起始地址: 0x{fileInfo.StartAddress:X8}";
            }
        }
        
        private void btnUpgrade_Click(object sender, EventArgs e)
        {
            if (lstDevices.SelectedItems.Count == 0)
            {
                MessageBox.Show("请选择DFU设备", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            
            if (string.IsNullOrEmpty(txtFirmwarePath.Text))
            {
                MessageBox.Show("请选择固件文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            
            if (!File.Exists(txtFirmwarePath.Text))
            {
                MessageBox.Show("固件文件不存在", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            
            StartUpgrade();
        }
        
        private async void StartUpgrade()
        {
            isUpgrading = true;
            btnUpgrade.Enabled = false;
            btnCancel.Enabled = true;
            progressBar.Value = 0;
            
            try
            {
                var selectedDevice = (USBDevice)lstDevices.SelectedItems[0].Tag;
                var upgradeParams = new DFUUpgradeParams
                {
                    VendorID = Convert.ToInt32(txtVendorID.Text, 16),
                    ProductID = Convert.ToInt32(txtProductID.Text, 16),
                    AltSetting = Convert.ToInt32(txtAltSetting.Text),
                    TransferSize = Convert.ToInt32(txtTransferSize.Text),
                    FirmwarePath = txtFirmwarePath.Text,
                    EraseFirst = chkEraseFirst.Checked,
                    VerifyAfterWrite = chkVerify.Checked,
                    ResetAfterUpgrade = chkReset.Checked
                };
                
                await dfuManager.UpgradeAsync(selectedDevice, upgradeParams);
            }
            catch (Exception ex)
            {
                Logger.Error($"升级失败: {ex.Message}");
                MessageBox.Show($"升级失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                isUpgrading = false;
                btnUpgrade.Enabled = true;
                btnCancel.Enabled = false;
            }
        }
        
        private void OnProgressChanged(object sender, DFUProgressEventArgs e)
        {
            Invoke(new Action(() =>
            {
                progressBar.Value = e.Percentage;
                lblStatus.Text = e.Message;
                
                if (e.CurrentSector > 0)
                {
                    lblSector.Text = $"扇区: {e.CurrentSector}/{e.TotalSectors}";
                }
            }));
        }
        
        private void OnStatusChanged(object sender, DFUStatusEventArgs e)
        {
            Invoke(new Action(() =>
            {
                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {e.Message}\r\n");
                txtLog.ScrollToCaret();
            }));
        }
        
        private void OnErrorOccurred(object sender, DFUErrorEventArgs e)
        {
            Invoke(new Action(() =>
            {
                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] 错误: {e.Message}\r\n");
                txtLog.SelectionColor = System.Drawing.Color.Red;
                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] 错误: {e.Message}\r\n");
                txtLog.SelectionColor = System.Drawing.Color.Black;
                txtLog.ScrollToCaret();
            }));
        }
        
        private void OnUpgradeCompleted(object sender, DFUCompleteEventArgs e)
        {
            Invoke(new Action(() =>
            {
                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] 升级完成!用时: {e.ElapsedTime:F2}秒\r\n");
                MessageBox.Show("固件升级完成!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }));
        }
        
        private void OnDeviceAdded(object sender, USBDeviceEventArgs e)
        {
            Invoke(new Action(() =>
            {
                RefreshDeviceList();
                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] 设备已连接: {e.Device.DevicePath}\r\n");
            }));
        }
        
        private void OnDeviceRemoved(object sender, USBDeviceEventArgs e)
        {
            Invoke(new Action(() =>
            {
                RefreshDeviceList();
                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] 设备已断开: {e.Device.DevicePath}\r\n");
            }));
        }
        
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            usbEnumerator.Dispose();
            dfuManager.Dispose();
        }
    }
}

3.2 DFU 管理器 (DFUManager.cs)

csharp 复制代码
using System;
using System.Threading;
using System.Threading.Tasks;
using LibUsbDotNet;
using LibUsbDotNet.Main;

namespace STM32_DFU_Tool.DFU
{
    public class DFUManager : IDisposable
    {
        private IUsbDevice dfuDevice;
        private UsbEndpointReader reader;
        private UsbEndpointWriter writer;
        private bool disposed = false;
        
        // 事件定义
        public event EventHandler<DFUProgressEventArgs> ProgressChanged;
        public event EventHandler<DFUStatusEventArgs> StatusChanged;
        public event EventHandler<DFUErrorEventArgs> ErrorOccurred;
        public event EventHandler<DFUCompleteEventArgs> UpgradeCompleted;
        
        // DFU命令定义
        private const byte DFU_DETACH = 0x00;
        private const byte DFU_DNLOAD = 0x01;
        private const byte DFU_UPLOAD = 0x02;
        private const byte DFU_GETSTATUS = 0x03;
        private const byte DFU_CLRSTATUS = 0x04;
        private const byte DFU_GETSTATE = 0x05;
        private const byte DFU_ABORT = 0x06;
        
        // DFU状态定义
        private const byte STATE_APP_IDLE = 0x00;
        private const byte STATE_APP_DETACH = 0x01;
        private const byte STATE_DFU_IDLE = 0x02;
        private const byte STATE_DFU_DOWNLOAD_SYNC = 0x03;
        private const byte STATE_DFU_DOWNLOAD_BUSY = 0x04;
        private const byte STATE_DFU_DOWNLOAD_IDLE = 0x05;
        private const byte STATE_DFU_MANIFEST_SYNC = 0x06;
        private const byte STATE_DFU_MANIFEST = 0x07;
        private const byte STATE_DFU_MANIFEST_WAIT_RESET = 0x08;
        private const byte STATE_DFU_UPLOAD_IDLE = 0x09;
        private const byte STATE_DFU_ERROR = 0x0A;
        
        public async Task UpgradeAsync(USBDevice device, DFUUpgradeParams parameters)
        {
            try
            {
                OnStatusChanged("开始DFU升级...");
                
                // 1. 连接设备
                await ConnectToDevice(device, parameters);
                
                // 2. 检查设备状态
                await CheckDeviceStatus();
                
                // 3. 解析固件文件
                OnStatusChanged("解析固件文件...");
                var firmware = DFUParser.ParseFirmwareFile(parameters.FirmwarePath);
                
                // 4. 擦除闪存(如果需要)
                if (parameters.EraseFirst)
                {
                    await EraseFlash(firmware.Segments);
                }
                
                // 5. 下载固件
                await DownloadFirmware(firmware, parameters.TransferSize);
                
                // 6. 验证固件(如果需要)
                if (parameters.VerifyAfterWrite)
                {
                    await VerifyFirmware(firmware, parameters.TransferSize);
                }
                
                // 7. 完成升级
                await CompleteUpgrade();
                
                // 8. 重置设备(如果需要)
                if (parameters.ResetAfterUpgrade)
                {
                    await ResetDevice();
                }
                
                OnUpgradeCompleted(TimeSpan.FromMilliseconds(0));
            }
            catch (Exception ex)
            {
                OnErrorOccurred(ex.Message);
                throw;
            }
            finally
            {
                DisconnectDevice();
            }
        }
        
        private async Task ConnectToDevice(USBDevice device, DFUUpgradeParams parameters)
        {
            OnStatusChanged($"连接设备 {device.DevicePath}...");
            
            var usbFinder = new UsbDeviceFinder(parameters.VendorID, parameters.ProductID);
            dfuDevice = UsbDevice.OpenUsbDevice(usbFinder);
            
            if (dfuDevice == null)
            {
                throw new Exception("无法打开DFU设备");
            }
            
            // 设置配置
            IUsbDevice wholeUsbDevice = dfuDevice as IUsbDevice;
            if (!ReferenceEquals(wholeUsbDevice, null))
            {
                wholeUsbDevice.SetConfiguration(1);
                wholeUsbDevice.ClaimInterface(0);
            }
            
            // 获取读写端点
            reader = dfuDevice.OpenEndpointReader(ReadEndpointID.Ep01);
            writer = dfuDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
            
            OnStatusChanged("设备连接成功");
        }
        
        private async Task CheckDeviceStatus()
        {
            OnStatusChanged("检查设备状态...");
            
            // 发送GETSTATUS命令
            byte[] status = await SendDFUCommand(DFU_GETSTATUS, null);
            
            if (status.Length < 6)
            {
                throw new Exception("获取设备状态失败");
            }
            
            byte state = status[4];
            byte statusCode = status[0];
            
            if (statusCode != 0x00)
            {
                throw new Exception($"设备状态错误: 0x{statusCode:X2}");
            }
            
            if (state != STATE_DFU_IDLE)
            {
                throw new Exception($"设备不在DFU_IDLE状态: 0x{state:X2}");
            }
            
            OnStatusChanged("设备状态正常");
        }
        
        private async Task EraseFlash(FirmwareSegment[] segments)
        {
            OnStatusChanged("擦除闪存...");
            
            int totalSectors = 0;
            foreach (var segment in segments)
            {
                totalSectors += (segment.EndAddress - segment.StartAddress) / 1024 + 1;
            }
            
            int currentSector = 0;
            foreach (var segment in segments)
            {
                uint address = segment.StartAddress;
                uint endAddress = segment.EndAddress;
                
                while (address < endAddress)
                {
                    // 发送擦除命令(通过DNLOAD命令)
                    byte[] eraseCommand = CreateEraseCommand(address);
                    await SendDFUCommand(DFU_DNLOAD, eraseCommand);
                    
                    address += 1024; // 按扇区擦除
                    currentSector++;
                    
                    int percentage = (currentSector * 100) / totalSectors;
                    OnProgressChanged(percentage, $"擦除扇区 0x{address:X8}...", currentSector, totalSectors);
                    
                    await Task.Delay(50); // 等待擦除完成
                }
            }
            
            OnStatusChanged("闪存擦除完成");
        }
        
        private async Task DownloadFirmware(FirmwareData firmware, int transferSize)
        {
            OnStatusChanged("下载固件...");
            
            int totalBytes = firmware.TotalSize;
            int transferredBytes = 0;
            
            foreach (var segment in firmware.Segments)
            {
                uint address = segment.StartAddress;
                byte[] data = segment.Data;
                
                for (int i = 0; i < data.Length; i += transferSize)
                {
                    int chunkSize = Math.Min(transferSize, data.Length - i);
                    byte[] chunk = new byte[chunkSize];
                    Array.Copy(data, i, chunk, 0, chunkSize);
                    
                    // 发送下载命令
                    byte[] downloadCommand = CreateDownloadCommand(address, chunk);
                    await SendDFUCommand(DFU_DNLOAD, downloadCommand);
                    
                    transferredBytes += chunkSize;
                    address += (uint)chunkSize;
                    
                    int percentage = (transferredBytes * 100) / totalBytes;
                    OnProgressChanged(percentage, $"下载 0x{address:X8}...", 0, 0);
                    
                    // 等待设备处理
                    await WaitForDeviceReady();
                }
            }
            
            OnStatusChanged("固件下载完成");
        }
        
        private async Task VerifyFirmware(FirmwareData firmware, int transferSize)
        {
            OnStatusChanged("验证固件...");
            
            // 这里可以实现读取并比较数据的验证逻辑
            await Task.Delay(1000);
            
            OnStatusChanged("固件验证通过");
        }
        
        private async Task CompleteUpgrade()
        {
            OnStatusChanged("完成升级...");
            
            // 发送完成命令
            await SendDFUCommand(DFU_DNLOAD, new byte[0]);
            
            OnStatusChanged("升级完成");
        }
        
        private async Task ResetDevice()
        {
            OnStatusChanged("重置设备...");
            
            // 发送重置命令
            byte[] resetCommand = { 0x00 };
            await SendDFUCommand(DFU_DNLOAD, resetCommand);
            
            OnStatusChanged("设备已重置");
        }
        
        private async Task<byte[]> SendDFUCommand(byte command, byte[] data)
        {
            // 构建DFU请求
            byte[] request = new byte[1 + (data?.Length ?? 0)];
            request[0] = command;
            
            if (data != null && data.Length > 0)
            {
                Array.Copy(data, 0, request, 1, data.Length);
            }
            
            // 发送请求
            int bytesWritten = 0;
            ErrorCode ec = writer.Write(request, 5000, out bytesWritten);
            
            if (ec != ErrorCode.None)
            {
                throw new Exception($"发送DFU命令失败: {UsbDevice.LastErrorString}");
            }
            
            // 读取响应
            byte[] response = new byte[64];
            int bytesRead = 0;
            ec = reader.Read(response, 5000, out bytesRead);
            
            if (ec != ErrorCode.None)
            {
                throw new Exception($"读取DFU响应失败: {UsbDevice.LastErrorString}");
            }
            
            byte[] result = new byte[bytesRead];
            Array.Copy(response, result, bytesRead);
            
            return result;
        }
        
        private async Task WaitForDeviceReady()
        {
            byte[] status = await SendDFUCommand(DFU_GETSTATUS, null);
            
            // 等待设备状态变为IDLE
            while (status.Length >= 6 && status[4] != STATE_DFU_IDLE)
            {
                await Task.Delay(10);
                status = await SendDFUCommand(DFU_GETSTATUS, null);
            }
        }
        
        private byte[] CreateEraseCommand(uint address)
        {
            // 擦除命令格式:0x41 + 地址
            byte[] command = new byte[5];
            command[0] = 0x41; // 擦除命令
            command[1] = (byte)(address & 0xFF);
            command[2] = (byte)((address >> 8) & 0xFF);
            command[3] = (byte)((address >> 16) & 0xFF);
            command[4] = (byte)((address >> 24) & 0xFF);
            return command;
        }
        
        private byte[] CreateDownloadCommand(uint address, byte[] data)
        {
            // 下载命令格式:地址 + 数据
            byte[] command = new byte[5 + data.Length];
            command[0] = (byte)(address & 0xFF);
            command[1] = (byte)((address >> 8) & 0xFF);
            command[2] = (byte)((address >> 16) & 0xFF);
            command[3] = (byte)((address >> 24) & 0xFF);
            command[4] = 0x00; // 保留位
            Array.Copy(data, 0, command, 5, data.Length);
            return command;
        }
        
        private void DisconnectDevice()
        {
            if (reader != null)
            {
                reader.Dispose();
                reader = null;
            }
            
            if (writer != null)
            {
                writer.Dispose();
                writer = null;
            }
            
            if (dfuDevice != null)
            {
                dfuDevice.Close();
                dfuDevice = null;
            }
        }
        
        protected virtual void OnProgressChanged(int percentage, string message, int currentSector, int totalSectors)
        {
            ProgressChanged?.Invoke(this, new DFUProgressEventArgs
            {
                Percentage = percentage,
                Message = message,
                CurrentSector = currentSector,
                TotalSectors = totalSectors
            });
        }
        
        protected virtual void OnStatusChanged(string message)
        {
            StatusChanged?.Invoke(this, new DFUStatusEventArgs { Message = message });
        }
        
        protected virtual void OnErrorOccurred(string message)
        {
            ErrorOccurred?.Invoke(this, new DFUErrorEventArgs { Message = message });
        }
        
        protected virtual void OnUpgradeCompleted(TimeSpan elapsedTime)
        {
            UpgradeCompleted?.Invoke(this, new DFUCompleteEventArgs { ElapsedTime = elapsedTime });
        }
        
        public void Dispose()
        {
            if (!disposed)
            {
                DisconnectDevice();
                disposed = true;
            }
        }
    }
}

3.3 固件文件解析器 (DFUParser.cs)

csharp 复制代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace STM32_DFU_Tool.DFU
{
    public class DFUParser
    {
        public static FirmwareInfo ParseFileInfo(string filePath)
        {
            string extension = Path.GetExtension(filePath).ToLower();
            
            switch (extension)
            {
                case ".bin":
                    return ParseBinFileInfo(filePath);
                case ".hex":
                    return ParseHexFileInfo(filePath);
                case ".dfu":
                    return ParseDFUFileInfo(filePath);
                default:
                    throw new Exception($"不支持的文件格式: {extension}");
            }
        }
        
        public static FirmwareData ParseFirmwareFile(string filePath)
        {
            string extension = Path.GetExtension(filePath).ToLower();
            
            switch (extension)
            {
                case ".bin":
                    return ParseBinFile(filePath);
                case ".hex":
                    return ParseHexFile(filePath);
                case ".dfu":
                    return ParseDFUFile(filePath);
                default:
                    throw new Exception($"不支持的文件格式: {extension}");
            }
        }
        
        private static FirmwareInfo ParseBinFileInfo(string filePath)
        {
            FileInfo fileInfo = new FileInfo(filePath);
            
            return new FirmwareInfo
            {
                FileName = Path.GetFileName(filePath),
                Size = (int)fileInfo.Length,
                StartAddress = 0x08000000, // STM32 Flash起始地址
                Format = "BIN"
            };
        }
        
        private static FirmwareInfo ParseHexFileInfo(string filePath)
        {
            List<FirmwareSegment> segments = new List<FirmwareSegment>();
            uint minAddress = uint.MaxValue;
            uint maxAddress = uint.MinValue;
            
            using (StreamReader reader = new StreamReader(filePath))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    if (line.StartsWith(":"))
                    {
                        HexRecord record = ParseHexRecord(line);
                        
                        if (record.RecordType == 0x00) // 数据记录
                        {
                            uint address = record.Address;
                            minAddress = Math.Min(minAddress, address);
                            maxAddress = Math.Max(maxAddress, address + (uint)record.Data.Length);
                        }
                    }
                }
            }
            
            return new FirmwareInfo
            {
                FileName = Path.GetFileName(filePath),
                Size = (int)(maxAddress - minAddress),
                StartAddress = minAddress,
                Format = "HEX"
            };
        }
        
        private static FirmwareInfo ParseDFUFileInfo(string filePath)
        {
            // 解析DFU文件格式
            using (FileStream fs = new FileStream(filePath, FileMode.Open))
            {
                // 读取DFU头部信息
                byte[] header = new byte[11];
                fs.Read(header, 0, 11);
                
                string signature = Encoding.ASCII.GetString(header, 0, 5);
                if (signature != "DfuSe")
                {
                    throw new Exception("不是有效的DFU文件");
                }
                
                uint targetAddress = BitConverter.ToUInt32(header, 7);
                uint targetSize = BitConverter.ToUInt32(header, 3);
                
                return new FirmwareInfo
                {
                    FileName = Path.GetFileName(filePath),
                    Size = (int)targetSize,
                    StartAddress = targetAddress,
                    Format = "DFU"
                };
            }
        }
        
        private static FirmwareData ParseBinFile(string filePath)
        {
            byte[] data = File.ReadAllBytes(filePath);
            
            return new FirmwareData
            {
                Segments = new[]
                {
                    new FirmwareSegment
                    {
                        StartAddress = 0x08000000,
                        EndAddress = 0x08000000 + (uint)data.Length,
                        Data = data
                    }
                },
                TotalSize = data.Length
            };
        }
        
        private static FirmwareData ParseHexFile(string filePath)
        {
            List<FirmwareSegment> segments = new List<FirmwareSegment>();
            Dictionary<uint, List<byte>> addressData = new Dictionary<uint, List<byte>>();
            uint currentAddress = 0;
            
            using (StreamReader reader = new StreamReader(filePath))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    if (line.StartsWith(":"))
                    {
                        HexRecord record = ParseHexRecord(line);
                        
                        switch (record.RecordType)
                        {
                            case 0x00: // 数据记录
                                if (!addressData.ContainsKey(record.Address))
                                {
                                    addressData[record.Address] = new List<byte>();
                                }
                                addressData[record.Address].AddRange(record.Data);
                                break;
                                
                            case 0x04: // 扩展线性地址记录
                                currentAddress = (uint)((record.Data[0] << 8) | record.Data[1]) << 16;
                                break;
                        }
                    }
                }
            }
            
            // 创建分段
            foreach (var kvp in addressData)
            {
                segments.Add(new FirmwareSegment
                {
                    StartAddress = kvp.Key,
                    EndAddress = kvp.Key + (uint)kvp.Value.Count,
                    Data = kvp.Value.ToArray()
                });
            }
            
            int totalSize = 0;
            foreach (var segment in segments)
            {
                totalSize += segment.Data.Length;
            }
            
            return new FirmwareData
            {
                Segments = segments.ToArray(),
                TotalSize = totalSize
            };
        }
        
        private static FirmwareData ParseDFUFile(string filePath)
        {
            // 解析DFU文件格式
            List<FirmwareSegment> segments = new List<FirmwareSegment>();
            
            using (FileStream fs = new FileStream(filePath, FileMode.Open))
            {
                // 跳过DFU头部
                fs.Seek(11, SeekOrigin.Begin);
                
                // 读取目标信息
                byte[] targetHeader = new byte[274];
                fs.Read(targetHeader, 0, 274);
                
                uint targetSize = BitConverter.ToUInt32(targetHeader, 0);
                uint targetAddress = BitConverter.ToUInt32(targetHeader, 4);
                
                // 读取固件数据
                byte[] firmwareData = new byte[targetSize];
                fs.Read(firmwareData, 0, (int)targetSize);
                
                segments.Add(new FirmwareSegment
                {
                    StartAddress = targetAddress,
                    EndAddress = targetAddress + targetSize,
                    Data = firmwareData
                });
            }
            
            return new FirmwareData
            {
                Segments = segments.ToArray(),
                TotalSize = (int)segments[0].EndAddress - (int)segments[0].StartAddress
            };
        }
        
        private static HexRecord ParseHexRecord(string line)
        {
            HexRecord record = new HexRecord();
            
            // 去掉冒号
            string data = line.Substring(1);
            
            // 解析长度
            record.Length = Convert.ToByte(data.Substring(0, 2), 16);
            
            // 解析地址
            record.Address = Convert.ToUInt16(data.Substring(2, 4), 16);
            
            // 解析记录类型
            record.RecordType = Convert.ToByte(data.Substring(6, 2), 16);
            
            // 解析数据
            if (record.Length > 0)
            {
                record.Data = new byte[record.Length];
                for (int i = 0; i < record.Length; i++)
                {
                    record.Data[i] = Convert.ToByte(data.Substring(8 + i * 2, 2), 16);
                }
            }
            
            // 解析校验和
            record.Checksum = Convert.ToByte(data.Substring(8 + record.Length * 2, 2), 16);
            
            return record;
        }
    }
    
    public class FirmwareInfo
    {
        public string FileName { get; set; }
        public int Size { get; set; }
        public uint StartAddress { get; set; }
        public string Format { get; set; }
    }
    
    public class FirmwareData
    {
        public FirmwareSegment[] Segments { get; set; }
        public int TotalSize { get; set; }
    }
    
    public class FirmwareSegment
    {
        public uint StartAddress { get; set; }
        public uint EndAddress { get; set; }
        public byte[] Data { get; set; }
    }
    
    public class HexRecord
    {
        public byte Length { get; set; }
        public ushort Address { get; set; }
        public byte RecordType { get; set; }
        public byte[] Data { get; set; }
        public byte Checksum { get; set; }
    }
}

3.4 USB 设备枚举器 (USBEnumerator.cs)

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using LibUsbDotNet;
using LibUsbDotNet.Info;
using LibUsbDotNet.Main;

namespace STM32_DFU_Tool.USB
{
    public class USBEnumerator : IDisposable
    {
        private UsbContext context;
        private bool disposed = false;
        
        // 事件定义
        public event EventHandler<USBDeviceEventArgs> DeviceAdded;
        public event EventHandler<USBDeviceEventArgs> DeviceRemoved;
        
        public USBEnumerator()
        {
            context = new UsbContext();
            context.SetDebugLevel(LogLevel.None);
        }
        
        public List<USBDevice> GetDFUDevices()
        {
            List<USBDevice> devices = new List<USBDevice>();
            
            try
            {
                // 枚举所有USB设备
                foreach (UsbRegistry registry in context.AllDevices)
                {
                    if (IsDFUDevice(registry))
                    {
                        USBDevice device = new USBDevice
                        {
                            DevicePath = registry.DevicePath,
                            VendorID = registry.Vid,
                            ProductID = registry.Pid,
                            SerialNumber = registry.SerialNumber,
                            Manufacturer = registry.ManufacturerString,
                            Product = registry.ProductString
                        };
                        
                        devices.Add(device);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"枚举USB设备失败: {ex.Message}");
            }
            
            return devices;
        }
        
        private bool IsDFUDevice(UsbRegistry registry)
        {
            // 检查是否为STM32 DFU设备
            return registry.Vid == 0x0483 && registry.Pid == 0xDF11;
        }
        
        public void StartMonitoring()
        {
            // 启动设备监控
            System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
            timer.Interval = 1000; // 每秒检查一次
            timer.Tick += (sender, e) =>
            {
                CheckDeviceChanges();
            };
            timer.Start();
        }
        
        private List<string> previousDevices = new List<string>();
        
        private void CheckDeviceChanges()
        {
            List<USBDevice> currentDevices = GetDFUDevices();
            List<string> currentPaths = currentDevices.Select(d => d.DevicePath).ToList();
            
            // 检查新增的设备
            foreach (string path in currentPaths)
            {
                if (!previousDevices.Contains(path))
                {
                    USBDevice device = currentDevices.First(d => d.DevicePath == path);
                    DeviceAdded?.Invoke(this, new USBDeviceEventArgs { Device = device });
                }
            }
            
            // 检查移除的设备
            foreach (string path in previousDevices)
            {
                if (!currentPaths.Contains(path))
                {
                    USBDevice device = new USBDevice { DevicePath = path };
                    DeviceRemoved?.Invoke(this, new USBDeviceEventArgs { Device = device });
                }
            }
            
            previousDevices = currentPaths;
        }
        
        public void Dispose()
        {
            if (!disposed)
            {
                context?.Dispose();
                disposed = true;
            }
        }
    }
    
    public class USBDevice
    {
        public string DevicePath { get; set; }
        public int VendorID { get; set; }
        public int ProductID { get; set; }
        public string SerialNumber { get; set; }
        public string Manufacturer { get; set; }
        public string Product { get; set; }
    }
    
    public class USBDeviceEventArgs : EventArgs
    {
        public USBDevice Device { get; set; }
    }
}

3.5 事件参数类 (DFUEvents.cs)

csharp 复制代码
using System;

namespace STM32_DFU_Tool.DFU
{
    public class DFUProgressEventArgs : EventArgs
    {
        public int Percentage { get; set; }
        public string Message { get; set; }
        public int CurrentSector { get; set; }
        public int TotalSectors { get; set; }
    }
    
    public class DFUStatusEventArgs : EventArgs
    {
        public string Message { get; set; }
    }
    
    public class DFUErrorEventArgs : EventArgs
    {
        public string Message { get; set; }
    }
    
    public class DFUCompleteEventArgs : EventArgs
    {
        public TimeSpan ElapsedTime { get; set; }
    }
    
    public class DFUUpgradeParams
    {
        public int VendorID { get; set; }
        public int ProductID { get; set; }
        public int AltSetting { get; set; }
        public int TransferSize { get; set; }
        public string FirmwarePath { get; set; }
        public bool EraseFirst { get; set; }
        public bool VerifyAfterWrite { get; set; }
        public bool ResetAfterUpgrade { get; set; }
    }
}

3.6 日志工具 (Logger.cs)

csharp 复制代码
using System;
using System.IO;

namespace STM32_DFU_Tool.Utils
{
    public static class Logger
    {
        private static readonly object lockObject = new object();
        private static string logFilePath = "dfu_tool.log";
        
        public static void Info(string message)
        {
            WriteLog("INFO", message);
        }
        
        public static void Warning(string message)
        {
            WriteLog("WARNING", message);
        }
        
        public static void Error(string message)
        {
            WriteLog("ERROR", message);
        }
        
        public static void Debug(string message)
        {
            WriteLog("DEBUG", message);
        }
        
        private static void WriteLog(string level, string message)
        {
            try
            {
                lock (lockObject)
                {
                    using (StreamWriter writer = new StreamWriter(logFilePath, true))
                    {
                        writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}");
                    }
                }
            }
            catch
            {
                // 忽略日志写入错误
            }
        }
        
        public static string ReadLog()
        {
            try
            {
                if (File.Exists(logFilePath))
                {
                    return File.ReadAllText(logFilePath);
                }
            }
            catch
            {
                // 忽略读取错误
            }
            
            return string.Empty;
        }
        
        public static void ClearLog()
        {
            try
            {
                if (File.Exists(logFilePath))
                {
                    File.Delete(logFilePath);
                }
            }
            catch
            {
                // 忽略删除错误
            }
        }
    }
}

参考代码 适用于STM32系列单片机的USB DFU的上位机程序 www.youwenfan.com/contentcsu/60724.html

四、项目配置文件

4.1 应用程序配置文件 (App.config)

xml 复制代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- DFU默认参数 -->
    <add key="DefaultVendorID" value="0x0483"/>
    <add key="DefaultProductID" value="0xDF11"/>
    <add key="DefaultTransferSize" value="1024"/>
    <add key="DefaultAltSetting" value="0"/>
    
    <!-- 超时设置 -->
    <add key="ConnectTimeout" value="5000"/>
    <add key="TransferTimeout" value="30000"/>
    <add key="EraseTimeout" value="10000"/>
    
    <!-- 重试设置 -->
    <add key="MaxRetryCount" value="3"/>
    <add key="RetryDelay" value="1000"/>
    
    <!-- 日志设置 -->
    <add key="EnableLogging" value="true"/>
    <add key="LogFilePath" value="dfu_tool.log"/>
    <add key="LogLevel" value="INFO"/>
  </appSettings>
</configuration>

4.2 驱动信息文件 (stm32_dfu.inf)

ini 复制代码
; STM32 DFU Driver Information File
; Copyright (c) STMicroelectronics

[Version]
Signature="$Windows NT$"
Class=USB
ClassGuid={36FC9E60-C465-11CF-8056-444553540000}
Provider=%STMicroelectronics%
DriverVer=01/01/2024,1.0.0.0
CatalogFile=stm32_dfu.cat

[Manufacturer]
%STMicroelectronics%=STMicroelectronics,NTx86,NTamd64

[STMicroelectronics.NTx86]
%USB\VID_0483&PID_DF11.DeviceDesc%=STM32_DFU_Install, USB\VID_0483&PID_DF11

[STMicroelectronics.NTamd64]
%USB\VID_0483&PID_DF11.DeviceDesc%=STM32_DFU_Install, USB\VID_0483&PID_DF11

[STM32_DFU_Install]
Include=usb.inf
Needs=USB_INSTALL
CopyFiles=STM32_DFU_CopyFiles
AddReg=STM32_DFU_AddReg

[STM32_DFU_Install.HW]
AddReg=STM32_DFU_HW_AddReg

[STM32_DFU_CopyFiles]
stm32_dfu.sys

[STM32_DFU_AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,stm32_dfu.sys
HKR,,EnumPropPages32,,"usbui.dll,USBPropPageProvider"

[STM32_DFU_HW_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{36FC9E60-C465-11CF-8056-444553540000}"

[Strings]
STMicroelectronics="STMicroelectronics"
USB\VID_0483&PID_DF11.DeviceDesc="STM32 DFU Device"

五、使用说明

5.1 安装和使用步骤

  1. 安装驱动:首次使用需要安装STM32 DFU驱动
  2. 连接设备:将STM32设备进入DFU模式(通常通过BOOT0引脚)
  3. 启动工具:运行STM32_DFU_Tool.exe
  4. 选择设备:在设备列表中选择DFU设备
  5. 选择固件:点击浏览选择.bin或.hex固件文件
  6. 开始升级:点击"开始升级"按钮
  7. 等待完成:观察进度条和日志输出

5.2 常见问题解决

问题 原因 解决方案
找不到DFU设备 设备未进入DFU模式 按住BOOT0引脚上电
连接设备失败 驱动未安装 安装stm32_dfu.inf驱动
固件下载失败 闪存写保护 清除写保护选项字节
校验失败 固件文件损坏 重新生成固件文件
升级后无法启动 向量表偏移错误 检查应用程序向量表

5.3 支持的STM32系列

  • STM32F1xx系列 (如STM32F103)
  • STM32F4xx系列 (如STM32F407)
  • STM32F7xx系列 (如STM32F746)
  • STM32H7xx系列 (如STM32H743)
  • STM32L4xx系列 (如STM32L476)
相关推荐
东京老树根1 小时前
Arduino - 入门01 - Arduino,SimulIDE 简介
单片机·机器人
kaikaile19953 小时前
基于 STM32 的双闭环控制直流无刷电机(BLDC)方案
stm32·单片机·嵌入式硬件
Heartache boy3 小时前
野火STM32_HAL库版课程笔记-DWT应用与DHT11温湿度传感器
笔记·stm32·单片机·嵌入式硬件
无人装备硬件开发爱好者10 小时前
STM32G474 + 1.32 寸 OLED(128×96)俄罗斯方块游戏实现指南
stm32·嵌入式硬件·游戏
三佛科技-1341638421210 小时前
SM2850P无电感离线稳压器 5V输出 典型应用电路分析(管脚、关键设计要点)
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
dqsh0612 小时前
关于STM32G474芯片有规律的自动重启的问题
stm32·单片机·嵌入式硬件·系统重启·原因解析
时空自由民.12 小时前
BLDC无刷直流电机作为发电机的波形图
单片机
yong999012 小时前
基于 STM32 的 4×4 矩阵键盘源码
stm32·矩阵·计算机外设
JSMSEMI1113 小时前
JSM63006 5A 28V三相无刷电机驱动电路
单片机·嵌入式硬件