AVR64DD28 で LED をチカチカさせる (Microchip Studio, AVRDUDESS)

# 1: はじめに

Arduino に搭載されている ATmega328P のような 8bit AVR マイコンの新しいシリーズとして、AVR Dx シリーズ（DA, DB, DD, DE）があります。

秋月電子通商で [AVR64DD28](https://akizukidenshi.com/catalog/g/g118314/) を買ったので、LEDをチカチカさせるまでを試してみました。Windows 環境で Arduino を使わずに試しています。

ATmega328P からの移行を考えている方の参考になるよう、比較しながら解説しています。XMEGA は触ったことがないので、特別に新しくない機能を解説している可能性はあります。

## 1-1: 従来と比べて感動した点

1. 専用書込み器が必要なくなり、USB-UART 変換アダプタでプログラムを書き込めるようになった (ISP -> UPDI)
1. 外部クリスタルの変更をヒューズビットではなく、プログラムから行えるようになった
1. GPIO に対するRead-Modify-Write 操作が、アセンブラを書かなくても1クロックで可能になった

## 1-2: 下調べ

AVR DD (AVR64DD28) について、インターネットで簡単に調べてみると、次のような記事がヒットしました。

* ねむいさんのぶろぐ - [秋月発売記念！AVR64DD28を使ってみる！](https://nemuisan.blog.bai.ne.jp/?eid=244270)
  * プログラムROMの話から専門的な話題でまとめられています
  * avr-gcc を直接コマンドラインで扱っており、ちょっと難しそうです
* ikkei blog - [AVR64DD28でLチカをやってみた](https://blog.goo.ne.jp/jh3kxm/e/f4cb5f93ac9baa14dc2ae665a61cb2db)
  * 書込み方法から簡単にまとめられています
  * Arduino IDE から DxCore というライブラリ？を使ってプログラミングしています
  * 私は avr-gcc で教えられた人間なので、Arduino は宗教上の理由から使えません……
* nike note.com - [マイコンde学習しよう（マイコン編）AVR128DA28](https://note.com/nike_purin/n/nf6a2f528b285)
  * 私は有料部分を読んでいませんが、分かりやすそうです

プログラミングのときに使えそうな一次・二次資料は次のとおりです。

* Microchip AVR64DD28 公式情報
  * [製品ページ](https://www.microchip.com/en-us/product/AVR64DD28#document-table)
  * [Complete データシート (DS40002315C)](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/AVR64DD32-28-Complete-DataSheet-DS40002315.pdf)
* avr.jp 非公式日本語データシート
  * [avr.jp データシート一覧](https://avr.jp/user/ds.htm)
  * 少し癖はありますが、有志による日本語訳データシートがあります
  * 直接のリンクが禁止されているため、一覧から AVR64DD28 を検索し、アクセスしてください

# 2: ハードウェア

## 2-1: ピン配置について

DIP 版 (AVR64DD28-I/SP) のピン配置を参考に、ブレッドボードなどに回路を準備します。

2.1 28-Pin SPDIP, SSOP and SOIC (データシート p.14 より引用)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/0e810929-929e-2883-9441-f2c6eab429f3.png)

ポイントは次の通りです。

* 電源ピンも含めて、ATmega328P とはピン配置が異なる
* 電源は VDD (2ピン) と VDDIO2 に必要
  * VDDIO2 を使えば一部のピンを VDD とは別電圧で制御できる
  * MVIO (Multi-Voltage I/O) 機能と呼ばれ、「マイコンは 5V で動かしつつ 3.3V センサを利用する」ときに便利
  * MVIO 機能を使わず、単一電源でも VDDIO2 に配線は必要 (データシート p.27)
* 通常 PF7 (19, UPDI) はプログラム書込み時に利用するので I/O ピンとしては使用不可
* 通常 PF6 (18, ~RESET) はリセットピンのため利用不可

## 2-2: UPDI 書込みについて

従来の AVR (ATmega328P, ATtiny2313 など) は ISP (In-System Programming) で書き込むため、プログラム書込みのための信号線が4本必要でした。
Atmel が Microchip に買収後、発売された新しい AVR は UPDI (Unified Program and Debug Interface) で書き込むため、信号線は1本で済みます。

UPDI は、USB-UART 変換アダプタを流用した SerialUPDI で代替することができます。次の記事を参考に配線します。

* なんでも独り言 - [Serial UPDI プログラマを作りました](https://ehbtj.com/electronics/make-serial-updi-programmer/)
  * ショットキーバリアダイオード (SBD) と抵抗が別途必要になり、半二重方式で通信するようです
  * 記事では Arduino IDE を書込みソフトとして利用していましたが、私は AVRDUDESS (avrdude) を利用しています（後述）

以下は引用画像です。
![引用画像](https://ehbtj.com/wp/wp-content/uploads/2023/09/4f62c9c1f47bb42ca77717a1a5b20c77.png)

## 2-3: 今回の配線について

今回は次のように配線しました。

* 5V 単一電源のため VDDIO2 (6), VDD (14, 20), GND (15, 21) を接続
* PA7 (PORTA 7番) に LED を電流制限抵抗経由で接続
* PF7 は自作の UPDI 書込み器と接続

あまりきれいに撮影していませんが、このような感じでブレッドボードに配線しています。USB-UART 変換アダプタは [秋月電子通商 AE-UM232R](https://akizukidenshi.com/catalog/g/g101977/) を使用していますが、何でも動くと思います。

![02EE597F-B9E0-4987-BA38-3ABDA7048FDE.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/288fe314-693a-60af-cc89-50a1a83db5dc.jpeg)

# 3: ソフトウェア

## 3-1: ビルド環境 Microchip Studio インストール

今回 Arduino IDE は使わず、Microchip Studio (旧 Atmel Studio) をビルド環境として利用します。Visual Studio がベースになっているソフトのようです。(Linux, macOS の方は使えません。すみません。）

インストール作業は次の Qiita 記事が参考になります。

https://qiita.com/toshidln/items/d811cfa0548d70926f89

## 3-2: プロジェクト作成

インストールが終わったら、プロジェクトを作成するため "New Project" を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/c3ccd2b8-4bc4-abd6-08c2-afae54f2c2df.png)

プロジェクトの種別は "GCC C Executable Project" を選択します。（C++ は使ったことないので分かりません）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/38e6c2cd-f9fc-7c5c-1b08-63db21ab49f7.png)

使用するマイコンの型番 (今回は AVR64DD28) を選択します。右上のテキストボックスから検索できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/ae64cf3b-ef13-b2a0-b3e1-81f29d911f01.png)

レジスタ情報が入った avr/io.h のインクルードと、メイン関数だけが書かれたプロジェクトが完成しました。

## 3-3: LED チカチカプログラム

LED を 1Hz でチカチカさせるためのプログラムは次のとおりです。

```c: main.c
#define F_CPU 4000000UL

#include <avr/io.h>
#include <util/delay.h>

void clock_init(void) {
	// CCP 保護解除
	CCP = CCP_IOREG_gc;
	// OSCHF クロック周波数設定
	CLKCTRL.OSCHFCTRLA = CLKCTRL_FRQSEL_4M_gc;
	// クロック安定化待機
	while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_OSCHFS_bm));
}

int main(void)
{
	// クロック初期化設定
	clock_init();
	
	// ポート出力設定
	PORTA.DIRSET = 0b10000000;

    while (1) {
		// ポート出力 反転
		PORTA.OUTTGL = 0b10000000;
		_delay_ms(500);
    }
}
```

以下はプログラムの解説です。

### 3-3-1: GPIO 出力について

clock_init 関数の説明は後回しにして、main 関数に書いた LED をチカチカさせるプログラムについて解説します。

#### GPIO 入出力方向設定
LED が接続されているポート PA7 を出力方向に設定するのが次のコードです。
**PA7 のビットのみ** を 1 (出力) に設定しています。

```c: 新しいレジスタアクセス
PORTA.DIRSET = 0b10000000;  // PA7 ビットのみ 1 にセット
```

:::note info
従来の AVR では、入出力方向を設定するレジスタ DDRx を使い、次のようにビット演算をしていました。

```c: 従来のレジスタアクセス
DDRA |= 0b10000000;  // PA7 ビットのみ 1 にセット
```

従来はレジスタ名で直接アクセスしていましたが、 `周辺機能名.レジスタ名` でアクセスするようになりました。`周辺機能名_レジスタ名` でアクセスすることもできます。
よって、従来の DDRx は PORTx.DIR または PORTx_DIR としてアクセスすることになります。

また、レジスタ名の後ろに SET・CLR・TGL がついたレジスタは、Read-Modify-Write 操作をすることなく、1クロックで設定できます。

詳細はデータシート 6.4 Registers and Bits (p.30) を参照してください。

```c: 新しいレジスタアクセス
PORTA.DIR = 0b10000000;  // 指定ビット列に一括指定
PORTA_DIR = 0b10000000;  // 上に同じ意味

PORTA.DIRSET = 0b10000000;  // PA7 ビットのみ 1 にセット
PORTA_DIRSET = 0b10000000;  // 上と同じ意味
PORTA_DIR   |= 0b10000000;  // 上と同じ意味だが遅い

PORTA.DIRCLR = 0b10000000;  // PA7 ビットのみ 0 にセット
PORTA_DIRCLR = 0b10000000;  // 上に同じ意味
PORTA_DIR   &= 0b01111111;  // 上に同じ意味だが遅い

PORTA.DIRTGL = 0b10000000;  // PA7 ビットのみ反転（トグル）
PORTA_DIRTGL = 0b10000000;  // 上に同じ意味
PORTA_DIR   ^= 0b10000000;  // 上に同じ意味だが遅い
```
:::

:::note info
Read-Modify-Write 操作（読取-変更-書込操作）は、次のコードをイメージすると分かりやすいかと思います。
先ほど出てきた `PORTA_DIR |= 0b10000000;` は、次のコードに書き換えられます。

```c: Read-Modify-Write
PORTA_DIR = PORTA_DIR | 0b10000000;
```

レジスタ PORTA_DIR の値を **読み取り** 、特定ビットを1にする **変更** を行い、レジスタ PORTA_DIR に値を **書き込んで** います。
従来は処理に3クロック必要でしたが、これらが1クロックで可能になりました。
（従来も1ビットだけなら、アセンブラの SBI/CBI 命令で可能ではあった）
:::

#### GPIO 出力設定

実際にピンの High/Low を切り替え、LEDをチカチカさせているのは次のコードです。

```c
PORTA.OUTTGL = 0b10000000;
PORTA.OUT   ^= 0b10000000;  // このように書いても同じ意味
```

### 3-3-2: クロックについて

AVR64DD28 はデフォルトで 4MHz で動作しています。従来は、内部RC発振回路で生成したクロックを8分周するかどうかぐらいしか設定できませんでしたが、内部でさまざまなクロックを生成し、分周させ、 **実行中に切り替えて** 使うことが可能になりました。
（他の 32bit マイコンだと当たり前にできるかもしれませんが……）

ブロック図をデータシート p.91 (12 CLKCTRL - Clock Controller) より引用 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/3d073632-d191-6a11-f70f-ecf5aa391fc0.png)

クロック源は4種類あります。
* OSCHF (内部 1 ~ 24 MHz、デフォルト 4MHz）
  * 1 / 2 / 3 / 4 / 8 / 12 / 16 / 20 / 24 MHz から選択できます（1, 2, 3 と 4 の倍数）
* OSC32K (内部 32.768Hz)
* XOSCHF (外部クリスタル)
* XOSC32K (外部 32.768Hz クリスタル)

メインクロックに関わるブロック図をデータシート p.92 (12 CLKCTRL - Clock Controller) より引用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/428c857a-d145-f3e6-1b3a-39813691d199.png)

OSCHF の周波数を変更したいときは、レジスタ OSCHFCTRLA の一部を変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/553943b4-a4e5-6c3e-96ed-1b8a9a09ca9f.png)

最高速の 24MHz で動かす場合は、次のようにレジスタを設定することになります。
```c
CLKCTRL.OSCHFCTRLA = 0x9 << 2;
```

C言語ヘッダファイルにて `CLKCTRL_FRQSEL_enum` が宣言されており、定数 `CLKCTRL_FRQSEL_24M_gc` を利用することもできます。よって、次のように設定することになります。

```c
CLKCTRL.OSCHFCTRLA = CLKCTRL_FRQSEL_24M_gc;
```

また、データシートのレジスタ定義の Property に "Configuration Change Protection" と記載されている場合は、レジスタに値を書き込む直前（4クロック以内）に設定変更保護 (CCP機能) を解除する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/3c23a413-19d5-9437-1099-7e947253e980.png)

重要なレジスタは、CCP による保護がかかっている場合がありますので、値を変更する直前に `CCP = CCP_IOREG_gc;` を挿入し、毎回解除する必要があります。

クロック周波数の変更を clock_init 関数にまとめると次のようなコードになります。

```c: クロック周波数の変更
void clock_init(void) {
	// CCP 保護解除
	CCP = CCP_IOREG_gc;
	// OSCHF クロック周波数設定
	CLKCTRL.OSCHFCTRLA = CLKCTRL_FRQSEL_4M_gc;  // 今回は 4MHz そのまま
	// クロック安定化待機
	while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_OSCHFS_bm));
}
```

CCP の詳細は、データシート 7.4.6 Configuration Change Protection (p.39) を参照してください。

### 3-3-3: ディレイ

先ほどまでは LED を点滅させるピン設定とクロック周波数の設定方法を見てきました。
人間が点滅して見えるようにするには、プログラムを遅延させる（Delay, 待たせる）必要がありますので、ソフトウェアディレイを確認します。

"util/delay.h" ファイルをインクルードすると、ミリ秒単位でディレイできる `_delay_ms` 関数が使えるようになりますが、インクルード前にマクロ `F_CPU` でクロック周波数を宣言する必要があります。

```
// util/delay.h のインクルード前に宣言すること
#define F_CPU 4000000UL  // 4MHz

#include <avr/io.h>
#include <util/delay.h>  // _delay_ms 関数などが使えるようになる
```

詳細は avr-libc の util/delay.h リファレンスを確認してください。
* [有志による日本語訳リファレンス](https://cega.jp/avr-libc-jp/group__util__delay.html)
* [英語リファレンス](https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html)

## 3-4: プログラムのビルド

LED チカチカプログラムを main.c に記述したら、プログラムをビルドします。ビルド作業によって C 言語のコードが AVR マイコンの機械語に変換され、マイコンに書き込めるようになります。
メニューバー [Build] > [Build Solution] を選択し、出力を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/83b917e5-f498-6bb8-2c84-14e1ab9cedf9.png)

Output パネルに、"Build Succeeded." と表示されたら、成功です！

AVR64DD28 には ROM 64KB, RAM 8KB が搭載されていますので、余裕がありますね。
```
				Program Memory Usage 	:	228 bytes   0.3 % Full
				Data Memory Usage 		:	0 bytes   0.0 % Full
```

<details>
  <summary>ものすごく長いログをすべて表示したい方はこちらをクリック</summary>

私のPCのディレクトリ構造がバレてますが、問題はないと判断しています。
```
------ Build started: Project: first, Configuration: Debug AVR ------
Build started.
Project "first.cproj" (default targets):
Target "PreBuildEvent" skipped, due to false condition; ('$(PreBuildEvent)'!='') was evaluated as (''!='').
Target "CoreBuild" in file "C:\Program Files (x86)\Atmel\Studio\7.0\Vs\Compiler.targets" from project "C:\Users\user\Documents\TempProject\20241013_AVR64DD28\first\first\first.cproj" (target "Build" depends on it):
	Task "RunCompilerTask"
		Shell Utils Path C:\Program Files (x86)\Atmel\Studio\7.0\shellUtils
		C:\Program Files (x86)\Atmel\Studio\7.0\shellUtils\make.exe all --jobs 12 --output-sync 
		Building file: .././main.c
		Invoking: AVR/GNU C Compiler : 5.4.0
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe"  -x c -funsigned-char -funsigned-bitfields -DDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\AVR-Dx_DFP\1.10.114\include"  -Og -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -g2 -Wall -mmcu=avr64dd28 -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\AVR-Dx_DFP\1.10.114\gcc\dev\avr64dd28" -c -std=gnu99 -MD -MP -MF "main.d" -MT"main.d" -MT"main.o"   -o "main.o" ".././main.c" 
		Finished building: .././main.c
		Building target: first.elf
		Invoking: AVR/GNU Linker : 5.4.0
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe" -o first.elf  main.o   -Wl,-Map="first.map" -Wl,--start-group -Wl,-lm  -Wl,--end-group -Wl,--gc-sections -mmcu=avr64dd28 -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\AVR-Dx_DFP\1.10.114\gcc\dev\avr64dd28"  
		Finished building target: first.elf
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -O ihex -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures  "first.elf" "first.hex"
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -j .eeprom  --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0  --no-change-warnings -O ihex "first.elf" "first.eep" || exit 0
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objdump.exe" -h -S "first.elf" > "first.lss"
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -O srec -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures "first.elf" "first.srec"
		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-size.exe" "first.elf"
		   text	   data	    bss	    dec	    hex	filename
		    228	      0	      0	    228	     e4	first.elf
	Done executing task "RunCompilerTask".
	Task "RunOutputFileVerifyTask"
				Program Memory Usage 	:	228 bytes   0.3 % Full
				Data Memory Usage 		:	0 bytes   0.0 % Full
				Warning: Memory Usage estimation may not be accurate if there are sections other than .text sections in ELF file
	Done executing task "RunOutputFileVerifyTask".
Done building target "CoreBuild" in project "first.cproj".
Target "PostBuildEvent" skipped, due to false condition; ('$(PostBuildEvent)' != '') was evaluated as ('' != '').
Target "Build" in file "C:\Program Files (x86)\Atmel\Studio\7.0\Vs\Avr.common.targets" from project "C:\Users\user\Documents\TempProject\20241013_AVR64DD28\first\first\first.cproj" (entry point):
Done building target "Build" in project "first.cproj".
Done building project "first.cproj".

Build succeeded.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
```

</details>

# 4: プログラム書込み

ビルドが成功すると、プロジェクトフォルダ内の Debug の中に機械語ファイル (.elf や .hex ファイル) が生成されます。
このファイルのどちらか一方を AVR マイコンに書き込むことで、LED をチカチカできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/c77fa6db-bf08-eb76-4233-bcaaa1168379.png)

今回は SerialUPDI 経由で書き込みますが、純正の書込みライターではないので、Microchip Studio の機能は使えません。
Arduino IDE でも書き込むことはできるようですが、ここでは avrdude コマンドの GUI アプリ版である AVRDUDESS を使って書き込んでみます。

## 4-1: AVRDUDESS (avrdude) の導入

AVRDUDESS は、[ZakKemble](https://github.com/zakkemble) 氏によって開発されているアプリです。

* GitHub ZakKemble/AVRDUDESS
  * [リポジトリ](https://github.com/ZakKemble/AVRDUDESS)
  * [ダウンロードページ](https://github.com/ZakKemble/AVRDUDESS/releases)

ダウンロードページにアクセスし、特にこだわりなければ "～～-setup.exe" (recommended, 推奨) をインストールします。（ダウンロード数が少ないアプリだからか、セキュリティの警告が出ました）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/9e32febd-65c5-d934-fac8-f4cce60db36c.png)

AVRDUDESS の GUI は日本語でも使えます。（すごくありがたい！）
ウィンドウ中央やや下部にある Option ボタンから Language を日本語設定に変更できます。（アプリの再起動が必要かも）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/0dc77501-55b8-2fe9-db88-6e87667dc155.png)

## 4-2: AVR への書込み

USB-UART 変換アダプタによる UPDI 接続や、電源が供給されていることを確認したら、AVRDUDESS を設定して書き込んでいきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125577/924c11ff-5a2e-7c2e-deaa-0f167e3abbc2.png)

* `MCU (-p)` で書込み先のマイコンを設定する
  * 今回は AVR64DD28 を設定します
  * 試したところ、検出ボタンは機能しませんでした
* `プログラマー (-c)` で書込みライターを設定する
  * プルダウンより "serialupdi (SerialUPDI)" を選択します
  * ポートは、USB-UART 変換アダプタの認識ポートを指定します
    今回は Windows なので、COM1とかCOM2などの番号になります
  * ボーレートは、UART 通信速度（ボーレート）を指定します
    * 今回は 225000 bps を指定していますが、好きな通信速度を設定できるようです
      本来、UART は調歩同期方式なので、好きな通信速度を設定できるのはすごいことなのでは？
* `フラッシュ` で、機械語コードファイル (.elf か .hex) を指定します
  * プロジェクトフォルダ内に生成されているはずです

設定できたら、太字で強調された「プログラム！」ボタンをクリックすると、書き込まれるはずです。

```
# avrdude コマンド直打ちするとき
avrdude -c serialupdi -p 64dd28 -P COM4 -b 225000 -U flash:w:"hexファイルパス":a 
```

成功すれば、LED が 1Hz で点滅（0.5秒消灯、0.5秒点灯）しているはずです！
この記事を読んだ方の電子工作がうまく行くことをお祈りしています。