#!/usr/bin/env python # coding=utf-8 from __future__ import division import os, sys, struct, zlib try: from PIL import Image, ImageOps except ImportError as error: raise ImportError("{}: {} requres Python Imaging Library (PIL). " "Install with `pip` or OS-specific package " "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( "<6sBI255s2I", b"Target", DFU_PINECIL_ALT, 1, DFU_TARGET_NAME, len(data), 1 ) + data ) data = ( 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("