ようやくPIC12F675でリモコンの赤外線受信に成功した。
環境は下記の通り。
・SONYのテレビリモコン
・MPLAB X IDE
・XC8
・pickit3
・受信モジュールPL−IRM2161−XD1
sonyの赤外線フォーマットは書いてあるとおりだけど、
簡単に説明するとT = 600μsが全ての信号の単位長さになっており、
Frameというところに書かれている様に信号群がやってくる。
なので最初にLeaderが4THIGHできてあとは
1TLOW+(1or2)HIGHがひたすらくる。
8ピンPICでは詳細なデバッグが出来ないので、
信号の解析はarduinoのシリアル通信で行った。
スケッチは下記の通り。
|c|
const int PIN = 7;
const long TIMEOUT = 250000;
const int DATA_MAX = 400;
unsigned int lastInput = HIGH;
unsigned long startTime = micros();
int inputCount = 0;
int dataCount = 0;
unsigned int val;
unsigned long currentTime;
boolean data[100];
boolean highVal;
boolean isSony = false;
String str = "";
void setup() {
Serial.begin(57600);
pinMode(PIN, INPUT);
}
void loop() {
val = digitalRead(PIN);
unsigned long time;
while (val == lastInput) {
if (micros() - startTime > TIMEOUT) {
if (inputCount > 2 && isSony) {
String tmp = "";
for (int i = 0; i < dataCount; i++) {
tmp += String(data[i]) + " ";
}
Serial.println(tmp);
// Serial.println(str);
}
isSony = false;
inputCount = 0;
dataCount = 0;
str = "";
break;
}
val = digitalRead(PIN);
}
currentTime = micros();
time = currentTime - startTime;
str += String(time) + " ";
inputCount++;
if (inputCount == 3) {
if (1800 < time && time < 3000) {
isSony = true;
}
} else if (inputCount > 3) {
if (isSony && (inputCount % 2)) {
if (time < 1000) {
data[dataCount] = 0;
} else {
data[dataCount] = 1;
}
dataCount++;
}
}
lastInput = val;
startTime = currentTime;
}
||
これを実行するとリーダー以降の値の0と1の並びを確認できる。
実際の値も見たい場合はstr変数周りを使用してみると良い。
んで実行してみるとわかるんだけど
2チャンネルボタンは
1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1
3チャンネルボタンは
0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1
のようになっていて、仕様書通り左の7bitを見てみると
1〜9チャンネルボタンは右詰めbitにすると普通に0〜8の値になっている。
とても簡単だ。
Addressという部分はよくわからなかったので今回は無視することにした。
charだけで判断できるようになるし。
BDのリモコンでBDとかTVとか切り替えができるので、多分そのあたりで変えているのだと思う。
(ただしその関係で、ボタンを長押しには対応できていないので
長押ししてると別のボタンと判断してしまう場合があるので
必要であれば残りは自分で解析して下さい)
上を元にPIC側のプログラムを作ってみた。
デバッグ方法がLEDしかないので、
想定通りの値が来ていたらLED点灯…等のようにして
ひたすら細かいデバッグを行っていった。大変…。
最初はシリアル通信かLCDかpickitでデバッグできるピン数の多いICで
作成した方が効率よさげ…。
delayとかのdefineが確認用なので実際は不要。
受信解析に成功するとチャンネル番号-1回LEDが光るようになっている。
|c|
const char buttons[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; //0=1チャンネルボタン
const char buttonSize = sizeof(buttons) / sizeof(char);
char lastInput = 1;
char isSony = 0;
char data = 0;
unsigned long time = 0;
unsigned char inputCount = 0;
unsigned char dataCount = 0;
void setData(int no, char row) {
//sonyは7bitなのでそれ以外は無視
if (no >= 7) return;
data |= row << no;
}
main() {
GPIO = 0;
CMCON = 0x07; // コンパレータ未使用
TRISIO = 0b00010000; // GP4:in
//プリスケーラ8=8μS…カウント75で600μ(1T)…だけどなんか64くらいっぽい…
OPTION_REG = 0b10000010;
TMR0 = 0;
ANSEL = 0b00110000;
char val;
while(1) {
val = IR;
while (val == lastInput) {
if (TMR0 > 200) {
time += TMR0;
TMR0 = 0;
}
if (time + TMR0 > TIMEOUT) {
if (inputCount > 2 && isSony) {
for (char i = 0; i < buttonSize; i++) {
if (buttons[i] == data) {
for (char j = 0; j < i; j++) {
LED = 1;
__delay_ms(200);
LED = 0;
__delay_ms(200);
}
}
}
}
isSony = 0;
inputCount = 0;
dataCount = 0;
data = 0;
}
val = IR;
}
time += TMR0;
inputCount++;
if (inputCount == 160) LED = 1;
if (inputCount == 2) {
if (192 < time && time < 320) {
isSony = 1;
}
} else if (inputCount > 2) {
if (isSony && !(inputCount % 2)) {
if (time < 100) {
setData(dataCount, 0);
} else {
setData(dataCount, 1);
}
dataCount++;
}
}
lastInput = val;
time = 0;
TMR0 = 0;
}
}
||
はまったところのメモ。
前述のように信号は0と1の並びで来るのだが、
ボタンを離すまで信号が連続していることに気づかず
大量の0と1が必要と勘違いしていた。
そうすると入力内容を保存しているdata変数を配列にしてしまっていたのだが、
なんとこのICは64バイトくらいしかメモリを使用できないらしく、
大量に値を保存するプログラムにするとビルドに失敗する。
その辺りで四苦八苦していた名残でdataは1バイトのcharに
綺麗にまとまった作りになっている。
button2 = {1, 0, 0, 0, 0, 0, 0}
等とは間違ってもやらないように…。
あとarduinoのスケッチとほぼ同じように作ったつもりなのだが、
arduino側は信号群の先頭2つがゴミ、
PIC側は先頭一つだけがゴミ、
となっておりそれが原因で上手くマッチング出来ず困っていた。
いまだに原因はわからないのでとりあえず注意。
とりあえずおもちゃの電車を動かしたいと思っていただけなので
ボタン長押し不要のためこれで問題ない。
ラジコンなどを作りたいひとはaddressの部分やループの部分も作りこんで下さい。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント