forked from me/IronOS-Meta
Rebuilding python into split classes
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*/__pycache__/*
|
||||||
|
*.hex
|
||||||
|
*.dfu
|
||||||
@@ -1,135 +1,59 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
import os, sys, struct, zlib
|
import argparse
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
from output_hex import HexOutput
|
||||||
|
from output_dfu import DFUOutput
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
except ImportError as error:
|
except ImportError as error:
|
||||||
raise ImportError("{}: {} requres Python Imaging Library (PIL). "
|
raise ImportError(
|
||||||
"Install with `pip` or OS-specific package "
|
"{}: {} requres Python Imaging Library (PIL). "
|
||||||
"management tool."
|
"Install with `pip` (pip3 install pillow) or OS-specific package "
|
||||||
.format(error, sys.argv[0]))
|
"management tool.".format(error, sys.argv[0])
|
||||||
|
|
||||||
VERSION_STRING = '0.03'
|
|
||||||
|
|
||||||
LCD_WIDTH = 96
|
|
||||||
LCD_HEIGHT = 16
|
|
||||||
LCD_NUM_BYTES = LCD_WIDTH * LCD_HEIGHT // 8
|
|
||||||
LCD_PADDED_SIZE = 1024
|
|
||||||
|
|
||||||
INTELHEX_DATA_RECORD = 0x00
|
|
||||||
INTELHEX_END_OF_FILE_RECORD = 0x01
|
|
||||||
INTELHEX_EXTENDED_LINEAR_ADDRESS_RECORD = 0x04
|
|
||||||
INTELHEX_BYTES_PER_LINE = 16
|
|
||||||
INTELHEX_MINIMUM_SIZE = 4096
|
|
||||||
|
|
||||||
DFU_PINECIL_ALT = 0
|
|
||||||
DFU_PINECIL_VENDOR = 0x28e9
|
|
||||||
DFU_PINECIL_PRODUCT = 0x0189
|
|
||||||
DFU_LOGO_ADDRESS = 0x0801F800
|
|
||||||
DFU_TARGET_NAME = b"Pinecil"
|
|
||||||
DFU_PREFIX_SIZE = 11
|
|
||||||
DFU_SUFFIX_SIZE = 16
|
|
||||||
|
|
||||||
def split16(word):
|
|
||||||
"""return high and low byte of 16-bit word value as tuple"""
|
|
||||||
return (word >> 8) & 0xff, word & 0xff
|
|
||||||
|
|
||||||
|
|
||||||
def compute_crc(data):
|
|
||||||
return 0xFFFFFFFF & -zlib.crc32(data) - 1
|
|
||||||
|
|
||||||
|
|
||||||
def intel_hex_line(record_type, offset, data):
|
|
||||||
"""generate a line of data in Intel hex format"""
|
|
||||||
# length, address offset, record type
|
|
||||||
record_length = len(data)
|
|
||||||
yield ':{:02X}{:04X}{:02X}'.format(record_length, offset, record_type)
|
|
||||||
|
|
||||||
# data
|
|
||||||
for byte in data:
|
|
||||||
yield "{:02X}".format(byte)
|
|
||||||
|
|
||||||
# compute and write checksum (now using unix style line endings for DFU3.45 compatibility
|
|
||||||
yield "{:02X}\n".format((((sum(data, # sum data ...
|
|
||||||
record_length # ... and other ...
|
|
||||||
+ sum(split16(offset)) # ... fields ...
|
|
||||||
+ record_type) # ... on line
|
|
||||||
& 0xff) # low 8 bits
|
|
||||||
^ 0xff) # two's ...
|
|
||||||
+ 1) # ... complement
|
|
||||||
& 0xff) # low 8 bits
|
|
||||||
|
|
||||||
|
|
||||||
def intel_hex(file, bytes_, start_address=0x0):
|
|
||||||
"""write block of data in Intel hex format"""
|
|
||||||
def write(generator):
|
|
||||||
file.write(''.join(generator))
|
|
||||||
|
|
||||||
if len(bytes_) % INTELHEX_BYTES_PER_LINE != 0:
|
|
||||||
raise ValueError("Program error: Size of LCD data is not evenly divisible by {}"
|
|
||||||
.format(INTELHEX_BYTES_PER_LINE))
|
|
||||||
|
|
||||||
address_lo = start_address & 0xffff
|
|
||||||
address_hi = (start_address >> 16) & 0xffff
|
|
||||||
|
|
||||||
write(intel_hex_line(INTELHEX_EXTENDED_LINEAR_ADDRESS_RECORD, 0,
|
|
||||||
split16(address_hi)))
|
|
||||||
|
|
||||||
size_written = 0
|
|
||||||
while size_written < INTELHEX_MINIMUM_SIZE:
|
|
||||||
offset = address_lo
|
|
||||||
for line_start in range(0, len(bytes_), INTELHEX_BYTES_PER_LINE):
|
|
||||||
write(intel_hex_line(INTELHEX_DATA_RECORD, offset,
|
|
||||||
bytes_[line_start:line_start + INTELHEX_BYTES_PER_LINE]))
|
|
||||||
size_written += INTELHEX_BYTES_PER_LINE
|
|
||||||
if size_written >= INTELHEX_MINIMUM_SIZE:
|
|
||||||
break
|
|
||||||
offset += INTELHEX_BYTES_PER_LINE
|
|
||||||
|
|
||||||
write(intel_hex_line(INTELHEX_END_OF_FILE_RECORD, 0, ()))
|
|
||||||
|
|
||||||
|
|
||||||
def build_dfu(file, bytes_):
|
|
||||||
data = b""
|
|
||||||
for byte in bytes_:
|
|
||||||
data += byte.to_bytes(1, byteorder="big")
|
|
||||||
|
|
||||||
data = (
|
|
||||||
struct.pack("<2I", DFU_LOGO_ADDRESS, len(data)) + data
|
|
||||||
)
|
)
|
||||||
data = (
|
|
||||||
struct.pack(
|
VERSION_STRING = "1.0"
|
||||||
"<6sBI255s2I", b"Target", DFU_PINECIL_ALT, 1, DFU_TARGET_NAME, len(data), 1
|
|
||||||
)
|
LCD_WIDTH = 96
|
||||||
+ data
|
LCD_HEIGHT = 16
|
||||||
)
|
LCD_NUM_BYTES = LCD_WIDTH * LCD_HEIGHT // 8
|
||||||
data = (
|
LCD_PAGE_SIZE = 1024
|
||||||
struct.pack(
|
|
||||||
"<5sBIB", b"DfuSe", 1, DFU_PREFIX_SIZE + len(data) + DFU_SUFFIX_SIZE, 1
|
|
||||||
)
|
|
||||||
+ data
|
|
||||||
)
|
|
||||||
data += struct.pack("<4H3sB", 0, DFU_PINECIL_PRODUCT, DFU_PINECIL_VENDOR, 0x011A, b"UFD", DFU_SUFFIX_SIZE)
|
|
||||||
crc = compute_crc(data)
|
|
||||||
data += struct.pack("<I", crc)
|
|
||||||
file.write(data)
|
|
||||||
|
|
||||||
|
|
||||||
def img2hex(input_filename,
|
class MiniwareSettings:
|
||||||
output_file,
|
IMAGE_ADDRESS = 0x0800F800
|
||||||
preview_filename=None,
|
DFU_TARGET_NAME = b"IronOS-dfu"
|
||||||
threshold=128,
|
DFU_PINECIL_ALT = 0
|
||||||
dither=False,
|
DFU_PINECIL_VENDOR = 0x1209
|
||||||
negative=False,
|
DFU_PINECIL_PRODUCT = 0xDB42
|
||||||
binary=False):
|
|
||||||
|
|
||||||
|
class PinecilSettings:
|
||||||
|
IMAGE_ADDRESS = 0x0801F800
|
||||||
|
DFU_TARGET_NAME = b"Pinecil"
|
||||||
|
DFU_PINECIL_ALT = 0
|
||||||
|
DFU_PINECIL_VENDOR = 0x28E9
|
||||||
|
DFU_PINECIL_PRODUCT = 0x0189
|
||||||
|
|
||||||
|
|
||||||
|
def img2hex(
|
||||||
|
input_filename,
|
||||||
|
preview_filename=None,
|
||||||
|
threshold=128,
|
||||||
|
dither=False,
|
||||||
|
negative=False,
|
||||||
|
isPinecil=False,
|
||||||
|
output_filename_base="out",
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Convert 'input_filename' image file into Intel hex format with data
|
Convert 'input_filename' image file into Intel hex format with data
|
||||||
formatted for display on TS100 LCD and file object.
|
formatted for display on LCD and file object.
|
||||||
Input image is converted from color or grayscale to black-and-white,
|
Input image is converted from color or grayscale to black-and-white,
|
||||||
and resized to fit TS100 LCD screen as necessary.
|
and resized to fit LCD screen as necessary.
|
||||||
Optionally write resized/thresholded/black-and-white preview image
|
Optionally write resized/thresholded/black-and-white preview image
|
||||||
to file specified by name.
|
to file specified by name.
|
||||||
Optional `threshold' argument 8 bit value; grayscale pixels greater than
|
Optional `threshold' argument 8 bit value; grayscale pixels greater than
|
||||||
@@ -143,49 +67,49 @@ def img2hex(input_filename,
|
|||||||
try:
|
try:
|
||||||
image = Image.open(input_filename)
|
image = Image.open(input_filename)
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
raise IOError("error reading image file \"{}\": {}".format(input_filename, e))
|
raise IOError('error reading image file "{}": {}'.format(input_filename, e))
|
||||||
|
|
||||||
# convert to luminance
|
# convert to luminance
|
||||||
# do even if already black/white because PIL can't invert 1-bit so
|
# do even if already black/white because PIL can't invert 1-bit so
|
||||||
# can't just pass thru in case --negative flag
|
# can't just pass thru in case --negative flag
|
||||||
# also resizing works better in luminance than black/white
|
# also resizing works better in luminance than black/white
|
||||||
# also no information loss converting black/white to grayscale
|
# also no information loss converting black/white to grayscale
|
||||||
if image.mode != 'L':
|
if image.mode != "L":
|
||||||
image = image.convert('L')
|
image = image.convert("L")
|
||||||
|
# Resize to lcd size using bicubic sampling
|
||||||
if image.size != (LCD_WIDTH, LCD_HEIGHT):
|
if image.size != (LCD_WIDTH, LCD_HEIGHT):
|
||||||
image = image.resize((LCD_WIDTH, LCD_HEIGHT), Image.BICUBIC)
|
image = image.resize((LCD_WIDTH, LCD_HEIGHT), Image.BICUBIC)
|
||||||
|
|
||||||
if negative:
|
if negative:
|
||||||
image = ImageOps.invert(image)
|
image = ImageOps.invert(image)
|
||||||
threshold = 255 - threshold # have to invert threshold
|
threshold = 255 - threshold # have to invert threshold as well
|
||||||
|
|
||||||
if dither:
|
if dither:
|
||||||
image = image.convert('1')
|
image = image.convert("1")
|
||||||
else:
|
else:
|
||||||
image = image.point(lambda pixel: 0 if pixel < threshold else 1, '1')
|
image = image.point(lambda pixel: 0 if pixel < threshold else 1, "1")
|
||||||
|
|
||||||
if preview_filename:
|
if preview_filename:
|
||||||
image.save(preview_filename)
|
image.save(preview_filename)
|
||||||
|
|
||||||
''' DEBUG
|
""" DEBUG
|
||||||
for row in range(LCD_HEIGHT):
|
for row in range(LCD_HEIGHT):
|
||||||
for column in range(LCD_WIDTH):
|
for column in range(LCD_WIDTH):
|
||||||
if image.getpixel((column, row)): sys.stderr.write('1')
|
if image.getpixel((column, row)): sys.stderr.write('1')
|
||||||
else: sys.stderr.write('0')
|
else: sys.stderr.write('0')
|
||||||
sys.stderr.write('\n')
|
sys.stderr.write('\n')
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# pad to this size (also will be repeated in output Intel hex file)
|
# pad to this size (also will be repeated in output Intel hex file)
|
||||||
data = [0] * LCD_PADDED_SIZE
|
data = [0] * LCD_PAGE_SIZE
|
||||||
|
|
||||||
# magic/undocumented/required header in endian-reverse byte order
|
# magic/required header in endian-reverse byte order
|
||||||
data[0] = 0x55
|
data[0] = 0x55
|
||||||
data[1] = 0xAA
|
data[1] = 0xAA
|
||||||
data[2] = 0x0D
|
data[2] = 0x0D
|
||||||
data[3] = 0xF0
|
data[3] = 0xF0
|
||||||
|
|
||||||
# convert to TS100 LCD format
|
# convert to LCD format
|
||||||
for ndx in range(LCD_WIDTH * 16 // 8):
|
for ndx in range(LCD_WIDTH * 16 // 8):
|
||||||
bottom_half_offset = 0 if ndx < LCD_WIDTH else 8
|
bottom_half_offset = 0 if ndx < LCD_WIDTH else 8
|
||||||
byte = 0
|
byte = 0
|
||||||
@@ -194,18 +118,29 @@ def img2hex(input_filename,
|
|||||||
byte |= 1 << y
|
byte |= 1 << y
|
||||||
# store in endian-reversed byte order
|
# store in endian-reversed byte order
|
||||||
data[4 + ndx + (1 if ndx % 2 == 0 else -1)] = byte
|
data[4 + ndx + (1 if ndx % 2 == 0 else -1)] = byte
|
||||||
|
deviceSettings = MiniwareSettings
|
||||||
if binary:
|
if isPinecil:
|
||||||
build_dfu(output_file, data)
|
deviceSettings = PinecilSettings
|
||||||
else:
|
# Generate both possible outputs
|
||||||
intel_hex(output_file, data, 0x0800F800)
|
DFUOutput.writeFile(
|
||||||
|
output_filename_base + ".dfu",
|
||||||
|
data,
|
||||||
|
deviceSettings.IMAGE_ADDRESS,
|
||||||
|
deviceSettings.DFU_TARGET_NAME,
|
||||||
|
deviceSettings.DFU_PINECIL_ALT,
|
||||||
|
deviceSettings.DFU_PINECIL_PRODUCT,
|
||||||
|
deviceSettings.DFU_PINECIL_VENDOR,
|
||||||
|
)
|
||||||
|
HexOutput.writeFile(
|
||||||
|
output_filename_base + ".hex", data, deviceSettings.IMAGE_ADDRESS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_commandline():
|
def parse_commandline():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||||
description="Convert image file for display on TS100 LCD "
|
description="Convert image file for display on LCD " "at startup",
|
||||||
"at startup")
|
)
|
||||||
|
|
||||||
def zero_to_255(text):
|
def zero_to_255(text):
|
||||||
value = int(text)
|
value = int(text)
|
||||||
@@ -213,79 +148,83 @@ def parse_commandline():
|
|||||||
raise argparse.ArgumentTypeError("must be integer from 0 to 255 ")
|
raise argparse.ArgumentTypeError("must be integer from 0 to 255 ")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
parser.add_argument('input_filename',
|
parser.add_argument("input_filename", help="input image file")
|
||||||
help="input image file")
|
|
||||||
|
|
||||||
parser.add_argument('output_filename',
|
parser.add_argument("output_filename", help="output file base name")
|
||||||
help="output Intel hex file")
|
|
||||||
|
|
||||||
parser.add_argument('-p', '--preview',
|
parser.add_argument(
|
||||||
help="filename of image preview (same data as "
|
"-p",
|
||||||
"Intel hex file, as will appear on TS100 LCD)")
|
"--preview",
|
||||||
|
help="filename of image preview",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('-n', '--negative',
|
parser.add_argument(
|
||||||
action='store_true',
|
"-n",
|
||||||
help="photo negative: exchange black and white "
|
"--negative",
|
||||||
"in output")
|
action="store_true",
|
||||||
|
help="photo negative: exchange black and white " "in output",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('-t', '--threshold',
|
parser.add_argument(
|
||||||
type=zero_to_255,
|
"-t",
|
||||||
default=128,
|
"--threshold",
|
||||||
help="0 to 255: gray (or color converted to gray) "
|
type=zero_to_255,
|
||||||
"above this becomes white, below becomes black; "
|
default=128,
|
||||||
"ignored if using --dither")
|
help="0 to 255: gray (or color converted to gray) "
|
||||||
|
"above this becomes white, below becomes black; "
|
||||||
|
"ignored if using --dither",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('-d', '--dither',
|
parser.add_argument(
|
||||||
action='store_true',
|
"-d",
|
||||||
help="use dithering (speckling) to convert gray or "
|
"--dither",
|
||||||
"color to black and white")
|
action="store_true",
|
||||||
|
help="use dithering (speckling) to convert gray or " "color to black and white",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('-f', '--force',
|
parser.add_argument(
|
||||||
action='store_true',
|
"-f", "--force", action="store_true", help="force overwriting of existing files"
|
||||||
help="force overwriting of existing files")
|
)
|
||||||
|
|
||||||
parser.add_argument('-v', '--version',
|
parser.add_argument(
|
||||||
action='version',
|
"-v",
|
||||||
version="%(prog)s version " + VERSION_STRING,
|
"--version",
|
||||||
help="print version info")
|
action="version",
|
||||||
|
version="%(prog)s version " + VERSION_STRING,
|
||||||
|
help="print version info",
|
||||||
|
)
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
|
||||||
args = parse_commandline()
|
args = parse_commandline()
|
||||||
|
|
||||||
if os.path.exists(args.output_filename) and not args.force:
|
if os.path.exists(args.output_filename) and not args.force:
|
||||||
sys.stderr.write("Won't overwrite existing file \"{}\" (use --force "
|
sys.stderr.write(
|
||||||
"option to override)\n"
|
'Won\'t overwrite existing file "{}" (use --force '
|
||||||
.format(args.output_filename))
|
"option to override)\n".format(args.output_filename)
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if args.preview and os.path.exists(args.preview) and not args.force:
|
if args.preview and os.path.exists(args.preview) and not args.force:
|
||||||
sys.stderr.write("Won't overwrite existing file \"{}\" (use --force "
|
sys.stderr.write(
|
||||||
"option to override)\n"
|
'Won\'t overwrite existing file "{}" (use --force '
|
||||||
.format(args.preview))
|
"option to override)\n".format(args.preview)
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if args.output_filename[-4:] == ".dfu":
|
img2hex(
|
||||||
with open(args.output_filename, 'wb') as output:
|
args.input_filename,
|
||||||
img2hex(args.input_filename,
|
args.preview,
|
||||||
output,
|
args.threshold,
|
||||||
args.preview,
|
args.dither,
|
||||||
args.threshold,
|
args.negative,
|
||||||
args.dither,
|
output_filename_base=args.output_filename,
|
||||||
args.negative,
|
isPinecil=False,
|
||||||
True)
|
)
|
||||||
else:
|
|
||||||
with open(args.output_filename, 'w', newline='\r\n') as output:
|
|
||||||
img2hex(args.input_filename,
|
|
||||||
output,
|
|
||||||
args.preview,
|
|
||||||
args.threshold,
|
|
||||||
args.dither,
|
|
||||||
args.negative)
|
|
||||||
except BaseException as error:
|
except BaseException as error:
|
||||||
sys.stderr.write("Error converting file: {}\n".format(error))
|
sys.stderr.write("Error converting file: {}\n".format(error))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
71
Bootup Logos/output_dfu.py
Normal file
71
Bootup Logos/output_dfu.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import struct, zlib
|
||||||
|
|
||||||
|
|
||||||
|
class DFUOutput:
|
||||||
|
|
||||||
|
DFU_PREFIX_SIZE = 11
|
||||||
|
DFU_SUFFIX_SIZE = 16
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compute_crc(cls, data):
|
||||||
|
return 0xFFFFFFFF & -zlib.crc32(data) - 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def writeFile(
|
||||||
|
cls,
|
||||||
|
file_name: str,
|
||||||
|
data: bytearray,
|
||||||
|
data_address: int,
|
||||||
|
tagetName: str,
|
||||||
|
alt_number: int,
|
||||||
|
product_id: int,
|
||||||
|
vendor_id: int,
|
||||||
|
):
|
||||||
|
data: bytearray = b""
|
||||||
|
|
||||||
|
for byte in data:
|
||||||
|
data += byte.to_bytes(1, byteorder="big")
|
||||||
|
|
||||||
|
data = struct.pack("<2I", data_address, len(data)) + data
|
||||||
|
data = (
|
||||||
|
struct.pack(
|
||||||
|
"<6sBI255s2I",
|
||||||
|
b"Target",
|
||||||
|
alt_number,
|
||||||
|
1,
|
||||||
|
tagetName,
|
||||||
|
len(data),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
+ data
|
||||||
|
)
|
||||||
|
data = (
|
||||||
|
struct.pack(
|
||||||
|
"<5sBIB",
|
||||||
|
b"DfuSe",
|
||||||
|
1,
|
||||||
|
cls.DFU_PREFIX_SIZE + len(data) + cls.DFU_SUFFIX_SIZE,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
+ data
|
||||||
|
)
|
||||||
|
data += struct.pack(
|
||||||
|
"<4H3sB",
|
||||||
|
0,
|
||||||
|
product_id,
|
||||||
|
vendor_id,
|
||||||
|
0x011A,
|
||||||
|
b"UFD",
|
||||||
|
cls.DFU_SUFFIX_SIZE,
|
||||||
|
)
|
||||||
|
crc = cls.compute_crc(data)
|
||||||
|
data += struct.pack("<I", crc)
|
||||||
|
with open(file_name, "wb") as output:
|
||||||
|
output.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print("DO NOT CALL THIS FILE DIRECTLY")
|
||||||
|
sys.exit(1)
|
||||||
104
Bootup Logos/output_hex.py
Normal file
104
Bootup Logos/output_hex.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import zlib
|
||||||
|
|
||||||
|
|
||||||
|
class HexOutput:
|
||||||
|
"""
|
||||||
|
Supports writing a blob of data out in the Intel Hex format
|
||||||
|
"""
|
||||||
|
|
||||||
|
INTELHEX_DATA_RECORD = 0x00
|
||||||
|
INTELHEX_END_OF_FILE_RECORD = 0x01
|
||||||
|
INTELHEX_EXTENDED_LINEAR_ADDRESS_RECORD = 0x04
|
||||||
|
INTELHEX_BYTES_PER_LINE = 16
|
||||||
|
INTELHEX_MINIMUM_SIZE = 4096
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def split16(cls, word):
|
||||||
|
"""return high and low byte of 16-bit word value as tuple"""
|
||||||
|
return (word >> 8) & 0xFF, word & 0xFF
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compute_crc(cls, data):
|
||||||
|
return 0xFFFFFFFF & -zlib.crc32(data) - 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def intel_hex_line(cls, record_type, offset, data):
|
||||||
|
"""generate a line of data in Intel hex format"""
|
||||||
|
# length, address offset, record type
|
||||||
|
record_length = len(data)
|
||||||
|
yield ":{:02X}{:04X}{:02X}".format(record_length, offset, record_type)
|
||||||
|
|
||||||
|
# data
|
||||||
|
for byte in data:
|
||||||
|
yield "{:02X}".format(byte)
|
||||||
|
|
||||||
|
# compute and write checksum (now using unix style line endings for DFU3.45 compatibility
|
||||||
|
yield "{:02X}\n".format(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
sum(
|
||||||
|
data, # sum data ...
|
||||||
|
record_length # ... and other ...
|
||||||
|
+ sum(cls.split16(offset)) # ... fields ...
|
||||||
|
+ record_type,
|
||||||
|
) # ... on line
|
||||||
|
& 0xFF
|
||||||
|
) # low 8 bits
|
||||||
|
^ 0xFF
|
||||||
|
) # two's ...
|
||||||
|
+ 1
|
||||||
|
) # ... complement
|
||||||
|
& 0xFF
|
||||||
|
) # low 8 bits
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def writeFile(cls, file_name: str, data: bytearray, data_address: int):
|
||||||
|
"""write block of data in Intel hex format"""
|
||||||
|
with open(file_name, "w", newline="\r\n") as output:
|
||||||
|
|
||||||
|
def write(generator):
|
||||||
|
output.write("".join(generator))
|
||||||
|
|
||||||
|
if len(data) % cls.INTELHEX_BYTES_PER_LINE != 0:
|
||||||
|
raise ValueError(
|
||||||
|
"Program error: Size of LCD data is not evenly divisible by {}".format(
|
||||||
|
cls.INTELHEX_BYTES_PER_LINE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
address_lo = data_address & 0xFFFF
|
||||||
|
address_hi = (data_address >> 16) & 0xFFFF
|
||||||
|
|
||||||
|
write(
|
||||||
|
cls.intel_hex_line(
|
||||||
|
cls.INTELHEX_EXTENDED_LINEAR_ADDRESS_RECORD,
|
||||||
|
0,
|
||||||
|
cls.split16(address_hi),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
size_written = 0
|
||||||
|
while size_written < cls.INTELHEX_MINIMUM_SIZE:
|
||||||
|
offset = address_lo
|
||||||
|
for line_start in range(0, len(data), cls.INTELHEX_BYTES_PER_LINE):
|
||||||
|
write(
|
||||||
|
cls.intel_hex_line(
|
||||||
|
cls.INTELHEX_DATA_RECORD,
|
||||||
|
offset,
|
||||||
|
data[line_start : line_start + cls.INTELHEX_BYTES_PER_LINE],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
size_written += cls.INTELHEX_BYTES_PER_LINE
|
||||||
|
if size_written >= cls.INTELHEX_MINIMUM_SIZE:
|
||||||
|
break
|
||||||
|
offset += cls.INTELHEX_BYTES_PER_LINE
|
||||||
|
|
||||||
|
write(cls.intel_hex_line(cls.INTELHEX_END_OF_FILE_RECORD, 0, ()))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print("DO NOT CALL THIS FILE DIRECTLY")
|
||||||
|
sys.exit(1)
|
||||||
Reference in New Issue
Block a user