フレフレ赤外線リモコン送信機 Rev.2
概要
・棒を一生懸命振ると、赤外線信号が発信されるリモコン送信機と受信機のセットだ。送信機のハードウェアを改良するとともに、送信機と受信機のファームウェアを改良した。
・今度のモデルでは、14種類のコマンドを区別してプログラムできる。プログラムを作りさえすれば一つの回路で14chのコントローラが作れるともいえる。
・本機に内蔵された圧電振動ジャイロモジュールが振動を検出したときに、周波数38kHzの赤外線信号を出力する。プログラムによって、4ビットのコマンドを送信することができる。理論的には16種類のコマンドが表現できるが、プロトコルが単純なので、14種類しか識別できない。(コマンドプロトコルについては添付ファイルを参照)
・回路図やファームウェアのソースなどは、添付ファイルを参照のこと。
送信機
・今回の回路では、スライドスイッチにより、2種類のコマンドを切り替えて送信する。
・ケースには、有名なチョコレート菓子のパッケージを再利用した。
・Eagle形式の回路図やファームウェアのバイナリファイル(HEXファイル)などは、添付ファイルを参照のこと。
使用部品
圧電振動ジャイロモジュール (村田製作所ENC-03R使用、秋月電子通商)
AVRマイコン ATMEGA168P
赤外線LED
LED(発信状態のモニタ用)
カーボン抵抗器 150Ω, 1kΩ
積層セラミックコンデンサ 0.1uF
ユニバーサル基板(ケースのサイズに合わせて切り取ったもの)
28ピンICソケット
スライドスイッチ×2個
電池ボックス(単4型1本用)×2個
回路図
送信信号のプロトコル
※AVRマイコンの出荷時クロック周波数(1MHz)で動作するように設定した。
※パルスは、38kHzで変調されている。
ファームウェア(ソースコード)
// Mega168 Ir-LED Remote Contorller Unit ( Transmitter )
// AVR Device : ATmega168 @ CK 1MHz ( or 8MHz )
// OC1B(PB2) : IR-LED
// PB0, PB1 : LED 1:ON, 0:OFF
// PB6 : Command Select Switch
// ADC0, ADC1 (PIN23, 24)
#include <avr/io.h>
#include <avr/interrupt.h>
#define led0_on() ( PORTB |= (1<<PB0) )
#define led0_off() ( PORTB &= ~(1<<PB0) )
#define is_sw() (PINB & (1<<PB6))
#define irled_on() ( PORTB |= (1<<PB2) )
#define irled_off() ( PORTB &= ~(1<<PB2) )
#define ADC_VAL_MIN 0x60
#define ADC_VAL_MAX 0xb0
volatile unsigned int TIMER1F = 0;
ISR(TIMER1_COMPA_vect)
{
TIMER1F++;
}
//#define FREQ38KHZ 96 // (CK 8Mhz) value of OCR1A at 38kHz Frequency
#define FREQ38KHZ 12 // (CK 1Mhz) value of OCR1A at 38kHz Frequency
#define FREQZERO 0 // value of OCR1A at No Frequency
void init_timer1_ctc(void)
// Timer/Counter1 を CTC (Clear Timer on Compare mode)モードで初期化
{
// Freq = IOclock (1MHz or 8MHz) / 2N(1 + OCR1A) , N=1(no Prescaling)
TCCR1A = (0<<WGM11) | (0<<WGM10) | (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (1<<COM1B0);
TCCR1B = (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10);
OCR1A = FREQ38KHZ; OCR1B = 0x0000; // Freq. 38kHz
TIMSK1 |= (1<<OCIE1A);
}
void connect_OC1(void)
{
TCCR1A = (0<<WGM11) | (0<<WGM10) | (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (1<<COM1B0);
}
void disconnect_OC1(void)
{
TCCR1A = (0<<WGM11) | (0<<WGM10) | (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0);
}
#define PULSEWIDTH 100 // Signal Pulse Interval (Number of TIMER1 Interrupt)
#define BLANKWIDTH 1500 // Signal blank Interval (Number of TIMER1 Interrupt)
void out_bit_1(void)
{
connect_OC1();
TIMER1F = 0;
while( TIMER1F < PULSEWIDTH);
}
void out_bit_0(void)
{
disconnect_OC1();
TIMER1F = 0;
while( TIMER1F < PULSEWIDTH);
}
void out_blank(void)
{
disconnect_OC1();
TIMER1F = 0;
while( TIMER1F < BLANKWIDTH );
}
void output_command_pattern(uint8_t bit_pattern)
{
out_bit_1();//START bit
if ( bit_pattern & 0x01) {
out_bit_1();
} else {
out_bit_0();
}
if ( bit_pattern & 0x02 ) {
out_bit_1();
} else {
out_bit_0();
}
if ( bit_pattern & 0x04 ) {
out_bit_1();
} else {
out_bit_0();
}
if ( bit_pattern & 0x08 ) {
out_bit_1();
} else {
out_bit_0();
}
out_blank();
}
void init_adc(void)
{
// AD Conv. Enable & Clock ferquency and the input clock to the ADC)
ADCSRA = ( (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (0<<ADPS0) );
}
uint8_t get_ad(char adcport)
// arg: adcport : 0 -
{
cli(); // Disable Interrupt
ADMUX = (1<<ADLAR) | adcport; // AD-Conv. 8-bit precision, Use ADC-PORT0
ADCSRA |= (1<<ADIF); // Set ADC Interrupt flag
ADCSRA |= (1<<ADSC); // Start AD Conv.
while ( ( ADCSRA & (1<<ADIF) ) == 0); //Wait to complete adc
sei(); //Enable Interrupt
return (uint8_t)ADCH; // 8-bit precision
}
#define COMMAND06 0x06
#define COMMAND09 0x09
int main(void)
{
uint8_t s0, s1;
uint8_t command = COMMAND06;
init_timer1_ctc(); // CTC mode
init_adc(); // ADC port initialize
// PORTB2, 1, 0 を出力モード, PORTB6 Input mode Pullup Resister ON
PORTB |= (1<<PB6);
DDRB = (1<<PB2) | (1<<PB1)| (1<<PB0);
// PORTC0,1,2,3 Input mode
PORTC = 0xff;
DDRC = ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) );
// PORTD2, 3 Input mode
PORTD = 0xff;
DDRD = ( (0<<PD2) | (0<<PD3) );
disconnect_OC1(); OCR1A = 0; led0_off(); // At First, No OUTPUT
sei(); // interrupt enable
while(1) {
// Setup Command code
if (is_sw()) {
command = COMMAND06;
} else {
command = COMMAND09;
}
// Detect Vibration
s0 = get_ad(0x00); // ADC0
s1 = get_ad(0x01); // ADC1
// Control Signal Output
if ( (ADC_VAL_MIN < s0 && s0 < ADC_VAL_MAX) ||
(ADC_VAL_MIN < s1 && s1 < ADC_VAL_MAX)) {
disconnect_OC1();
OCR1A = 0;
led0_off();
} else {
led0_on();
OCR1A = FREQ38KHZ;
output_command_pattern(command);
output_command_pattern(command);
output_command_pattern(command);
output_command_pattern(command);
output_command_pattern(command);
}
}
return 0;
}
makefile ( Ubuntuで開発するときに使用したもの)
# AVR-GCC Makefile
PROJECT=transmitter-mega168
SOURCES=transmitter-mega168.c
CC=avr-gcc
OBJCOPY=avr-objcopy
MMCU=atmega168
TARGETDEV=m168P
CFLAGS=-mmcu=$(MMCU) -Wall
$(PROJECT).hex: $(PROJECT).out
$(OBJCOPY) -j .text -O ihex $(PROJECT).out $(PROJECT).hex
$(PROJECT).out: $(SOURCES)
$(CC) $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES)
program: $(PROJECT).hex
sudo avrdude -p $(TARGETDEV) -P usb -c avrispmkII -e -U flash:w:$(PROJECT).hex
clean:
rm -f $(PROJECT).out
rm -f $(PROJECT).hex
受信機と制御対象
・発信された赤外線信号を受信して動くロボットも作ってみた。このロボットは、以前作成した「イタスマシ」に、赤外線リモコン受信モジュールを接続し、ファームウェアを変更したもの。
・このロボットは、2台の送信機から送られてくる別々のコマンドに反応する。といっても動作は単純で、1台の送信機からの信号で右のモータが回転し、別の1台の送信機からの信号で左のモータが回転するだけだ。
使用部品
AVRマイコン ATtiny2313V
赤外線リモコン受信モジュール PL-IRM2161-C438 (3V動作対応)
FET 2SK2231 (2個)
ショットキーバリアダイオード 1S10
整流用ダイオード 1N4007 (2個)
電解コンデンサ 470uF (2個)
カーボン抵抗 150Ω(2本), 1kΩ, 10kΩ
20ピンICソケット
スライドスイッチ
ユニバーサル基板
バッテリースナップ
電池ボックス(単3型2本用)
回路図
受信信号のプロトコル
※赤外線受信モジュールPL-IRM2161-C438の出力は、Active Lowなので、送信側と電圧レベルが逆になる。
ファームウェア(ソースコード)
// INT0/PD2 : Ir-remote controller unit ( Reciever )
// PB4, PB3 : Motor driver
// PB0 : LED
#include <avr/io.h>
#include <avr/interrupt.h>
#define led_off()
( PORTB |= (1<<PB0) )
#define led_on()
( PORTB &= ~(1<<PB0) )
#define led() ( PINB |= (1<<PB0) )
#define motor0_on() ( PORTB |= (1<<PB3) )
#define motor1_on() ( PORTB |= (1<<PB4) )
#define motor0_off() ( PORTB &= ~(1<<PB3) )
#define motor1_off() ( PORTB &= ~(1<<PB4) )
#define is_ir() ( PIND & (1<<PD2) )
volatile unsigned int TIMER1F = 0;
ISR(TIMER1_COMPA_vect)
{
TIMER1F++;
}
//#define FREQ38KHZ 96 // (CK 8Mhz) value of OCR1A at 38kHz Frequency
#define FREQ38KHZ 12 // (CK 1Mhz) value of OCR1A at 38kHz Frequency
#define FREQZERO 0 // value of OCR1A at No Frequency
void init_timer1_ctc(void)
// Timer/Counter1 を CTC (Clear Timer on Compare mode)モードで初期化
{
// Freq = IOclock (1MHz or 8MHz) / 2N(1 + OCR1A) , N=1(no Prescaling)
TCCR1A = (0<<WGM11) | (0<<WGM10) | (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0);
TCCR1B = (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10);
OCR1A = FREQ38KHZ; OCR1B = 0x0000; // Freq. 38kHz
TIMSK |= (1<<OCIE1A);
}
#define PULSEWIDTH 100 // Signal Pulse Interval (Number of TIMER1 Interrupt)
#define BLANKWIDTH 1000 // Signal blank Interval (Number of TIMER1 Interrupt)
#define COMMANDWIDTH 2000 // Signal COMMAND Sequence Period (Number of TIMER1 Interrupt)
#define PATTERN_NG 0xff
uint8_t get_data(void)
{
uint8_t d = 0;
uint8_t i;
if ( is_ir() ) {
led_off();
// Search blank period
TIMER1F = 0;
while ( is_ir() ) {
if (TIMER1F >= COMMANDWIDTH ) return PATTERN_NG;
}
if ( TIMER1F >= BLANKWIDTH ) {
led_on();
// Wait for Start Bit
while ( is_ir() );
for ( i = 0; i < 4; i++) {
// Step to Next Pulse
TIMER1F = 0;
while ( TIMER1F < PULSEWIDTH ) {
// Data BIt 1
if ( is_ir() == 0 ) {
d |= (1<<i); led_on();
} else {
d |= (0<<i); led_off();
}
}
}
}
return d;
}
uint8_t generate_bit_data(uint8_t bit_pattern)
{
uint8_t data = 0;
int i;
for (i = 3; i >= 0; i-- ) {
data |= ( ( bit_pattern & 0x01) << i);
bit_pattern >>= 1;
}
return data;
}
#define COMMAND06 0x06
int main(void)
{
uint8_t d, target;
// Initialize I/O Port
PORTD &= ~(1<<PD2);
DDRD &= ~(1<<PD2) ; // PD2 Input Mode
DDRB = (1<<PB0) | (1<<PB3) | (1<<PB4);
init_timer1_ctc();
sei();
while (1) {
// Test Recieve Data Pattern
d = get_data();
target = generate_bit_data(COMMAND06);
if (d == target) {
motor0_on(); motor1_off();
} else if (d == ( (~target) & 0x0f) ) { // reverse bit pattern
motor0_off(); motor1_on();
} else {
motor0_off(); motor1_off();
}
}
return 0;
}
makfile (Ubuntuのgccで制作するときに使用)
# AVR-GCC Makefile
PROJECT=ir-reciever-tiny2313
SOURCES=ir-reciever-tiny2313.c
CC=avr-gcc
OBJCOPY=avr-objcopy
MMCU=attiny2313
TARGETDEV=t2313
CFLAGS=-mmcu=$(MMCU) -Wall
$(PROJECT).hex: $(PROJECT).out
$(OBJCOPY) -j .text -O ihex $(PROJECT).out $(PROJECT).hex
$(PROJECT).out: $(SOURCES)
$(CC) $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES)
program: $(PROJECT).hex
sudo avrdude -p $(TARGETDEV) -P usb -c avrispmkII -e -U flash:w:$(PROJECT).hex
clean:
rm -f $(PROJECT).out
rm -f $(PROJECT).hex