PWMについて調べて、リモコン信号出力してみた。(ATtiny13A) [Arduino]
また赤外線リモコン関係です。
赤外線リモコンは、38kHz のキャリア波にのせてデータを送ります。
この、38kHz を PWM (pulse width modulation) で作成してみたいと思いました。
そこで PWM について勉強しました。
AVR.jp
http://www.avr.jp/
ここに、データシートの日本語訳があるので調べてみました。
いつもの ATtiny13A を使います。
OC0A, OC0B が PWM 出力に必要なピンです。
PWM にはいろいろモードがあるようですが、波形の調節がわかりやすいものということで、高速PWM動作を選びました。
PWM 出力周波数を 38kHz とするは、ATtiny13A を 1.2MHz で駆動すると、
・分周なしで、( (1.2MHz / 38kHz ) / 1(分周) ) - 1 = 30.6 CPU cycles が必要。
・duty比を1/3にする ( 1.2MHz / 38kHz ) / 3 = 10.5 CPU cycles となる。
タイマ/カウンタ制御レジスタA,B (TCCR0A, TCCR0B) の設定は、
・比較出力選択 COM0B1-0 : 10 で OC0B が、高速PWM時 BOTTOMでHIGH、比較一致でLOW
(COM0A1-0 は、10 に設定しても、TOP値を OCR0A にするので、常にHIGHしか出ない)
出力を OFF にするには、 COM0B1-0 : 00 でいいみたい。
・波形生成種別 WGM02-0 : 111 で 高速PWM動作
・クロック選択 CS02-0 : 001 分周なし (000 で停止、010で8分周 など)
タイマ/カウンタ0 比較Aレジスタ (OCR0A)
・TOP値を入れる。つまり38kHzの周期にかかるCPU cycle数
( ( 1.2MHz / 38kHz ) / 1(分周) ) - 1 = 30.6 → OCR0A = 31;
・TOP値なので、OC0A に出力しても、HIGH となるだけ
タイマ/カウンタ0 比較Bレジスタ (OCR0B)
・ COM0B1-0 : 10 としたので、HIGH → LOW までの CPU Cycle数
duty比を1/3にする ( 1.2MHz / 38kHz ) / 3 = 10.5 → OCR0B = 11;
そのほか、 、
・タイマー割り込みの許可に、TIMSK0 というのがあり、Arduino の時間系の関数がこれを使っています。
割り込まれると、PWM が狂いました。
TIMSK0 = 0; として タイマー割り込みを不許可としました。
・delay() などに代わる関数も作ってみました。
・sbi(), sbi() というのは、wiring_private.h にあり、レジスタに指定のビットをセット/クリアする。
<サンプルスケッチ>
東芝テレビ REGZA のチャンネルを5秒ごとに変えていくスケッチです。
Arduino の機能を潰してまでレジスタをいじっています。
もはや Arduino である必要がないなぁ、、、。
赤外線リモコンは、38kHz のキャリア波にのせてデータを送ります。
この、38kHz を PWM (pulse width modulation) で作成してみたいと思いました。
そこで PWM について勉強しました。
AVR.jp
http://www.avr.jp/
ここに、データシートの日本語訳があるので調べてみました。
いつもの ATtiny13A を使います。
OC0A, OC0B が PWM 出力に必要なピンです。
PWM にはいろいろモードがあるようですが、波形の調節がわかりやすいものということで、高速PWM動作を選びました。
PWM 出力周波数を 38kHz とするは、ATtiny13A を 1.2MHz で駆動すると、
・分周なしで、( (1.2MHz / 38kHz ) / 1(分周) ) - 1 = 30.6 CPU cycles が必要。
・duty比を1/3にする ( 1.2MHz / 38kHz ) / 3 = 10.5 CPU cycles となる。
タイマ/カウンタ制御レジスタA,B (TCCR0A, TCCR0B) の設定は、
・比較出力選択 COM0B1-0 : 10 で OC0B が、高速PWM時 BOTTOMでHIGH、比較一致でLOW
(COM0A1-0 は、10 に設定しても、TOP値を OCR0A にするので、常にHIGHしか出ない)
出力を OFF にするには、 COM0B1-0 : 00 でいいみたい。
・波形生成種別 WGM02-0 : 111 で 高速PWM動作
・クロック選択 CS02-0 : 001 分周なし (000 で停止、010で8分周 など)
タイマ/カウンタ0 比較Aレジスタ (OCR0A)
・TOP値を入れる。つまり38kHzの周期にかかるCPU cycle数
( ( 1.2MHz / 38kHz ) / 1(分周) ) - 1 = 30.6 → OCR0A = 31;
・TOP値なので、OC0A に出力しても、HIGH となるだけ
タイマ/カウンタ0 比較Bレジスタ (OCR0B)
・ COM0B1-0 : 10 としたので、HIGH → LOW までの CPU Cycle数
duty比を1/3にする ( 1.2MHz / 38kHz ) / 3 = 10.5 → OCR0B = 11;
そのほか、 、
・タイマー割り込みの許可に、TIMSK0 というのがあり、Arduino の時間系の関数がこれを使っています。
割り込まれると、PWM が狂いました。
TIMSK0 = 0; として タイマー割り込みを不許可としました。
・delay() などに代わる関数も作ってみました。
・sbi(), sbi() というのは、wiring_private.h にあり、レジスタに指定のビットをセット/クリアする。
<サンプルスケッチ>
東芝テレビ REGZA のチャンネルを5秒ごとに変えていくスケッチです。
// PWMで38kHzをつくる赤外線リモコン (ATtiny13A (1.2MHz)版)
#include <util/delay_basic.h> // _delay_loop_2() を使うため
#include "wiring_private.h" // sbi(), cbi() を使うため
void setup() { // OC0B (Arduino的には1番ピン(PB1)) を 38kHz PWM 出力にする。
DDRB |= B00000010; // 出力方向にする
TCCR0A = (0<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
TCCR0B = (1<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);
// 比較出力選択 COM0x1-0: 10 で OC0B が、高速PWM時 BOTTOMでHIGH、比較一致でLOW
// 波形生成種別 WGM02-0 : 111 で 高速PWM動作
// クロック選択 CS02-0 : 001 分周なし (000 で停止、010で8分周 など)
OCR0A = 31; // (( 1.2MHz / 38kHz ) / 1(分周) ) - 1 = 30.6
OCR0B = 11; // duty比を1/3にする ( 1.2MHz / 38kHz ) / 3 = 10.5
TIMSK0 = 0; // タイマー割り込みの不許可 (Arduinoの時間系関数と競合するのでつぶす)
}
void loop() {
sendIrNEC1M2Hz( 0xE41BBF40 ); // REGZA 0x40 0xBF, 0x1B (Ch.Up)
sendIrNEC1M2Hz( 0xE41BBF40 ); // 2回送ってみる。
delayDeciseconds1M2Hz( 50 ); // 5秒のdelay
}
void sendIrNEC1M2Hz( uint32_t d ) {
// NEC形式の時間単位(T)は、9/16 msec = 562.5usec
// 1 MHzだと 562.5 CPU cycles 必要
// 1.2MHzだと 562.5 x 1.2 = 675 CPU cycles 必要
// delay_basic.h の _delay_loop_2() では 1ループで 4 CPU cycles 消費できる
// 562.5 x 1.2 / 4 = 168.75 ループすればいい
sbi(TCCR0A, COM0B1); _delay_loop_2( 2700 ); // リーダ ON (16T)
cbi(TCCR0A, COM0B1); _delay_loop_2( 1350 ); // リーダ OFF (8T)
for(uint16_t i=0; i<32; i++) {
sbi(TCCR0A, COM0B1); _delay_loop_2( 168 ); // データ ON (1T)
cbi(TCCR0A, COM0B1); _delay_loop_2( (d>>i) % 2 ? 506 : 168 ); // データ OFF (0:1T/1:3T)
}
sbi(TCCR0A, COM0B1); _delay_loop_2( 168 ); // トレーラ ON (1T)
cbi(TCCR0A, COM0B1); _delay_loop_2( 11981 ); // トレーラ OFF (71T) (計192T)
}
void delayDeciseconds1M2Hz( uint16_t ds ) { // 1.2MHz動作時 0.1sec単位のdelay
while( ds-- ) _delay_loop_2( 30000 ); // 1MHzなら 25000
}
Arduino の機能を潰してまでレジスタをいじっています。
もはや Arduino である必要がないなぁ、、、。
2012-09-29 12:41
nice!(0)
コメント(1)
トラックバック(0)
はじめまして。
PWMについて調べて、リモコン信号出力してみましたについて
掲載くださいまして、ありがとうございます。
wiring_private.hのファイルの内容について、解りませんので、
教えて頂けませんか。
以上です。
by kawauchi (2015-09-29 20:03)