PSoCでSDカードにGPSログを取りつつNikon D200へ位置情報を送る

psoc_gps_sd_d200_01.gif
今回は「PSoCでSDカードにGPSログを保存する」で作成したプロジェクトをさらに拡張して、デジカメ「Nikon D200」から電源を取り、Nikon D200での撮影画像内に位置情報が記録されるようにする。

psoc_gps_sd_d200_02.gif
前回はGPSから受信したデータをSDカードに保存するのみだった。今回は受信データをさらにNikon D200へ送信するので、送信用のモジュールをプロジェクトに追加する。
前回作成したプロジェクトを開いたら、「Design Editor」の「Selection」画面を開き、「Digital Comm」にある「TX8」を右クリックして現れるメニューから「Select」を選択して追加する。

psoc_gps_sd_d200_03.gif
「TX8_1」のアイコンが追加されたら、そのアイコンを右クリックした現れるメニューから「Rename」を選択して名前を「TX8」に変更しておく。

psoc_gps_sd_d200_04.gif
Nikon D200ではGPSが接続されている間は半押しタイマーが無効になる。半押しタイマーが無効になると露出系が常時作動した状態になるため、電池消耗が激しくなる。普段は24時間電源を入れっぱなしでも問題ないデジカメだが、半押しタイマーが無効になった状態では一晩置いただけで電池が空になり、メモリーカードの残撮影枚数すら表示されなくなる。
そのため自動的にGPS信号をデジカメへ送らないようにするためのタイマーを作るため、先ほどと同様に「Counters」にある「Counter16」を追加する。名前も「Counter16_1」から「Counter16」に変えておく。
ちなみに半押しタイマーの無効化はスピードライト用のシューに機器を接続したときにも起きる。Nikon D200を水中で使うためのハウジングに電源をONにしたまま入れて放置しておいたら完全に電源が切れてしまい、撮影ができなくなったことがある。半押しタイマー無効の挙動は困ったものだ。

psoc_gps_sd_d200_05.gif
モジュールの追加が済んだら「Interconnect」画面に切り替える。そして「Counter16」を右クリックして現れるメニューから「Place」を選択して配置する。

psoc_gps_sd_d200_06.gif
そして「Clock」を「CPU_32_KHz」に、
「Enable」を「High」に、
「CompareOut」を「None」に、
「TerminalCountOut」を「None」に、
「Period」を「30000」に、
「CompareType」を「Less Than Or Equal」に、
「InterruptType」を「Compare True」に、
「ClockSync」を「Sync to SysClk」にする。これでおおよそ1秒(0.92秒ぐらい)ごとに割り込みが起きるようになる。この割り込みを利用してタイマーを作る。

psoc_gps_sd_d200_07.gif
次に「TX8」を右クリックして現れるメニューから「Place」を選択して配置する。

psoc_gps_sd_d200_08.gif
そして「Clock」を「VC3」に、
「Output」を「Row_1_Output_1」に、
「TX Interrupt Mode」を「TXRegEmpty」に、
「ClockSync」を「Sync to SysClk」に、
「Data Clock Out」を「None」にする。さらに内部配線図でTX8の出力がポート01に接続されるようにする。


psoc_gps_sd_d200_09.gif
次にポート14の「Drive」を「Open Drain Low」に、「Interrupt」を「RisingEdge」に設定しておく。
このポート14にNikon D200からのVcc端子を接続しておく。Vcc端子はデジカメの半押しタイマーが作動している間のみHighになる。Highになったことを検出してGPS信号をNikon D200へ送り込むようにする。GPS信号を送り始めると半押しタイマーが無効になり、Vcc端子がHighのままになり続ける。

psoc_gps_sd_d200_10.gif
モジュール設定が済んだら、「Application Editor」画面に切り替えて、まず「main.c」の内容を修正する。


//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules

#include "stdlib.h"

//#define _DEBUG

int g_nCount;
BOOL g_bTx;

#pragma interrupt_handler COUNT16_INT
void COUNT16_INT(void)
{
g_nCount++;
if(g_nCount >= 60)
{
Counter16_Stop();
g_bTx = FALSE;
TX8_Stop();
}

#ifdef _DEBUG
LCD_Position(0,0);
LCD_PrHexInt(g_nCount);
#endif
}

#pragma interrupt_handler GPIO_INT
void GPIO_INT(void)
{
if(PRT1DR & 0x10)
{
//D200 semi-release, so timer start/restart
#ifdef _DEBUG
LCD_Position(0,0);
LCD_PrHexInt(g_nCount);
#endif
g_nCount = 0;
g_bTx = TRUE;
Counter16_Start();
TX8_Start(TX8_PARITY_NONE);
#ifdef _DEBUG
LCD_PrCString("ok");
#endif
}
}

// Can not retreave last data of CSV
BOOL GetCSVData(char* pszCSV,int nIndex,char** ppszStart,char** ppszEnd)
{
int i;
int nFind;

if(nIndex < 0)
return FALSE;

*ppszStart = pszCSV;
*ppszEnd = pszCSV;
nFind = 0;
i = -1;
while(1)
{
i++;
if(pszCSV[i] != ',')
continue;

nFind++;

if(nFind == nIndex)
*ppszStart = &(pszCSV[i+1]);
if(nFind == nIndex + 1)
{
*ppszEnd = &(pszCSV[i-1]);
return TRUE;
}
}

return FALSE;
}


BOOL IsFileExist(char* pszFileName)
{
char fp;

fp = SDCard_fopen(pszFileName,"r");
if(fp > SDCard_MAXFILES) // != SDCard_MAXFILES
return TRUE;
// if(fp == SDCard_MAXFILES || SDCard_ferror(fp) & 0x02) //check error flag of "File not found."
if(fp == SDCard_MAXFILES)
{
SDCard_fclose(fp);
return FALSE;
}
if(SDCard_GetFileSize(fp) == 0) //File is empty.
{
SDCard_fclose(fp);
return FALSE;
}
SDCard_fclose(fp);

return TRUE;
}

BOOL OpenSDCardFile(char* pfp,char* pszFileName)
{
int i;

for(i = 0; i < 2; i++)
{
BYTE ret;

*pfp = SDCard_fopen(pszFileName,"a");
#ifdef _DEBUG
LCD_Position(2,0);
LCD_PrCString("fp:");
LCD_PrHexByte(*pfp);
#endif
if(*pfp < SDCard_MAXFILES) // != SDCard_MAXFILES
return TRUE;

ret = SDCard_ferror(*pfp);
SDCard_fclose(*pfp);

#ifdef _DEBUG
LCD_Position(1,4);
LCD_PrCString("error ");
LCD_Position(1,10);
LCD_PrHexByte(ret);
#endif
}

return FALSE;
}


BOOL CreateFileName(char* pszRMCData,char* pszFileName)
{
//LOG file is not open, so make file name and open...
//file name is "ayymmdda.txt" to "zyymmdda.txt"

int j;
int k;
char* pszStart;
char* pszEnd;
BOOL ret;

//file name is made from date, and date data is only in RMC data.
if(!(pszRMCData[3] == 'R' && pszRMCData[4] == 'M' && pszRMCData[5] == 'C'))
return FALSE;

//Get date from RMC data.
ret = GetCSVData(pszRMCData,9,&pszStart,&pszEnd);
if(ret == FALSE || pszStart == pszEnd || pszEnd - pszStart < 5)
return FALSE;

pszFileName[1] = pszStart[4]; //year
pszFileName[2] = pszStart[5];
pszFileName[3] = pszStart[2]; //month
pszFileName[4] = pszStart[3];
pszFileName[5] = pszStart[0]; //day
pszFileName[6] = pszStart[1];

pszFileName[8] = '.';
pszFileName[9] = 't';
pszFileName[10] = 'x';
pszFileName[11] = 't';
pszFileName[12] = 0;


for(j = 0; j < 26; j++)
{
pszFileName[0] = 'a' + j;
for(k = 0; k < 26; k++)
{
pszFileName[7] = 'a' + k;

ret = IsFileExist(pszFileName);
if(ret == FALSE)
return TRUE;
}
}

return FALSE;
}


void main()
{
char fp;

g_bTx = FALSE;

// make Port16 to "strong" for LED
PRT1DM0 |= 0x40;
PRT1DM1 &= ~0x40;
PRT1DM2 &= ~0x40;

// make Port15 to "pull-down" for input sw
PRT1DM0 &= ~0x20;
PRT1DM1 &= ~0x20;
PRT1DM2 &= ~0x20;

#ifdef _DEBUG
LCD_Start();
#endif

SDCard_Start();
{
char cardInfo; // Card information

SDCard_Select(SDCard_ENABLE); // Select card
cardInfo = 0;
while ( ! cardInfo ) // Wait for card to communicate
{
cardInfo = SDCard_InitCard();
}
}

RX8_Start(RX8_PARITY_NONE);
RX8_DisableInt();
TX8_Start(TX8_PARITY_NONE);
TX8_DisableInt();
Counter16_EnableInt();

M8C_EnableGInt;
M8C_EnableIntMask(INT_MSK0,INT_MSK0_GPIO);

PRT0DR &= ~0x02; //Port01 is LOW (for TX8)
fp = SDCard_MAXFILES;
while(1)
{
int i;
int nLen;
char pszBuff[160];

i = 0;
while(1)
{
while ( !(RX8_bReadRxStatus() & (RX8_RX_COMPLETE | RX8_RX_NO_ERROR)))
{
}
pszBuff[i] = RX8_bReadRxData();
if(g_bTx)
TX8_SendData(pszBuff[i]); //ignore tx buffer empty flag
if(pszBuff[i] == '\n' && i > 0 && pszBuff[i-1] == '\r')
break;
i++;
if(i >= 158)
i = 0;
}
//pszBuff[i+1] = NULL;
nLen = i + 1;

if(!(pszBuff[0] == '$' && pszBuff[1] == 'G' && pszBuff[2] == 'P'))
continue;

for(i = 0; i <= nLen-3; i++) //pszBuff[nLen-2]=='\r' pszBuff[nLen-1]=='\n'
{
if(pszBuff[i] < 0x20 || pszBuff[i] >= 0x7f)
break;
}
if(i != nLen-2)
continue;

if(!((pszBuff[3] == 'G' && pszBuff[4] == 'G' && pszBuff[5] == 'A')
|| (pszBuff[3] == 'R' && pszBuff[4] == 'M' && pszBuff[5] == 'C')))
continue;

if(fp == SDCard_MAXFILES)
{
BOOL ret;
char pszFileName[13]; //File name is 8.3 format

ret = CreateFileName(pszBuff,pszFileName);
if(ret == FALSE)
{
fp = SDCard_MAXFILES;
continue;
}

#ifdef _DEBUG
LCD_Position(0,0);
LCD_PrString(pszFileName);
#endif

ret = OpenSDCardFile(&fp,pszFileName);
if(ret == FALSE)
{
fp = SDCard_MAXFILES;
continue;
}
#ifdef _DEBUG
LCD_Position(1,0);
LCD_PrCString(" ok ");
#endif
}

#ifdef _DEBUG
LCD_Position(1,0);
LCD_PrHexByte(fp);
#endif


{
BYTE bRet;
PRT1DR |= 0x40; //Busy LED On

bRet = SDCard_fputBuff(pszBuff, nLen, fp);
SDCard_fflush(fp);
if(SDCard_ferror(fp))//bRet == SDCard_EOF)//
{
//error!!
#ifdef _DEBUG
LCD_Position(3,0);
LCD_PrCString(" ");
LCD_Position(3,0);
LCD_PrCString("error ");
//LCD_PrHexByte(SDCard_ferror(fp));
LCD_PrHexByte(fp);
#endif

SDCard_fclose(fp);
fp = SDCard_MAXFILES;
}
PRT1DR &= ~0x40; //Busy LED Off
}

if((PRT1DR & 0x20)) //break sw is on
break;
}
SDCard_fclose(fp);
SDCard_Select(SDCard_DISABLE); // Deselect card
}





psoc_gps_sd_d200_11.gif
そして1回、「Build」メニューの「Build」を選択してビルドする。

psoc_gps_sd_d200_12.gif
すると新しく追加したモジュールの割り込み設定用ソースコードがプロジェクトに追加される。その中から「counter16int.asm」を開き、「ljmp _COUNT16_INT」の1行を追加する。

psoc_gps_sd_d200_13.gif
同様に「psocgpioint.asm」の中には「ljmp _GPIO_INT」の1行を追加する。これでソースコードの修正まで終わった。


_DSC9380.JPG
PSoCへプログラムを書き込み、上の回路図のようにNikon D200と接続するとGPSログをSDカードに保存しつつ、Nikon D200へ流し込めるようになった。Nikon D200でシャッターを半押しするとGPSマークが表示され、撮影画像に位置情報が記録される。1分ほど経つとGPSマークが消え、さらに数秒たつと半押しタイマーが作動する。シャッターの押下情報を利用していないのでまだ使い勝手は悪いもののなんとか形になったかな?


カテゴリー「電子工作」 のエントリー