– 20070709 —
最近 PHD Guiding というフリーのソフトによるオートガイド環境の構築がはやっているようでして、私もやってみようと思っています。ただし、私の環境ではリレーボックスがあれば簡単になるのでPHD Guiding ではASCOMのドライバを使用するようにして、ASCOMの設定でLX200用のシリアル出力するようにします。(撮影用のノートパソコンにはパラレルポートが無いのでこの方法を選択しています)
大雑把な仕様はPCのシリアルポートから出力される LX200用のコマンド文字列をAVRマイコンで受けます。東西南北の移動コマンドが来れば対応するポートをON/OFFさせることでリレーを動かします。リレーは昔作成したのがあるので、今回はマイコン部分のみが作成対象になります。使用するマイコンはAVRマイコンです。(手持ち在庫の関係です)
※表現としてリレーと言ってますが、トラジスタのスイッチングでも全然問題はありません。
開発環境の確認
AVRマイコンに対応する gcc があります。今回はこのWebサーバーにコンパイラをインストールして環境の作成をします。このWebサーバーは Debian/GNU Linux etch で稼動していますのでdebパッケージがあれば簡単です。パッケージがあるか確認してみます。
# apt-cache search avr ava - Algebraical Virtual Assembler for Atmel's AVR MCUs avarice - use GDB with Atmel's JTAG ICE for the AVR avr-libc - Standard C library for Atmel AVR development avra - Assembler for Atmel AVR microcontrollers avrdude - software for programming Atmel AVR microcontrollers avrdude-doc - documentation for avrdude avrp - Programmer for Atmel AVR microcontrollers avrprog - Programmer for Atmel AVR microcontrollers binutils-avr - Binary utilities that support Atmel's AVR targets. gcc-avr - The GNU C compiler (cross compiler for avr) gdb-avr - The GNU Debugger for avr libgringotts2 - encapsulate data in an encrypted and compressed file sdcc - Small Device C Compiler sdcc-doc - Small Device C Compiler (documentation) sdcc-libraries - Small Device C Compiler (libraries) simulavr - Atmel AVR simulator traceroute-nanog - Determine route of packets in TCP/IP networks (NANOG variant) uisp - Micro In-System Programmer for Atmel's AVR MCUs #
apt-cache の結果、gcc-avr と avr-libc がありましたので apt-get install でインストールすれば c言語で開発出来るようになります。コンパイル出来るかどうか確認するために、サンプルデモをコンパイルしてみます。
$ cp -a /usr/share/doc/avr-libc/examples/demo .
$ cd demo
$ gzip -d iocompat.h.gz
$ make
avr-gcc -g -Wall -O2 -mmcu=atmega8 -c -o demo.o demo.c
avr-gcc -g -Wall -O2 -mmcu=atmega8 -Wl,-Map,demo.map -o demo.elf demo.o
avr-objdump -h -S demo.elf > demo.lst
avr-objcopy -j .text -j .data -O ihex demo.elf demo.hex
avr-objcopy -j .text -j .data -O binary demo.elf demo.bin
avr-objcopy -j .text -j .data -O srec demo.elf demo.srec
avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O ihex demo.elf demo_eeprom.hex \
|| { echo empty demo_eeprom.hex not generated; exit 0; }
avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O binary demo.elf demo_eeprom.bin \
|| { echo empty demo_eeprom.bin not generated; exit 0; }
avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O srec demo.elf demo_eeprom.srec \
|| { echo empty demo_eeprom.srec not generated; exit 0; }
$ ls
Makefile demo.c demo.hex demo.map demo.srec demo_eeprom.hex iocompat.h
demo.bin demo.elf demo.lst demo.o demo_eeprom.bin demo_eeprom.srec
$
まぁ、demo.hex とかが作成されているので問題無いでしょう。
ASCOM がLX200を認識する条件を確認
— 20070713 —
ASCOM のLX200ドライバが接続を認識するために、何らかのコマンドをLX200に送信して、返ってくるレスポンスをもとに判断しているものと思われます。ので、どんなやりとりをやってるのか確認してみました。結果は以下のようなやりとりをしていました。これは、ASCOM 4.1 と LX200 3.34L の組合せの場合です。
***Generic LX200 Type Scopes の場合 ASCOM LX200 #:GR# 15:15.6# #:GD# -00゚01# #:GR# 15:15.6# #:U# #:GR# 15:15:37# #:GR# 15:15:37# #:GD# -00゚01:03# ***Meade Telescope and Focuser の場合 ASCOM LX200 #:GS# 15:25:32# :GVF# 無応答 ACK P :Sw 3# 1 :GZ# 004゚17# :GZ# 004゚17# :U# :GZ# 004゚17:51# :GR# 15:15:37# :GD# -00゚01:03# :GZ# 004゚17:51# :Gt# +35゚19# :GA# +54゚35:22# :GZ# 004゚18:00# :GD# -00゚01:03#
というやりとりした結果 connected となりました。
上記シーケンスの中で’゚’の記号は 0xdf ACKと書いてるのは 0x06 のコードです。これを見るとこれから作成するエミュレータには、Get系のコマンドに応答出来るような仕込みが必要と思います。
とりあえず作成開始
— 20070714 —
さて、3連休なのですが台風4号が来るとのことですので、家で工作することにしました。物は以下の写真のようになりました。ピンボケですが撮り直しは気が向いたらします。
使用したチップは AT90S2313 が手持ちでありましたのでそれを使用しています。とにかく、ASCOMのドライバと通信しないといけないのでUART付きのマイコンを使用すれば大丈夫です。
パソコンとの接続はシリアルになるわけですが、ノートパソコンにはRS232Cのポートが無いのでUSB-シリアル変換基板を大須のタケイムセンで買ってきました。FTDIのFT232RLが乗っているやつです。あとは AVRマイコンと接続させればOKです。
本日作成したのは、上記の写真までで、リレー側の出力はまだ配線していませんが早速PHD Guiding + ASCOM の LX200 ドライバで接続出来るか確認した結果、Generic LX200 Type Scopes で LXP Connected と Meade Telescope and Focuser で LX200 Connected というのが PHD Guiding のウィンドウの左下のステータス領域に出たので第一段階はクリアしましたということでしょう。
とりあえず作成開始(その2)
— 20070716 —
新潟県中越沖地震が発生しました。これ以上被害が広がることが無ければいいと思います。
さて、3連休も最終日です。ある程度は形にしておこうということで、残りの配線をしました。写真のような感じになっちゃいました。
最初はマイコン部分だけ作って、リレーボックスはpictor 201xt互換のものを使用するつもりでしたが、基板に余裕があったので、フォトカプラを付けました。フォトカプラにミニDIN 6ピンのコネクタを付けて、タカハシのオートガイダー端子に直接差せるようにしておきました。しかしながら私のは EM200b なのでオートガイダーの端子は無いため、ハンドコントローラからコネクタを分岐させて、それと接続しました。6ピンなのでPS/2 のケーブルが使用可能です。
動作確認はパソコンの端末ソフトから LX200のコマンドを手入力して行いました。東西南北の移動コマンドを入れていきます。赤道儀に耳を付けてモーター音が変化すればOKということですが、ちゃんとそのような動きをしていたので一安心です。あとはケースに入れないとだめですね。
晴れたので動作確認をしました。
7月23日薄曇のコンディションでしたが、久し振りの晴れです。
作った LX200エミュレータが動くか確認しました。
ガイド鏡とかは以下のとおりです。
- ガイド鏡: Meade 60mm/F11
 - ガイド用カメラ: Meade DSIPro
 - ノートパソコン(PHDGuiding 1.5/ASCOM 4.1)
 - 自作 LX200エミュレータ
 - EM-200b
 
ベランダに赤道儀を出してテストしました。
下のグラフのように、補正しながら稼動しました。
とりあえず、赤道儀を南向きのベランダに出した状態でテストしましたので、極軸はズレたままです。この状態でも、PHDGuiding の画面ではガイド星は動かなかったので一安心です。
今回は作ったハードが動くかどうかの確認ですので
目的は達成出来ました。
今回作った ASCOM 対応 LX200 エミュレータのソース
/* ——————————————————————————–
   —
   — Source name: lx200emu.c
   — Version: 0.1(20070714)
   — Author: Masahiro Kusunoki
   —
   — PHD Guiding –> ASCOM ドライバ –> LX200 コマンド出力 –> 当マイコンボックス –> EM-200b
   —
   — AT90S2313
   — 01 RESET
   — 02 PD0(RXD)
   — 03 PD1(TXD)
   — 04 XTAL2
   — 05 XTAL1
   — 06 PD2(INT0)
   — 07 PD3(INT1)
   — 08 PD4(T0)
   — 09 PD5(T1)
   — 10 GND
   — 11 PD6(ICP)
   — 12 PB0(AIN0)
   — 13 PB1(AIN1)
   — 14 PB2
   — 15 PB3(OC1)
   — 16 PB4
   — 17 PB5(MOSI)
   — 18 PB6(MISO)
   — 19 PB7(SCK)
   — 20 VCC
   —
   — シリアル回線については、9600/8N1 で接続
   —
   — 問題は、ASCOM にしても DSI にしても、どこまで返事を返せば
   — 接続 OK として認識してくれるのかが不明
   —
   ——————————————————————————– */
#include 
#include 
#define west PB0
#define east PB1
#define north PB2
#define south PB3
void commandCheck_start(unsigned char);
void commandCheck_Major(unsigned char);
void commandCheck_MandDirection(unsigned char);
void commandCheck_QandDirection(unsigned char);
void commandCheck_Get(unsigned char);
void uartPutChar(unsigned char);
void resRA(void);
void resDec(void);
void resAzi(void);
void resTrack(void);
void resSite(void);
int main(void);
/*
  commandStatus = 0 “:” が来るのを待つ
  commandStatus = 1 “M” “Q” の判定
  commandStatus = 2 “M” に続く方角の文字判定とリレー制御
  commandStatus = 3 “Q” に続く方角の文字判定とリレー制御
  commandStatus = 4 “G” に続く方角の文字判定とリレー制御
 */
unsigned char commandStatus = 0;
unsigned char longFormat = 0;
/* ——————————————————————————–
   — UART 受信割り込み処理(1文字来た)
   ——————————————————————————– */
SIGNAL(SIG_UART_RECV) {
  unsigned char ch01 = UDR;
  switch(commandStatus) {
	case 0:
	  commandCheck_start(ch01);
	  break;
	case 1:
	  commandCheck_Major(ch01);
	  break;
	case 2:
	  commandCheck_MandDirection(ch01);
	  break;
    case 3:
	  commandCheck_QandDirection(ch01);
	  break;
    case 4:
	  commandCheck_Get(ch01);
	  break;
  }
  return;
}
/* ——————————————————————————–
   — コマンド文字列開始判定
   ——————————————————————————– */
void commandCheck_start(unsigned char ch01) {
  if(ch01 == ‘:’) {
	commandStatus = 1;
  } else if(ch01 == 0x06) { //ACK が来たら無条件に’P'(赤道儀モード)を返す
	uartPutChar(‘P’);
  }
}
/* ——————————————————————————–
   — 移動開始か移動停止コマンドかの判定
   ——————————————————————————– */
void commandCheck_Major(unsigned char ch01) {
  switch(ch01) {
  case ‘M’:  // Movement Command
	commandStatus = 2;
	break;
  case ‘Q’:  // Movement Command
	commandStatus = 3;
	break;
  case ‘G’:  // Get Telescope Information
	commandStatus = 4;
	break;
  case ‘U’:  // Precision Toggle
	if(longFormat == 0)
	  longFormat = 1;
	else
	  longFormat = 0;
	commandStatus = 0;
	break;
  case ‘S’:  // Set command
	uartPutChar(‘1’);  // Setコマンドは無条件に1(true)を返す
	commandStatus = 0;
  default:
	commandStatus = 0;
	break;
  }
}
/* ——————————————————————————–
   — 移動開始(該当のポートを 1 にする)
   ——————————————————————————– */
void commandCheck_MandDirection(unsigned char ch01) {
  switch(ch01) {
  case ‘n’:
	PORTB |= (1 << north);
	break;
  case 's':
	PORTB |= (1 << south);
	break;
  case 'e':
	PORTB |= (1 << east);
	break;
  case 'w':
	PORTB |= (1 << west);
	break;
  }
  commandStatus = 0;
}
/* --------------------------------------------------------------------------------
   -- 移動停止(該当のポートを 0 にする)
   -------------------------------------------------------------------------------- */
void commandCheck_QandDirection(unsigned char ch01) {
  switch(ch01) {
  case 'n':
	PORTB &= ~(1 << north);
	break;
  case 's':
	PORTB &= ~(1 << south);
	break;
  case 'e':
	PORTB &= ~(1 << east);
	break;
  case 'w':
	PORTB &= ~(1 << west);
	break;
  case '#':
	PORTB &= ~((1 << north) | (1 << south) | (1 << east) | (1 << west));
	break;
  }
  commandStatus = 0;
}
/* --------------------------------------------------------------------------------
   -- Getコマンド処理
   -------------------------------------------------------------------------------- */
void commandCheck_Get(unsigned char ch01) {
  unsigned char saveLongFormat = 0;
  switch(ch01) {
  case 'A':  // Get Telescope Altitude
  case 'D':  // Get Telescope Declination
	resDec();
	break;
  case 'R':  // Get Telescope RA
  case 'r':  // Get current/target RA
	resRA();
	break;
  case 'Z':  // Get Telescope Azimuth
	resAzi();
	break;
  case 'T':
	resTrack();
	break;
  case 'S':  // Get the Sideral time
  case 'a':  // Get Telescope local time in 12Hour Format
	saveLongFormat = longFormat;
	longFormat = 1;
	resRA();
	longFormat = saveLongFormat;
	break;
  case 't': // Get current Site Latitude
	saveLongFormat = longFormat;
	longFormat = 0;
	resDec();
	longFormat = saveLongFormat;
	break;
  case 'M':
  case 'N':
  case 'O':
  case 'P':
	resSite();
	break;
  }
  commandStatus = 0;
}
/* --------------------------------------------------------------------------------
   -- レスポンス 赤経
   -- 00:00.0#
   -- 00:00:00#
   -------------------------------------------------------------------------------- */
void resRA(void) {
  uartPutChar('0');
  uartPutChar('0');
  uartPutChar(':');
  uartPutChar('0');
  uartPutChar('0');
  if(longFormat == 1) {
	uartPutChar(':');
	uartPutChar('0');
  } else {
	uartPutChar('.');
  }
  uartPutChar('0');
  uartPutChar('#');
}
/* --------------------------------------------------------------------------------
   -- レスポンス 赤緯
   -- 対象コマンド :GR :Gr
   -- +00*00#
   -- +00*00:00#
   -------------------------------------------------------------------------------- */
void resDec(void) {
  
  uartPutChar('+');
  uartPutChar('0');
  uartPutChar('0');
  uartPutChar(223);
  uartPutChar('0');
  uartPutChar('0');
  if(longFormat == 1) {
	uartPutChar(':');
	uartPutChar('0');
	uartPutChar('0');
  }
  uartPutChar('#');
}
/* --------------------------------------------------------------------------------
   -- レスポンス アジマス
   -- 対象コマンド :GZ
   -- 000*00#
   -- 000*00:00#
   -------------------------------------------------------------------------------- */
void resAzi(void) {
  uartPutChar('0');
  uartPutChar('0');
  uartPutChar('0');
  uartPutChar(223);
  uartPutChar('0');
  uartPutChar('0');
  if(longFormat == 1) {
	uartPutChar(':');
	uartPutChar('0');
	uartPutChar('0');
  }
  uartPutChar('#');
}
/* --------------------------------------------------------------------------------
   -- レスポンス トラッキング
   -- 対象コマンド :GT
   -- 00.0#
   -------------------------------------------------------------------------------- */
void resTrack(void) {
  uartPutChar('0');
  uartPutChar('0');
  uartPutChar('.');
  uartPutChar('0');
  uartPutChar('#');
}
/* --------------------------------------------------------------------------------
   -- レスポンス サイト名
   -- 対象コマンド :GM :GN :GO :GP
   -- ???#
   -------------------------------------------------------------------------------- */
void resSite(void) {
  uartPutChar('?');
  uartPutChar('?');
  uartPutChar('?');
  uartPutChar('#');
}
/* --------------------------------------------------------------------------------
   -- UART 一文字出力
   -------------------------------------------------------------------------------- */
void uartPutChar(unsigned char ch01) {
  loop_until_bit_is_set(USR, UDRE);
  UDR = ch01;
}
/* --------------------------------------------------------------------------------
   -- メイン処理
   -------------------------------------------------------------------------------- */
int main(void) {
  cli();
  PORTB = 0x00;
  DDRB = 0xFF;
  PORTD = 0x00;
  DDRD = 0xFF;
  /*
   * UART 制御レジスタ UCR の設定
   * RXCIE 受信完了割り込み
   * TXCIE 送信完了割り込み
   * UDRIE 送信データレジスタ空き割り込み
   * RXEN  受信許可
   * TXEN  送信許可
   */
  UCR = (1<











