前回の記事 「XIAOで PIC12F1822ライタの製作 (ameblo.jp)」では
SAMD21G18マイコンを使った無印の Seeeduino XIAO を使用しましたが、
今では XIAOシリーズのラインナップが増えたためか、区別するために
Seeeduino XIAO SAMD21 と後ろにマイコン名を付けているようです。
いずれにしても Seeeduino XIAO SAMD21 では
CircuitPythonでスクリプトを作るにはメモリ容量が不足気味でしたので、
今回は RP2040マイコンを使った Seeeduino XIAO RP2040 を使用して
PIC12F1822ライタを製作しました。
■ 前回からの変更点
マイコン接続
・ MCLRピンの制御を D7から D6に変更
・ ICSPDATAピンの制御を D9(入力)/D10(出力)から D7(入出力切替)に変更
スクリプトファイル
・ イメージファイル(image.hex)のパース処理を実装
→ Intel HEXファイルから専用イメージファイルを作成するための
事前の変換処理は不要になり、Intel HEXファイルを
そのまま CIRCUITPYドライブにコピーするだけ済むようにした。
・ ベリファイ処理を実装
→ マイコンにファームを書込んだ後に、改めてマイコンから
読出したファームとイメージファイルの照合を行えるようにした。
・ コマンド実行状態の表示処理を実装
→ 実行しているコマンドの内容に応じて三色LEDの色を変えて
表示するようにした。また、コマンド待機中は、
サポートされたマイコンかどうかを表示するようにした。
■ Seeeduino XIAO RP2040と PIC12F1822マイコンの接続
Seeeduino XIAO RP2040 | - | PIC12F1822 | ||
---|---|---|---|---|
3V3 | 3V3 | - | 1-pin | VDD |
GND | GND | - | 8-pin | VSS |
D6 | GPO | 10 kΩ | 4-pin (RA3) | MCLR |
D8 | GPO | 10 kΩ | 6-pin (RA1) | ICSPCLK |
D7 | GPO/GPI | 10 kΩ | 7-pin (RA0) | ICSPDATA |
■ CircuitPythonスクリプトのソースコード
前回は Mery というフリーウェアのテキストエディタを使って作ったのですが、
今回は Visual Studio Code で作って black という Pythonコードフォーマッターで
整形しました。不要な情報ですね。
# ----------------------------------------------------------------------------- # PIC16F1xxx LV-ICSP Programmer by Seeeduino XIAO RP2040 and CircuitPython # # Seeeduino XIAO RP2040 Microchip PIC12F1822 # 3V3 ----------- 1 VDD # GND ----------- 8 VSS # D6 : GPO --- 10k --- 4 RA3: MCLR # D8 : GPO --- 10k --- 6 RA1: ICSPCLK # D7 : GPO/GPI --- 10k --- 7 RA0: ICSPDAT # import time import board import digitalio import neopixel_write DEVICE_LIST = { 0x2700: { # Device ID "P": [0x0000, 0x0800, 0x3FFF], # Address, Size, Value "C": [0x8007, 0x0002, 0x3FFF], # Address, Size, Value "D": [0xF000, 0x0100, 0x00FF], # Address, Size, Value "N": "PIC12F1822", # Device Name } } # ----------------------------------------------------------------------------- # Low-Voltage In-Circuit Serial Programming (LV-ICSP) Class class ICSP: WAIT_TCLK = 200e-9 # 200 ns WAIT_TDLY = 1e-6 # 1 us WAIT_TENT = 1e-3 # 1 ms WAIT_TERA = 5e-3 # 5 ms COLUMN = 0x10 def __init__(self, MCLR, ICSPCLK, ICSPDAT): self.MCLR = digitalio.DigitalInOut(MCLR) self.MCLR.direction = digitalio.Direction.OUTPUT self.ICSPCLK = digitalio.DigitalInOut(ICSPCLK) self.ICSPCLK.direction = digitalio.Direction.OUTPUT self.ICSPDAT = digitalio.DigitalInOut(ICSPDAT) self.ICSPDAT.direction = digitalio.Direction.OUTPUT # Communication Routine def send_bit(self, length, value): for i in range(length): # TCKH: Min 100 ns self.ICSPCLK.value = True self.ICSPDAT.value = value & 1 # TDS: Min 100 ns time.sleep(self.WAIT_TCLK) # TCKL: Min 100 ns self.ICSPCLK.value = False # TDH: Min 100 ns time.sleep(self.WAIT_TCLK) value = value >> 1 def send_command(self, value): self.send_bit(6, value) # TDLY: Min 1 us time.sleep(self.WAIT_TDLY) def send_data(self, value): self.send_bit(1, 0) self.send_bit(14, value) self.send_bit(1, 0) def recv_data(self): value = 0 self.send_bit(1, 0) self.ICSPDAT.direction = digitalio.Direction.INPUT for i in range(14): # TCKH: Min 100 ns self.ICSPCLK.value = True # TCO: Max 80 ns time.sleep(self.WAIT_TCLK) value |= self.ICSPDAT.value << i # TCKL: Min 100 ns self.ICSPCLK.value = False time.sleep(self.WAIT_TCLK) self.ICSPDAT.direction = digitalio.Direction.OUTPUT self.send_bit(1, 0) return value def set_lvp_mode(self): # TENTS: Min 100 ns self.MCLR.value = True time.sleep(self.WAIT_TENT) # TENTH: Min 250 us self.MCLR.value = False time.sleep(self.WAIT_TENT) # Key Sequence: 0x4D434850 (MCHP in ASCII) self.send_bit(8, 0x50) self.send_bit(8, 0x48) self.send_bit(8, 0x43) self.send_bit(8, 0x4D) # Total 33 Clocks self.send_bit(1, 0) time.sleep(self.WAIT_TENT) def set_normal_mode(self): self.MCLR.value = True # Command Routine def run_load_configuration(self): self.send_command(0x00) self.send_data(0x00) def run_load_data_for_program_memory(self, value): self.send_command(0x02) self.send_data(value) def run_load_data_for_data_memory(self, value): self.send_command(0x03) self.send_data(value) def run_read_data_from_program_memory(self): self.send_command(0x04) return self.recv_data() def run_read_data_from_data_memory(self): self.send_command(0x05) return self.recv_data() def run_increment_address(self): self.send_command(0x06) def run_reset_address(self): self.send_command(0x16) def run_begin_internally_timed_programming(self): self.send_command(0x08) # TPINT: Max 5 ms time.sleep(self.WAIT_TERA) def run_bulk_erase_program_memory(self): self.send_command(0x09) # TERAB: Max 5 ms time.sleep(self.WAIT_TERA) def run_bulk_erase_data_memory(self): self.send_command(0x0B) # TERAB: Max 5 ms time.sleep(self.WAIT_TERA) # Read Routine def read_memory(self, size, run_read_data, show=True): base_address = 0 data = [0] * size for address in range(size): data[address] = run_read_data() next_address = address + 1 self.run_increment_address() if not show: continue if ((next_address % self.COLUMN) == 0) or (next_address == size): column_data = data[base_address:next_address] print_data_line(base_address, column_data) base_address = next_address return data def read_program_memory(self, size): run_read_data = self.run_read_data_from_program_memory self.run_reset_address() return self.read_memory(size, run_read_data) def read_configulation(self, size, show=True): run_read_data = self.run_read_data_from_program_memory self.run_load_configuration() return self.read_memory(size, run_read_data, show) def read_data_memory(self, size): run_read_data = self.run_read_data_from_data_memory self.run_reset_address() return self.read_memory(size, run_read_data) # Erase Routine def erase_program_memory(self): self.run_load_configuration() self.run_bulk_erase_program_memory() def erase_data_memory(self): self.run_bulk_erase_data_memory() # Write Routine def write_memory(self, data, latch, run_load_data): base_address = 0 size = len(data) for address in range(size): run_load_data(data[address]) next_address = address + 1 if ((next_address % latch) == 0) or (next_address == size): self.run_begin_internally_timed_programming() self.run_increment_address() if ((next_address % self.COLUMN) == 0) or (next_address == size): column_data = data[base_address:next_address] print_data_line(base_address, column_data) base_address = next_address def write_program_memory(self, data): if data: run_load_data = self.run_load_data_for_program_memory self.erase_program_memory() self.run_reset_address() self.write_memory(data, 16, run_load_data) def write_configulation(self, data): if data: run_load_data = self.run_load_data_for_program_memory self.run_load_configuration() for i in range(7): self.run_increment_address() self.write_memory(data[0:2], 1, run_load_data) def write_data_memory(self, data): if data: run_load_data = self.run_load_data_for_data_memory self.erase_data_memory() self.run_reset_address() self.write_memory(data, 1, run_load_data) # ----------------------------------------------------------------------------- # Sub Routine def hexstr(data): return " ".join([("%04X" % value) for value in data]) def print_data_line(address, data): print(("%04X:" % address), hexstr(data)) def print_data(data): for address in range(0, len(data), ICSP.COLUMN): print_data_line(address, data[address : address + ICSP.COLUMN]) def verify_data(memory, config, read_data): print("File Data") data_file = read_hex_file(file, memory) print_data(data_file) print("Read Data") if config: data_read = icsp.read_configulation(11)[7:9] else: data_read = read_data(memory[1]) if data_file == data_read: print("Verify OK") else: print("Verify NG") def read_configulation(): data = icsp.read_configulation(11, False) device_id = data[6] & 0x3FE0 device_infomation = DEVICE_LIST.get(device_id) if device_infomation is None: device_name = "(Not Supported)" else: device_name = "(" + device_infomation["N"] + ")" # Print print("# Configuration") print("User ID Location :", hexstr(data[0:4])) print("Device ID :", hexstr([device_id]), device_name) print("Revision ID :", hexstr([data[6] & 0x1F])) print("Configuration Word :", hexstr(data[7:9])) print("Calibration Word :", hexstr(data[9:11])) return device_infomation def read_hex_file(name, memory): memory_address = memory[0] memory_size = memory[1] memory_buffer = [memory[2]] * memory_size extended_linear_address = 0 # Read File file = open(name, "r") for line in file: line = line.rstrip() # Parse Record Structure start_code = line[0] # # Start code byte_count = line[1:3] # # Byte count address = line[3:7] # # Address record_type = line[7:9] # # Record type data = line[9:-2] # # Data checksum = line[-2:] # # Checksum # Check if start_code != ":": print("Invalid Start Code") return if (int(byte_count, 16) * 2) != len(data): print("Invalid Data Length") return byte_data = [int(line[i : i + 2], 16) for i in range(1, len(line), 2)] if sum(byte_data) & 0xFF: print("Invalid Checksum") return # Handle if record_type == "00": # # Data absolute_address = int(extended_linear_address + address, 16) >> 1 offset_address = absolute_address - memory_address if 0 <= offset_address < memory_size: for i in range(0, len(data), 4): value = int(data[i + 2 : i + 4] + data[i : i + 2], 16) memory_buffer[offset_address + (i >> 2)] = value elif record_type == "04": # # Extended Linear Address extended_linear_address = line[9:13] elif record_type == "01": # # End Of File break else: print("Invalid Record Type") return file.close() return memory_buffer class LED: mode = 0 def __init__(self): self.PWR = digitalio.DigitalInOut(board.NEOPIXEL_POWER) self.PWR.direction = digitalio.Direction.OUTPUT self.PWR.value = True self.DAT = digitalio.DigitalInOut(board.NEOPIXEL) self.DAT.direction = digitalio.Direction.OUTPUT def set_error(self, value): self.mode = 2 if value else 1 def OFF(self): data = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] neopixel_write.neopixel_write(self.DAT, bytearray(data[self.mode])) def ON_MODE(self): # White neopixel_write.neopixel_write(self.DAT, bytearray([0x30, 0x30, 0x30])) self.mode = 0 def ON_READ(self): # Green neopixel_write.neopixel_write(self.DAT, bytearray([0x30, 0x00, 0x00])) def ON_ERASE(self): # Yellow neopixel_write.neopixel_write(self.DAT, bytearray([0x20, 0x40, 0x00])) def ON_WRITE(self): # Red neopixel_write.neopixel_write(self.DAT, bytearray([0x00, 0x60, 0x00])) def ON_VERIFY(self): # Cyan neopixel_write.neopixel_write(self.DAT, bytearray([0x20, 0x00, 0x20])) # ----------------------------------------------------------------------------- # Main Routine file = "image.hex" icsp = ICSP(board.D6, board.D8, board.D7) icsp.set_lvp_mode() device = read_configulation() led = LED() led.set_error(device is None) while True: led.OFF() print("") print("# PIC16F1xxx LV-ICSP Programmer") print("MI/MO : Enter/Exit LV-ICSP Mode (White)") if device: print("RP/RD/RC : Read Program/Data/Configuration Memory (Green)") print("EP/ED : Erase Program/Data Memory (Yellow)") print("WP/WD/WC : Write Program/Data/Configuration Memory (Red)") print("VP/VD/VC : Verify Program/Data/Configuration Memory (Cyan)") else: print("RC : Read Configuration Memory (Green)") print("> ", end="") text = input().upper() if text == "MI": led.ON_MODE() icsp.set_lvp_mode() elif text == "MO": led.ON_MODE() icsp.set_normal_mode() device = None elif text == "RC": led.ON_READ() device = read_configulation() led.set_error(device is None) elif device is None: print("Invalid") elif text == "RP": led.ON_READ() icsp.read_program_memory(device["P"][1]) elif text == "RD": led.ON_READ() icsp.read_data_memory(device["D"][1]) elif text == "EP": led.ON_ERASE() icsp.erase_program_memory() elif text == "ED": led.ON_ERASE() icsp.erase_data_memory() elif text == "WP": led.ON_WRITE() icsp.write_program_memory(read_hex_file(file, device["P"])) elif text == "WD": led.ON_WRITE() icsp.write_data_memory(read_hex_file(file, device["D"])) elif text == "WC": led.ON_WRITE() icsp.write_configulation(read_hex_file(file, device["C"])) elif text == "VP": led.ON_VERIFY() verify_data(device["P"], False, icsp.read_program_memory) elif text == "VD": led.ON_VERIFY() verify_data(device["D"], False, icsp.read_data_memory) elif text == "VC": led.ON_VERIFY() verify_data(device["C"], True, None) elif text == "TF": print("Program Memory") print_data(read_hex_file(file, device["P"])) print("Configuration Memory") print_data(read_hex_file(file, device["C"])) print("Data Memory") print_data(read_hex_file(file, device["D"])) else: print("Invalid") time.sleep(0.1)
以上です。