maruaのアイコン画像

スケッチ例「Ardino ISP」を GUIで制御(Python)

marua 2022年01月08日に作成  (2022年01月08日に更新) © CC BY-SA 4+ 製作品 製作品

スケッチ例「Ardino ISP」を GUIで制御(Python)

ISP Writer

Arduino Nano等で使用されている「ATmega328P」ですが、外付けの水晶を外すことで、I/Oポートを2本増やすことが出来ます。
また、生のATmega328Pは 初期状態で「8MHzの内臓発振回路を8分周(=1MHz)」となっているため、外付けの水晶で動かす時事や CPUクロックを変えたい事 が発生します。

これらは、CPU内の「ヒューズビット」をプログラムする事で 変えられるのですが、一般的に使用される Arduinoのブートローダ経由での書込みでは 変更する事が出来ません。
ヒューズビットを変更する場合は、
 ・並列プログラミング(8bitのバスと制御信号を使う)
 ・直列プログラミング(SPI信号を使う)
を使用する必要が有ります。

幸い Arduino IDEには、スケッチ例に「Arduino ISP」が初めから入っているので、直列プログラミングの書込み器を 簡単に作成することが出来ます。

スケッチ例 - 11. Arduino ISP

しかし、ヒューズbitを操作しようとすると、コマンドプロンプトから長い呪文を打ち込まなければならないので、使い勝手がイマイチです。
そこで PythonとTKinterを使って 簡単な書込みプログラムを作ってみました。

使用に当たっては、「COMポート選択」と「ヒューズbit E, H, L」を設定して、Fuse:の「Write」ボタンを押すだけです。
(初期値は 「内臓オシレータ 8MHz」)
また、おまけで プログラムも書き込めるようになっています。

ヒューズの書き換えには、Arduino IDEで導入されている「avrdude.exe」を使用しています。
私のPCの場合、「C:\Users<ユーザ名>\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino18\bin」という 深い場所にあるので、予め パスを通しておくか Pythonのプログラムと同じフォルダにコピーして使ってください。
フォルダにコピーする場合、隣の etcフォルダにある「avrdude.conf」も一緒にコピーする必要があるようです。
キャプションを入力できます

arduino_ISP.py

from tkinter import * from tkinter import ttk from tkinter import messagebox import serial from serial.tools import list_ports import os import tkinter.filedialog import subprocess from subprocess import PIPE def show_selection(): try: com_txt = lb.get( lb.curselection() ) com = '' for i in com_txt: if str(i) != ' ': com = com + str(i) else: break print(com) return(com) except: messagebox.showwarning('確認', 'COMポートを選択してください') def fuse_entry_enable(): e_fuse_entry.config(state="normal") h_fuse_entry.config(state="normal") l_fuse_entry.config(state="normal") def fuse_read(): cmd_string=avrdude+show_selection()+' -U efuse:r:"./tmp/_efuse.bin":r -U hfuse:r:"./tmp/_hfuse.bin":r -U lfuse:r:"./tmp/_lfuse.bin":r' run_win_cmd(cmd_string) def fuse_write(): cmd_string=avrdude+show_selection()+' -U efuse:w:0x'+e_fuse.get()+':m -U hfuse:w:0x'+h_fuse.get()+':m -U lfuse:w:0x'+l_fuse.get()+':m' run_win_cmd(cmd_string) def get_file(): hex_file.set(tkinter.filedialog.askopenfilename(filetypes=fTyp, initialdir=iDir) ) def flash_write(): if hex_file.get() != '': cmd_string=avrdude+show_selection()+' -U flash:w:'+hex_file.get()+':i' print('start Flash write. please wait.') run_win_cmd(cmd_string) else: messagebox.showwarning('確認', '書込みファイルを選択してください') def run_win_cmd(cmd_string): try: print(cmd_string) #res = subprocess.check_output('dir') res = subprocess.run(cmd_string, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True) except: res = "error" print(res) if __name__ == '__main__': root = Tk() root.title('ISP Writer') avrdude='avrdude -p m328P -c avrisp -b 19200 -P ' cmd_string=avrdude fTyp = [("HEXファイル", "*.hex")] iDir = os.path.abspath(os.path.dirname(__file__)) # フレーム1 frame1 = ttk.Frame(root, padding=10) frame1.grid() # *** row 0 *** label_Com = ttk.Label( frame1, text='Com:', anchor="w", foreground='#000000', padding=(5)) label_Com.grid(row=0, column=0, sticky=W) # *** row 1 *** # リストボックス currencies = serial.tools.list_ports.comports(include_links=False) v = StringVar(value=currencies) lb = Listbox( frame1, listvariable=v, width=40, height=5) lb.grid(row=1, column=0) # Scrollbar scrollbar = ttk.Scrollbar( frame1, orient=VERTICAL, command=lb.yview) lb['yscrollcommand'] = scrollbar.set scrollbar.grid(row=1, column=1, sticky=(N, S)) # Button button1 = ttk.Button( frame1, text='Select', command=lambda: show_selection()) button1.grid(row=1, column=2) # フレーム2 frame2 = ttk.Frame(root, padding=10) frame2.grid() # *** row 0 *** label_Fuse = ttk.Label( frame2, text='Fuse:', anchor="w", foreground='#000000', padding=(5)) label_Fuse.grid(row=0, column=0, sticky=W) # *** row 1 *** # E Fuse label_E = ttk.Label( frame2, text=' E:', foreground='#0000aa', padding=(5,0)) label_E.grid(row=1, column=0) e_fuse = StringVar() e_fuse.set('FD') e_fuse_entry = ttk.Entry( frame2, textvariable=e_fuse, state="normal", width=4) e_fuse_entry.grid(row=1, column=1) # H Fuse label_H = ttk.Label( frame2, text=' H:', foreground='#0000aa', padding=(5,0)) label_H.grid(row=1, column=2) h_fuse = StringVar() h_fuse.set('D2') h_fuse_entry = ttk.Entry( frame2, textvariable=h_fuse, state="normal", width=4) h_fuse_entry.grid(row=1, column=3) # L Fuse label_L = ttk.Label( frame2, text=' L:', foreground='#0000aa', padding=(5,0)) label_L.grid(row=1, column=4) l_fuse = StringVar() l_fuse.set('E2') l_fuse_entry = ttk.Entry( frame2, textvariable=l_fuse, state="normal", width=4) l_fuse_entry.grid(row=1, column=5) # EEP Read Button button_ERd = ttk.Button( frame2, text='Read', command=lambda: fuse_read() ) button_ERd.grid(row=1, column=6) # EEP Write Button button_EWr = ttk.Button( frame2, text='Write', command=lambda: fuse_write() ) button_EWr.grid(row=1, column=7) # フレーム3 frame3 = ttk.Frame(root, padding=10) frame3.grid() # *** row 0 *** label_Flash = ttk.Label( frame3, text='Flash:', foreground='#000000', padding=(5)) label_Flash.grid(row=0, column=0, sticky=W) # *** row 1 *** # File hex_file = StringVar() hex_file_entry = ttk.Entry( frame3, textvariable=hex_file, state="disable", width=30) hex_file_entry.grid(row=1, column=0) # File Button button_FFile = ttk.Button( frame3, text='File', #command=lambda: hex_file_entry.config(state="normal") command=lambda: get_file() ) button_FFile.grid(row=1, column=1) # Frash Write Button button_FWrite = ttk.Button( frame3, text='ROM Write', command=lambda: flash_write() ) button_FWrite.grid(row=1, column=2) # フレーム4 frame4 = ttk.Frame(root, padding=10) frame4.grid() # *** row 0 *** root.mainloop()

【参考-1】ATmega328Pのヒューズbit
・ヒューズbit E
キャプションを入力できます

・ヒューズbit H
キャプションを入力できます

・ヒューズbit L
キャプションを入力できます

【参考-2】Mini Core
Arduino IDEは、標準では 内臓オシレータ等のクロック変更に対応していません。
外付け16MHzのままで 内臓 8MHzのプログラムを作成すると、delay() や Serial.print()のタイミングが狂ってしまい、正しく動作しません。
私は、ボードマネージャで「Mini Core」を導入しています。
Mini Coreを導入すると、クロックの違いも 簡単に吸収してくれます。
キャプションを入力できます

キャプションを入力できます

  • marua さんが 2022/01/08 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する

投稿者の人気記事