高速度カメラ同期 PIV システムについて 速度カメラ同期PIV...
Transcript of 高速度カメラ同期 PIV システムについて 速度カメラ同期PIV...
高速度カメラ同期 PIV システムについて
2011/12/09
鳴海 智博
・ 概要
高速度カメラを用いたPIVシステムの構築についてのメモを記述する。低レイノルズ数でのPIV
においては撮影間隔が数マイクロ秒~数十マイクロ秒でなければならないが、そのような時間間
隔で高解像で撮影可能な高速度カメラは非常に高価であるため、数千 fps のカメラでも PIV が
行えるようレーザ照射のタイミングを工夫する。具体的には下図のようになる。1 フレーム目の
最後の方で 1 回目のレーザを照射し、2 フレーム目の最初の方で 2 回目のレーザ照射を行うこと
を繰り返すことで、任意の時間間隔の PIV 撮影を、カメラの撮影周波数の 1/2 の周波数で行う
ことが可能となる。高速度カメラはトリガ入力により撮影を開始し、また同期信号を出力してい
るのでこれらを利用する。同期信号カウント、トリガ出力、レーザ操作はマイクロコンピュータ
で行う。
・ 回路選定および製作
出力のタイミングを数マイクロ秒(数百 kHz)で制御するため、マイクロコンピュータの性能
は少なくともその数十倍以上のクロック数が必要となることから、今回は手元にある SH2-7144
(25MHz)を利用する。十分な速度が出せるのであれば PIC でも AVR でも H8 でも SH4 でも
STM32 でも何でも良い。7144 は外部クロックカウントの機能があるため、同期信号は TCLKA
端子に接続する。トリガとレーザ出力信号は汎用 I/O ポートに接続する。信号レベルは TTL で
あり、7144 の電源電圧は 3.3V である。コネクタは BNC であるが、インピーダンスに注意する
必要がある。
尚、マイコンボードには RS232C レベル変換 IC を通した DSub9 ピン、ブートモード切替スイ
ッチ、クロック切替スイッチ、汎用スイッチ、汎用 LED(赤と緑)、電源 LED などが取り付け
られている。
・ HEW4 の導入
マイコンのプログラム開発環境はいろいろ存在するが、ここではルネサス純正の HEW4 を用い
る。256KB までの書き込みなら無料で使用可能であり、ウェブサイトからダウンロードして使
用する。(今のところ Windows のみ対応)
インストールして HEW を起動した後、まず新規ワークスペースを作成する。
ワークスペース名は適当に決める。CPU は SuperH RISC engine、ツールチェインは Renesas
SuperH Standard を選択。
CPU は SH-2、CPU タイプは 7144F を選択。
Endian はどちらでもよいが、ここでは Big を選択。またスピードが必要なときは double->float
変換を選択。
I/O ライブラリ使用にはチェック、ヒープはデフォルトでよいが、足りない場合は後で増やすこ
とが可能。Main 関数生成は C source file、I/O レジスタ定義ファイルもチェックして C/C++
source file を選択。
ライブラリは今回は使用しないが、必要なときはチェックを入れる。
スタックポインタとスタックサイズはデフォルトでかまわないが、使用量が増えそうであれば
適宜変更する。
ベクタテーブル定義はチェック。
後はそのままで完了となる。
すべて完了すると、ワークスペースが生成される。既に自分で書いたプログラムがある場合は、
下図のようなファイルの追加を行う。
プログラムの記述が終われば、右上の Debug を Release に変更。
そしてビルドを行う。最初だけ時間がかかる。
エラーがなければ、ウィンドウの下部に 0 Error と表示される。これで Release フォルダに書き込
み用ファイル(mot ファイル)が生成される。
・ プログラム作成
まず、タイマ(MTU)関連のプログラムを以下に示す。同期信号カウンタ用とレーザ用に、2
つのタイマ(MTU0 と MTU3)を使用している。レジスタの詳細については、ハードウェアマ
ニュアルを参考のこと。
#ifndef __TIMERH__
#define __TIMERH__
#include "iodefine.h"
// --- CMT1割り込み関数プロトタイプ宣言 ---
void int_mtu0_tgia0(void);
#pragma interrupt INT_MTU0_TGIA0
extern void INT_MTU0_TGIA0(void)
{
int_mtu0_tgia0();
}
void int_mtu3_tgia3(void);
#pragma interrupt INT_MTU3_TGIA3
extern void INT_MTU3_TGIA3(void)
{
int_mtu3_tgia3();
}
// ----------------------------------------
static unsigned char counter;
// sync outからの割り込み
void int_mtu0_tgia0(void)
{
MTU0.TSR.BIT.TGFA = 0;
counter = 0;
PE.DRL.BIT.B1 ^= 1; // LED1(緑)の点灯
PE.DRL.BIT.B3 = 1; // trigger on
MTU.TSTR.BIT.CST3 = 0; // TCNT3 stop
MTU.TSTR.BIT.CST3 = 1; // TCNT3 start
}
void int_mtu3_tgia3(void)
{
MTU3.TSR.BIT.TGFA = 0;
PE.DRL.BIT.B0 ^= 1; // LED1(赤)の点灯
counter++;
if(counter == 79){
PE.DRL.BIT.B8 = 1; // Laser 1 on
PE.DRL.BIT.B3 = 0; // trigger off
}
else if(counter == 99){
PE.DRL.BIT.B8 = 0; // Laser 1 off
}
else if(counter == 101){
PE.DRL.BIT.B12 = 1; // Laser 2 on
}
else if(counter == 121){
PE.DRL.BIT.B12 = 0; // Laser 2 off
}
}
void mtu_start(void)
{
counter = 0;
PE.DRL.BIT.B3 = 1; // trigger on
}
void mtu_init(void)
{
MST.CR2.BIT._MTU = 0; // release stand-by
MTU.TSTR.BYTE = 0x00; // MTU count stop
PFC.PACRL2.BIT.PA6MD = 1; // PA6/TCLKA端子を MTUタイマクロック入力に
設定
//MTU0 : camera sync in
MTU0.TCR.BYTE = 0x24; // TGRA compare match, rising, TCLKA
// MTU0.TCR.BYTE = 0x20; // TGRA compare match, rising, phi/1
MTU0.TIOR.BYTE.H = 0x00;
MTU0.TIOR.BYTE.L = 0x00;
MTU0.TIER.BIT.TGIEA = 1; // TGRA interrupt enable
MTU0.TGRA = 80; // camera sync, 80 回
(2ms)で MTU3を同期
// MTU0.TGRA = 250; // 10us / 0.04us, PE8, PE12を操
作
MTU0.TSR.BIT.TGFA = 0; // TGRA flag clear
//MTU3 : Laser sybc out
MTU3.TCR.BYTE = 0x20; // TGRA compare match, rising, phi/1
MTU3.TIOR.BYTE.H = 0x00;
MTU3.TIOR.BYTE.L = 0x00;
MTU3.TIER.BIT.TGIEA = 1; // TGRA interrupt enable
MTU3.TGRA = 250; // 10us / 0.04us, PE8, PE12を操
作
MTU3.TSR.BIT.TGFA = 0; // TGRA flag clear
INTC.IPRD.WORD |= 0x7700; // set MTU0 interrupt level 7
INTC.IPRE.WORD |= 0x0077; // set MTU3 interrupt level 7
MTU.TSTR.BIT.CST0 = 1; // TCNT0 start
MTU.TSTR.BIT.CST3 = 1; // TCNT3 start
}
#endif
次にメインプログラムを示す。
#include "serial.h"
#include "timer.h"
//#include "typedefine.h"
#ifdef __cplusplus
//#include <ios> // Remove the comment when you use ios
//_SINT ios_base::Init::init_cnt; // Remove the comment when you use
ios
#endif
void main(void);
#ifdef __cplusplus
extern "C" {void abort(void);}
#endif
void main(void)
{
char data;
int i;
PFC.PEIORL.WORD = 0xfffb; // PE2以外を出力に設定
sci1_init(38400, 8, 0, 1);
sci1_putstr("--- Hit enter key to start PIV ---¥n");
// wait for starting PIV
while(1){ // スイッチ ONを待つ
if(PE.DRL.BIT.B2 == 0){
break;
}
}
// PIV start
sci1_putstr("Start¥n");
mtu_init();
mtu_start();
while(1){
data = sci1_getchar();
sci1_putchar(data);
}
}
#ifdef __cplusplus
void abort(void){}
#endif
また、今回はほとんど必要ないが、以下にシリアル通信用プログラムも載せておく。
#ifndef __SERIALH__
#define __SERIALH__
#include "iodefine.h"
#define BUFF_SIZE 256
void sci1_init(unsigned short,unsigned char,unsigned char,unsigned char);
char sci1_putchar(char);
void sci1_putstr(char *);
char sci1_getchar(void);
typedef struct { // バッファデータ型構造体宣言
unsigned short remain; // バッファ中の残データ数
unsigned short read; // 書込み位置
unsigned short write; // 読出し位置
char *buff; // 読書きデータ
} BUFF;
static char rx1buff[BUFF_SIZE]; // 受信データバッファ
static char tx1buff[BUFF_SIZE]; // 送信データバッファ
static BUFF rx1; // BUFF型受信データ構造体
static BUFF tx1; // BUFF型送信データ構造体
// SCI1割り込み関数プロトタイプ宣言
void int_sci1_eri1(void);
#pragma interrupt INT_SCI1_ERI1
void INT_SCI1_ERI1(void){
int_sci1_eri1();}
void int_sci1_rxi1(void);
#pragma interrupt INT_SCI1_RXI1
void INT_SCI1_RXI1(void){
int_sci1_rxi1();}
void int_sci1_txi1(void);
#pragma interrupt INT_SCI1_TXI1
void INT_SCI1_TXI1(void){
int_sci1_txi1();}
void int_sci1_tei1(void);
#pragma interrupt INT_SCI_TEI1
void INT_SCI1_TEI1(void){
int_sci1_tei1();}
/* SCI1初期化 ********************************************/
#define PCLOCK 25000000
void sci1_init(unsigned short bps,unsigned char bit,unsigned char
parity,unsigned char stopbit){
// bps 4800 / 9600 / 14400 / 19200 / 28800 / 31250 / 38400
// bit 8 / 7
// parity 0:無し / 1:偶数パリティ / 2:奇数パリティ
// stopbit 1 / 2
// RX FIFO 初期化
rx1.remain = 0;
rx1.read = 0;
rx1.write = 0;
rx1.buff = rx1buff;
// TX FIFO 初期化
tx1.remain = 0;
tx1.read = 0;
tx1.write = 0;
tx1.buff = tx1buff;
MST.CR1.BIT._SCI1 = 0; // SCI1 スタンバイ解除
INTC.IPRF.WORD |= 0x000A; // SCI1 割込み LEVEL = 10
SCI1.SCR.BYTE = 0x00; // rx,tx割り込み送受信禁止
// 通信フォーマット設定
// SMR設定
SCI1.SMR.BYTE = 0; // SMR通信設定白紙
if(bit == 7){
SCI1.SMR.BIT.CHR = 1; // データ長 7bit設定
}
if(parity == 1){
SCI1.SMR.BIT._PE = 1; // パリティ付加
SCI1.SMR.BIT.OE = 0; // 偶数パリティ付加
}
else if(parity == 2){
SCI1.SMR.BIT._PE = 1; // パリティ付加
SCI1.SMR.BIT.OE = 1; // 奇数パリティ付加
}
if(stopbit == 2){
SCI1.SMR.BIT.STOP = 1; // ストップビット 2bit
}
// BRR設定
SCI1.BRR = (PCLOCK/(32*bps))-1; // BRR設定計算
// PFC設定
PFC.PACRL2.BIT.PA3MD=1; // 入力端子 PA3 → RXD1 有効
PFC.PACRL2.BIT.PA4MD=1; // 出力端子 PA4 → TXD1 有効
// SSR設定
SCI1.SSR.BYTE &= 0x87; // RDRF / ORER / FER / PER クリア
// SCR設定
SCI1.SCR.BIT.TIE = 0; // 送信割り込み禁止
SCI1.SCR.BIT.RIE = 1; // 受信割り込み許可
SCI1.SCR.BIT.TE = 1; // 送信許可
SCI1.SCR.BIT.RE = 1; // 受信許可
}
/* 一文字送信バッファへ格納 ******************************/
char sci1_putchar(char data){
char txdata;
txdata = -1; // エラー時の戻り値
if(data > 0){
SCI1.SCR.BIT.TIE = 0; // 送信割り込み禁止
if(tx1.remain < BUFF_SIZE){ // データ量がバッファより少なけれ
ば
tx1.buff[tx1.write] = data; // データをバッファに書き込み
tx1.remain ++; // データ残数を1増
tx1.write ++; // 書込み位置を1増
if(BUFF_SIZE <= tx1.write){ // 書込み位置とバッファサイズの比較
tx1.write = 0; // 大きければ書込み位置を0に。
}
txdata = data; // 戻り値用の代入処理
}
SCI1.SCR.BIT.TIE = 1; // 送信割り込み許可
}
return txdata; // txdataを返す
}
/* 文字列送信バッファへ格納 *****************************/
void sci1_putstr(char *str){
while(*str != '¥0'){ // Nullの検出で終了
if(*str == '¥n'){ // 改行処理
sci1_putchar(0x0a); // LFラインフィード
sci1_putchar(0x0d); // CRキャリッジリターン
}
else{
sci1_putchar(*str); // 一文字送信処理
}
str++; // ポインタの1増
}
return;
}
/* 送信割り込み *****************************************/
void int_sci1_txi1(void){
char data;
if(tx1.remain > 0){ // データ残量が0より大きければ
data = tx1.buff[tx1.read]; // バッファからデータを読出し
tx1.remain --; // データ残数を1減
tx1.read ++; // 読出し位置を1増
if(tx1.read >= BUFF_SIZE){ // 読出し位置とバッファサイズの比較
tx1.read = 0; // 大きければ読出し位置を0に。
}
while(SCI1.SSR.BIT.TDRE == 0){} // 送信レジスタが空になるまで待つ。
SCI1.TDR = data; // 読み出したデータを送信レジスタへ。
SCI1.SSR.BIT.TDRE = 0; // 送信レジスタエンプティフラグのクリア
}
else {
SCI1.SCR.BIT.TIE = 0; // データ残がなければ送信割り込み禁
止
}
}
/* 送信終了割り込み *************************************/
void int_sci1_tei1(void){
SCI1.SCR.BIT.TEIE = 0; // 送信終了割り込み禁止
}
/* 一文字受信バッファから読出し**************************/
char sci1_getchar(void){
char data;
SCI1.SCR.BIT.RIE = 0; // 受信割り込み禁止
if(rx1.remain == 0){ // バッファデータが無ければ
data = -1; // -1を戻り値とする。
}
else { // バッファデータがあれば
data = rx1.buff[rx1.read]; // データのバッファ読み出し
rx1.remain --; // データ残数を1減
rx1.read ++; // 読出し位置を1増
if(BUFF_SIZE <= rx1.read){ // 読出し位置とバッファサイズの比較
rx1.read = 0; // 大きければ読出し位置を0に。
}
}
SCI1.SCR.BIT.RIE = 1; // 受信割り込み許可
return data; // 戻り値 data
}
/* 受信割り込み ****************************************/
void int_sci1_rxi1(void){
char data;
data = SCI1.RDR; // 受信データ取り込み
SCI1.SSR.BIT.RDRF = 0; // 受信レジスタフルフラグクリア
if(rx1.remain < BUFF_SIZE){ // データ量がバッファより少なけれ
ば
rx1.buff[rx1.write] = data; // 受信データをバッファに書込み
rx1.remain ++; // データ残数を1増
rx1.write ++; // 書込み位置を1増
if(rx1.write >= BUFF_SIZE){ // 書込み位置とバッファサイズの比較
rx1.write = 0; // 大きければ書込み位置を0に。
}
}
}
/* 受信エラー割り込み **********************************/
void int_sci1_eri1(void){
SCI1.SSR.BYTE &= 0x87; // RDRF / ORER / FER / PER クリア
}
#endif
さらに、割り込み関数宣言を変更するため、intprg.c を以下のように変更する。
// 88 MTU0 TGIA0
//void INT_MTU0_TGIA0(void){/* sleep(); */}
// 112 MTU3 TGIA3
//void INT_MTU3_TGIA3(void){/* sleep(); */}
// 132 SCI1 ERI1
//void INT_SCI1_ERI1(void){/* sleep(); */}
// 133 SCI1 RXI1
//void INT_SCI1_RXI1(void){/* sleep(); */}
// 134 SCI1 TXI1
//void INT_SCI1_TXI1(void){/* sleep(); */}
// 135 SCI1 TEI1
//void INT_SCI1_TEI1(void){/* sleep(); */}
・ FLASH 書き込み
mot ファイルの書き込みには H8 Writer というフリーウェアを用いる。COM ポートはシリアル
通信が可能なポート(通常 USB-Serial 変換ケーブルを差したときに割り当てられるポート)、
書き込み制御ファイルは 7144_45_F50M_P384.MOT、書き込むファイルはワークスペースの
Release フォルダにある mot ファイルを選択する。Baud Rate は 19200 と 38400 で通常問題な
い。
書き込みの際はボードの電源を OFF にした後、スイッチの FMP と MD1 を 0 にする。他の 3
つのスイッチは 1 とする。その後電源を入れ、すぐに書き込み開始ボタンを押すと書き込みが
始まる。
書き込みが終われば電源を OFF にし、FMP と MD1 を 1 にしてから電源を入れる。このとき
汎用スイッチは OFF のままにしておくこと。(ON にするとトリガが出力される)
・ 接続テスト
パルスジェネレータでカメラ同期信号を模擬し、マイコンの挙動をオシロスコープで見てみる。
同期信号のカウントが始まれば赤 LED が点灯し、レーザ信号が出力されれば緑 LED が点灯す
るようにしている。
下図では CH1(黄)がトリガ出力、CH2(青)がレーザ B の出力信号となっている。カメラを
1kfps としているため、トリガが 500Hz で出力されていることがわかる。またトリガより遅れ
てレーザ出力信号が出ていることがわかる。
また、下図では CH1 にレーザ A の出力を入れている。レーザ B との間にわずかな(20us)時
間差があることがわかる。
以上により、PIV システムの構築が出来たが、撮影開始のタイミングがトリガ信号から遅れる
可能性もあるため、実際にカメラに接続して修正が必要となるかもしれない。