ATmega328Pの調査 ～Fuseビットについて調べてみた～

# はじめに

今までArduinoを自作して色々工作とかしてたんですが、Arduino IDEを使ってプログラムを作ってるとマイコンのスペックをフル活用できてない感じがずっとしてました。
じゃあマイコンのスペックをちゃんと活用しろよ！と言われたどうしたらいいかというと、マイコンマニュアル（データシート）を読んでちゃんとマイコン機能や動作、限界を理解した上でプログラムを作るべきだと思います。

というわけで、その手始めとしてArduino起動時に必要なブートローダを書き込む際に設定するFuseビット（ヒューズビット）について調べてみました。

※参照したマニュアルは本ページの最後に記載しておきます。

# 概要

そもそもFuseビットとは何者なのか。。。
マニュアルを見ても概要的なことは書いてなかったですが、おそらくマイコン自体の設定をするものです。
設定項目を見ると、作成したプログラムに影響するものもあるため、ちゃんと設定しておかないと想定外の動作をする可能性があります。
分かりやすい例だと、動作クロックの設定が違っていて想定した時間よりも早くor遅くなることがあります。

## Fuseビットのフォーマット

Fuseビットは、「拡張」、「上位」「下位」の3つのバイトで構成されており、
それぞれのビットにマイコン設定の機能が割り当てられています。
とりあえず詳細は後回しにして、各バイト（ビット）の名称と内容（概要）を示します。

### 拡張バイト

|名称|ﾋﾞｯﾄ|意味|規定値|
|:---|:-:|:---|:----:|
| -         | 7 |  | 1 |
| -         | 6 |  | 1 |
| -         | 5 |  | 1 |
| -         | 4 |  | 1 |
| -         | 3 |  | 1 |
| BODLEVEL2 | 2 | 低電圧検出(BOD)リセットの制御と検出電圧選択 | 1 |
| BODLEVEL1 | 1 | ↑ | 1 |
| BODLEVEL0 | 0 | ↑ | 1 |

### 上位バイト

|名称|ﾋﾞｯﾄ|意味|規定値|
|:---|:-:|:---|:----:|
| RSTDISBL | 7 | PC6がI/Oピン or RESETピンか選択 | 1 |
| DWEN     | 6 | デバッグWIRE機能許可            | 1 |
| SPIEN    | 5 | 直列プログラミング許可          | 0 |
| WDTON    | 4 | ウォッチドッグタイマ常時有効     | 1 |
| EESAVE   | 3 | チップ消去からEEPROM内容を保護  | 1 |
| BOOTSZ1  | 2 | ブートローダ容量選択            | 0 |
| BOOTSZ0  | 1 | ↑                             | 0 |
| BOOTRST  | 0 | リセットベクタ選択              | 1 |

### 下位バイト

|名称|ﾋﾞｯﾄ|意味|規定値|
|:---|:-:|:---|:----:|
| CKDIV8 | 7 | システムクロック選択(8分周) | 0 |
| CKOUT  | 6 | システムクロック出力許可 | 1 |
| SUT1   | 5 | 起動時間選択 | 1 |
| SUT0   | 4 | ↑ | 0 |
| CKSEL3 | 3 | クロック種別選択 | 0 |
| CKSEL2 | 2 | ↑ | 0 |
| CKSEL1 | 1 | ↑ | 1 |
| CKSEL0 | 0 | ↑ | 0 |

上表の規定値ですが、論理0で記載されているので 0 だと有効、1 だと無効を示す値になってます。
各ビットで設定する内容、詳細については以降に記載します。

# Fuseビットで設定すること

フォーマットのところに書いたように、Fuseビットでは以下の12個の設定をしないといけないです。

1. 低電圧検出
2. リセットピン
3. デバッグWIRE機能
4. 直列プログラミング
5. ウォッチドッグタイマ
6. EEPROM保護
7. ブートローダ選択
8. リセットベクタ選択
9. クロック分周
10. クロック出力
11. 起動時間
12. クロック種別

こうやって書き並べてみると結構ありますね。。。（´・ω・｀）
詳細は以下、順番に書いていきます。

## 1. 低電圧検出

低電圧検出とは、電源電圧が低くなった時にリセットを発生させる機能です。

個人レベルの電子工作ではそんなに重視しなくてもいいことが多いと思いますが、
電源電圧が安定している時だけ動作させたい場合に有効にしたり、
逆に不安定だったり電池が減ってきた時でもなるべく動いてほしい場合は無効にしたりします。（たぶん）　

BODLEVEL0～2（拡張バイトの0～2bit）で低電圧検出を設定します。
設定と値の関係は次のようになってます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7974/7d0998a3-ea24-ac6d-30b1-61dd5fbdf38d.png)

電圧値にMinとMaxがあるのは、検出する際にチラつきが発生しないようにヒステリシスになっているからです。
ちょっと見づらいですが、こんな感じで動作します。👇👇👇
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7974/88cd3a1a-6d32-508e-d009-676a0b5037a1.png)

参照してるマニュアルが違うので表記がズレてますが、「VBOT-」が「Min. VBOT」に相当します。
電源電圧がVBOT-の電圧値よりも低くなった瞬間にリセットが発生し、VBOT+よりも電圧が高くなったらリセットから復帰します。
ただし、復帰する際は遅延時間が設定されているらしく、復帰するのは一定時間経過後になります。
遅延時間はどこで設定されてるのかは知らない( ﾟДﾟ)

ちなみにBODはBrown Out Detectの略称、BOTはBrown Out Thresholdの略称っぽいです。

## 2. リセットピン

上位バイトの7ビット目では、PC6のピンをI/Oピンにするか、RESETピンにするかを選択します。
PC6はピン配置図を見ると1番ピンのところですね。（まぁ、RESETピンなのでわかると思いますが、、、、）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7974/1f7388b8-0e26-e6a8-fd62-ba490b158343.png)

このビットの設定値と設定される機能は次のようになってます。

| 設定値 | 機能 |
| :----: | :-- |
|   1   | RESETピン |
|   0   | I/Oピン |

## 3. デバッグWIRE機能

ブレークポイントを設定したりしてデバッグできる機能があるらしいです。
上位バイトの6ビット目を 0 にすると、このデバッグ機能を使用できるようになるみたいですが、
Arduino IDEではデバッグできないので基本的には 1 (規定値)を設定しておくことになるかと。

## 4. 直列プログラミング

RESETピンをLowにしている状態でSPI通信でプログラムを書き込み出来るようにするらしいですが、正直なところ詳しくはわかりませんでした。
（マニュアルを読み込めばわかるかもですが、大変で時間かかりそうなので後で読みます）

AVI ISP mkIIで書き込みする際には使用してるっぽいので設定は必要だと思いますが、ひとまず規定値 0 (使用する側)にしておけばよさそうです。

## 5. ウォッチドッグタイマ

ウォッチドッグタイマの使用有無を設定するビットです。
ここではウォッチドッグタイマについては説明しないので知らない人はググってください。

ちゃんとして動作保証をするためには使用すべきなのでしょうが、ちょろっと動かすだけとか極単純な動作しかしないなら使わなくてもいい気がします。
あと、ウォッチドッグタイマを使うと消費電力が増えるみたいなのでバッテリー駆動させる場合とかは使用しないほうがいいかもです。
ということで、特に使用する要件がなければ規定値 1 のままで使用しない側にしておけばよさそうです。

## 6. EEPROM保護

AVRアーキテクチャのマイコンにはFlashとEEPROMの2種類のメモリがあり、これらの領域を削除するチップ消去という命令を実行するとプログラムやデータすべてが消去されます。

EEPROM保護が有効になっていると、このチップ消去命令を実行してもEEPROM領域のデータは消去されなくなります。
（動作確認してないですが、保護されると書いてあるので恐らく消去されなくなるのだろうかと...）

Arduino IDEを使っている限りはチップ消去命令は実行しないので、あまり気にしなくてもいいと思います。
どちらの設定にしておくべきなのかはなんとも言えませんが、規定値通りにしておけばいいと思われます。

## 7. ブートローダ容量選択

マイコンが電源ONになった瞬間に最初に動作するプログラムがブートローダなのですが、このブートローダの容量と開始アドレスをブートローダ容量選択ビット（上位バイトの1,2ビット目）で設定するみたいです。
ブートローダは自作するプログラムと同じ領域に格納されるので、この設定によってプログラム領域として使用できる範囲・容量が変わってきます。
そこまで大きなプログラムを作成することは少ないと思うので、ブートローダ容量が大きいほうに設定しておけば動作すると思いますが、使用するArduinoに合わせて適切な値に設定しておくべきです。

設定値と容量は下表のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7974/1fb2bc0c-d42e-8431-e89e-7fe8c088cb17.png)

## 8. リセットベクタ選択

リセット発生後にプログラムカウンタに格納されるアドレスを設定します。
リセット後は通常は先頭アドレス0x0000からプログラムが開始されると思うんですが、ブートローダが格納されているアドレスから開始させることができるようです。
普通に動かす時はブートローダに動いてもらう必要はないと思うので、規定値通り 1 を設定しておけば良いと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7974/d8558392-15c7-79b8-1afa-cb909b039a1b.png)

じゃあ、ブートローダはいつ動いて何をしてるんだ？(・・?

👇ブートローダについてめちゃめちゃ丁寧に説明してくれている人がいました。
https://synapse.kyoto/tips/bootloader/page001.html

## 9. クロック分周

マイコンには様々な種類のクロックが存在していて、ATmega328Pだと下図のようにクロックが存在・使用されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7974/2d85fa17-fe09-3a59-a463-47a2ffb5650f.png)

この図にある"System Clock Prescaler"（中央の少し大きい四角のすぐ下）で、マイコンがメインで使用するクロックを分周しており、ある程度遅く動作させることができます。
この分周を設定できるレジスタがあるのですが、下位バイトの7ビット目（CKDIV8）の値によってこのレジスタのマイコン初期値を変更させることができます。
有効値 0 を設定すると、レジスタ初期値が8分周する値になりマイコン動作を1/8に遅くします。
無効値 1 を設定すると、レジスタ初期値が分周しない値になり、最速のクロック設定となります。
（あくまで初期値なので、マイコン起動後にプログラムからこのレジスタを書き換えればクロック速度は変更されます。）

クロックが速いと処理性能を上げることができますが、代わりに消費電力も大きくなります。
逆にクロックが遅いと処理性能は悪くなるけど消費電力を抑えることができます。
クロック設定については性能、消費電力以外にもタイマを使用する場合は計測時間にもろに影響してくるので、作ろうとしているプログラムに合わせてちゃんと設計・設定してやる必要があります。

## 10. クロック出力

システムクロックをポートB0端子（14番ピン）に出力する／しないを設定します。
ここで出力されるクロックは上記9.クロック分周の設定が反映されたものになります。
出力されたクロックは他の回路の入力させることができます。
なお、この設定が有効の場合、ポートB0端子（14番ピン）をI/Oピンとして設定していても、Fuseビットの設定で上書きされてクロック出力されます。

## 11. 起動時間

下位バイトの5,4ビット目で、電源ONやリセット復帰後からプログラム開始までの遅延時間を選択することができます。
設定できる内容は後述のクロック種別とも関わってきて複雑になってますが、個人レベルの電子工作では電源ONからプログラムが開始されるまでの起動時間が重要になることはほぼ無いと思うので、規定値通りに設定しておけばいいと思います。

（詳細はクロック周りのことを調べるときにちゃんと書こうかな？）

## 12. クロック種別

下位バイトの3-0ビット目でプログラムを動作させるクロックに何を使用するか選択します。
プログラムの動作速度にモロに影響してくることなので、ちゃんと設定してやる必要があります。
下表に設定値とクロック種別の関係を示します。
（マニュアルにはCKSELに対して表にまとめたものがなかったので、一部省略してる部分もあります。）

| クロック種別 | CKSEL3-0 | 補足 |
| :---------: | :------: | :--- |
| Low Power Crystal | 111* | 8.0～16.0 MHz |
| Low Power Crystal | 110* | 3.0～8.0 MHz |
| Low Power Crystal | 101* | 0.9～3.0 MHz |
| Low Power Crystal | 100* | 0.4～0.9 MHz |
| Full Swing Crystal | 011* | 0.4-20 MHz |
| Low Frequency Crystal | 0101 | 32.768 kHz（時計とかでよく使う安定してるやつ） |
| Low Frequency Crystal | 0100 | 32.768 kHz（時計とかでよく使う安定してるやつ） |
| 128kHz Internal | 0011 | 128 kHz |
| Calibrated Internal RC | 0010 | 7.3-8.1 MHz |
| ???? | 0001 | マニュアルに記載なし？ |
| External Clock | 0000 | 0-20 MHz |

上表のうち、よく使うのは次の2つだと思います。

* "111*"のLow Power Crystal（外付けの水晶振動子）
* "0010"のCalibrated Internal RC（マイコン内臓で外部素子を使わなくていい）

名称から「External Clock」が外付けの水晶振動子とかを使うやつかと思ってましたが、この設定にしたときはXTAL1端子の1つだけにクロック入力して、XTAL2端子はポートB7にする使用方法みたいなので予想と違ってました...
水晶振動子（よく使うのは16MHzかな？）を使う場合は"111*"に設定して下さい。

# 参考

公式マニュアル？（日本語だけど翻訳がおかしい。情報が足りてない気がする...）
https://avr.jp/user/DS/PDF/mega328P.pdf

秋月電子にあるやつ（ATmega328**系全体のマニュアルで、328P以外のマイコンの仕様も含まれてるがちゃんと書いてありそう）
https://akizukidenshi.com/download/ds/microchip/atmega328.pdf