Raspberry PiのGPIOからAVRマイコンに書き込む方法

## はじめに
Arduinoなどでよく使われているAVRマイコンにRaspberry PiのGPIOからプログラムを書き込む方法です。
AVRマイコンへの書き込みは専用の書き込み機を使用したり、Arduinoでライターをエミュレートしたりといった方法がありますが、Raspberry PiがあればGPIOから安価に書き込みを行うことが可能です。

## 書き込みソフトの準備
書き込みにはavrdudeを使用します。
Arduinoの開発環境でも使われているAVR開発ではメジャーなツールで、
パッケージでも配布されていますが、GPIOを使用するにはビルドが必要です。

### 環境
Raspberry Pi 3 model b+

Raspberry Pi 4 model bでも動作します。その他のSBCでも動作するかもしれません。
今回はdockerでubuntuコンテナをたてて作業しました。


### avrdudeのビルド
avrdudeのビルドを行っていきます。
ほとんどのディストリビューションにgccなどの開発ツールがインストールされていますが、なければインストールしておきます。

```
# apt install build-essential autoconf wget
```

続いてflex bisonのインストール

```
# apt install flex bison
```

次にavrdudeのビルドですが、バージョンには注意が必要です。
現在最新のv6.3にはバグがあり、Raspberry PiでGPIOが利用できません。
ですので今回はv6.2を使用します。

公式リポジトリからダウンロードして展開

```
# wget http://download.savannah.gnu.org/releases/avrdude/avrdude-6.2.tar.gz
# tar -zxf avrdude-6.2.tar.gz
```
ビルドとインストール
configureに--lenable-linuxgpioオプションを渡すのを忘れずに

```
# cd avrdude-6.2
# ./configure --enable-linuxgpio
# make
# make install
```
avrdude.confにデバイスの定義を追加します。
以下のファイルを適当なエディタで編集します。
`/usr/local/etc/avrdude.conf`
エディタ上で"linuxgpio"を検索すると次のセクションが見つかるはずです。

```/usr/local/etc/avrdude.conf
#programmer
#  id    = "linuxgpio";
#  desc  = "Use the Linux sysfs interface to bitbang GPIO lines";
#  type  = "linuxgpio";
#  reset = ?;
#  sck   = ?;
#  mosi  = ?;
#  miso  = ?;
#;
```

コメントアウトを外して適当なピンをアサインします。

```/usr/local/etc/avrdude.conf
programmer
  id    = "linuxgpio";
  desc  = "Use the Linux sysfs interface to bitbang GPIO lines";
  type  = "linuxgpio";
  reset = 4;
  sck   = 16;
  mosi  = 20;
  miso  = 21;
;
```
avrdudeはGPIOで書き込みプロトコルをソフトウェアエミュレートで行います。いわゆるbit bangingです。
ですのでハードウェアリソースによらず空いてるピンなら何でも構いません。

次のコマンドでlinuxgpioが追加されていることを確認します。

```
# avrdude -c?
```

以上でavrdudeのセットアップは終了です。

## 書き込み
準備ができたらいよいよ書き込みを行っていきます。
プログラムを書き込むマイコンはatmega328pを想定していますが、ISP(SPI programming protocol)が使用できるAVRマイコンならほとんど対応しています。

### ヒューズビット
atmega328pには豊富な動作モードが用意されており、ヒューズビットと呼ばれている特殊な領域に設定値を書き込むことでsystem clockなどの変更が行えます。
avrdudeはヒューズビットの書き込みにも対応しているのですが、設定を間違えるとISPでの書き込みが行えなくなったりするので、今回は初期値のままにしておきます。

### 書き込み回路
ヒューズビットが初期値の場合、clock sourceは内部RC発振8Mhzで分周が8なので1Mhz動作です。これは3.3vの給電でも動作に十分な電力を確保できますし、外部クリスタルも必要ないので先ほどアサインした各ピンをatmega328pのreset,clk,mosi,misoに接続して電源を接続するだけで動作します。


![rpi_avr_writer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406753/1df097e7-5c1a-3b1c-cb98-8ec7833111a3.png)



コンデンサを持っていればVCCとGNDの間にパスコンを挿したほうがいいかもしれません。
以下のコマンドで正常に接続できているか確認します。

```
# avrdude -c linuxgpio -p m328p
```

問題なければデバイスシグネチャとヒューズビットの値が取得できます。

```
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)

avrdude: safemode: Fuses OK (E:FF, H:D9, L:62)

avrdude done.  Thank you.
```

以下のようなメッセージがでるようなら配線に間違いがあります。
電源を切り、配線を確認しましょう。

```
avrdude: AVR device not responding
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.


avrdude done.  Thank you.
```

### プログラムのビルド
ビルド作業とプログラムの詳説は省略します。
普通のLチカです。
Arduinoのプロジェクトをビルドするコマンドラインツールもあるようですが、私はavr-gccで直接コンパイルしています。

```
# apt install gcc-avr avr-libc
```

```blink.c
#define F_CPU 1000000
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
        DDRB  = DDRB  | 0b00001111;
        PORTB = PORTB | 0b00001111;

        while(1) {
                PORTB |= 0b00000001;
                _delay_ms(1000);
                PORTB &= 0b11111110;
                _delay_ms(1000);
        }
        return 0;
}
```

```
# avr-gcc -Os -mmcu=atmega328p blink.c -o blink.out
# avr-objcopy -F ihex blink.out blink.hex
```

以下のhexファイルができます。

```blink.hex
:100000000C9434000C943E000C943E000C943E0082
:100010000C943E000C943E000C943E000C943E0068
:100020000C943E000C943E000C943E000C943E0058
:100030000C943E000C943E000C943E000C943E0048
:100040000C943E000C943E000C943E000C943E0038
:100050000C943E000C943E000C943E000C943E0028
:100060000C943E000C943E0011241FBECFEFD8E04C
:10007000DEBFCDBF0E9440000C945B000C940000DA
:1000800084B18F6084B985B18F6085B9289A2FE3D8
:100090008DE093E0215080409040E1F700C00000E7
:1000A00028982FE38DE093E0215080409040E1F7C5
:0A00B00000C00000EBCFF894FFCF72
:00000001FF
```

### アップロード
先ほどのhexファイルをavrdudeでatmega328pにアップロードします。
avrdudeはUオプションの後にメモリタイプ、操作、ファイル、フォーマットを:で区切って指定します。
フォーマットは指定しなくても自動で判別できます。

```
# avrdude -c linuxgpio -p m328p -U flash:w:blink.hex:i
```

特にエラーとかが出なければ書き込みは成功です。
以下のようにPB0からledを接続するとおよそ1秒おきに点滅するはずです。

![led4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406753/7513805b-0660-a76e-e25e-4a48ef52d718.png)

以上でraspberry piからavrマイコンにファームをアップロードできるようになりました。
avr単体で使用する以外にもUSBのついてないarduino pro miniなどに書き込んだり、arduinoにブートローダーを書き込んだりも可能です。