一、系统架构设计
┌─────────────────────────────────────────────────────────────┐
│ 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 安装和使用步骤
- 安装驱动:首次使用需要安装STM32 DFU驱动
- 连接设备:将STM32设备进入DFU模式(通常通过BOOT0引脚)
- 启动工具:运行STM32_DFU_Tool.exe
- 选择设备:在设备列表中选择DFU设备
- 选择固件:点击浏览选择.bin或.hex固件文件
- 开始升级:点击"开始升级"按钮
- 等待完成:观察进度条和日志输出
5.2 常见问题解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 找不到DFU设备 | 设备未进入DFU模式 | 按住BOOT0引脚上电 |
| 连接设备失败 | 驱动未安装 | 安装stm32_dfu.inf驱动 |
| 固件下载失败 | 闪存写保护 | 清除写保护选项字节 |
| 校验失败 | 固件文件损坏 | 重新生成固件文件 |
| 升级后无法启动 | 向量表偏移错误 | 检查应用程序向量表 |
5.3 支持的STM32系列
- STM32F1xx系列 (如STM32F103)
- STM32F4xx系列 (如STM32F407)
- STM32F7xx系列 (如STM32F746)
- STM32H7xx系列 (如STM32H743)
- STM32L4xx系列 (如STM32L476)