@@ -29,17 +29,35 @@ public class JLinkCli
2929 /// </summary>
3030 private const string FlashAddressToken = "{FLASH_ADDRESS}" ;
3131
32+ /// <summary>
33+ /// Token to replace with address to flash.
34+ /// </summary>
35+ private const string LoadFileListToken = "{LOAD_FILE_LIST}" ;
36+
3237 /// <summary>
3338 /// Template for JLink command file to flash a file.
3439 /// </summary>
35- private const string FlashFileCommandTemplate = $@ "
40+ private const string FlashSingleFileCommandTemplate = $@ "
3641USB
3742speed auto
3843Halt
3944LoadFile { FilePathToken } { FlashAddressToken }
4045Reset
4146Go
4247Exit
48+ " ;
49+
50+ /// <summary>
51+ /// Template for JLink command file to flash multiple files.
52+ /// </summary>
53+ private const string FlashMultipleFilesCommandTemplate = $@ "
54+ USB
55+ speed auto
56+ Halt
57+ { LoadFileListToken }
58+ Reset
59+ Go
60+ Exit
4361" ;
4462
4563 /// <summary>
@@ -240,7 +258,7 @@ public ExitCodes ExecuteFlashBinFiles(
240258 }
241259
242260 // compose JLink command file
243- var jlinkCmdContent = FlashFileCommandTemplate . Replace ( FilePathToken , binFilePath ) . Replace ( FlashAddressToken , addresses . ElementAt ( index ++ ) ) ;
261+ var jlinkCmdContent = FlashSingleFileCommandTemplate . Replace ( FilePathToken , binFilePath ) . Replace ( FlashAddressToken , addresses . ElementAt ( index ++ ) ) ;
244262 var jlinkCmdFilePath = Path . Combine ( Path . GetTempPath ( ) , $ "{ Guid . NewGuid ( ) } .jlink") ;
245263
246264 // create file
@@ -293,6 +311,152 @@ public ExitCodes ExecuteFlashBinFiles(
293311 return ExitCodes . OK ;
294312 }
295313
314+ /// <summary>
315+ /// Executes an operation that flashes a collection of Intel HEX format files to a connected J-Link device.
316+ /// </summary>
317+ /// <param name="files">List of files to flash to the device.</param>
318+ /// <param name="probeId">ID of the J-Link device to execute the operation into. Leave <see langword="null"/> to use the default address.</param>
319+ /// <returns></returns>
320+ public ExitCodes ExecuteFlashHexFiles (
321+ IList < string > files ,
322+ string probeId )
323+ {
324+ // check file existence
325+ if ( files . Any ( f => ! File . Exists ( f ) ) )
326+ {
327+ return ExitCodes . E5004 ;
328+ }
329+
330+ List < string > shadowFiles = new List < string > ( ) ;
331+
332+ // J-Link can't handle diacritc chars
333+ // developer note: reported to Segger (Case: 60276735) and can be removed if this is fixed/improved
334+ foreach ( string hexFile in files )
335+ {
336+ if ( ! hexFile . IsNormalized ( NormalizationForm . FormD ) ||
337+ hexFile . Contains ( ' ' ) )
338+ {
339+ var tempFile = Path . Combine (
340+ Environment . GetEnvironmentVariable ( "TEMP" , EnvironmentVariableTarget . Machine ) ,
341+ Path . GetFileName ( hexFile ) ) ;
342+
343+ // copy file to shadow file
344+ File . Copy (
345+ hexFile ,
346+ tempFile ,
347+ true ) ;
348+
349+ shadowFiles . Add ( tempFile ) ;
350+ }
351+ else
352+ {
353+ // copy file to shadow list
354+ shadowFiles . Add ( hexFile ) ;
355+ }
356+ }
357+
358+ // erase flash
359+ if ( DoMassErase )
360+ {
361+ var eraseResult = ExecuteMassErase ( probeId ) ;
362+
363+ if ( eraseResult != ExitCodes . OK )
364+ {
365+ return eraseResult ;
366+ }
367+
368+ // toggle mass erase so it's only performed before the first file is flashed
369+ DoMassErase = false ;
370+ }
371+
372+ if ( Verbosity < VerbosityLevel . Normal )
373+ {
374+ Console . ForegroundColor = ConsoleColor . White ;
375+ Console . Write ( "Flashing device..." ) ;
376+ }
377+ else
378+ {
379+ Console . ForegroundColor = ConsoleColor . White ;
380+ Console . WriteLine ( "Flashing device..." ) ;
381+ }
382+
383+ // program HEX file(s)
384+ StringBuilder listOfFiles = new StringBuilder ( ) ;
385+
386+ foreach ( string hexFile in shadowFiles )
387+ {
388+ // make sure path is absolute
389+ var hexFilePath = Utilities . MakePathAbsolute (
390+ Environment . CurrentDirectory ,
391+ hexFile ) ;
392+
393+ if ( Verbosity > VerbosityLevel . Normal )
394+ {
395+ Console . ForegroundColor = ConsoleColor . Cyan ;
396+ Console . WriteLine ( $ "{ Path . GetFileName ( hexFilePath ) } ") ;
397+ }
398+
399+ listOfFiles . AppendLine ( $ "LoadFile { hexFilePath } ") ;
400+ }
401+
402+ // compose JLink command file
403+ var jlinkCmdContent = FlashMultipleFilesCommandTemplate . Replace (
404+ LoadFileListToken ,
405+ listOfFiles . ToString ( ) ) ;
406+
407+ var jlinkCmdFilePath = Path . Combine (
408+ Path . GetTempPath ( ) ,
409+ $ "{ Guid . NewGuid ( ) } .jlink") ;
410+
411+ // create file
412+ var jlinkCmdFile = File . CreateText ( jlinkCmdFilePath ) ;
413+ jlinkCmdFile . Write ( jlinkCmdContent ) ;
414+ jlinkCmdFile . Close ( ) ;
415+
416+ var cliOutput = RunJLinkCLI ( jlinkCmdFilePath ) ;
417+
418+ // OK to delete the JLink command file
419+ File . Delete ( jlinkCmdFilePath ) ;
420+
421+ if ( Verbosity >= VerbosityLevel . Normal
422+ && cliOutput . Contains ( "Skipped. Contents already match" ) )
423+ {
424+ Console . ForegroundColor = ConsoleColor . Yellow ;
425+
426+ Console . WriteLine ( "" ) ;
427+ Console . WriteLine ( "******************* WARNING *********************" ) ;
428+ Console . WriteLine ( "Skip flashing. Contents already match the update." ) ;
429+ Console . WriteLine ( "*************************************************" ) ;
430+ Console . WriteLine ( "" ) ;
431+
432+ Console . ForegroundColor = ConsoleColor . White ;
433+ }
434+ else if ( ! ( cliOutput . Contains ( "Flash download: Program & Verify" )
435+ && cliOutput . Contains ( "O.K." ) ) )
436+ {
437+ ShowCLIOutput ( cliOutput ) ;
438+
439+ return ExitCodes . E5006 ;
440+ }
441+
442+ ShowCLIOutput ( cliOutput ) ;
443+
444+ if ( Verbosity < VerbosityLevel . Normal )
445+ {
446+ Console . ForegroundColor = ConsoleColor . Green ;
447+ Console . WriteLine ( " OK" ) ;
448+ }
449+ else
450+ {
451+ Console . ForegroundColor = ConsoleColor . Green ;
452+ Console . WriteLine ( "Flashing completed..." ) ;
453+ }
454+
455+ Console . ForegroundColor = ConsoleColor . White ;
456+
457+ return ExitCodes . OK ;
458+ }
459+
296460 public void ShowCLIOutput ( string cliOutput )
297461 {
298462 // show CLI output, if verbosity is diagnostic
0 commit comments