スケッチ例「Ardino ISP」を GUIで制御(Python)
Arduino Nano等で使用されている「ATmega328P」ですが、外付けの水晶を外すことで、I/Oポートを2本増やすことが出来ます。
また、生のATmega328Pは 初期状態で「8MHzの内臓発振回路を8分周(=1MHz)」となっているため、外付けの水晶で動かす時事や CPUクロックを変えたい事 が発生します。
これらは、CPU内の「ヒューズビット」をプログラムする事で 変えられるのですが、一般的に使用される Arduinoのブートローダ経由での書込みでは 変更する事が出来ません。
ヒューズビットを変更する場合は、
・並列プログラミング(8bitのバスと制御信号を使う)
・直列プログラミング(SPI信号を使う)
を使用する必要が有ります。
幸い Arduino IDEには、スケッチ例に「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
【参考-2】Mini Core
Arduino IDEは、標準では 内臓オシレータ等のクロック変更に対応していません。
外付け16MHzのままで 内臓 8MHzのプログラムを作成すると、delay() や Serial.print()のタイミングが狂ってしまい、正しく動作しません。
私は、ボードマネージャで「Mini Core」を導入しています。
Mini Coreを導入すると、クロックの違いも 簡単に吸収してくれます。
投稿者の人気記事
-
marua
さんが
2022/01/08
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する