フレフレ赤外線リモコン送信機 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

受信機と制御対象

Itasumashi

・発信された赤外線信号を受信して動くロボットも作ってみた。このロボットは、以前作成した「イタスマシ」に、赤外線リモコン受信モジュールを接続し、ファームウェアを変更したもの。

・このロボットは、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