LoginSignup
2
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-07-21

学習用・備忘録

個人的に作っているので間違いを記述している場合がある。

進数の記述方法

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マイコンには、外部割込み端子が最低1つある。
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
設定 > シリアルポート
・スピードを115200に設定する。
ScreenShot_20201109114849.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

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1