Click here to Skip to main content
15,891,951 members
Articles / Programming Languages / C#
Tip/Trick

ST Micro-electronics Device Firmware Upgrade (DFU/DfuSe) from C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (14 votes)
12 Mar 2013CPOL4 min read 135.2K   4.4K   26   38
ST provide a DFU module for their STM32 micro-controllers for firmware upgrade over USB. This is an example of how to use it from C#.

Introduction

ST Micro-electronics STM32F series micro-controllers have the possibility to be programmed via USB using ST's DFU protocol. Some of their micros have the DFU module in ROM and all can have it in FLASH. DFU is a very handy way to program a micro-controller, particularly when it is in the micro's ROM, but all the example code from ST is in C++ and is somewhat complicated and buggy. ST's version of DFU is called DfuSe (Device firmware upgrade STMicroelectronics Extension).

Using this code, I have been able to implement a scheme whereby the C# application which is communicating with the ST micro-controller based hardware, is able to automatically update the hardware's firmware. So when the C# application is updated, the firmware gets updated too.

Background

ST provides a C++ demo application, which includes the Windows drivers for the DFU device and two DLLs which this application requires (STDFU.dll, STTubeDevice30.dll). I'm not sure of the redistribution terms of the DLLs so you should download them directly from ST by downloading and installing ST's demo package (see below). Then copy the two DLLs into the main solution directory, from where they will be copied into the appropriate bin directory when the solution is built.

ST provides a STDFU library specification (UM0384) and file format document (UM0391) as part of the demo package.

Some of the USB communications has been borrowed from an excellent article on USB HID programming that I found here.

ST's application uses another two DLLs, STDFUFiles.dll and STDFUPRT.dll. These perform higher level functions which are either not performed by my example, or done by the C#. I just wanted to write as much of the code in C# as possible, and minimise time spent trying to decode DLL function calls. I also haven't bothered to implement any more functionality than the bare minimum I needed, but I hope this will provide a starting point for people wanting to do more.

DFU files can be generated from HEX or S19 files by an application which is included in ST's demo package. So you can take the HEX file output from your compiler (e.g., Keil's MDK-ARM) and convert it into a DFU file which can then be programmed into a micro-controller using this application. Normally, this is something you would do with released code, rather than during software development. Using the DFU avoids the need for the production department or service engineer to have a JTAG programmer, and the need to directly access the target hardware as long as the USB port is accessible.

Using the Code

The supplied code is a demo application plus my FirmwareUpdate class which does the hard work of communicating with the hardware. My intention is more that it provides an example of how to implement DFU programming in your own test harness than that it be used standalone. I don't claim to be an expert in USB or P/Invoke, what I am presenting has been patched together by running the C++ demo under a debugger and experimentation.

IFirmwareUpdate is of course an interface class to the FirmwareUpdate class. To do a firmware update, first hook the OnFirmwareUpdateProgress event to an event handler, then call UpdateFirmware with the path of the DFU file. You make like to use ParseDFU_File beforehand to obtain the intended VID and PID of the DFU file so you can check that they are correct for the target hardware.

This code has been tested against a STM32F207's ROM bootloader, a STM32F103 running a FLASH DFU module (as copied from ST's example) in the bottom $3000 bytes to load an application into the FLASH from $08003000 onwards, and another STM32F103 running a slightly different version of ST's DFU example. I have built the project both "x86" and "x64". You will need to install the correct version of the DFU driver, as supplied in the form of an installer EXE file in ST's demo package.

You may notice a function called FindAndDetachHID. This is for use with a HID device (Human Interface Device, such as keyboard or joystick, but also one of the easiest USB classes to use for any kind of low data rate I/O device) which supports the "HID detach" feature. This is basically a special message telling the device to stop being whatever it is, disconnect from the computer, and then re-connect as a DFU device. ST's demo app supports this feature.

History

  • 19/2/2013: First posted.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer Expedition Electrics
United Kingdom United Kingdom
Started programming aged 10 on a ZX81 ("Daddy - what's a program?"), since then have done electronics and software for variety of things, mostly instrumentation, in a variety of languages but these days mostly C for the firmware and C# for the PC. Perhaps the project I'm proudest of was the Z80 based computer entirely hand soldered on veroboard!

Comments and Discussions

 
QuestionNeeded C# application for .hex file conversion into DFU(.dfu) file. Pin
Vinod KUmar S N4-Dec-14 23:20
Vinod KUmar S N4-Dec-14 23:20 
AnswerRe: Needed C# application for .hex file conversion into DFU(.dfu) file. Pin
vivchun8-Apr-15 13:49
vivchun8-Apr-15 13:49 
Questionwhat IDE to use Pin
Member 112871774-Dec-14 15:21
Member 112871774-Dec-14 15:21 
AnswerRe: what IDE to use Pin
Garth J Lancaster4-Dec-14 16:36
professionalGarth J Lancaster4-Dec-14 16:36 
GeneralRe: what IDE to use Pin
Member 112871774-Dec-14 17:56
Member 112871774-Dec-14 17:56 
News*.hex to *.dfu Pin
ZsirosD22-Jun-13 8:38
ZsirosD22-Jun-13 8:38 
GeneralMy vote of 5 Pin
jeffataylor6-Mar-13 6:53
jeffataylor6-Mar-13 6:53 
Bug_ParentHandle Null pointer. Pin
jeffataylor6-Mar-13 6:45
jeffataylor6-Mar-13 6:45 
Hi Mark,

Started going over the code this morning. Compiled fine. Modified the Guid's, VID and PID for my application. Here's something I noticed. When my device is in HID mode and connected, I click on the Program button, select the *.dfu file (which the program likes), then the FindDevice() method does indeed find my device, but when the call to HidD_SetFeature(_ParentHandle, Feature, 65) is made (to kick the HID device from HID to DFU mode, _ParentHandle hasn't been set yet to an opened HID device.

Thought you might like to know. See suggested fixes below. Only the FirmwareUpdate.cs file is affected. Please do a diff to see changes. Sorry, I untabbed and outdented to make it more readable.

Best to you,

Jeff

====== Updates to FirmwareUpdates.cs ==========================
/// <summary>
/// Look for a HID device with the expected VID and PID, if found, send the detach message
/// </summary>
private void FindAndDetachHID()
{
int i;
string hidPath;

// If we are connected to a device with the expected VID and PID, send the detach message
if (FindDevice(HID_VID, HID_PID, out hidPath))
{
_ParentHandle = CreateFile(hidPath, (GENERIC_READ | GENERIC_WRITE), 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);

// Assign a buffer in unmananged memory
IntPtr Feature = Marshal.AllocHGlobal(65);
Marshal.WriteByte(Feature, 0, HID_DETACH_REPORT_ID);
Marshal.WriteByte(Feature, 1, USAGE_DETACH);
for (i = 2; i < 65; i++)
{
Marshal.WriteByte(Feature, i, 0);
}
if (HidD_SetFeature(_ParentHandle, Feature, 65))
{
if (OnFirmwareUpdateProgress != null) OnFirmwareUpdateProgress(this, new FirmwareUpdateProgressEventArgs(1, "HID detach success", false));
}
else
{
// SensiPOD usually replies to this with 31, ERROR_GEN_FAILURE, but it still works...
if (OnFirmwareUpdateProgress != null) OnFirmwareUpdateProgress(this, new FirmwareUpdateProgressEventArgs(1, "HID detach error = " + (Marshal.GetLastWin32Error()).ToString(), false));
//throw (new Exception("HID detach failed"));
}
Marshal.FreeHGlobal(Feature);
_ParentHandle.Close();
System.Threading.Thread.Sleep(5000);
}
}

/// <summary>
/// Function to find a device with a given VID and PID
/// </summary>
/// <param name="nVid">VID to search for</param>
/// <param name="nPid">PID to search for</param>
/// <param name="sPath">Path to device if found</param>
/// <returns>True if matching device is found</returns>
public bool FindDevice(UInt16 nVid, UInt16 nPid, out string sPath)
{
String strDevicePath;
string strPath = string.Empty;
string strSearch = string.Format("vid_{0:x4}&pid_{1:x4}", nVid, nPid); // first, build the path search string
Guid gHid;
sPath = ""; // For the case of no device.
HidD_GetHidGuid(out gHid); // next, get the GUID from Windows that it uses to represent the HID USB interface
IntPtr hInfoSet = SetupDiGetClassDevs(ref gHid, null, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); // this gets a list of all HID devices currently connected to the computer (InfoSet)
try
{
DeviceInterfaceData oInterface = new DeviceInterfaceData(); // build up a device interface data block
oInterface.Size = Marshal.SizeOf(oInterface); // 28 in 32bit or 32 in 64bit mode,
// Now iterate through the InfoSet memory block assigned within Windows in the call to SetupDiGetClassDevs
// to get device details for each device connected
int nIndex = 0;
while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface)) // this gets the device interface information for a device at index 'nIndex' in the memory block
{
strDevicePath = GetDevicePath(hInfoSet, ref oInterface); // get the device path (see helper method 'GetDevicePath')
if (strDevicePath.IndexOf(strSearch) >= 0) // do a string search, if we find the VID/PID string then we found our device!
{
sPath = strDevicePath; // save the path to our device.
return true;
}
nIndex++; // if we get here, we didn't find our device. So move on to the next one.
}
if (0 != Marshal.GetLastWin32Error())
{
if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
{
// Do nothing, this just means the ARM_Board wasn't found
}
else if (ERROR_INVALID_USER_BUFFER == Marshal.GetLastWin32Error())
{
throw new Exception("Size member of hInfoSet is not set correctly (5 for 32bit or 8 for 64bit)");
}
else
{
throw new Exception("SetupDiEnumDeviceInterfaces returned error " + Marshal.GetLastWin32Error().ToString());
}
}
}
finally
{
// Before we go, we have to free up the InfoSet memory reserved by SetupDiGetClassDevs
SetupDiDestroyDeviceInfoList(hInfoSet);
}
return false; // oops, didn't find our device
}
GeneralRe: _ParentHandle Null pointer. Pin
Mark McLean (ExpElec)7-Mar-13 8:51
professionalMark McLean (ExpElec)7-Mar-13 8:51 
QuestionThank you! Pin
jeffataylor5-Mar-13 13:17
jeffataylor5-Mar-13 13:17 
GeneralMy vote of 5 Pin
hanzibal24-Mar-13 7:06
hanzibal24-Mar-13 7:06 
QuestionHow to download? Pin
jeffataylor1-Mar-13 7:15
jeffataylor1-Mar-13 7:15 
AnswerRe: How to download? Pin
klightspeed2-Mar-13 4:30
klightspeed2-Mar-13 4:30 
GeneralRe: How to download? Pin
hanzibal23-Mar-13 22:02
hanzibal23-Mar-13 22:02 
GeneralRe: How to download? Pin
Mark McLean (ExpElec)5-Mar-13 1:23
professionalMark McLean (ExpElec)5-Mar-13 1:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.