Arduino ATmega328PとC/C++の基礎知識(備忘録)

## 学習用・備忘録
個人的に作っているので間違いを記述している場合がある。

## 進数の記述方法
0b00000001, B00000001 - 2進数を表す為に先頭に0bまたはBを加える
0x00000001 - 16進数を表すために先頭に0xを加える
ピンの入力や出力、HIGH、LOWを設定するときに使う。
基本は2進数で記述する。

## よく使われる標準ライブラリ
### ```#include <avr/io.h>```
全AVRデバイスで使われているポート入出力用のライブラリである。  

```io_example.c
DDRB = 0b00001111;
PORTB |= 0b00000001;
```
### ```#include <util/delay.h>```
実行中のコードの途中でスリープ（待機）を入れることが出来るライブラリである。

```delay_example.c
_delay_ms(100); // 100ms (0.1s)
_delay_us(100); // 100us (10^-6s)
```
### ```#include <avr/interrupt.h>```
割り込み処理をすることが出来るライブラリである。

## 論理演算子
### "|" OR演算子
左と右どちらかに1があれば1を返す。

IN1|IN2|OUT
:-:|:-:|:-:
0|0|0
0|1|1
1|0|1
1|1|1

#### 使用用途
元の設定を崩さずに新しい設定を追加したい場合に用いる。
少し専門的な言葉で表すと、ビットを立てるという。

```example.c
DDRB = 0b01000000;
DDRB = DDRB | 0b00100000; // PB5を出力ポートに設定するときにも使われる
// DDRB |= 0b00100000; 省略したかたち
// 結果：DDRB -> 0b01100000
```

### "^" XOR演算子
左右が異なれば1を返す。

IN1|IN2|OUT
:-:|:-:|:-:
0|0|0
0|1|1
1|0|1
1|1|0

#### 使用用途
ビットの反転に使われる。
具体的な使用例としては、**「ループでLEDを点灯、消灯を1秒ごとに繰り返すコード」**で使う。

```example.c
while(){
    PORTB = PINB ^ 0b00000001;
    _ms_delay(1000);
}
```

```example.c
PORTB = 0b00100000;
PORTB = PINB ^ 0b00100000;

// 結果：PORTB -> 0b00000000
```

### "~" NOT演算子
反転した結果を返す。

IN|OUT
:-:|:-:
0|1
1|0

```example.c
PORTB = ~0b00100000;

// 結果：PORTB -> 0b11011111
```

### "&" AND演算子
左右が1であれば1を返す。

IN1|IN2|OUT
:-:|:-:|:-:
0|0|0
0|1|0
1|0|0
1|1|1

#### 使用用途
特定のピンがオンかオフかを調べたり、特定のピンを操作したりするために用いる。
少し専門的な言葉で表すと、ビットマスクという。

```example.c
PORTB = 0b00111111;
PORTB = PINB & 0b00000001;

// 結果：PORTB -> 0b00000001
```

## シフト演算子
ビット列を指定数分ずらす
" << " 左シフト
" >> " 右シフト

```example.c
int result;
int data = 0b00000011;
result = data << 3;  // dataを3bit左にシフトする

// 結果：00011000
```
## レジスタのコントロール

### PORTレジスタ
ポートのデータレジスタ。読み書き可能なレジスタ。

|レジスタ|ピン番号|
|:-:|:-:|
|PORTB|デジタルピン 8～13|
|PORTC|アナログピン|
|PORTD|デジタルピン 0～7|

```example.c
// ピンを出力設定
PORTB = 0b01000000;
```
### PINレジスタ
PIN情報の読み取り専用のレジスタ。

```example.c
// 情報を読み取ってデータビットの反転
PORTB = PINB ^ 0b00100000;
// 結果：PORTB ->  0b00000000
```
### DDRレジスタ
ポートの入出力を設定するレジスタ。　読み書き可能
0 - 入力ポート
1 - 出力ポート

```example.c
DDRB |= 0b11110000; // 右端の2bitはRXとTXが占領
DDRB  = 0b00110000; // PORTB4,5を出力設定
PORTB = 0b00110000; // PORTB4,5をHIGHにする
```
## プロトタイプ宣言
プロトタイプ宣言は、コンパイラに対して関数の情報を与えるためのものである。
複数の関数を個別のファイルに分けて整理したり、main関数の下に関数を書いたりすることができる。

### プロトタイプ宣言を用いて関数を別のファイルに記述した例

```main.cpp
#include "sum.h"
int main(void) {
    int result;
    result = sum(3, 5); // sum関数の内容は別のファイルに記述
    printf("3+5 = %d", result);
    return 0;
}
```

```sum.h
#ifndef SUM_H_
#define SUM_H_
// ヘッダーファイル
int sum(int a, int b); // プロトタイプ宣言
#end
```

```sum.cpp
#include "sum.h"
int sum(int a, int b){
    return a + b;
}
```

## クラスと継承
クラスとは、構造体にメンバ関数を追加したものである。
メンバ関数とは、クラスが持つ関数のことである。
継承とは簡単に表すと、メンバ関数をクラスの外で書くことができることである。

```class_template.cpp
class Rectangle { // クラス
    public:       // どこからでもアクセス可能
        int Area() {  // メンバ関数
            return height * width;
        }
        int height;
        int width;
    private:   // クラス内のみアクセス可能
        int rect_area;
}
```
クラスにはアクセス制限があり、「public」「private」「protected」の3つがある。
**publicとは**：「公開」であり、下のメンバ関数や変数はどこからでもアクセスできる。
**privateとは**：「非公開」であり、下のメンバ関数や変数はクラス内のみアクセスできる。
**protectedとは**：クラス内及び継承した派生クラスからのみアクセス可能

## オーバーロード
作成中...

## テンプレート関数
テンプレート関数は、一つの関数で異なるデータ型を受け取ることができる関数である。
オーバーロード関数の簡略化した関数である。
オーバーロードとの違いは、型で処理内容を変えることができないことである。
汎用関数とも呼ばれる。

```template_function.cpp
// typeは関数が呼ばれたとき、その型として動く。
template <class type> type comp(type a, type b) {
    if(a < b) {
        return b;
    }else{
        return a;
    }
}
float res;
res = comp(1, 2.5); // どのデータ型でも指定できる
printf("%f", res);
```

## A/D変換
センサーが使うアナログピンから値を読み取るため、アナログデータからデジタルデータに変換する。
基準電圧（リファレンス電圧）と入力電圧、AD変換用レジスタを使ってA/D変換をする。
読み取れる範囲は0～基準電圧までである。
**A/D変換用レジスタとは**：計測した電圧を(10bitで)格納するレジスタ。ADCH, ADCLレジスタに格納される。
**入力電圧とは**：ピンから入力される電圧。
**基準電圧とは**：ADMUXレジスタで基準電圧の選択が出来る。範囲は0～電源電圧(5V)

ATmega328PのA/D変換用レジスタは10bitを採用している。

#### [ ADCSRA ] - A/D制御/状態レジスタA
|bit|7|6|5|4|3|2|1|0|
|---|---|---|---|---|---|---|---|---|
|名称|ADEN|ADCS|ADATE|ADIF|ADIE|ADPS2|ADPS1|ADPS0|

|名称	|bit	|動作モード
|---    |---    |---
|ADEN	|1	|A/D許可（電源制御）
|ADCS	|1	|A/D変換開始(変換開始時に1を書く)
|ADATE	|1	|A/D自動起動許可
|ADIF	|1	|A/D完了割り込み要求フラグ
|ADIE	|1	|A/D変換完了割り込み許可

|ADPS2	|ADPS1	|ADPS0	|A/D変換クロック選択
|---    |---    |---    |---
|0	|0	|0	|分周比2
|0	|0	|1	|分周比2
|0	|1	|0	|分周比4
|0	|1	|1	|分周比8
|1	|0	|0	|分周比16
|1	|0	|1	|分周比32
|1	|1	|0	|分周比64
|1	|1	|1	|分周比128

#### [ ADMUX ] - A/D多重器選択レジスタ
|bit|7|6|5|4|3|2|1|0|
|---|---|---|---|---|---|---|---|---|
|名称|REFS1|REFS0|ADLAR|-|MUX3|MUX2|MUX1|MUX0|

|REFS1	|REFS0	|基準電圧選択
|---    |---    |---
|0	|0	|AREFピンの電圧
|0	|1	|AVCC (5V) - 標準
|1	|0	|(予約)
|1	|1	|内部1.1V基準電圧

|名称	|bit	|動作モード
|---    |---    |---
|ADLAR	|1	|変換データレジスタ左揃え

|MUX3	|MUX2	|MUX1	|MUX0	|A/Dチャンネル選択
|---    |---    |---    |---    |---
|0	|0	|0	|0	|ADC0
|0	|0	|0	|1	|ADC1	
|0	|0	|1	|0	|ADC2
|0	|0	|1	|1	|ADC3
|0	|1	|0	|0	|ADC4
|0	|1	|0	|1	|ADC5
|0	|1	|1	|0	|ADC6
|0	|1	|1	|1	|ADC7
|1	|0	|0	|0	|ADC8 (温度)
|1	|1	|1	|0	|1.1V
|1	|1	|1	|1	|0V

#### [ADCL,ADCH] - A/D変換データレジスタ上位/下位
計測した電圧の結果を格納するレジスタ。

- ADLARが0のとき

|ADCH |ADCL
|---  |---
|- - - - - - 0 0  |0 0 0 0 0 0 0 0

- ADLARが1のとき

|ADCH |ADCL
|---  |---
|0 0 0 0 0 0 0 0  |0 0 - - - - - -
↑ 0の箇所に10bitのデータが格納される

ATmega328PのAD変換用レジスタは8ビットレジスタを2個使って10ビットを表現する。

```analog_read.cpp
#define F_CPU 16000000UL
#include <avr/io.h>
int main(void){
    ADSCRA = 0b10000000; // AD起動時設定 テンプレ
    ADMUX  = 0b01100000;  // AD起動時設定 テンプレ
    result = ADCH<<2 + ADCL>>6; // ADCHとADCLを結合して10bit分取得する
}
```

## 割り込み処理
### タイマ割り込み
設定した時間ごとに割り込みを発生させる。

**[Timer0]** - 8ビットタイマ
時計の役割をする。0~255までカウントできる。それを超えるとオーバーフローして0に戻る。
**[Timer1]** - 16ビットタイマ
時計の役割をする。0~65535までカウントできる。それを超えるとオーバーフローして0に戻る。

タイマーには主に4つのモードがある。
・標準動作
・CTC動作
・位相基準PWM
・高速PWM

### プリスケーラとは
クロックで時計を扱うには速すぎて使いにくい場合のために、
クロックを割り引いて（分周　最大1/1024）使うことが出来る。

### 500msで割り込むプログラム
```main.cpp
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>

ISR(TIMER1_COMPA_vect){ // 割り込み関数
    // 処理内容
    PORTD = (PIND & 0b00000001) ^ 0b00000001; // Arduino上のLED点灯(確認用)
}

int main(void){
    // 16ビットタイマ
    TCNT1  = 0;           // タイマカウンタ初期化
    OCR1A  = 31250;       // 256分周で500ms
    TCCR1A = 0b00000000;  // OC1A / OC1Bを標準ポート動作に設定
    TCCR1B = 0b00001100;  // CTC動作、プリスケーラ1/256
    TIMSK1 = 0b00000010;  // 割り込み許可レジスタ
    TIFR1  = 0b00100111;  // TCNT1割り込み要求フラグレジスタ
    sei();

    PORTD |= 0b00000000; // Arduino上のLED設定(確認用)
    DDRD  |= 0b00000001; // Arduino上のLED設定(確認用)
    while (1){}
    return 0;
}
```

### Timer0 - 8ビットタイマ

#### [ TCCR0A ] - 制御レジスタA

|bit|7|6|5|4|3|2|1|0|
|---|---|---|---|---|---|---|---|---|
|名称|COM0A1|COM0A0|COM0B1|COM0B0|-|-| WGM01| WGM00|
#### [ TCCR0B ] - 制御レジスタB

|bit|7|6|5|4|3|2|1|0|
|---|---|---|---|---|---|---|---|---|
|名称|FOC0A|FOC0B|-|-|WGM02|CS02|CS01|CS00|

|COM0A1|COM0A0|動作モード|
|---|---|---|
|0|0|標準ポート動作(OCR0A切断)|
|0|1|WGM02が0のときは、標準操作(OC0A切断)、WGM02が1のときは、比較一致で出力を反転。|
|1|0|タイマとOCR0Aとの比較一致でLOW、タイマが0になるとHIGH。|
|1|1|タイマとOCR0Aとの比較一致でHIGH、タイマが0になるとLOW。|

|COM0B1|COM0B0|動作モード|
|---|---|---|
|0|0|標準ポート動作(OCR0B切断)|
|0|1|OCR0Bとの比較一致で交互出力。|
|1|0|タイマとOCR0Bとの比較一致でLOW、タイマが0になるとHIGH。|
|1|1|タイマとOCR0Bとの比較一致でHIGH、タイマが0になるとLOW。|

|WGM02|WGM01|WGM00|動作モード|
|---|---|---|---|
|0|0|0|標準動作（標準）|
|0|0|1|8ビット位相基準PWM動作|
|0|1|0|比較一致タイマ/CTC動作|
|0|1|1|8ビット高速PWM動作|
|1|0|0|（予約）|
|1|0|1|位相基準PWM動作|
|1|1|0|（予約）|
|1|1|1|高速PWM動作|

|CS02|CS01|CS00|動作モード|
|---|---|---|---|
|0|0|0|クロックなし|
|0|0|1|分周比1|
|0|1|0|分周比8|
|0|1|1|分周比64|
|1|0|0|分周比256|
|1|0|1|分周比1024|
|1|1|0|外部クロック。立下りでオン。|
|1|1|1|外部クロック。立ち上がりでオン。|

|FOC0A|動作モード|
|---|---|
|1|WGMの設定がPWM動作でない場合、OCR0A出力がPWM動作になる。|

|FOC0B|動作モード|
|---|---|
|1|WGMの設定がPWM動作でない場合、OCR0B出力がPWM動作になる。|

#### [ TIMSK0 ] - 割り込み設定レジスタ

|bit|7|6|5|4|3|2|1|0|
|---|---|---|---|---|---|---|---|---|
|名称|-|-|-|-|-|OCIE0B|OCIE0A|TOIE0|

|OCIE0B|動作モード|
|---|---|
|1|タイマ/カウンタ0比較B割り込み許可（OCR0BとTCNT0の一致）|

|OCIE0A|動作モード|
|---|---|
|1|タイマ/カウンタ0比較A割り込み許可（OCR0AとTCNT0の一致）|

|TOIE0|動作モード|
|---|---|
|1|カウンターがTOP(OCR0Aで設定した値)に来た時に割り込み処理を行う。|

#### [ OCR0A ] - タイマ/カウンタ0比較Aレジスタ（TOP値）
カウンタ（TCNT0）と比較を行うためのレジスタ。
設定しない場合、225がMAXなので225がTOPとなる。
8ビットでは255で0に戻すがこれに値を入れるとその値で0に戻る。

#### [ OCR0B ] - タイマ/カウンタ0比較Bレジスタ（TOP値）
OCR0Aと同じ。

#### [ TCNT0 ] - タイマ/カウンタ0計測レジスタ
クロックを数えるカウンタ。
タイマ情報を読み取れる。

### 外部割込み
スイッチの状態変化を捕らえられる処理。
特定のピンがHIGHになると割り込みを開始する。
AVRマイコンには、外部割込み端子が最低１つある。
INT0は必ずあり、チップによりINT1、INT2まである。
これらの端子を利用し、スイッチの状態変化を捕らえられる。

ビットの組み合わせは4通り
00 Lowレベル割込み
01 レベル変化割込み
10 ダウンエッジ割込み
11 アップエッジ割込み

## シリアル通信 TeraTeam
Arduinoで実行中の状態を確認することができる。
### プログラム
シリアル通信するにはUartLib.cとUartLib.hファイルが必要なので、プロジェクトのフォルダに配置しておく。

```example.c
#include "UartLib.h"     // シリアル通信用ヘッダーファイルを読み込み
int main(){
    uartInit();          // UartLib準備
    printf("Kimetsu\n"); // 送信
}
```
### TeraTeam設定
設定 > 端末に移動
・改行コードの受信と送信をLFに設定する。
・漢字受信と送信をSJISに設定する。
![ScreenShot_20201109114809.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197942/abf2a728-4453-7929-8339-90f775cf50f4.png)
設定 > シリアルポート
・スピードを115200に設定する。
![ScreenShot_20201109114849.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197942/53d15aa0-82eb-3ebc-54e1-e64352f5cb0c.png)

# リファレンス
http://wisdom.sakura.ne.jp/programming/cpp/cpp16.html
http://wisdom.sakura.ne.jp/programming/cpp/cpp33.html
http://www.natural-science.or.jp/article/20101215012553.php
http://avrwiki.osdn.jp/cgi-bin/wiki.cgi?page=Timer0
http://avrwiki.osdn.jp/cgi-bin/wiki.cgi?page=Timer1
https://stastaka.wordpress.com/2012/03/20/avr-timer1/
https://garretlab.web.fc2.com/arduino/inside/hardware/arduino/avr/cores/arduino/wiring_analog.c/analogWrite.html
http://avrwiki.osdn.jp/cgi-bin/wiki.cgi?page=Timer1#p8
https://stastaka.wordpress.com/2012/03/20/avr-timer1/
http://usicolog.nomaki.jp/engineering/avr/avrPWM.html