Advent Calendar 2025LinuxClub技術ATtiny10マイコン

ATtiny10の環境構築とLチカまで

ATtiny10の環境の構築とサンプルプログラムの解説

ATtiny10の環境構築とLチカまで

この記事は TUT (powered by LinuxClub) Advent Calendar 2025 の13日目の記事です。

はじめに

あいさつ

12月13日となり、クリスマスまで2週間を切りましたね。今回は、すみさんのアドベントカレンダー第一弾として、クリスマスにちなんだ内容をやりたいと思いました。

そこで、「クリスマス→イルミネーション→LED→Lチカ」 ということで、前回のサークルで行ったLT会のスライドをベースに、ATtiny10の環境構築からLチカまでを解説することにしました。

以下は、LT会のスライドです。

ATtiny10とは

ATtiny10 とは、Microchip Technology社(旧Atmel社)が提供している「tinyAVR」シリーズに属するマイコンです。

スペックを簡単に見ると以下の通りです。

項目仕様備考
コア8bit AVR (Reduced)汎用レジスタは16個 (r16〜r31)
ROM (Flash)1 kBプログラム領域 1024Bまで
RAM (SRAM)32 Bytes変数領域 ちっちゃい
クロック8MHz (最大12MHz)内蔵オシレーター、デフォルト1MHz
動作電圧1.8V〜5.5V幅広い電源電圧に対応
GPIO4本電源ピンを除くと4本のみ
パッケージSOT23-62mm × 3mm程度
ATtiny10
ATtiny10 (SOT23-6パッケージ)

このように、ATtiny10はパッケージサイズ・RAM・ROMのすべてにおいて極めて小さいのが特徴です。

環境構築

Microchip Studioのインストール

ATtiny10の開発には、Microchip社が提供する統合開発環境(IDE)であるMicrochip Studio(旧Atmel Studio)を使用します。

  1. Microchip社の公式サイト からインストーラーをダウンロードする。
  2. インストーラーを起動し、指示に従ってインストールを進める。
  3. アーキテクチャの選択画面では、「AVR 8-bit MCUs」にチェックが入っていることを確認する。

書き込み装置 (TPI) の準備

ATtiny10へのプログラム書き込みには、TPI (Tiny Programming Interface) というプロトコルを使用します。 ATtiny10は足が6本しかなく書き込み方式が特殊なため、今回はArduino Unoを書き込み装置として利用する方法を採用します。

Arduinoを書き込み装置にする

Arduino UnoをTPIプログラマとして動作させるためのスケッチをArduinoIDEやPlatformIOで書き込みます。 今回は bdpdx/ATtiny10Programmer のコードを使用します。

  1. 上記リポジトリからZIPファイルをダウンロードするか、git clone してコードを入手する。
  2. ATtiny10Programmer.ino をArduino IDEやPlatformIOで開く。
  3. ボード選択欄でArduino Unoを選択し、スケッチを書き込む。

配線(書き込み回路)

ArduinoとATtiny10を以下のように接続します。

注意: Arduino D11とATtiny10のTPIDATAの間には 2.2kΩの抵抗 が必要です。

Arduino PinATtiny10 Pin機能備考
D106 (PB3)RESET
D111 (PB0)TPIDATA抵抗を経由して接続
D121 (PB0)TPIDATA
D133 (PB1)TPICLK
5V5 (VCC)VCC
GND2 (GND)GND

※ 安定動作のために、ATtiny10のVCC-GND間に0.1μF〜100μF程度のパスコンを入れることを推奨します。

ATtiny10書き込み回路
Arduinoを使った書き込み回路

書き込み手順

  1. Microchip Studioでビルドして生成された .hex ファイルの内容をすべてコピーする。(ファイルはプロジェクトフォルダ内の Debug または Release フォルダに出力されます。)
  2. 書き込み装置化したArduinoをPCに接続し、シリアルモニタを開く。(ポーレートは115200bps)
  3. シリアルモニタに Command [d,e,i,m,u,v,?] > といったメニューが表示されていることを確認する。
  4. i を送信して接続確認を行う。ATtiny10 connected と表示されれば配線成功。
  5. 再度メニューが表示されたら u を送信してアップロードモードに入る。Upload hex file content to serial monitor と表示される。
  6. 入力欄にコピーしたhexファイルの内容をそのまま貼り付けて送信する。
  7. Writing flash に続いて各行が OK と表示され、すべて完了したら書き込み成功。
ATtiny10への書き込み成功画面
書き込み成功時のシリアルモニタ出力

サンプルプログラム解説

環境構築と書き込みができたら、実際に動くコードを見てみましょう。 今回のサンプルでは、アセンブリを用いて単純なON/OFFではなく「正弦波テーブルに合わせてPWM制御をする」というところまで実装しています。

実際に動作させた様子が以下の動画で、LEDが正弦波に合わせてなめらかに明滅します。

以下でこのコードを解説していきます。サンプルコードは SmiSANN/attiny10-asm-templates-01 リポジトリで公開しています。

LED接続について: このサンプルでは PB0(1番ピン)PB1(3番ピン) がPWM出力ピンになります。各ピンに適切な抵抗(330Ω〜1kΩ程度)を介してLEDを接続してください。

Lチカ(正弦波PWM)のコード

このプログラムは、Flashメモリ上に配置した正弦波データを順次読み出します。その値をPWMのデューティ比として出力することで、LEDの明るさを滑らかに変化させています。

1;
2; AssemblerProject1_Optimized.asm
3; Created: 2025/11/28 17:46:57
4; Author : smi
5; Target: ATtiny10
6; Clock: 8MHz (Derived from code)
7; Function: PWM Waveform Generator (Likely Sine wave on PB0/PB1)
8
9.device ATtiny10
10.cseg
11.org 0x0000
12
13RESET:
14 ; --- システムクロック設定 (8MHz) ---
15 LDI r16, 0xD8 ; CCP書き込みキー
16 OUT CCP, r16
17 LDI r16, 0x00 ; 分周なし
18 OUT CLKPSR, r16
19
20 ; --- スタックポインタ設定 ---
21 OUT SPH, r16 ; SPH = 0
22 LDI r16, low(RAMEND)
23 OUT SPL, r16
24
25 ; --- I/O設定 ---
26 ; PB0=OC0A, PB1=OC0B (タイマー出力ピン)として出力設定
27 LDI r16, 0x03 ; PB0, PB1を出力設定 (0b00000011)
28 OUT DDRB, r16
29
30 ; --- タイマー0設定 (Phase Correct PWM, 8-bit) ---
31 ; COM0A1:0=10, COM0B1:0=10 (非反転), WGM01:0=01
32 LDI r16, 0xA1
33 OUT TCCR0A, r16
34 ; WGM03:2=00, CS02:0=001 (分周なし)
35 LDI r16, 0x01
36 OUT TCCR0B, r16
37
38 ; --- ポインタ初期化 ---
39 ; Flash先頭(0x4000) + データオフセット(0x0100) = 0x4100
40 LDI XH, 0x41
41 LDI XL, 0x00 ; Phase 0
42
43 LDI YH, 0x41
44 LDI YL, 0x55 ; Phase +85 (約120度位相差)
45
46 ; --- メインループ ---
47loop:
48 ; データ読み出し (ロード & インクリメント)
49 LD r16, X+
50 LD r17, Y+
51
52 ; PWM更新
53 OUT OCR0AL, r16 ; PB0
54 OUT OCR0BL, r17 ; PB1
55
56 ; --- ディレイ処理 (明滅を視認可能にする) ---
57 ; 8MHz動作時、約6ms程度のディレイ
58 LDI r19, 64 ; 外側ループカウンタ
59delay_outer:
60 LDI r20, 255 ; 内側ループカウンタ
61delay_inner:
62 DEC r20
63 BRNE delay_inner ; r20 != 0 ならループ
64 DEC r19
65 BRNE delay_outer ; r19 != 0 ならループ
66
67 ; ポインタ補正 (256バイトループ)
68 ; X+, Y+で下位バイトがオーバーフロー(FF->00)すると上位バイト(XH/YH)が
69 ; インクリメント(41->42)されてしまうため、強制的に0x41に戻す
70 LDI r18, 0x41
71 MOV XH, r18
72 MOV YH, r18
73
74 RJMP loop
75
76; --- 波形データ (256バイト) ---
77.org 0x0080 ; Byte Address 0x0100
78SineTable:
79 .db 0x7F, 0x82, 0x85, 0x88, 0x8B, 0x8F, 0x92, 0x95
80 .db 0x98, 0x9B, 0x9E, 0xA1, 0xA4, 0xA7, 0xAA, 0xAD
81 .db 0xB0, 0xB3, 0xB6, 0xB8, 0xBB, 0xBE, 0xC1, 0xC3
82 .db 0xC6, 0xC8, 0xCB, 0xCD, 0xD0, 0xD2, 0xD5, 0xD7
83 .db 0xD9, 0xDB, 0xDD, 0xE0, 0xE2, 0xE4, 0xE5, 0xE7
84 .db 0xE9, 0xEB, 0xEC, 0xEE, 0xEF, 0xF1, 0xF2, 0xF4
85 .db 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFB
86 .db 0xFC, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE
87 .db 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD
88 .db 0xFC, 0xFB, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6
89 .db 0xF5, 0xF4, 0xF2, 0xF1, 0xEF, 0xEE, 0xEC, 0xEB
90 .db 0xE9, 0xE7, 0xE5, 0xE4, 0xE2, 0xE0, 0xDD, 0xDB
91 .db 0xD9, 0xD7, 0xD5, 0xD2, 0xD0, 0xCD, 0xCB, 0xC8
92 .db 0xC6, 0xC3, 0xC1, 0xBE, 0xBB, 0xB8, 0xB6, 0xB3
93 .db 0xB0, 0xAD, 0xAA, 0xA7, 0xA4, 0xA1, 0x9E, 0x9B
94 .db 0x98, 0x95, 0x92, 0x8F, 0x8B, 0x88, 0x85, 0x82
95 .db 0x7F, 0x7C, 0x79, 0x76, 0x73, 0x6F, 0x6C, 0x69
96 .db 0x66, 0x63, 0x60, 0x5D, 0x5A, 0x57, 0x54, 0x51
97 .db 0x4E, 0x4B, 0x48, 0x46, 0x43, 0x40, 0x3D, 0x3B
98 .db 0x38, 0x36, 0x33, 0x31, 0x2E, 0x2C, 0x29, 0x27
99 .db 0x25, 0x23, 0x21, 0x1E, 0x1C, 0x1A, 0x19, 0x17
100 .db 0x15, 0x13, 0x12, 0x10, 0x0F, 0x0D, 0x0C, 0x0A
101 .db 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x03
102 .db 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
103 .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02
104 .db 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09
105 .db 0x0A, 0x0C, 0x0D, 0x0F, 0x10, 0x12, 0x13, 0x15
106 .db 0x17, 0x19, 0x1A, 0x1C, 0x1E, 0x21, 0x23, 0x25
107 .db 0x27, 0x29, 0x2C, 0x2E, 0x31, 0x33, 0x36, 0x38
108 .db 0x3B, 0x3D, 0x40, 0x43, 0x46, 0x48, 0x4B, 0x4E
109 .db 0x51, 0x54, 0x57, 0x5A, 0x5D, 0x60, 0x63, 0x66
110 .db 0x69, 0x6C, 0x6F, 0x73, 0x76, 0x79, 0x7C, 0x7F

機能ごとの解説

Note: 以下の解説は ATtiny4/5/9/10 - Complete Datasheet に基づいています。

プログラムは大きく分けて以下の6つのブロックで構成されています。

  1. クロック設定: ATtiny10はデフォルトで内蔵発振8MHzを8分周した1MHzで動作します。PWM周波数を可聴域より高くし、かつ滑らかにするために、保護レジスタ(CCP)を解除して分周なしの8MHzで動作するように設定しています。
  2. スタックポインタ設定: スタックはサブルーチン呼び出し時の戻りアドレスや、一時的なデータ退避に使用されるメモリ領域です。SPHSPLでスタックの先頭アドレス(RAMEND)を設定します。今回のプログラムではサブルーチンを使用していませんが、初期化として設定しておくのが一般的です。
  3. I/O設定: DDRBレジスタを設定し、PB0とPB1ピンを出力モードにします。ATtiny10ではPB0がOC0A出力PB1がOC0B出力に対応しています。PB2はDDRBのビット2が0のままであるため入力(ハイインピーダンス)となり、出力されません。
  4. PWM(タイマー)設定: タイマー機能でPWM波形を自動生成します。今回はPhase Correct PWMモードを使用しており、カウントアップとダウンを繰り返す(三角波)ため、周波数は半分になりますが位相が揃いやすい特徴があります。
  5. メインループ: メモリ上の正弦波データを順番に読み込み、PWMの明るさ設定レジスタ(OCR0AL/BL)に書き込みます。
  6. ディレイ処理: LEDの明滅を人間の目で視認できるように、ネストしたループで約6ms程度の待ち時間を作っています。これにより正弦波1周期が約1.5秒になります。

アセンブラディレクティブとメモリマップの解説

ATtiny10のアセンブラコードを理解する上で、最もつまずきやすい「アドレス」の概念について補足します。

ディレクティブ意味詳細解説
.device対象マイコン指定ATtiny10のメモリマップ定義を読み込みます。
.csegコードセグメントFlashメモリ(プログラム領域)への配置を指示します。
.org配置アドレス指定.org 0x0080 は「ワードアドレス」を指定しています。
AVRの命令は16bit単位なので、バイトアドレスに換算すると 0x0100 になります。
.dbデータ定義正弦波テーブル(256バイト)をFlashに埋め込みます。

なぜ LD 命令でFlashが読めるのか?

通常のAVRマイコン(ATmega328Pなど)では、Flashメモリを読むために専用のLPM命令が必要です。 しかし、ATtiny10はデータ空間にFlashメモリがマッピングされているという特殊な構造を持っています。

  • データメモリ空間 0x4000番地 から先に、Flashメモリの中身がそのままコピー(マッピング)されています。
  • そのため、SRAMを読むのと同じ LD 命令を使って、アドレスに 0x4000 を足すだけでFlashデータを読み出せます。

計算式: Flash先頭オフセット (0x4000) + データ配置場所 (0x0100) = 0x4100

これが、コード中のポインタ初期化で 0x4100 を設定している理由です。

ラベルについて

RESET:loop:SineTable:はラベルと呼ばれ、その位置のアドレスに名前を付けるものです。RJMP loopのようにジャンプ先を分かりやすく指定でき、コードの可読性が向上します。

使った命令の解説

アセンブリ言語は命令の羅列です。このプログラムで使う主要な命令は7つだけ。これさえ分かればコードが読めます!

命令正式名称意味意訳
LDILoad ImmediateRd = K「箱(レジスタ)に数字を入れる」
LDI r16, 0x03 は r16という箱に3を入れる命令。
OUTOut to I/OI/O(A) = Rr「ハードウェアの設定を変更する」
レジスタの値を、設定用の場所(I/O)に書き込む。
LDLoad IndirectRd = (X)「ポインタが指す場所のデータを読む」
Xポインタ(住所)にあるデータを取得。X+と書くと「読んだ後に住所を1つ進める」動作になる。
MOVCopy RegisterRd = Rr「箱の中身をコピーする」
あるレジスタの値を別のレジスタにコピーする。
DECDecrementRd = Rd - 1「1つ減らす」
レジスタの値を1減算する。ディレイループのカウンタに使用。
BRNEBranch if Not Equalif (Z=0) PC += k + 1「0じゃないならジャンプ」
直前の計算結果が0でなければ指定ラベルへジャンプ。ループの継続判定に使用。
RJMPRelative JumpPC = PC + k + 1「ジャンプする」
RJMP loop で、loopというラベルの場所へ戻る。無限ループを作成。

使った機能レジスタの解説

データシートに基づき、各ビット設定の意味を正確に解説します。

  • CCP (Configuration Change Protection): 誤動作防止用の保護解除レジスタ。書き込みキー 0xD8 を書き込んだ直後の 4クロックサイクル以内 に限り、重要レジスタ(CLKPSRなど)の変更が許可されます。

    Bit76543210
    NameCCP7CCP6CCP5CCP4CCP3CCP2CCP1CCP0
    Set11011000
  • CLKPSR (Clock Prescaler Register): システムクロックの分周比設定。0x00 を書き込むことで分周比1(8MHz直結)となります。

    Bit76543210
    Name----CLKPS3CLKPS2CLKPS1CLKPS0
    Set00000000
  • TCCR0A (Timer/Counter Control Register A): 設定値 0xA1 (1010 0001) の内訳:

    Bit76543210
    NameCOM0A1COM0A0COM0B1COM0B0--WGM01WGM00
    Set10100001
    • COM0A1:0 = 10: OC0A一致でLow、BOTTOMでHigh(非反転動作)。
    • COM0B1:0 = 10: OC0B一致でLow、BOTTOMでHigh(非反転動作)。
    • WGM01:0 = 01: PWM, Phase Correct, 8-bit の下位ビット設定。
  • TCCR0B (Timer/Counter Control Register B): 設定値 0x01 (0000 0001) の内訳:

    Bit76543210
    NameICNC0ICES0-WGM03WGM02CS02CS01CS00
    Set00000001
    • WGM03:2 = 00: モード設定の上位ビット。Aと合わせてモード1(Phase Correct)になります。
    • CS02:0 = 001: クロック源を分周なしで使用。
  • OCR0AL / OCR0BL (Output Compare Register): 8bitのコンペアレジスタ。タイマーの値とこのレジスタの値が一致した瞬間に、対応するピン(OC0A=PB0 / OC0B=PB1)の出力が反転します。これがPWMのパルス幅になります。

  • DDRB (Data Direction Register B): ポートBの各ピンを入力(0)にするか出力(1)にするかを決めるレジスタ。0x03(= 0b00000011)を設定することで、PB0とPB1を出力に設定しています。

    Bit76543210
    Name----DDB3DDB2DDB1DDB0
    Set00000011

AVR特有の X, Y, Z ポインタの解説

ATtiny10はレジスタベースのアーキテクチャですが、メモリ間接アクセス(ポインタ)が強力です。

16bitポインタの構造

8bitマイコンで16bitのアドレス(0x4000〜)を扱うため、2つの8bitレジスタを連結して使用します。

注意: ATtiny10は「Reduced AVR Core」を採用しており、汎用レジスタは r16〜r31 の16個のみです(通常のAVRは r0〜r31 の32個)。そのため、ポインタレジスタもこの範囲から使用されます。

  • X ポインタ: r27(XH) : r26(XL) — 汎用的なデータアクセスに使用。今回のコードでは正弦波テーブルの読み出しに使用しています。
  • Y ポインタ: r29(YH) : r28(YL) — Xと同様に汎用的なデータアクセスに使用。複数のデータ列を同時に扱う場合に便利です。
  • Z ポインタ: r31(ZH) : r30(ZL) — LPM(Load Program Memory)命令でFlash読み出しに使える唯一のポインタ。ただしATtiny10ではLD命令でもFlashにアクセスできるため、今回は使用していません。

これらのポインタは、配列やテーブルを順番に読み書きする処理で特に威力を発揮します。

オートインクリメント機能

コード中の LD r16, X+ は「ポストインクリメント付きロード」と呼ばれる動作をします。1命令で以下の2つを同時に行います。

  1. ロード: Xポインタが指す番地(0x4100)のデータを r16 にコピー。
  2. インクリメント: Xポインタの値(0x4100)を +1 して 0x4101 に更新。

これにより、ループ内でポインタを自分で足し算する命令を書く必要がなくなり、コードサイズが削減され高速化します。

; XH(r27)に0x41, XL(r26)に0x00を入れる
LDI  XH, 0x41
LDI  XL, 0x00
; これで Xペア は「0x4100」という16bitのアドレスを指差している状態になる

おわりに

今回は、ATtiny10の環境構築手順と、前回のLTでは触れられなかったアセンブリコードの詳細について解説しました。

レジスタ操作やポインタなど、少しとっつきにくい部分もあったかと思いますが、「命令が上から順に実行され、ハードウェアが動く」というマイコンならではの挙動を実感していただけたなら嬉しいです。

これでPWM制御ができるようになり、イルミネーション完成へ一歩近づきました。ここからソフトウェアPWMも組み合わせた3色LED制御や、他のLED制御をしているマイコンとの通信などを組み合わせることにより高度なイルミネーションにすることができます。

私は「マイコン間通信」の実現に向けて、引き続き勉強を進めていこうと思います。

皆さんもぜひATtiny10を使った小さな電子工作に、挑戦してみてください!

TUT (powered by LinuxClub) Advent Calendar 2025の次回担当は、kash / Hytus(@KashEight)です。