Hatena::ブログ(Diary)

ふつうのプログラマーが電子工作に再チャレンジ このページをアンテナに追加 RSSフィード

2012-06-16

2008-09-09

2008-09-06

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

オリンパスのリモコンRM-1

のコードを5分おきに送信してシャッターを切ります。

材料

// 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();
}

wdtサンプル

_delay_msは電力の無駄なのでwdtLEDをチカチカさせるサンプル。


// tiny2313V @ 1MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <util/delay.h>

/*
volatile long timer = 0;
ISR(WDT_OVERFLOW_vect)
{
}
*/

EMPTY_INTERRUPT(WDT_OVERFLOW_vect);
int main(void)
{
  int i;

  // start WDT
  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)|_WDT_1S;
                                // wdt設定 reset=0, interrupt=1, 1sec.
  sei();

  DDRB  = 0b00001111;
  for(i=0;i<5;i++){
    PORTB = 0b00001111;
    _delay_ms(100);
    PORTB = 0b00000000;
    _delay_ms(100);
  }

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  for(;;){
    for(i=0; i<8; i++){ // 0.02mA
      PORTB = i % 16;
      sleep_mode();
    }
    for(i=8; i<16; i++){ // 5.5mA-6.0mA
      PORTB = i % 16;
      _delay_ms(1000);
    }
  }
}

参考: http://avrwiki.jpn.ph/wiki.cgi?page=Getting+Started+Notes+%2D+SLEEP

2008-08-23

秋月通販で買えるAVRの比較

tiny2313V mega88 mega164P mega644P
秋月価格 100 250 400 550
ピン数 20 28 40 40
フラッシュメモリ 2k 8k 16k 64k
EEPROM 128 512 512 2048(2k)
SRAM 128 1024(1k) 1024(1k) 4096(4k)
タイマー 8*1,16*1 8*2,16*1 8*2,16*1
RTC -
PWM 4 6 6
ADC - 6 8
AC 1 8 8
2線シリアル USI TWI TWI
USART 1 1 2
SPI USI SPI SPI

2008-07-12

AVRホタル

暗いところに持っていくと点滅します。

部品はtiny2313とLEDを1個ずつ。

LEDを光センサーとして使っています。

f:id:elec_avr:20080712122902j:image

f:id:elec_avr:20080712122947j:image

f:id:elec_avr:20080712122838j:image

f:id:elec_avr:20080712124217j:image

// AVR Hotaru

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


#define SERIAL_DEBUG 0
#if SERIAL_DEBUG
  #define BAUD 2400
//#define SET_PORT(PORT, MASK, VALUE) PORT = (VALUE & MASK)|((~MASK) & PORT)
  void usart_init(void)
  {
    #define _UBRR (F_CPU/16/BAUD-1)
    UBRRH = _UBRR>>8;
    UBRRL = _UBRR;
    UCSRB = 0b00011000;
    UCSRC = 0b00000110;
  }
#endif

#define LED_DEBUG 1

int main(void)
{
  char ac0;
  int count    = 0;
  int on_count = 0;
  int on_rate  = 0;
  int i,j,k;
  int bright   = 0;
  int led_on   = 1;

#if SERIAL_DEBUG
  usart_init();
#endif

#if LED_DEBUG
  DDRD  = 0b01111100;
  PORTD = 0b00000000;
#endif

  for(;;){
    // 参考) http://elm-chan.org/junk/leddet/report.html
    // LEDポートに短時間"L"レベルを出力して接合容量Cjや浮遊容量Csを放電する。
    DDRB  = 0b00000001;
    PORTB = 0b00000000;
    _delay_ms(1);

    // LEDポートを入力に設定する→光電流により容量が充電され、入力電圧が直線的に上昇してくる。
    DDRB  = 0b00000000;
    PORTB = 0b00000000;

    // 4ms待ってから入射有りか判断
    _delay_ms(4);
    ac0 = ACSR & 0b00100000;
    if(ac0){
      on_count++;
    }
    count++;
    if(50<=count){
      on_rate  = 100*on_count/count; // 50サンプル取って平均
      count    = 0;
      on_count = 0;
    }

#if SERIAL_DEBUG
    while(!(UCSRA & 0b00100000));
    UDR = ac0 ? '*' : '.';
#endif

#if LED_DEBUG
    {
      unsigned char d = 0;
      if(on_rate<45){ d |= (1<<2); }
      if(on_rate<40){ d |= (1<<3); }
      if(on_rate<35){ d |= (1<<4); }
      if(on_rate<30){ d |= (1<<5); }
      if(on_rate<25){ d |= (1<<6); }
      PORTD = d;
    }
#endif

    DDRB  = 0b00000001;
    if(0){
      // 閾値でON/OFF
      if(on_rate<20){
        PORTB = 0b00000001;
      }else{
        PORTB = 0b00000000;
      }
      _delay_ms(30);
    }else{
      // 明るさ 0-100,100-0 のこぎり型に明滅
      int b;

      bright++;
      b = bright;
      if(100<bright){
        b = 200-bright;
        if(200<bright){
          bright = 0;
          led_on = (on_rate<20);
        }
      }

      //b = 100-on_rate;
      for(j=0; j<10; j++){
        if(led_on){
          PORTB = 0b00000001;
        }
        for(i=0; i<b; i++){
          _delay_ms(0.01);
        }

        PORTB = 0b00000000;
        for(i=0; i<100-b; i++){
          _delay_ms(0.01);
        }
      }
    }
  }
}