2014年10月29日水曜日

Arduinoでエアコン制御 4 (送信編―LMC555 失敗)

概要

結論から言えば555を使おうとしたのは良い選択ではなかったと思われる。
と言うかこの時点でマイコンのタイマによる"割込み"や"CTCモード"を知り555が得策でない事はわかっているが、それでも尚まとめようとした結果でもやはりその選択は良くなかった。

いずれ電池でと考えている事もあり電源は当然共有しているが、赤外線LEDを発光させる度に――おそらくその為に電圧が10数mV下がる。
それが555の周期をわずかに変え、結果安定した周波数での送信を難しくしている。
およそ1200Hzの信号に対して2Hz程度、0.2%弱であるが、信号全体で300[ms]ある為0.6[ms]…机上だけでも1bit以上ずれてしまう。
※1単位の波だの山や谷だのと書くのが煩雑な為、以下0.4[ms]の1山(谷)を1bitと表現する事にする。
また電圧低下から周波数の変動までタイミングが一定であればまあそれでも良いのかもしれないが、そう上手くはいかないようだ。
実際"HandyOscillo"――結局使う事になったがw――で内容を見ると1bitどころではなく大きくぶれている。
リモコンのLEDのA-Kから直接取得した波形と555を用いた送信機との比較
がんばって調整しようともいいだけぶれる
きちんと計算を行い組み立てをすればうまくいくのか…わからないが、そもそもそういったシビアな検討や調整を行えないとして選んだはずである為、間違いだったと言える。

以下失敗を記録しておく。

回路


555はデータシートに書いてあるまま、50[%]デューティー比オシレータとして機能させ、Arduinoの外部割込みピンに接続している。
555の温度特性は"⊿t/⊿T = 75 [ppm/℃]"となっている
0~50[℃]間で常温(25[℃])を中心とすると±0.19[%]と言える為上の"ぶれ"のオーダーであり、あまり温度変化のある所では使えないだろう。
はじめControlをフロートにしていたが周波数の変動が大きすぎて使えなかった。
また全てにセラミックコンデンサを使っていたがそれも安定しない要素であり、C3をフィルムコンデンサにする事で明らかに安定度が向上した。
抵抗R2は大きめに(C3は小さめに)しないと調整しづらい。
(…とか根本はそういう問題ではないが)

スケッチ

       //---一般Const---
const byte pin_irout = 5;
const byte pin_clk = 3;  //割込み1
const byte pin_led = 13;  //送信確認用LED

       //---通信データ---
const byte snd_dt_idct[3] = {8, 4, 31};  //bit個数 reader(1),reader(0),repeat(0)
const byte snd_dt_cstm[5] = {B00100011, B11001011, B00100110, B00000001, B00000000};

const byte snd_dt_C[12] = {B00100000, B00011000, B00000101, B00110110,
                         B01000000, B00000000, B00000000, B00000000,
                         B00000000, B00000000, B00000000, B00000000};

volatile byte itpt_clk = 0;
volatile byte snd_sw = 0;  //送信トグル

void setup() {
       //---割込み設定---
  attachInterrupt(1, Ext_clk, CHANGE);

       //---  ---
  pinMode(pin_irout, OUTPUT);
  pinMode(pin_clk, INPUT);
  pinMode(pin_led, OUTPUT);
}
//----------[2].

void loop() {
  digitalWrite(pin_led, HIGH);
  Ir_snd(snd_dt_C, sizeof(snd_dt_C));
  digitalWrite(pin_led, LOW);
  delay(5000);
}

//----------[3].各機能
      //---データ送信---
void Ir_snd(const byte dt[], byte s) {

  for (byte i = 0; i < 2; i++){  //2回繰り返す
    itpt_clk = 0;
    Ir_ns(snd_dt_idct[0], 1);                 //--Reader code
    Ir_ns(snd_dt_idct[1], 0);
    Ir_cs(snd_dt_cstm, sizeof(snd_dt_cstm));  //--Customer code
    Ir_cs(dt, s);                             //--Data code
    const byte chksm[1] = {Mk_chksm(snd_dt_cstm, sizeof(snd_dt_cstm), dt, s)};
    Ir_cs(chksm, 1);                          //--Check sum
    Ir_ns(1,1);                               //--Stop bit
    Ir_ns(snd_dt_idct[2], 0);                 //--Repeat code
  }
}
      //---回数送信---
void Ir_ns(const byte n, byte c) {  //回数 n, 1/0 c
  byte i = 0;
  while (i < n) {
    if (itpt_clk == 1) {
      snd_sw = c;
      i += 1;
      itpt_clk = 0;
    }
  }
  i = 0;
}
      //---変換送信---
void Ir_cs(const byte dt[], byte s) {
  byte lsb;
  byte i = 0;
  byte j = 0;

  while (i < s * 8) {  //ビットが1なら(1)(0)(0)(0)、0なら(1)(0)
    lsb = dt[i / 8] >> (i % 8) & 1;
    if (itpt_clk == 1) {
      snd_sw = 1;
      i++;
      itpt_clk = 0;
      while (j < (2 * lsb) + 1) {
        if (itpt_clk == 1) {
          snd_sw = 0;
          j++;
          itpt_clk = 0;
        }
      }
    }
    j = 0;
  }  //--while
}
       //---チェックサム生成---
byte Mk_chksm(const byte dt_c[], byte s_c, const byte dt[], byte s) {
  volatile byte chksm = 0;  //挙動がおかしいよくわからない

  for (int i; i < s_c; i++) { chksm += dt_c[i]; }
  for (int i; i < s; i++) { chksm += dt[i]; }
  return chksm;
}

//----------[4].割込み

       //---555の入力---
void Ext_clk() {
  bitWrite(PORTD, pin_irout, snd_sw);
  itpt_clk = 1;
}
送信したいデータを1bitずつ変数にセットしておいて、555による外部割込みのタイミングを利用し5PINのHIGH/LOWを切替えている。

考察

他に知らないからという理由で555を選んだわけだが、今回RC発振の彼を使うのは上策では無いのだろう…。
エアコンの解するデータ数で約189[bit]*2[回]、約0.4[ms]で区切った時の――送信用に変換した後のデータ数は378~756[bit]*2[回]。それだけの情報を非同期で(歩調同期で)送るのでも0.n%というオーダーの誤差では大きすぎる。
0.0n%の変化で1bitずれるおそれが充分にあると考える。

なおマイコンのタイマを利用する場合、ソフトウェア的には外部割込みをそれに置き換える程度の事だろうと見込んでいる。

0 件のコメント :

コメントを投稿