Raspberry Pi でAVRマイコンのシグネチャーバイトを読み出す

C言語でGPIOを制御して、AVRマイコンをシリアルプログラミングモードにします。シグネチャーバイト（チップの種類固有の値）を読み取ってみましょう。

Raspberry Pi で、AVR書き込みプログラムを作成しようと思っている方が最初につまずくのが、AVRマイコンから何の反応も返ってこなくて、通信できているかわからないケースでしょう。最初のシグネチャーバイトの読み取りさえ成功すれば、あとは、データシートのコマンド表を見ながら、チップの消去や、書き込み、読み取りを実装することができます。

## 配線

Raspberry Pi とAVRマイコンをつなぎます。

![board.png](https://qiita-image-store.s3.amazonaws.com/0/61222/76765c87-e377-5ab8-a18e-b7c770071cb2.png)

**(2019-07-14)** この図には間違いがります。図中でMOSIとMISOが逆になっています。ソースコードでは、「`#define ISP_MISO 2`」「`#define ISP_MOSI 3`」と定義されており、こちらが正解です。GPIO2はMISO、GPIO3はMOSIにつなぐ必要がありますので、図中の青と緑の線は逆に接続してください。

![wiring.jpg](https://qiita-image-store.s3.amazonaws.com/0/61222/ba494bce-27f4-3959-0094-223eb541d35a.jpeg)

## 作業ディレクトリ

名前は何でも良いですが、とりあえず、`avrsig`ということにします。

```
$ mkdir avrsig
$ cd avrsig
```


## GPIO制御ライブラリ

下記サイトで公開されているBCM2835ライブラリを用いて、GPIOを制御します。
http://www.airspayce.com/mikem/bcm2835/

```
$ wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.52.tar.gz
$ tar zxvf bcm2835-1.52.tar.gz
```

中に含まれている src ディレクトリを確認します。

```
pi@raspberrypi:~ $ ls bcm2835-1.52/src/
bcm2835.c  bcm2835.h  Makefile.am  Makefile.in  test.c
```

このうち bcm2835 で始まるファイルを、作業ディレクトリにコピーします。

```
$ cp bcm2835-1.52/src/bcm2835.* avrsig
```

## メインプログラム

次のようなファイルを作成します。

``` main.c
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include "bcm2835.h"

#define ISP_MISO   2
#define ISP_MOSI   3
#define ISP_SCK    4
#define ISP_RESET 14

struct Signature {
	uint8_t a, b, c;
};

struct Fuse {
	uint8_t l, h;
};

void usleep(unsigned int us)
{
	struct timespec ts;
	ts.tv_sec  = (time_t)(us / 1000000);
	ts.tv_nsec = (long)(us % 1000000) * 1000;
	nanosleep(&ts, NULL);
}

void msleep(unsigned int ms)
{
	struct timespec ts;
	ts.tv_sec  = (time_t)(ms / 1000);
	ts.tv_nsec = (long)(ms % 1000) * 1000000;
	nanosleep(&ts, NULL);
}

int digitalRead(int pin)
{
	return bcm2835_gpio_lev(pin);
}

void digialWrite(int pin, int val)
{
	bcm2835_gpio_write(pin, val);
}

uint8_t spi_transfer(uint8_t c)
{
	int i;
	for (i = 0; i < 8; i++) {
		digialWrite(ISP_MOSI, c & 0x80);
		digialWrite(ISP_SCK, HIGH);
		usleep(100);
		c <<= 1;
		if (digitalRead(ISP_MISO) != LOW) {
			c |= 1;
		}
		digialWrite(ISP_SCK, LOW);
		usleep(100);
	}
	return c;
}

uint8_t transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
	spi_transfer(a);
	spi_transfer(b);
	spi_transfer(c);
	return spi_transfer(d);
}

void waitProgramming()
{
	while (transaction(0xf0, 0, 0, 0) & 1) {
		msleep(1);
	}
}

void reset()
{
	bcm2835_gpio_fsel(ISP_MISO, BCM2835_GPIO_FSEL_INPT);
	bcm2835_gpio_fsel(ISP_MOSI, BCM2835_GPIO_FSEL_INPT);
	bcm2835_gpio_fsel(ISP_SCK, BCM2835_GPIO_FSEL_INPT);
	bcm2835_gpio_fsel(ISP_RESET, BCM2835_GPIO_FSEL_OUTP);

	digialWrite(ISP_RESET, LOW);
	msleep(10);
	digialWrite(ISP_RESET, HIGH);

	bcm2835_gpio_fsel(ISP_RESET, BCM2835_GPIO_FSEL_INPT);
}

void enterProgrammingMode()
{
	bcm2835_gpio_fsel(ISP_MISO, BCM2835_GPIO_FSEL_INPT);
	bcm2835_gpio_fsel(ISP_MOSI, BCM2835_GPIO_FSEL_OUTP);
	bcm2835_gpio_fsel(ISP_SCK, BCM2835_GPIO_FSEL_OUTP);
	bcm2835_gpio_fsel(ISP_RESET, BCM2835_GPIO_FSEL_OUTP);

	digialWrite(ISP_RESET, LOW);
	digialWrite(ISP_SCK, LOW);
	msleep(20);
	digialWrite(ISP_RESET, HIGH);
	usleep(100);
	digialWrite(ISP_RESET, LOW);
	msleep(50);

	transaction(0xac, 0x53, 0x00, 0x00);
}

struct Signature readSignature()
{
	struct Signature sig;
	sig.a = transaction(0x30, 0x00, 0x00, 0x00);
	sig.b = transaction(0x30, 0x00, 0x01, 0x00);
	sig.c = transaction(0x30, 0x00, 0x02, 0x00);
	return sig;
}


struct Fuse readFuse()
{
	struct Fuse f;
	f.l = transaction(0x50, 0x00, 0x00, 0x00);
	f.h = transaction(0x50, 0x08, 0x00, 0x00);
	return f;
}

uint8_t readProgramL(int i)
{
	return transaction(0x20, 0x00, i, 0x00);
}

uint8_t readProgramH(int i)
{
	return transaction(0x28, 0x00, i, 0x00);
}

int main()
{
	bcm2835_init();

	enterProgrammingMode();

	struct Signature sig = readSignature();
	printf("Signature: %02x %02x %02x\n", sig.a, sig.b, sig.c);

	struct Fuse f = readFuse();
	printf("     Fuse: %02x %02x\n", f.h, f.l);

	reset();

	bcm2835_close();

	return 0;
}
```

## コンパイル

Makefileを作成します。

``` 
all: avrsig

avrsig: main.o bcm2835.o
	cc $^ -o $@
```

メイクします。

```
$ make
```

## 実行

```
pi@raspberrypi:~/avrsig $ ./avrsig 
Signature: 1e 95 0f
     Fuse: ff e2
```

AVRマイコンのチップ型番ごとに異なるシグネチャーバイトが取得できます。どんな値かは、AVRマイコンのデータシートに記載されています。

## AVR開発環境

raspbian の標準パッケージにコンパイラとライブラリが用意されていますので、Raspberry Pi だけでAVRマイコンの開発環境を構築できます。

```
$ sudo apt-get install gcc-avr avr-libc
```