XBee応用製作第1弾 リモートレリーズ 実験編

前回の調査結果に基づいてレリーズケーブルの改造を行い、以下のような形態で実験に成功した。

なんのことはなくて、ATmega88のPORTDにTD62083を接続して、シャッター端子を駆動しているだけ。

カメラとつなぐとこんな感じ。

XBee親機の先のTeraTermからキー入力するとピント合わせとシャッターを切ることができた。

XBee応用製作第1弾 リモートレリーズ 調査編

無事にXBeeが動くようになったので、その応用を考えてみることにする。
第一弾は最近使わなくなったデジカメSP-560UZのリモートレリーズに挑戦しようと思う。
普通、リモートレリーズというと、

な感じのケーブル(これはETSUMIのRM-L1-O2・・・のはず)を付けて、有線でシャッターを切るのだが、今回はそれをXBeeとAVRを使って無線式に改めることにする。早速中をばらしてみると、

な感じの基板が出てきた。左上のジャックは2.5ミリのステレオミニジャックで、どうやらカメラ以外にもう1台レリーズできるようになっているようである。

今回は、有線レリーズの機能を生かしたまま無線レリーズの機能を追加したいので、この2.5ミリのステレオミニジャックを入力端子として使うことを考えてみることにした。(注:まだ実際に改造して確認したわけではないので注意!)

右上のケーブルの黒はGNDで、シャッターを半押しすると白がダイオードを経由してGNDへ、さらに全押しすると赤がダイオードを経由してGNDへ落ちるようになっている。さらにパターンを追っていくと、左上のジャックからは写真で横向きになっているダイオード2つを通して、真ん中のスイッチに接続されているようなので、この横向きのダイオード2つを取っ払い、オレンジで書いた線の通りに接続すると、2.5ミリステレオミニジャックから制御できるようになるはず。2.5ミリのミニジャックは根元がGND、RチャンネルをGNDに落とすと半押し、Lチャンネル(先端)をGNDに落とすと全押しになるはずである。(しつこいけど、動作未確認)
この状態でSWを押すとショートするように見えるけど、実際には2.5ミリのミニジャックからはオープンコレクタまたはオープンドレインで駆動するので、問題は生じない。

~つづく~

ATmegaとのシリアル通信~AVRLibのtimerのテスト(その後)

しばらく前にAVRlibのテストをしていましたが、その際にPWMが動いていないようだ、ということを書きました。

結局、端子の割り付けの差だったようで、
  // set the OC1x port pins to output
  // We need to do this so we can see and use the PWM signal
  // ** these settings are correct for most processors, but not for all
  sbi(DDRD, PD4);
  sbi(DDRD, PD5);

の部分を、ATmega88のOC1xの端子配置に合わせて、
  // set the OC1x port pins to output
  // We need to do this so we can see and use the PWM signal
  // ** these settings are correct for most processors, but not for all
  sbi(DDRB, PB1);
  sbi(DDRB, PB2);
に直してやると、あっけなく動作しました。
(よく見ると、コメントに違うCPUもあるよ、って書いてありますね・・・)
その際にいろいろと調べていたのですが、結局開発環境としてはArduinoへの乗り換えを検討しています。
死蔵しているATmega88がいっぱいあるのでちょっともったいないのですが、趣味の世界では他の人が築き上げたものを使わない手はありません。貴重な時間は最終的なアプリケーションに費やした方が楽しいですし。(それがArduinoがウケている理由だと思うのです)
で、調べてみると、Arduinoの世界では、ATmega168とかだと19200bpsでのシリアル通信が使われるようなので、XBeeの通信速度も19200bpsに変更しました。

PIC24USBでUSBシリアルに挑戦 (5)結末

XBeeの通信モニタ用に作っていたPIC24USBによるシリアルモニタだが、挫折してしまった。
Microchipから提供されているUSB-CDCクラスのフレームワークは、割り込みで動作させていても結局ポーリングをし続ける必要があり、そこに大きな構造上の制約を受けてしまう。

そこに時間を割いても面白くないので、ホストとのI/FはFT245にやらせることにして、以下のようなボードと、その上で動く簡単なシリアルラインモニタソフトを作成した。9600bps程度で動かしているので、速度的にはまったく問題ないはずである。

 
ボード上にはAE-FT245とPIC24FJ64GB006が載っている。
左上のコネクタはISP用である。

FT245でホストとI/Fしながら、いくつかの端子を下の10ピンコネクタに出し、PIC24の特徴であるピンアサイン機能を使って、2チャンネルのUARTレシーバを10pinコネクタの5pinと6pinに出すようにした。
ホスト側に接続するとこんな感じになる。

 

上半分が子機、下半分が親機である。これでシリアルの状態が見えるようになったので、先に進めることにしたい。

PIC24USBでUSBシリアルに挑戦 (4)送信バッファ組込み

アナライザとしてPIC24USBを使う場合、大量のデータがPIC24USBからPCに上がってくることになるが、Microchip社が提供するUSB Frameworkでは送信バッファがそのままUSBコントローラに渡されるため、送信データは毎回ブロッキングされてしまう。これではちょっと都合が悪いので、ソフトウェアのバッファを噛ませることにした。
1KBのソフトバッファをかませたうえでのUSB-CDCの通信速度は約200~250Kbpsといったところのようである。PIC24USBからSOF割り込みの回数カウント値とデータ表示回数をカウントした値をsprintfで表示したものを、PC側でTeraTermProでひたすら表示しているだけである。
XBeeとAVRの間は9600bpsなので、上り下りで約20Kbpsである。凝った解釈をしなければなんとか表示できると思うのだが・・・。

PIC24USBでUSBシリアルに挑戦 (3)ソースを改造してみる

USB Frameworkは無事に動作したのだが、ループの中で常にCDCTxService()を呼び出し続けなければならない構造になっていて、改造しにくい。できればそんなものは割り込みなどで監視してほしいものである。

なので、もう少しコードを眺めてみることにした。そこでわかったこと、試してみたことは、

  • usb_config.h の中に、
    #define USB_INTERRUPTがあり、PIC24FJ256GB106ではUSBは割り込みで動作している。
  • main.c の最後に、BOOL USER_USB_CALLBACK_EVENT_HANDLER()という関数があり、そこでUSB割り込み発生後のユーザー用のハンドラを記述できる。
    EVENT_TRANSFERがパケット転送後、EVENT_TRANSFER_TERMINATEDがパケット転送中断後のイベントのようである。Start of Frame は EVENT_SOF
  • 問題の面倒くさい処理 CDCTxService()は、usb_function_cdc.c の最後にある。
    細かくは見ていないが、USBHandleBusy(CDCDataInHandle)を確認し、BUSYでなければ送信データをエンドポイントのサイズに分割しながらメモリ中にコピーして、USBTxOnePacket()を呼び出している。このUSBTxOnePacket()は、usb_device.h の中で
    #define USBTxOnePacket(ep,data,len) USBTransferOnePacket(ep,IN_TO_HOST,data,len)
    となっている。
  • putUSBUART()は、mUSBUSARTTxRam()を呼び出している。その際、cdc_trf_stateがCDC_TX_READYかどうかをチェックしていて、CDC_TX_READYでない場合には、何もせずに帰ってくる。即ち、送信しようとしたデータは捨てられることになるが、事前にUSBUSARTIsTxTrfReady()をチェックしていれば、そのようなことは起きないはず。
  • cdc_trf_stateはエンドポイントの初期化でCDC_TX_READYにセット、CDC_TX_COMPLETINGの状態でCDCTxService()が呼ばれるとCDC_TX_READYにセット、mUSBUSARTTxRam()などを呼ばれるとCDC_TX_BUSYにセットされるようである。整理すると、「CDC_TX_READY」→「(送信データセットで)CDC_TX_BUSY」→「(データの終わりのZeroLengthPacket送信で)CDC_TX_ZLP(省略あり)」→「CDC_TX_COMPLETING」→「CDC_TX_READY」となるようである。
  • mUSBUSARTTxRam()は渡されたデータのポインタ(アドレス)、長さなどをモジュール内のグローバル宣言の構造体や変数などにコピーするだけ。実際の転送処理はCDCTxService()に任される形になる。

つまり、putUSBUART()などから送信データをポインタ渡しされたら、そのポインタをコピーしておき、CDCTxService()が呼ばれる度に状態をチェックして、送信データをエンドポイントのサイズに分割しながらコピー、送信する、という動作のようである。

これらのことから、以下のようにすれば概ね上手く行くのではないかと考えた。

  • 送信データをセットする前に、USBUSARTIsTxTrfReady()をチェックして、CDCTxService()が抱えている未送信データの有無をチェックする。(CDC_TX_READYかチェックする)
  • 送信データを準備し、putUSBUART()でそのポインタを教えてやる
  • その直後に、一度だけCDCTxService()を呼び出し、送信を開始する。CDCTxService()はデータをエンドポイントのサイズに切り出して最初のパケットを送信する。
  • USER_USB_CALLBACK_EVENT_HANDLER()の中のEVENT_TRANSFERのところに、CDCTxService()を追加して、1パケット送信ごとに自動的にCDCTxService()が呼ばれるようにする。割り込み処理内なのでネストしてしまうリスクがあるが、USBUSARTIsTxTrfReady()を実行してから送信処理をしていれば大丈夫なはず・・・。

ということで、実際に試してみた。確認のため、usb_config.hの

#define CDC_DATA_OUT_EP_SIZE 64
#define CDC_DATA_IN_EP_SIZE 64
を共に4に変更してみたところ、送信(PCから見ると受信)に関しては問題ないようである。
しかし、逆方向はファイルを流し込むと送受信を同時に行った際にデータを取りこぼしてしまう。もう少し調べなければ・・・。

PIC24USBでUSBシリアルに挑戦 (1)準備編

ずっと前に買ったオプティマイズのPIC24USBを引っ張り出してきた。
このボードに搭載されているPIC24FJ256GB106にはUSBデバイスインタフェースとシリアルポートが4チャンネルあるので、これらを使ってシリアルラインモニタを作ろうと考えている。シリアルラインモニタを使って、XBeeのプロトコルをモニタする作戦である。

USBプロトコルの処理は、Microchip Technology社から提供されているMicrochip Application Librariesの中のUSB Frameworkを使うつもりである。Microchip Application Librariesは、MicrochipのホームページのSoftware Librariesの中からダウンロードできる。

ダウンロードしてインストールすると、Microchip Solutionsというフォルダができるので、その中の「USB Device – CDC – Basic Demo」フォルダの中の、「CDC – Basic Demo – Firmware」フォルダの中の、「USB Device – CDC – Basic Demo – C30.mcw」をダブルクリックすると、MPLABが立ち上がり、プロジェクトが開く。

ここで、「Configure」→「Select Device」でPIC24FJ256GB106を選択し、さらに「Project」→「Package in .zip」を選択すると、ビルドに必要なファイルだけが「USB Device – CDC – Basic Demo – C30.zip」という名称でZIPに圧縮して保存される。これを別のフォルダで展開したものを作業のベースとすることにした。

試しに、「Project」→「Build All」すると、ちゃんとHEXファイルが生成されることを確認できる。

PIC24USBでUSBシリアルに挑戦 (2)サンプルソースを動かしてみる

まず、回路図を比較してみる。後述の通り、PIC24FJ256GB106を使うボードのターゲットはPIC24F Starter Kitである。こいつの回路図はMicrochip社のWebサイトで入手できた。PIC24USBとの主な差分は、

  • Starter Kitのクロックは、PIC18F67J5(デバッグ用のPICか?こいつのクリスタルは12MHzとなっている。)から供給されているので、周波数がよくわからない。一方で、PIC24USBのクリスタルは16MHz。
  • Starter Kitには32kHzのクリスタルがついている。
  • Starter KitはPGEC2/PGED2にICSP端子がつながるようになっているが、PIC24USBはPGEC1/PGED1にICSP端子がつながるようになっている。

であった。さらにソースを一通り眺めてみた。

  • 「main.c」からは「HardwareProfile.h」がインクルードされている。
  • 「HardwareProfile.h」を見ると、PIC24FJ256GB106を選択すると「Hardware Profile – PIC24F Starter Kit.h」がインクルードされることがわかる。
  • 「Hardware Profile – PIC24F Starter Kit.h」をみると、91行目で「PIC24F_STARTER_KIT」がdefineされる。
  • 再び「main.c」を見ると、213行目でCONFIGビットの設定が記述されていることがわかる。
    _CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx2)
    _CONFIG2( 0xF7FF & IESO_OFF & FCKSM_CSDCMD & OSCIOFNC_ON & POSCMOD_HS & FNOSC_PRIPLL & PLLDIV_DIV3 & IOL1WAY_ON)

    CONFIG1は、JTAG禁止、コードプロテクトOFF、ライトプロテクトOFF、Clip-on Emulatorイネーブル、ウォッチドッグタイマOFF、さらに「ICS_PGx2」でPGEC2/PGED2とICSP端子をシェアする設定になっているので、ここを「ICS_PGx1」に変更する必要がありそう。
  • CONFIG2はTwo Speed Start UpがOFF、Clock Switch & Monitorが両方OFF、OSCO端子機能はRC15、HS oscillator、Primary oscillarot w/ PLL、RPレジスタへの書込み制限無しで、さらに「PLLDIV_DIV3」はオシレータの周波数が12MHzの時の設定だから、ここは「PLLDIV_DIV4」へ変更が必要と思われる。
  • 再び「Hardware Profile – PIC24F Starter Kit.h」をみると、95行めから122行目でデモボード上のLEDやスイッチに関する入出力の記述がある。

ということがわかった。

そこで、「HardwareProfile – PIC24F Starter Kit.h」の94行めから123行めのLEDとSWのマクロ定義の中身を適当に修正する。ポート読出し用は0か1固定、ポート書込み用は中身を空にすることにした。この状態だと、「main.c」のProcessIO()関数の中身によれば、入力されたASCIIコードに1を足したものをエコーする(つまり、AをタイプするとBを表示する)プログラムとなるようだ。

修正してコンパイルしたところ問題なくコンパイルが通ることが確認できた。

これをPICkit2で書き込んでリセット解除すると、新しいCDCクラスのデバイスとして認識され、「USB Device – CDC – Basic Demo」フォルダの下の「inf」フォルダ内のinfファイルをインストールすると、COM12ポートが生成された。TeraTermでCOM12を開いて、キーを入力すると、期待通りタイプしたキーのASCIIコードに1を足した文字が表示された。

ATmegaとのシリアル通信~AVRLibのtimerのテスト

同様に、AVRlibのタイマーのテストを行いました。修正箇所は、

  1. プロジェクトディレクトリの global.h のCPU動作周波数を8MHzに修正
  2. プロジェクトディレクトリの Makefile のROM書込みに関する部分を追加変更前は、

    変更後は、
  3. 同じく Makefile のソースファイルに関する部分を変更
    変更前は、

    変更後は、
  4. 同じく Makefile の依存関係に関する部分を変更
    変更前は、

    変更後は、

結局、タイマー周りの修正が要る、ってことですね。
これで一応シリアルの表示上は動いている(ポーズもそれっぽく掛かっている)気がするのですが、PWM出力の方は出ていないようです。
この辺はまた今度にすることにします。

ATmegaとのシリアル通信~AVRLibの導入

ATMEGA88を使うことにしたのですが、シリアル制御を一から作るのも面倒くさい、ということで、ちょっと古いようですが、AVRLibを試してみることにしました。

まず、ZIPファイルをダウンロードしてきて、展開します。
環境変数AVRLIBに avrlib のディレクトリ位置を(avrlibの名前を)設定して、rprintf のサンプルディレクトリに移動。
makeすると、makeはされるのですが、ATmega163用にmakeされてしまいます。
今回のターゲットはATmega88なので修正を試みます。

現状の修正箇所は、

  1. サンプルプログラムの makefile の MCU の定義
    「MCU=atmega88」に書き直します。
  2. サンプルプログラムの makefile の AVRLIB の設定
    環境変数ではなく、ここで記述してみます。(未検証)
    AVRLIB = /(ほげほげ)/AVR-PROJ/avrlib
  3. サンプルプログラムの makefile の SRC の設定
    timer.c を使うとレジスタ定義が異なるので、timerx8.c に変更
  4. サンプルプログラムの makefile の終わりの方の依存関係(dependencies)の修正
    「timer.o : timer.c timer.h global.h」を「timer.o : timerx8.c timerx8.h global.h」に修正
  5. global.h の F_CPUの値の修正
    現状、7372800 が選択されているが、内蔵8MHzを使用するので、8000000に修正
    サンプルプログラムのプロジェクトディレクトリにも global.h がいるので、そちらも直す。
  6. 最近のGCCならCOFファイルはなくても大丈夫だったような気がするので、avrproj_make の「all :」の依存ファイルから $(TRG).cof を削除
  7. どうせなら書込みまでやってしまいたいので、avrproj_make の最後に

    ###### BLOCK 12) make instruction to delete created files ######prog: all
    avrdude $(WRITER) -p $(WRITER_MCU) -F -U flash:w:$(TRG).hex -U eeprom:w:$(TRG).eep -U efuse:w:$(EFUSE):m -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m

    を追加。(このままだと拡張ヒューズビット無しのCPUに書くとき困るけど、とりあえずそのままで・・・)

  8. 同じくサンプルのmakefileに

    # FUSE BIT for ATMEGA88/168(default is E=0x01/H=0xDF/L=0x62)
    EFUSE = 0x01
    HFUSE = 0xDF
    # LHUSE = 0x62
    LHUSE = 0xe2# WRITER parameter
    WRITER = -c avrispmkII -P usb
    WRITER_MCU = m88

    を追加

とすることで、rprintfのテストを実施が通るようになりました。(サンプルプログラムの方の global.h を修正し忘れていて、通信速度がおかしく、しばらく悩んでしまいました・・・)

無事にシリアル通信が通るようになりました!