Skip to content

Instantly share code, notes, and snippets.

@pavel-a
Last active August 6, 2022 16:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pavel-a/89d71b3aba9d7a9e6f8a61d728b08a8e to your computer and use it in GitHub Desktop.
Save pavel-a/89d71b3aba9d7a9e6f8a61d728b08a8e to your computer and use it in GitHub Desktop.
Binary file converter to Intel hex (python script)
#!/usr/bin/python3
'''
Converts a binary file into an intel hex file
Usage: Bin2Hex.py [-A base_address] [-n] [-F yes|no] -b <binary_file> -o <hex_to_be_created>
pavel_a@fastmail.fm 05-aug-2022
Original: https://community.silabs.com/s/article/converting-binary-firmware-image-files-to-intel-hex-files-bin-to-hex-x
'''
import argparse
import sys
sDESC="Converts binary file to Intel Hex"
sEPILOG="Long series of FF bytes will not be copied to the output file unless -F yes flag is given"
HEX_LINE_LEN = 32
def ConstructRecord(recordType, address, data) -> bytes:
assert 0 <= address < 0x10000
assert 0 <= recordType <= 255
record = []
recordType = recordType & 0xFF
address = (address >> 8) & 0xFF, address & 0xFF
numBytes = len(data) & 0xFF
record.append(numBytes)
record += address
record.append(recordType)
record += data
checksum = 0
for x in record:
checksum += x
checksum &= 0xFF
# Two's complement
checksum = ~checksum + 1
checksum &= 0xFF
record.append(checksum)
recordStr = ':'
for byte in record:
recordStr += '{:02X}'.format(byte)
recordStr += '\n'
return recordStr.encode('ascii', errors='strict')
def convertBinaryToHex(binaryPath, hexPath, start_addr = 0, noEndRecord = False, ignoreErasedRecords = True):
address = start_addr & 0xFFFF # initial offset, low part
addr_high = (start_addr >> 16) & 0xFFFF
hexFile = open(hexPath, 'wb')
if address != 0 :
hexFile.write(ConstructRecord(0x04, 0x0000, addr_high.to_bytes(2, 'big')))
with open(binaryPath, 'rb') as binaryFile:
byte = binaryFile.read(1)
data = []
while byte != b'':
if address == 0 :
hexFile.write(ConstructRecord(0x04, 0x0000, addr_high.to_bytes(2, 'big')))
byte = int.from_bytes(byte, byteorder='big') & 0xFF
data.append(byte)
address += 1
if len(data) >= HEX_LINE_LEN:
for val in data:
if val != 0xFF or not ignoreErasedRecords:
hexFile.write(ConstructRecord(0x00, address - len(data), data))
break
data = []
if address > 0xFFFF:
addr_high += 1
address -= 0x10000
assert address == 0, "start addr must be aligned on HEX_LINE_LEN else revise!"
byte = binaryFile.read(1)
# last incomplete row
if len(data) :
for val in data:
if val != 0xFF or not ignoreErasedRecords:
hexFile.write(ConstructRecord(0x00, address - len(data), data))
break
if not noEndRecord:
hexFile.write(ConstructRecord(0x01, 0x0000, []))
hexFile.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=sDESC, prog="bin2hexp", epilog=sEPILOG)
parser.add_argument("-o", "--hexPath", required=True,
help="Path to the hex file to be generated")
parser.add_argument("-b", "--binaryPath", required=True,
help="Path to the source binary file")
parser.add_argument("-A", "--baseAddress", default = 0,
type= lambda arg: int(arg, 16),
help= 'Base address for the hex file (32-bit)')
parser.add_argument("-n", "--noEndRecord", action='store_true', default=False,
help="Do not end the output file with 'end' record. Use for merging several hex files.")
#Note: type=bool args do not work! so use choice:
parser.add_argument("-F", "--copyFF", choices=['yes','no'], default='no',
help="Copy long sequences of FF bytes to output ('erased flash' areas). default: no")
args = parser.parse_args()
if not (0 <= args.baseAddress < 0xFFFFFFFF) :
print("Base address is longer than 32 bits!")
sys.exit(1)
if args.baseAddress & (HEX_LINE_LEN -1) :
print("Base address must be aligned on", HEX_LINE_LEN, "bytes!")
sys.exit(1)
convertBinaryToHex(args.binaryPath, args.hexPath, args.baseAddress, args.noEndRecord, args.copyFF == 'no')
print("Done! Created hex file: {}".format(args.hexPath))
sys.exit(0)
@pavel-a
Copy link
Author

pavel-a commented Aug 5, 2022

A 'modernized' version of Bin2Hex script from here + bug fixed + specify the base address.

The address can be specified with option -A. Example: -A 0x08000000.
The address defaults to 0. If specified, must be aligned on the line size in the hex file (currently 32).

Long sequences of FF bytes are not copied to the hex file (usually FF is the content of erased flash) - but flash tools can fill holes in the file as they see fit. To copy all the FF bytes as is, specify option --copyFF yes.

Option -n (--noEndRecord) is for producing hex files for subsequent merge. Only the last part should have the end record :00000001FF

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment