これまで見てきたとおり、PWMの信号(パルス)を、IOポートをソフトウエアで制御することで作成することができた。
でも、これって面倒じゃないか?
PWMの出力をソフトでやろうとすると、他の処理に手が回らなくもなる。
こういった処理は専用のハードウエアにやらせたほうが効率的である...
ということでAVRマイコンにはタイマ機能の一部として、PWMの生成機能が実装されている。(タイマ機能の全体については、こちら)
AVR マイコンのPWMには、高速PWMモードと位相基準PWMモードがある。ここでは、タイマ0を用いた高速PWMモード(8bitタイマ)について説明す
る。(タイマ1、3は16bitタイマ。タイマ4は10bit。これらや、位相基準PWMモードなどのその他のモードについては、別に説明する。)
高速PWMモードは、更に2つのモードに分かれている。どちらのモードを使うかはTCCR0Bの第3ビットで選択する。
- モード3
-
OC0AとOC0Bの2チャンネルを出力。いずれも同一のTCNT(TCNT0)を参照
- PWMの周期はプリスケーラのみで決定
(システムクロックの1/1、1/8、1/64、1/256、1/1024が設定可能)
-
比較・一致の値を保管するレジスタ(OCR0A、OCR0B)はデューティを設定
- モード7
- OC0Bの1チャンネル出力
- OC0AはTCNTの上限値(PWMの周期)、OC0Bはデューティを設定
- PWMの周期はプリスケーラとOCR0Aで設定
(システムクロックの1/1、1/8、1/64、1/256、1/1024とTCNTの上限値が設定可能)
PWMの設定にはTCNT、OCR0A、OCR0B、TCCR0A、TCCR0B、TIMSK0(割り込みマスクレジスタ)、TIFR0(割り込みフラグレジスタ)のレジスタが関係する。また、TCNTは8bitの勝手に動作するカウンタで、システムクロック(16MHzもしくは1MHz)をプリスケーラで分周(周波数を何分の一かにすること)しカウントする。
OCR0A、OCR0Bレジスタも8bitのレジスタで、TCNTと値が一致したとき(比較・一致)に、PWM信号が切り替わる。(OCR0AとTCNTが一致した時はOC0A、OCR0BとTCNTが一致した時はOC0B)
各レジスタの役割を整理すると以下のとおり。
| レジスタ名 |
機 能 |
| TCNT0 |
システムクロックをプリスケーラで分周した信号で自走するカウンタ |
| TCCR0A |
タイマ0の設定を行うレジスタ。
タイマモード、比較・一致発生時の出力 |
| TCCR0B |
タイマ0の設定を行うレジスタ。
タイマモード、プリスケーラの分周比 |
| OCR0A |
TCNT0との比較・一致を行う際に値を保管するレジスタ。
OC0A出力時のデューティ可変(モード3)、TCNT0の上限値(モード7) |
| OCR0B |
TCNT0との比較・一致を行う際に値を保管するレジスタ。
OC0B出力時のデューティ可変(モード3、7) |
| TIMSK0 |
割り込みマスクレジスタ。比較・一致、TCNT0のオーバフローによる割り込み
ハンドラの呼び出しを許可・禁止する。 |
| TIFR0 |
割り込みフラグレジスタ。割り込みイベントの発生を記録する。 |
具体的な動作について、以下に説明する。

モード3では、
-
TCNTはプリスケーラで分周されたシステムクロック(1MHzか16MHz)をカウントする。(つまりシステムクロックの信号が1/0を繰り返すたびに+1する)TCNTの値は255になると0に戻る。
-
OCR0A、OCR0Bに設定された値と、TCNTの値を比較し、これが一致したとき(これをコンペア・マッチと呼ぶこともある)にPWM出力を変化させる。(変化のさせ方は、下表、「値の一致での動作」を参照)
OCR0A、OCR0Bのいずれも、デューティを決める。
- TCNTの値が0に戻るときに、PWM出力を変化させる。
(a)下表(値の一致での動作)で「値の一致で0」に設定されている場合は、TCNT=255でPWM=1
(OC0AとOC0B)
(b)下表(値の一致での動作)で「値の一致で1」に設定されている場合は、TCNT=255でPWM=0
(OC0AとOC0B)
※上の図の例は、(a)の「値の一致で0」に設定した場合の動作。(b)に設定した場合は、信号が反転する。
モード7では、
-
TCNTはプリスケーラで分周されたシステムクロック(1MHzか16MHz)をカウントする。(つまりシステムクロックの信号が1/0を繰り返すたびに+1する)TCNTの値はOCR0Aと同じ値になると0に戻る。
-
OCR0A、OCR0Bに設定された値と、TCNTの値を比較し、これが一致したときにPWM出力を変化させる。(変化のさせ方は、下表、「値の一致での動作(TCCR0A)」を参照)
OCR0Aは周期、OCR0Bはデューティを決める。
- TCNTの値が0に戻るときに、PWM出力を変化させる。
(a)下表(値の一致での動作)で「値の一致で0」に設定されている場合は、TCNT=OCR0AでPWM=1
(OC0Bのみ)
(b)下表(値の一致での動作)で「値の一致で1」に設定されている場合は、TCNT=OCR0AでPWM=0
(OC0Bのみ)
※上の図の例は、(a)の「値の一致で0」に設定した場合の動作。(b)に設定した場合は、信号が反転する。
のように動作する。
TCCR0Aの各設定ビットの詳細は、以下のとおり。
まず、タイマの動作を、高速PWMモードに設定する。このためには、TCCR0AとTCCR0BのWGM0x(xは0~3)ビットを下表(タイマモードの設定)の様に設定する。
表:タイマモードの設定(TCCR0A、TCCR0B)
動作モード
レジスタ:bitの位置
(bitの名前) |
TCCR0B:3
(WGM02)
|
TCCR0A:1
(WGM01) |
TCCR0A:0 (WGM00) |
TOP
(TCNTが0に戻る時の上限値) |
TOV
割り込み |
| モード0 (標準) |
0 |
0 |
0 |
0xFF |
0xFF |
| モード1 (位相基準PWM) |
0 |
0 |
1 |
0xFF |
0 |
| モード2 (CTC) |
0 |
1 |
0 |
OCR0Aの値 |
0xFF |
| モード3 (高速PWM) |
0 |
1 |
1 |
0xFF |
0xFF |
| モード4 (予約) |
1 |
0 |
0 |
- |
|
| モード5 (位相基準PWM) |
1 |
0 |
1 |
OCR0Aの値 |
0 |
| モード6 (予約) |
1 |
1 |
0 |
- |
|
| モード7 (高速PWM) |
1 |
1 |
1 |
OCR0Aの値 |
0xFF |
ここで、WGM02ビットを0に設定した場合は、TCNTの上限値は255、WGM02を1に設定した場合はTCNTの上限値はOCR0Aと同じ値になる。PWMの周期を様々に変えたい場合は、このモード(モード7)を使うと便利である。
また、WGM02を0とすると、OC0AとOC0Bの2本のPWM信号を出力可能だが、WGM02を1とすると、OC0Bのみとなる。
表:値の一致での動作(TCCR0A)
TCCR0Aのbitの位置
(ビットの名前) |
7
(COM0A1) |
6
(COM0A0) |
5
(COM0B1) |
4
(COM0B0) |
| |
PWM出力
(OC0Aに出力) |
PWM出力
(OC0Bに出力) |
| 波形出力なし |
0 |
0 |
0 |
0 |
|
TCNTとOCR0A・OCR0Bとの値の一致でPWM出力切替 |
0 |
1 |
0 |
1 |
|
TCNTとOCR0A・OCR0Bとの値の一致でPWM出力0 |
1 |
0 |
1 |
0 |
|
TCNTとOCR0A・OCR0Bとの値の一致でPWM出力1 |
1 |
1 |
1 |
1 |
※モード3の動作
表:プリスケーラの設定(TCCR0B)
TCCR0Bのbitの位置
(ビットの名前) |
2
(CS02) |
1
(CS01) |
0
(CS00) |
| タイマ停止 |
0 |
0 |
0 |
| 1/1 |
0 |
0 |
1 |
| 1/8 |
0 |
1 |
0 |
| 1/64 |
0 |
1 |
1 |
| 1/256 |
1 |
0 |
0 |
| 1/1024 |
1 |
0 |
1 |
| 外部クロック(T0ピンの立下り) |
1 |
1 |
0 |
| 外部クロック(T0ピンの立上がり) |
1 |
1 |
1 |
プリスケーラは、TCNTのカウント入力の周波数を決める。システムクロック(16MHz、変更可能)を何分の一(分周)かにして、TCNTに供給する。
例えば、CS02:CS01:CS00を1:0:0と設定したとすると、システムクロックは1/256に分周される事になる。従って、TCNTは、
| 16000000 / 256 = 62500 Hz |
でカウントされることになる。(つまり一秒間に62500回、カウントアップされる)
この状態で、WGM02:WGM01:WGM00を0:1:1としたとすると、PWMの1周期は、
となる。
また、WGM02:WGM01:WGM00を1:1:1として、OCR0Aを100とすると、
となる。
サンプルプログラム1
(モード3:OC0AとOC0Bの2チャンネル出力)
|
/*
* タイマ0ハードウエアによるPWM出力サンプル
* PWMの出力は、モード3の場合
* OC0A ~ 基板の29ピン (PB7と共用)
* OC0B ~ 基板の4ピン (PD0と共用)
*
* PWMの出力は、モード7の場合
* OC0B ~ 基板の4ピン (PD0と共用)
*/
... 略 ....
void hw_init()
{
// PWM
DDRD |= 0b00000001; //PD0を出力に(OC0B)
DDRB |= 0b10000000; //PB7を出力に(OC0A)
TCCR0A |= _BV(WGM01) | _BV(WGM00); // タイマ・カウンタ・コントロールレジスタ
モード3(高速PWMモード)
TCCR0A |= _BV(COM0A1); //
TCNT==OCR0Aで、OC0Aが0(GND)、TCNT=255でOC0Aが1(+5V)
//
_BV(a)は、aで指定した桁のbitが1の8bit幅の定数を返す。例えば、
// a=3のとき、_BV(a) の値は
0b00001000である。
TCCR0A |= _BV(COM0B1); //
TCNT==OCR0Bで、OC0Bをlow(=0)、TCNT=255でOC0Bをhi(=1)
TCCR0B |= _BV(CS01); // 周波数設定(PWMの一周期) (TCNT:8bit)
//
システムクロックが16MHzでTCNTがリセットされる値が255(上限値)であるため、
// 16000000 / 分周比 /
256がPWMの一周期となる。
//
(分周比が1/8なら2000000Hz→0.5マイクロ秒)
}
int main()
{
CLKPR = 0x80; CLKPR = 0; // 16MHz動作のための設定①
hw_init();
// 周期は、16000000(Hz) / 8(CS01) / 256(TCNT) = 7812.5 Hz固定
OCR0A = 200; // OC0A(29ピン~PB7)にduty = 200/256で出力
OCR0B = 100; // OC0B(4ピン~PD0)にduty = 100/256で出力
while(1) {
}
}
|
サンプルプログラム2
(モード7:OC0A=周期、OC0B=デューティ、OC0Bの1チャンネル出力)
|
/*
* タイマ0ハードウエアによるPWM出力サンプル
* PWMの出力は、モード7の場合
* OC0B ~ 基板の4ピン (PD0と共用)
*
*/
... 略 ....
void hw_init()
{
// PWM
DDRD |= 0b00000001; //PD0を出力に(OC0B)
DDRB |= 0b10000000; //PB7を出力に(OC0A)
TCCR0A |= _BV(WGM01) | _BV(WGM00); //
タイマ・カウンタ・コントロールレジスタの設定(TCCR0A、TCCR0B)
TCCR0B |= (WGM02); // 高速PWMモードのモード7
TCCR0A |= _BV(COM0A1); //
TCNT==OCR0Aで、OC0Aが0(GND)、TCNT=OCR0AでOC0Aが1(+5V)
//
_BV(a)は、aで指定した桁のbitが1の8bit幅の定数を返す。例えば、
// a=3のとき、_BV(a) の値は
0b00001000である。
TCCR0A |= _BV(COM0B1); //
TCNT==OCR0Bで、OC0Bをlow(=0)、TCNT=255でOC0Bをhi(=1)
TCCR0B |= _BV(CS01); // 周波数設定(PWMの一周期) (TCNT:8bit)
//
システムクロックが16MHzでTCNTがリセットされる値が255(上限値)であるため、
// 16000000 / 分周比 /
256がPWMの一周期となる。
//
(分周比が1/8なら2000000Hz→0.5マイクロ秒)
}
int main()
{
CLKPR = 0x80; CLKPR = 0; // 16MHz動作のための設定①
hw_init();
OCR0A = 200; // 周期は、16000000(Hz) / 8(CS01) / 200(OCR0A) = 10000 Hz
OCR0B = 100; // OC0B(4ピン~PD0)にduty = 100/256で出力
while(1) {
}
}
|
PWMと割り込み
タイマ0の高速PWMでは、TCNTとOCR0A・OCR0Bとの比較・一致が発生した時に、割り込み(イベント)を発生させる事が出来る。
発生可能な割り込みイベントは、以下の3つである。
- TCNTとOCR0Aとの比較・一致
TCNTとOCR0Aの値が一致した時に発生する割り込みイベント。この割り込みイベントが発生すると、割り込みフラグOCF0Aが1にセットされ、更に、割り込みマスクビットOCIE0Aが1であれば、割り込みハンドラ(TIMER0_COMPA_vect)が起動される。
- TCNTとOCR0Bとの比較・一致
TCNTとOCR0Bの値が一致した時に発生する割り込みイベント。この割り込みイベントが発生すると、割り込みフラグOCF0Bが1にセットされ、更に、割り込みマスクビットOCIE0Bが1であれば、割り込みハンドラ(TIMER0_COMPB_vect)が起動される。
- TCNTのオーバフロー
TCNTが0xFF(255)から0になる時に発生。この割り込みイベントが発生すると、割り込みフラグTOV0が1にセットされ、更に、割り込みマスクビットTOIE0が1であれば、割り込みハンドラ(TIMER0_OVF_vect)が起動される。
割り込み関連レジスタとビットの配置は、以下の通り。
表:割り込みフラグレジスタ(TIFR0)
bitの位置
(ビットの名前) |
7
(未使用) |
6
(未使用) |
5
(未使用) |
4
(未使用) |
3
(未使用) |
2
(OCF0B) |
1
(OCF0A) |
0
(TOV0) |
| 初期値 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
表:割り込みマスクレジスタ (TIMSK0)
bitの位置
(ビットの名前) |
7
(未使用) |
6
(未使用) |
5
(未使用) |
4
(未使用) |
3
(未使用) |
2
(OCIE0B) |
1
(OCIE0A) |
0
(TOIE0) |
| 初期値 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
サンプルプログラム(TCNTオーバフロー時の割り込みを使う例)
|
/*
* タイマ0ハードウエアによるPWM出力及び、TCNTオーバフロー時の割り込み発生サンプル
* PWMの出力は、モード3の場合
* OC0A ~ 基板の29ピン (PB7と共用)
* OC0B ~ 基板の4ピン (PD0と共用)
*
* PWMの出力は、モード7の場合
* OC0B ~ 基板の4ピン (PD0と共用)
*/
.....
ISR(TIMER0_OVF_vect) //
タイマ0でオーバフローが発生すると呼び出される割り込みハンドラ
{
static int cnt = 0;
cnt++;
}
void hw_init()
{
// PWM
DDRD |= 0b00000001; //PD0を出力に(OC0B)
DDRB |= 0b10000000; //PB7を出力に(OC0A)
PORTD = 0b00000001;
PORTB = 0b10000000;
TCCR0A = 3; // タイマ・カウンタ・コントロールレジスタ
モード3(高速PWMモード)
// もし、高速PWMモードのモード7を使いたい場合は
TCCRB |= _BV(WGM02)が必要
// モード7の設定を行うと、PWM出力はOC0B
(4ピン) の1チャンネルのみとなる。
TCCR0A |= _BV(COM0A1); //
TCNT==OCR0Aで、OC0Aが0(GND)、TCNT=255でOC0Aが1(+5V)
//
_BV(a)は、aで指定した桁のbitが1の8bit幅の定数を返す。例えば、
// a=3のとき、_BV(a) の値は
0b00001000である。
TCCR0A |= _BV(COM0B1); //
TCNT==OCR0Bで、OC0Bをlow(=0)、TCNT=255でOC0Bをhi(=1)
TCCR0B |= _BV(CS01); // 周波数設定(PWMの一周期) (TCNT:8bit)
//
システムクロックが16MHzでTCNTがリセットされる値が255(上限値)であるため、
// 16000000 / 分周比 /
256がPWMの一周期となる。
//
(分周比が1/256なら62500Hz→16マイクロ秒)
TIMSK0 |= _BV(TOIE0); //
TCNTが最大値を超えた時(0xFFもしくはOCR0Aの値)、割り込み発生
}
int main()
{
CLKPR = 0x80; CLKPR = 0; // 16MHz動作のための設定①
hw_init();
OCR0A = 200; // OC0A(29ピン~PB7)にduty = 200/256で出力
OCR0B = 100; // OC0B(4ピン~PD0)にduty = 100/256で出力
sei(); //
全ての割り込みを許可
while(1) {
}
}
|
最終更新:2016年03月22日 10:13