オリンパスのデジカメのリモコンをAVRで作った

オリンパスのリモコンRM-1
のコードを5分おきに送信してシャッターを切ります。

材料

  • Atmel AVR Tiny2313V
  • 赤外線LEDが1個
  • (パスコン)
  • 単三電池2本&電池ボックス
// Olympus Remocon

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <util/delay.h>
#define nop() __asm__ __volatile__ ("nop")

void send_ir(int us, char on)
{
  // 38KHz 1sec=1000ms=1000000us => 38000
  // 1000000/38000 = 26.3157895us on/off 13.1578947us
  // 1MHz => 1sec => 1000000 => 1op=1us
  for(; 0<=us; us-=26){
    PORTB = on ? 0b00000011 : 0b00000000;
    nop(); nop(); nop(); nop(); nop();
    nop(); nop(); nop(); nop(); nop();

    // ON 12 / OFF 1.. 6
    // ON 10 /     3.. 8
    // ON  5 /     8..13

    PORTB = 0b00000000;
    nop(); nop(); nop(); nop(); nop();
  }
}

void send_data(char* data, int bits)
{
  int i;
  int lead_high_len = 9000;
  int lead_low_len  = 4500;
  int data_high_len =  560;
  int data_low0_len =  560;
  int data_low1_len = 1690;
  int stop_high_len =  560;

  DDRB  = 0b00000011;
  send_ir(lead_high_len, 1);
  send_ir(lead_low_len,  0);
  for(i=0; i<bits; i++){
    int on = data[i/8] & (1<<(7-i%8));
    send_ir(data_high_len, 1);
    send_ir(on ? data_low1_len : data_low0_len, 0);
  }
  send_ir(stop_high_len, 1);
  _delay_ms(1000);
}

EMPTY_INTERRUPT(WDT_OVERFLOW_vect);

void init_wdt_interrupt(int interval)
{
  cli();
  wdt_reset();
  MCUSR &= ~(1<<WDRF);          // 「WDTリセットされた」フラグクリア
  WDTCSR |= (1<<WDCE)|(1<<WDE); // wdt変更前処理
#define _WDT_15MS  ((0<<WDP3)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0))
#define _WDT_30MS  ((0<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0))
#define _WDT_60MS  ((0<<WDP3)|(0<<WDP2)|(1<<WDP1)|(0<<WDP0))
#define _WDT_120MS ((0<<WDP3)|(0<<WDP2)|(1<<WDP1)|(1<<WDP0))
#define _WDT_250MS ((0<<WDP3)|(1<<WDP2)|(0<<WDP1)|(0<<WDP0))
#define _WDT_500MS ((0<<WDP3)|(1<<WDP2)|(0<<WDP1)|(1<<WDP0))
#define _WDT_1S    ((0<<WDP3)|(1<<WDP2)|(1<<WDP1)|(0<<WDP0))
#define _WDT_2S    ((0<<WDP3)|(1<<WDP2)|(1<<WDP1)|(1<<WDP0))
#define _WDT_4S    ((1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0))
#define _WDT_8S    ((1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0))
  WDTCSR = (1<<WDIE)|(0<<WDE)|interval;
                                // wdt設定 reset=0, interrupt=1
  sei();
}

void main_olympus(void)
{
  char shutter[] = {0b01100001, 0b11011100, 0b10000000, 0b01111111};
  char wide[]    = {0b01100001, 0b11011100, 0b01000000, 0b10111111};
  char tele[]    = {0b01100001, 0b11011100, 0b11000000, 0b00111111};
  char minus[]   = {0b01100001, 0b11011100, 0b00100000, 0b11011111};
  char plus[]    = {0b01100001, 0b11011100, 0b10100000, 0b01011111};
  int  i, t;

  init_wdt_interrupt(_WDT_1S);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  for(;;){
    send_data(shutter, 32);
    send_data(shutter, 32);
    send_data(shutter, 32);
    for(i=0; i<57; i++){ sleep_mode(); } // 57sec..

    for(t=0; t<4; t++){ // 1min x 4.
      send_data(wide, 32);
      for(i=0; i<59; i++){ sleep_mode(); }
    }
  }
}

int main(void)
{
  main_olympus();
}