日々の記録

ほどよく書いてきます。

ニコン一眼レフリモコン

2014年05月18日 01時32分23秒 | AVR

やりたいこと:微速度撮影の制御。ロータリースイッチでシャッター間隔の時間を決める。
機材:ニコンのデジタル一眼レフカメラ(持ってるのはD40,D90)

以前、ニコンの一眼レフリモコンのプログラムを作ったのだが、ブレッドボード上で回路を作っただけで長時間野外で使うにはお粗末なものであった。
また、カメラの電池の容量が稼働時間を制限するため、夜通しの撮影などは無理だった。

で、ヤフオク。Nikonの一眼レフのACアダプタを落札@3800円。決して安くないカメラなので、純正部品にした。最近めっきり出番のないD40を一晩屋外放置などしようと思ったので、D40用のアダプタも落札@1000円。こちらも純正部品。

回路は簡単なもので、さくっと出来上がる。当初は赤外線LEDだけだったが、途中で動いているのか不安になったので、赤色LEDを追加した。リモコンコード送信のときに赤LEDも光るので電池切れで動かないなどがわかる。暗闇の中で使うのを前提にしたので赤色のあまり明るくないLEDを採用。電源はNiMH×4で最大5.2Vほど。5.5Vまでは使っていいみたいなので、ぎりぎりセーフ。電池の過放電が気になるが、無視しよう。一晩程度なら問題ない。

 

まず赤外線のフォーマットだが、キャリア周波数38kHz程度で、Duty 1/3のパルスである。
ニコンのレリーズ信号はこんな漢字らしい。
    turn_on();  _delay_us( 2000);  //2ms発光
    turn_off();  _delay_us(27850);  //27.85ms消灯
    turn_on();  _delay_us(  390);  //0.39ms発光
    turn_off();  _delay_us( 1580);  //1.58m消灯
    turn_on();  _delay_us(  410);  //0.41ms発光
    turn_off();  _delay_us( 3580); //3.58ms消灯
    turn_on();  _delay_us(  400); //0.4ms発光
    turn_off();  _delay_us(63200); //63.2ms消灯
合計99.4msだが、実際は100msだと思う。誰かが測定した結果上記のようになったんだと思う。

では、マイコンのプログラム要素を考える。
38kHz Duty 1/3を8ビットタイマで実現する。出力をピンに接続するかどうかで上記のturn_on()とturn_off()を作ることにする。

マイコンは内臓9.6MHzのDIV8で1.2MHzで動かす(出荷状態どおり)→38kHzの1サイクルは1200/38≒31.5カウントで到達する。カウンタは31までカウントしたらリセットかかるようにして、0カウントでHigh, 10カウントでLowとなるように設定する。DIV8なしで9.6MHz駆動の場合はこれの8倍の値になって、たぶん普通のPWMモード(255カウントでリセットかかる)が使える。
カウンタのトップ値を設定するPWMモードの場合、OCR0Aレジスタで上限決定、OCR0BでDuty決定といったことをやるため、PWM出力がOC0B(PB1)になってしまいます。なので外付けDIPスイッチはPB0,PB2,PB3,PB4という飛び地になってしまいます。

8ビットカウンタの設定

カウンタでは何をするかというと、こんな設定
TCCR0A=0b00000011; //0b00100011と切り替えることでLEDオンオフをやる。


TCCR0B=0b00001001;

CS02-CS00はカウンタへのクロック供給である。1.2MHzをそのまま入れるのでCS02-CS00は001になる。

WGM02-00はレジスタを跨いでいるので注意が必要だが、111の設定(最終行)にする。TOPが0CR0Aになる高速PWMモード。

最後にCOM0Bxの設定だが、非反転の動作を使う。カウンタ0で出力high、カウンタがOCR0Bと一致でlowの出力になる。

ちなみに、このPWMの設定にたどり着くのに一週間以上かかった。

ここまでくれば後は簡単、LEDのON/OFFは関数作るか、そのまま書くかで出来上がり。
void turn_on(void){
      TCCR0A=0b00100011;
}

void turn_off(void){
      TCCR0A=0b00000011;
}


ポートの設定とピン状態の読み込み

PB1を出力にして、ほかを入力端子かつ内部で抵抗プルアップを施す。外に抵抗つけるの面倒くさいじゃない。そして、ロータリースイッチがコンプリメンタリである必要はここからくる。ツマミがオレンジというか朱色のを使うとよい。
DDRB = 0b00000010;
PORTB = 0b00011101;  //入力なのにPORTレジスタに1を書き込むと内部プルアップ!!なるほど!

さて、PB0,PB2,PB3,PB4という4端子を使うのですが、飛び地なので、そのままでは面倒があります。一番右のビットをひとつ左にシフトして全体を右にビットシフトできればいいなー、いいなー、いいなー・・・こういうときは風呂だな。
で、風呂で思いつく。(PINB++)>>1だ!

(0b00011100++)>>1=(0b00011101)>>1=0b00001110
(0b00011101++)>>1=(0b00011110)>>1=0b00001111

きっと世の中じゃ常識なんだろうけど、思い通り程度の計算量で出来上がって便利。

 

ソースコード

/*ハジマリ*/

#define F_CPU 1200000

#include <avr/io.h>
#include <util/delay.h>
#include
#define SignalTime 99

int main(void)
{
    DDRB   = 0b00000010;    // 出力方向にする
    PORTB  = 0b00011101;    //抵抗プルアップ
    TCCR0A = 0b00000011;
    TCCR0B = 0b00001001;
    OCR0A = 31;  // (( 1.2MHz / 38kHz ) / 1(分周) ) - 1 = 30.6
    OCR0B = 10;  // duty比を1/3にする ( 1.2MHz / 38kHz ) / 3 = 10.5

    while(1)
    {
        TCCR0A = 0b00100011;        _delay_us( 2000);     //サブルーチン使わず、コピペでやったれ!
        TCCR0A = 0b00000011;         _delay_us(27850);
        TCCR0A = 0b00100011;         _delay_us(  390);
        TCCR0A = 0b00000011;         _delay_us( 1580);
        TCCR0A = 0b00100011;         _delay_us(  410);
        TCCR0A = 0b00000011;         _delay_us( 3580);
        TCCR0A = 0b00100011;         _delay_us(  400);
        TCCR0A = 0b00000011;         _delay_us(63200);

        uint8_t IntervalStatus  = ((PINB & 0b11101) + 1) >> 1;   //ふっ、おいらC育ちじゃないから++演算子わすれてたよ。

        if(IntervalStatus ==   0)  {_delay_ms(  1000-SignalTime);} //1秒ごとのシャッター
        if(IntervalStatus ==   1)  {_delay_ms(  2000-SignalTime);}
        if(IntervalStatus ==   2)  {_delay_ms(  5000-SignalTime);}
        if(IntervalStatus ==   3)  {_delay_ms( 10000-SignalTime);}
        if(IntervalStatus ==   4)  {_delay_ms( 20000-SignalTime);}
        if(IntervalStatus ==   5)  {_delay_ms( 30000-SignalTime);}
        if(IntervalStatus ==   6)  {_delay_ms( 40000-SignalTime);}  //星の撮影だと露光30sほどやるのでたぶんこの辺をよく使う
        if(IntervalStatus ==   7)  {_delay_ms( 60000-SignalTime);}
        if(IntervalStatus ==   8)  {_delay_ms(120000-SignalTime);}
        if(IntervalStatus ==   9)  {_delay_ms(240000-SignalTime);}
    }
}

/*オワリ*/

待機時間はカウンタが止まっていても問題ないが、一連の信号を送信する間、2ms発光から最後の0.4ms発光まではコヒーレントな38kHzがいいかとおもってタイマはとめていない。もっとも面倒くさくなったので、上記のプログラムではずっとタイマは動いている。タイマとめても大して節電にならない気がするから。

スリープに入って、など節電できる工夫はまだできるが、充電電池使うし、いつ飽きるかわからないので、今回はこれでよし。

そんなブログを書いている間に月は昇った。送電線が邪魔なので、引っ越したいくらい。ACアダプタがないと長時間の露光ができないので事実上部屋のまどから外を撮影、というのが今の限界である。


 

組みあがった回路はこちら。配線がほとんどない。赤外線LEDはデジカメのところまで引っ張っていってる。

撮影の様子

送電線が邪魔だ!これさえなければ!これさえなければ!

 

備忘録

シャッター速度、長いほうから
Bulb, 30s, 25s, 20s, 15s, 10s, 8sというような感じなので、後で比較明コンポジットなどを行いたい場合はこれらの時間よりやや長い程度のものにしておくとよいかも。
35s, 30s, 25s, 20s,というような時間がいいだろうかと。

コメント (3)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ハイサイドP-MOSのゲート充放電

2014年05月16日 20時06分28秒 | 電子工作

MP4212というフルブリッジICのハイサイド(P-MOS)の駆動がどうなるか検討してみる。ゲートの充放電を滞りなく行う。

回路シミュレータは例によってLTSpiceIV。

R1の値を1k、3.3k、10kと変更してのシミュレーション。P-MOSはMP4212のP-MOSと同じ程度のゲート入力電荷(22nC)程度のものをつかった。

黄色:MOSFETの電力損失
紫:MOSFETのゲート電位
赤:入力のロジック信号(グラフ右端で0に落ちている)
緑:R1に流れる電流

10kΩの抵抗ではターンオフ開始まで70us、オフ完了までに120usもかかるが、1kΩ抵抗にするだけでかなり早くなる。たぶんMOSFETの損失も1/10くらいになる。
スイッチオフしたつもりでもまたMOSFETがONのままである時間が結構あるので、ここも要注意。ブリッジ回路だと上下のトランジスタが同時にONになるとまずい。

 

これでも満足できない場合は次のようにトランジスタを入れることでゲートの放電をサクっとやってしまう方法があります。しかしながらP-MOSのスレッショルド電圧があまりにも低いとトランジスタでオフしきれないばあいがあるので注意が必要です。

3usくらいでスイッチオフできているが、上の1kΩのものは大体5usでスイッチオフできているので、微妙といえば微妙かもしれない。12Vで動かしたとき、1kΩ抵抗だと12mAが無駄にながれるが、こっちは1.2mAだ。モーターを駆動するような電流の食い方をする回路では12mA程度はもしかしたら無視できるものかもしれない。

注意点はあまりにもスレッショルド電圧の低いMOSFETだとターンオフしきれない可能性がある点。ゲート電圧はVccまでは到達せず、トランジスタのベースエミッタ電圧だけ低い電位になる(Vcc-VBE)。なのでスレッショルド電圧が0.6VとかいうMOSFETを使うとこの回路ではだめ。

MOSFETのターンオフ(ターンオンもだけど)で、ゲート電圧が一定になっている箇所があるが、MOSFETのチャンネルに電荷を送り込むだけのゲートの電荷なのだろう。
実際のデバイスの動きがちゃんと盛り込まれているようだ。以下ルネサスの資料から抜粋。

コメント (1)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

備忘録(シンタックスハイライト, Syntax Highlight)

2014年05月01日 13時34分06秒 | プログラム

ソースコードをそのまま載せるとき、<などを変換してくれる。
http://sei-street.sakura.ne.jp/page/doujin/site/doc/tool_text2html/index.html

 

きれいに見せたいときは次で変換かな。文字数がブログのリミットに迫るから厳しい時もあるけど。
http://hilite.me/

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void Start_ADC(void)	/*ADC変換を開始する。自動トリガでADC1の値を読み込み続ける。プログラムからはADCを読めばよい*/
{
	//10bit分解能用ルーチン ADCは連続で動作させる
	//デジタル入出力からの切り離し
	DIDR0
	= (0<<ADC0D)	//ADC0ピンの切り離し。しかし、ADC0はリセット端子になってるから設定しちゃダメ
	| (1<<ADC1D)	//同ADC1, PB2
	| (0<<ADC2D)	//同ADC2, PB4
	| (0<<ADC3D);	//同ADC3, PB3

	ADMUX
	= (0<<REFS0)	//Reference: 0でVCC参照, 1で1.1V内部電圧源
	| (0<<ADLAR)	//ADC Left Adjust Result: 1にセットすると左詰で結果を出してくる 
	| (1);			//ADMUX1, ADMUX0で入力チャンネルを選べる 00から11までADC0からADC3まで対応 今はADC1を使う

	ADCSRA
	= (1<<ADEN)	//ADC enable
	| (1<<ADSC)	//ADC Start Conversion 1にすると変換開始
	| (1<<ADATE)	//ADC Auto Trigger Enable (for Continuous Conversion)
	| (0<<ADIF)	//ADC Interrupt Flag 変換完了すると1になるらしい
	| (0<<ADIE)	//ADC Interrupt Enable 変換完了したときに割り込み許可
	| (0b101);		//Clock Division: ADコンバータはクロック50-200kHzで性能がよい
	//分周は0のとき2、それ以上の時2^n分周。クロック4.8MHzのとき32分周(101)で150kHz。
	//ADCは起動時にアナログ回路リセットが入るので変換に25クロック(167us)必要
	//以降連続変換で回せば13クロックサイクル(87us)で変換完了→87us以内にADC読みに行くと前の値と同じってことになる

}
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする