masayasanのアイコン画像
masayasan 2022年05月06日作成 (2022年05月06日更新)
製作品 製作品 閲覧数 3010
masayasan 2022年05月06日作成 (2022年05月06日更新) 製作品 製作品 閲覧数 3010

PICマイコンを使用した赤外線リモコンカー

PICマイコンを使用した赤外線リモコンカー

概要

無線通信に多く使われる赤外線の送信機及び受信機を作成します。
赤外線通信は日光や距離、遮蔽物の影響を受けやすいですが、材料費が安く制作しやすいので遠隔操作する手段としては汎用性があります。
今回は赤外線通信で動作するリモコンカーの製作を目標とします。

送信機

キャプションを入力できます
入力側は前進、後退、右、左の4点。
出力側は赤外線LED、黄色LEDの2点。
赤外線LEDは視認出来ないので、黄色LEDで電源ON及びプログラムが動作していることを確認します。

回路図

キャプションを入力できます
使用マイコンはPIC16F1823。
MPLABでMCCが使用できるマイコンです。MCCが使用できるのならば他のPICでも良いです。
電源は3.0V(単三電池 2本)
ここでのポイントは赤外線の出力はトランジスタで増幅させてあげることです。
PICの出力ポートでは10数mA程度しか電流が流せないため、赤外線の届く距離が短すぎます。
今回は秋月電子で下記の赤外線LEDを使用しました。

3mm赤外線LED 940nm OSI5FU3A11C 
■主な仕様
・標準電流:50mA
・VF:1.35V(IF=100mA)
単三電池2本(3V)で使用する予定ですので33(Ω)以上の抵抗器で標準電流値以下で使用することになります。
3-1.35=1.65V
1.65/0.05=33(Ω)
今回は47Ωの抵抗器を使用しましたので、約28mAの電流での使用となります。

ソフト

MCC設定
動作周波数は4MHz。
ボタンは前後左右のみとなります。入力側は内部抵抗でプルアップしています。
キャプションを入力できます

赤外線受信モジュールは38kHzで反応する様になってますので、38kHzの波形をつくり16ビットコードで動作信号を送ります。
OFF時間が長いビットを"1"、OFF時間が短いビットを"0"としました。

送信機側コード

#include "mcc_generated_files/mcc.h" unsigned int IRData=0; bool IRplsPattern; void IR0(void) { for (int i = 0; i < 15; i++) { IRLED_SetHigh(); LED_SetHigh(); __delay_us(15); IRLED_SetLow(); LED_SetLow(); __delay_us(0); } __delay_us(500); } void IR1(void) { for (int i = 0; i < 15; i++) { IRLED_SetHigh(); LED_SetHigh(); __delay_us(15); IRLED_SetLow(); LED_SetLow(); __delay_us(0); } __delay_us(1500); } void IRblink(void) { for (int k = 0; k < 2; k++) { for (int i = 0; i < 16; i++) { IRplsPattern = IRData << i & 0x8000; if (IRplsPattern == 0) { IR0(); } else { IR1(); } } IR0(); __delay_ms(100); } } void main(void) { SYSTEM_Initialize(); while (1) { if (FOW_GetValue() == 1 && BACK_GetValue() == 1 && LEFT_GetValue() == 1 && RIGHT_GetValue() == 1) { IRData=0x6000; } else if (FOW_GetValue() == 0 && BACK_GetValue() == 1 && LEFT_GetValue() == 1 && RIGHT_GetValue() == 1) { IRData=0x6100; } else if (FOW_GetValue() == 1 && BACK_GetValue() == 0 && LEFT_GetValue() == 1 && RIGHT_GetValue() == 1) { IRData=0x6200; } else if (FOW_GetValue() == 1 && BACK_GetValue() == 1 && LEFT_GetValue() == 0 && RIGHT_GetValue() == 1) { IRData=0x6010; } else if (FOW_GetValue() == 1 && BACK_GetValue() == 1 && LEFT_GetValue() == 1 && RIGHT_GetValue() == 0) { IRData=0x6001; } else if (FOW_GetValue() == 0 && BACK_GetValue() == 1 && LEFT_GetValue() == 0 && RIGHT_GetValue() == 1) { IRData=0x6110; } else if (FOW_GetValue() == 0 && BACK_GetValue() == 1 && LEFT_GetValue() == 1 && RIGHT_GetValue() == 0) { IRData=0x6101; } else if (FOW_GetValue() == 1 && BACK_GetValue() == 0 && LEFT_GetValue() == 0 && RIGHT_GetValue() == 1) { IRData=0x6210; } else if (FOW_GetValue() == 1 && BACK_GetValue() == 0 && LEFT_GetValue() == 1 && RIGHT_GetValue() == 0) { IRData=0x6201; } IRblink(); } }

何もボタンを押していない時は停止信号を送り続ける様にしました。
送信機側をオシロスコープで波形を取るとこんな感じで確認できます。
0x6000(停止信号)
キャプションを入力できます

受信機側

キャプションを入力できます
使用マイコンはPIC16F18326。
電源は4.5V(単三電池 3本)
サーボモータ及びDCモータを使用するので、電圧降下を考慮して電源には余裕を持たせた方が良いです。
前後左右の動作だけであるならばIO点数が少ない4ピンタイプのマイコンでも良いのですが、
後付けで色々な機能を持たせたいので12ピンタイプを選びました。

回路図

モーターはNPN,PNPトランジスタを使用したHブリッジ回路で制御。
照度センサーを使って暗い場所ではLEDが点灯するようにしました。
赤外線受信機に関しては、
将来的には複数の受信機を付けて多方面からの信号を受信できるようにしたいですが今回はとりあえずIR受信機は一つ。
キャプションを入力できます

ソフト

MCC設定
動作周波数は2MHz。
動作周波数が少ないほど必要とする電源電圧は低いので、低めに設定します。
また、シリアルモニタで受信した数値などのデータを確認するためシリアルピンも設定しておきます。
キャプションを入力できます
キャプションを入力できます

プログラムコード

赤外線LEDからの波長(38kHz)を受ける赤外線受信機はOFFします。
今回はコードを受信してからのON時間を計測して、ON時間が長いビットを"1"、短いビットを"0"としました。
一定時間コードを受信しないと停止する様にしています。

受信機側コード

#include "mcc_generated_files/mcc.h" #include "stdio.h" #define mode 0 unsigned int IRCode; unsigned int TMR0vale = 0; unsigned int TMR0stop = 0; void LIGHT() { int Bright; Bright = ADC_GetConversion(Brightness); // printf("%u\n",ADC_GetConversion(Brightness)); if (Bright > 1000) { LED_SetHigh(); Music_SetHigh(); } else { LED_SetLow(); Music_SetLow(); } } void Servo1QuickMove(int Angle1) { for (int i = 0; i < 1; i++) { Servo1_SetHigh(); for (int j = 0; j < Angle1; j++) { __delay_us(10); } Servo1_SetLow(); for (int k = 0; k < 500 - Angle1; k++) { __delay_us(10); } } } void Offtime(void) { int num = 0; int point = 0; int IRcheck = 0; unsigned int TMR1offvale = 0; unsigned int TMR1offvale2 = 0; unsigned int IRoffvaletime[17]; TMR0vale = TMR0_ReadTimer(); // printf("tim0_%u\n", TMR0stop); while (IRcheck == 0) { TMR0stop = TMR0_ReadTimer() - TMR0vale; if (TMR0stop > 500) { IRCode=0x6000; IRcheck = 1; } // printf("tim0_%u\n", TMR0stop); if (IR_GetValue() == 0 && point == 0) { point = 1; } while (point == 1) { TMR0stop = TMR0_ReadTimer() - TMR0vale; if (TMR0stop > 500) { LED_SetHigh(); IRCode=0x9999; IRcheck = 1; } if (IR_GetValue() == 1 && point == 1) { TMR1offvale = TMR1_ReadTimer(); point = 2; } } while (point == 2) { TMR1offvale2 = TMR1_ReadTimer() - TMR1offvale; //IR_GetValue()==1,IR信号OFF時の時間計測 if (IR_GetValue() == 0) { //設定値未満、IR信号ONになった時、IRoffvaletime[]に記録 // printf("touroku\n"); IRoffvaletime[num] = TMR1offvale2; if (TMR1offvale2 < 5000) { num = num + 1; point = 1; } else if (TMR1offvale2 >= 5000) { //信号OFF時間が設定値以上以上の場合は計測終了 point = 3; } } if (TMR1offvale2 >= 5000) { //信号OFF時間が設定値以上の場合は計測終了 point = 3; } } while (point == 3) { //計測時間表示と初期化 if (num == 16) { for (int i = 0; i < num; i++) { printf("[%doff]%d\n", i, IRoffvaletime[i]); //シリアルモニタに出力 IRCode = IRCode << 1; if (IRoffvaletime[i] < 1000) { } else { IRCode = IRCode + 1; } } } point = 0; num = 0; IRcheck = 1; } } } void ACTION(void) { switch (IRCode) { case 0x6000: IN1_SetLow(); IN2_SetLow(); Music_SetLow(); Servo1QuickMove(38); break; case 0x6100://FORW IN1_SetLow(); IN2_SetHigh(); Servo1QuickMove(38); break; case 0x6200://BACK IN1_SetHigh(); IN2_SetLow(); Servo1QuickMove(38); break; case 0x6010://LEFT Servo1QuickMove(33); break; case 0x6001://RIGHT Servo1QuickMove(43); break; case 0x6110://FORWleft Servo1QuickMove(33); IN1_SetLow(); IN2_SetHigh(); break; case 0x6101://FORWright Servo1QuickMove(43); IN1_SetLow(); IN2_SetHigh(); break; case 0x6210://BACKleft Servo1QuickMove(33); IN1_SetHigh(); IN2_SetLow(); break; case 0x6201://BACKright Servo1QuickMove(43); IN1_SetHigh(); IN2_SetLow(); break; case 0x6333://LIGHT Music_SetHigh(); default: break; } } float Distance() { int num = 1; float distance1; float distance2; for (int i = 0; i < num; ++i) { Trig_SetLow(); __delay_us(2); Trig_SetHigh(); __delay_us(10); Trig_SetLow(); uint16_t Time1 = TMR1_ReadTimer(); while (Echo_GetValue() == 0 || TMR1_ReadTimer() - Time1 > 65500); // while (Echo_GetValue() == 0); Time1 = TMR1_ReadTimer(); while (Echo_GetValue() == 1 || TMR1_ReadTimer() - Time1 > 65500); // while (Echo_GetValue() == 1 ); Time1 = TMR1_ReadTimer() - Time1; // distance=Time1; distance1 = Time1 / 8 * (331.5 + 0.61 * 20) *100 / 1000000; distance2 = distance2 + distance1; __delay_ms(60); } distance2 = distance2 / num; return (distance2); } void main(void) { SYSTEM_Initialize(); while (1) { LIGHT(); Offtime(); // printf("IRCode_%X\n", IRCode); ACTION(); } }

受信機側をオシロスコープで計測すると送信機側のコードを受信出来ていることが確認できます。
0x6000(停止信号)
キャプションを入力できます

シリアルモニタでOFF時間の確認もできます。
キャプションを入力できます

動作確認

赤外線LEDと受信機の向きが合っていないと動作しないので、近づかないと停止してしまいます。
赤外線の届く距離に関して言えば5mくらいでも大丈夫でしたので方向性さえ合えば、もっとスムーズに操作出来るかも。

ここに動画が表示されます

次は複数の受信機を付けて多方面からの信号を受けれる様にします。

1
  • masayasan さんが 2022/05/06 に 編集 をしました。 (メッセージ: 初版)
  • masayasan さんが 2022/05/06 に 編集 をしました。 (メッセージ: シリアルモニタ画像追加)
  • masayasan さんが 2022/05/06 に 編集 をしました。 (メッセージ: 送信機側回路図修正、誤字修正)
  • masayasan さんが 2022/05/06 に 編集 をしました。 (メッセージ: 送信機回路図修正)
ログインしてコメントを投稿する