SSブログ

PWMについて調べて、リモコン信号出力してみた。(ATtiny13A) [Arduino]

また赤外線リモコン関係です。

pwm_ir_tn13a.jpg
赤外線リモコンは、38kHz のキャリア波にのせてデータを送ります。
この、38kHz を PWM (pulse width modulation) で作成してみたいと思いました。

そこで PWM について勉強しました。

AVR.jp
http://www.avr.jp/

ここに、データシートの日本語訳があるので調べてみました。
いつもの ATtiny13A を使います。

pmw13a_0.png
OC0A, OC0B が PWM 出力に必要なピンです。

pmw13a_1.png
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 となる。

pmw13a_2.png
タイマ/カウンタ制御レジスタ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 である必要がないなぁ、、、。

nice!(0)  コメント(1)  トラックバック(0) 

nice! 0

コメント 1

kawauchi

はじめまして。

PWMについて調べて、リモコン信号出力してみましたについて
掲載くださいまして、ありがとうございます。

wiring_private.hのファイルの内容について、解りませんので、
教えて頂けませんか。

 以上です。
by kawauchi (2015-09-29 20:03) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。