2019年05月08日
電子トリガーを作る その4
その3の続き。
ヒューズが飛ぶ問題への対処を行います。
どんな感じに飛ぶというと、撃っていると突然ショートしたようになってヒューズが飛びます。
色々と調べてみるとこういったページを見つけました。
ページに書いてあることによるとアンダーシュートが原因と思われます。

アンダーシュートによって2つのFETが同時にオンになりショートしてしまい、ヒューズが飛んでしまうようです。
そこで対策として、
1.ブートストラップコンデンサの容量を再計算、470μF→47μFへ変更。
2.Vcc端子とCOM端子の間に470μFの電解コンデンサを追加。
3.ゲート抵抗を33Ω→47Ωへ変更。
その他に、バッテリー電圧を測定する分圧抵抗を追加したり
センサー用の電源をマイコンと別にしました。
最終的な回路はこちらです。

マガジンに磁石を仕込んで、これを磁気センサーで検出してボルトストップするようにしました。

ボルトストップはチャージングハンドルを引くことで解除できるようにしています。

プログラムは以下の通り。
ヒューズが飛ぶ問題への対処を行います。
どんな感じに飛ぶというと、撃っていると突然ショートしたようになってヒューズが飛びます。
色々と調べてみるとこういったページを見つけました。
ページに書いてあることによるとアンダーシュートが原因と思われます。
アンダーシュートによって2つのFETが同時にオンになりショートしてしまい、ヒューズが飛んでしまうようです。
そこで対策として、
1.ブートストラップコンデンサの容量を再計算、470μF→47μFへ変更。
2.Vcc端子とCOM端子の間に470μFの電解コンデンサを追加。
3.ゲート抵抗を33Ω→47Ωへ変更。
その他に、バッテリー電圧を測定する分圧抵抗を追加したり
センサー用の電源をマイコンと別にしました。
最終的な回路はこちらです。
マガジンに磁石を仕込んで、これを磁気センサーで検出してボルトストップするようにしました。
ボルトストップはチャージングハンドルを引くことで解除できるようにしています。
プログラムは以下の通り。
//F2000用FCUプログラムVer.1.03
//改訂記録============================================================================
//Ver.1.01 モーターロック保護処理の追加
//Ver.1.02 ボルトストップ時にピストンを前進位置で停止するよう変更
//Ver.1.03 ボルトストップ解除時に発射されてしまう不具合を修正
//入出力ピンの名称変更=================================================================
#define SEMI_SW A0 //セミオートスイッチ
#define VAL_IN A1 //電圧測定入力
#define FULL_SW 4 //フルオートスイッチ
#define SAFETY_SW 2 //セーフティスイッチ
#define CUTOFF_SW 3 //カットオフスイッチ
#define NO_AMMO_SW 6 //残弾無し検知スイッチ
#define COCKING_FOWARD_SW 7 //コッキングハンドル前進検知スイッチ
#define COCKING_BACK_SW 8 //コッキングハンドル後退検知スイッチ
#define MOTOR 9 //モーター制御出力
#define INDICATOR 10 //インジケーターLED
#define LED 13 //マイコンLED
//定数の宣言===========================================================================
const byte PWM = 254; //フルオートサイクル調整用定数、254まで
const unsigned int PRECOCK = 30;//プリコック時間(ミリ秒)
const unsigned int DECOCK = 0; //デコック時間(ミリ秒)
const unsigned int SW_ON = 280; //セミオートスイッチオン閾値
const unsigned int SW_OFF = 90; //セミオートスイッチオフ閾値
const unsigned int CHATA_SEMI = 0; //セミオートスイッチチャタリング防止(マイクロ秒、1/1000ミリ秒)
const unsigned int CHATA_FULL = 5000; //フルオートスイッチチャタリング防止(マイクロ秒、1/1000ミリ秒)
const unsigned int CHATA_CUTOFF = 0; //カットオフスイッチチャタリング防止(マイクロ秒、1/1000ミリ秒)
const bool ON = LOW, OFF = HIGH; //スイッチ用定数、混乱防止のため
const bool SEMI = false, FULL = true; //フル・セミ切り替え用定数
const unsigned int V_R1 = 1000; //電圧検知VIN側抵抗値(Ω)
const unsigned int V_R2 = 1000; //電圧検知GND側抵抗値(Ω)
const unsigned int LIMIT_VAL_S = 740; //待機時電圧下限値(V×100)
const unsigned int LIMIT_VAL_M = 610; //稼働時電圧下限値(V×100)
const unsigned int VF = 70; //ダイオード順方向降下電圧値(V×100)
//変数の宣言===========================================================================
volatile int Fire = 0; //発射制御用変数
volatile unsigned int Semi_Sw = 0; //セミオートスイッチ用変数
volatile bool Cutoff = LOW, Old_Cutoff = LOW; //カットオフセンサー用変数
volatile bool SemiTrg = OFF, Old_SemiTrg = OFF; //セミオート処理用変数
volatile bool FullTrg = OFF, Old_FullTrg = OFF; //フルオート処理用変数
volatile bool Shot_Mode = SEMI;//フル/セミ切り替え用変数
volatile bool No_Ammo = LOW;//残弾無し検知用変数
volatile bool Bolt_Stop = false;//ボルトストップ処理用変数
volatile bool Cocking_Fowad = LOW;//コッキングハンドル前進検知用変数
volatile bool Cocking_Back = LOW;//コッキングハンドル後退検知用変数
volatile bool Fast_Shot = true;//ファストショット処理用変数
bool Battery_Low = false;//バッテリー低下処理用変数
float Vcc = 0; //基準電圧(V)
unsigned int Val = 0;//測定電圧(V×100)
unsigned long Time = 0, Old_Time = 0; //電圧測定時間記録(ミリ秒)
unsigned long M_Time = 0 ,Old_M_Time = 0; //モーター駆動時間記録(ミリ秒)
//セットアップ=========================================================================
void setup() {
//入出力ピンの設定=====================================================================
pinMode(FULL_SW,INPUT);
pinMode(CUTOFF_SW,INPUT);
pinMode(NO_AMMO_SW,INPUT);
pinMode(COCKING_FOWARD_SW,INPUT);
pinMode(COCKING_BACK_SW,INPUT);
pinMode(SAFETY_SW,INPUT);
attachInterrupt(digitalPinToInterrupt(SAFETY_SW),SAFETY_MODE,LOW);
pinMode(LED,OUTPUT);
pinMode(INDICATOR,OUTPUT);
//起動処理============================================================================
digitalWrite(INDICATOR,HIGH);
analogWrite(MOTOR,0);
delay(1000); //1秒待つ
}
//メインプログラム====================================================================
void loop() {
digitalWrite(INDICATOR,LOW);
Time = millis(); //現在の時間を取得(バッテリー電圧測定用)
M_Time = millis(); //現在の時間を取得(モーターロック保護処理用)
//現在の各スイッチの状態を検知========================================================
Cutoff = digitalRead(CUTOFF_SW);
Semi_Sw = analogRead(SEMI_SW);
/*セミオートスイッチに光センサーを使うため
*オン・オフの処理を追加
*/
if(Semi_Sw >= SW_ON){
SemiTrg = ON;
/*光センサーの数値が定数SW_ONの閾値以上の場合
*セミオートスイッチをオンとする
*/
}
if(Semi_Sw <= SW_OFF){
SemiTrg = OFF;
/*光センサーの数値が定数SW_OFFの閾値以下の場合
*セミオートスイッチをオフとする
*/
}
FullTrg = digitalRead(FULL_SW);
No_Ammo = digitalRead(NO_AMMO_SW);
Cocking_Back = digitalRead(COCKING_BACK_SW);
//カットオフスイッチの状態をLEDで表示
if(Cutoff == HIGH){
digitalWrite(LED,HIGH);
}
if(Cutoff == LOW){
digitalWrite(LED,LOW);
}
//モーター動作処理====================================================================
if(Fire >= 1){
//変数Fireが1以上のときモーター作動
switch (Shot_Mode == FULL){
//モーターの出力変更
case FULL:
if(Fast_Shot == true){
analogWrite(MOTOR,254);
}
else{
analogWrite(MOTOR,PWM);
}
break;
/*フルオート時、最初の1発は最大出力
*それ以降、変数PWMの値の出力
*/
default:
analogWrite(MOTOR,254);
//セミオート時、最大出力
}
//モーターロック保護処理================================================================
while(M_Time - Old_M_Time >= 200){
analogWrite(MOTOR,0);
digitalWrite(INDICATOR,HIGH);
delay(100);
digitalWrite(INDICATOR,LOW);
delay(100);
/*トリガーが押されてから、または
*カットオフスイッチを検出してから200ミリ秒以上
*新たなカットオフスイッチを検出しなかった場合
*モーターを停止し、インジケーターLEDを点滅する
*/
}
//残弾無し処理=========================================================================
if(No_Ammo == HIGH){
Fire = 1;
/*残弾無し検知スイッチがオンの時、
*変数Fireの値を1にする
*/
}
}
//カットオフ処理=======================================================================
if(Fire > 1){
if(Cutoff == HIGH && Old_Cutoff == LOW){
Fire = Fire - 1;
Fast_Shot = false;
Old_M_Time = M_Time; //カットオフ検出の時間を記録(モーターロック保護処理用)
Val = GetVal(analogRead(VAL_IN)); //電圧を取得
if(Val < LIMIT_VAL_M){ //稼働時下限値を下回ったとき
Battery_Low = true; //変数Battery_Lowをtrueにし、
Fire = 1; //変数Fireの値を1にする
}
/*カットオフスイッチがLOWからHIGHなったときに
*変数Fireの数字を1引き、変数Fast_Shotをfalseにする
*/
}
Old_Cutoff = Cutoff;
}
if(Fire == 1){
if(Cutoff == HIGH && Old_Cutoff == LOW){
analogWrite(MOTOR,254);
Val = GetVal(analogRead(VAL_IN)); //電圧を取得
if(Val < LIMIT_VAL_M){ //稼働時下限値を下回ったとき
Battery_Low = true; //変数Battery_Lowをtrueにする
}
if(Cocking_Back == ON || No_Ammo == HIGH){
delay(DECOCK);
}else{
delay(PRECOCK);
}
analogWrite(MOTOR,0);
Fire = 0;
Shot_Mode = SEMI;
Fast_Shot = true;
Time = millis();
Old_Time = Time; //モーターが停止した時間を記録(バッテリー電圧測定用)
/*変数Fireが1に等しい場合、
*カットオフスイッチがLOWからHIGHになった時に
*コッキングハンドル検知スイッチがオフの時
*定数PRECOCKミリ秒後、モーターを停止し、
*コッキングハンドル検知スイッチがオンの時、
*あるいは残弾無し検知スイッチがオンの時、
*定数DECOCKミリ秒後、モーターを停止する
*/
if(No_Ammo == HIGH){
//残弾無し処理
Bolt_Stop = true;
/*残弾無し検知スイッチがオンの時、
*ボルトストップを行う
*/
}
}
Old_Cutoff = Cutoff;
}
if(Cutoff == LOW && Old_Cutoff == HIGH ){
delayMicroseconds(CHATA_CUTOFF);
/*カットオフスイッチがONからOFFになったとき
*チャタリング防止のため
*定数CHATA_CUTOFFマイクロ秒停止する
*/
}
//トリガー処理=========================================================================
/* 変数Bolt_Stopがfalseの時
* かつ、変数Battery_Lowがfalseの時のみ
* トリガー入力を受け付ける
*/
if(Bolt_Stop == false && Battery_Low == false){
//セミオート処理=======================================================================
if(Fire == 0 ){
analogWrite(MOTOR,0);
Shot_Mode = SEMI;
/*変数Fireの値が0の時
*セミオート入力を受け付ける
*/
if(SemiTrg == ON && Old_SemiTrg == OFF){
Fire = 1;
Shot_Mode = SEMI;
/*セミオートスイッチがOFFからONになったとき
*変数Fireに1を代入する
*/
Old_M_Time = M_Time; //セミオートトリガーが押された時間を記録(モーターロック保護処理用)
}
if(SemiTrg == OFF && Old_SemiTrg == ON){
delayMicroseconds(CHATA_SEMI);
/*セミオートスイッチがONからOFFになったとき
*チャタリング防止のため
*定数CHATA_SEMIマイクロ秒停止する
*/
}
}
//フルオート処理=======================================================================
if(SemiTrg == ON && No_Ammo == LOW){
if(FullTrg == ON){
Fire = 2;
Shot_Mode = FULL;
/*セミオートスイッチがONのままの場合フルオートスイッチを受け付け
*フルオートスイッチがONになっている間
*変数Fireに2を代入し続ける
*/
if(Old_FullTrg == OFF){
Old_M_Time = M_Time; //フルオートトリガーが押された時間を記録(モーターロック保護処理用)
}
}
if(FullTrg == OFF && Old_FullTrg == ON){
Fire = 1;
delayMicroseconds(CHATA_FULL);
/*フルオートスイッチがONからOFFになったとき
*変数Fireに1を代入し、
*チャタリング防止のため
*定数CHATA_FULLマイクロ秒停止する
*/
}
}
}
//フル・セミオートスイッチの状態を記録する=============================================
Old_SemiTrg = SemiTrg;
Old_FullTrg = FullTrg;
//待機時電圧測定処理===================================================================
if(Fire == 0 && Time - Old_Time >= 500){ //Fireが0かつ、発射後500msおきに
Val = GetVal(analogRead(VAL_IN)); //電圧を取得
if(Val < LIMIT_VAL_S){ //待機時下限値を下回ったとき
Battery_Low = true; //変数Battery_Lowをtrueにする
}
Old_Time = Time; //測定した時間を記録
}
//オートカット処理=====================================================================
while(Fire == 0 && Battery_Low == true){ //バッテリー電圧の低下を検出した場合、動作を停止し、
analogWrite(MOTOR,0); //インジケーターLEDを点滅する
digitalWrite(INDICATOR,HIGH);
delay(100);
digitalWrite(INDICATOR,LOW);
delay(1000);
}
noInterrupts();
//ボルトストップ処理===================================================================
while(Bolt_Stop == true){
Fire = 0;
SemiTrg = OFF, Old_SemiTrg = OFF;
FullTrg = OFF, Old_FullTrg = OFF;
analogWrite(MOTOR,0);
digitalWrite(INDICATOR,HIGH);
while(Cocking_Back == OFF){
Cocking_Back = digitalRead(COCKING_BACK_SW);
}
Cocking_Fowad = digitalRead(COCKING_FOWARD_SW);
if(Cocking_Fowad == ON){
No_Ammo = digitalRead(NO_AMMO_SW);
switch(No_Ammo == LOW){
case true:
Bolt_Stop = false;
digitalWrite(INDICATOR,LOW);
break;
default:
Cocking_Back = OFF;
}
}
/*コッキングハンドル後退検知スイッチがオンになった後、
*残弾無し検知スイッチがオフかつ、
*コッキングハンドル前進検知スイッチがオンになった時、
*ボルトストップを解除する
*コッキングハンドル前進検知スイッチがオンになった時、
*残弾無し検知スイッチがオンの場合はやり直し
*/
}
interrupts();
if(Fire < 0){
Fire = 0;
}
}
//セーフティモード=====================================================================
void SAFETY_MODE() {
analogWrite(MOTOR,0);
Fire = 0;
Semi_Sw = 0;
Cutoff = LOW, Old_Cutoff = LOW;
SemiTrg = OFF, Old_SemiTrg = OFF;
FullTrg = OFF, Old_FullTrg = OFF;
Shot_Mode = SEMI;
Fast_Shot = true;
Cocking_Fowad = LOW;
Cocking_Back = LOW;
/*セーフティスイッチがオンの時、
*モーターを停止し、変数の初期化を行う
*/
}
//センサーの値から電圧を計算===========================================================
unsigned int GetVal(unsigned int A){
float V,Vcc;
Vcc = cpuVcc(); //基準電圧の取得
V = A * Vcc / 1024.0 /V_R2 * (V_R1 + V_R2);
return(unsigned int)(V * 100.0+ VF);//100倍して整数にする
}
// 電源電圧(AVCC)測定関数==============================================================
float cpuVcc(){
unsigned long sum=0;
adcSetup(0x4E); // Vref=AVcc, input=internal1.1V
for(int n=0; n < 10; n++){
sum = sum + adc(); // adcの値を読んで積分
}
return (1.1 * 10240.0)/ sum; // 電圧を計算して戻り値にする
}
// ADコンバーターの設定================================================================
void adcSetup(byte data){
ADMUX = data; // ADC Multiplexer Select Reg.
ADCSRA |= ( 1 << ADEN); // ADC イネーブル
ADCSRA |= 0x07; // AD変換クロック CK/128
delayMicroseconds(500); // 安定するまで待つ
}
// ADCの値を読む=======================================================================
unsigned int adc(){
unsigned int dL, dH;
ADCSRA |= ( 1 << ADSC); // AD変換開始
while(ADCSRA & ( 1 << ADSC) ){ // 変換完了待ち
}
dL = ADCL; // LSB側読み出し
dH = ADCH; // MSB側
return dL | (dH << 8); // 10ビットに合成した値を返す
}
2019年05月04日
電子トリガーを作る その3
その2の続き。
電動ガンへの実装を行います。
今回取り付けるのは、G&GのF2000。
ETUが搭載されていない旧式です。

テスト基板を少し変更した実装基板を作りました。


この基板はメカボックスのスイッチが付いていた所に取り付けました。

マイコン基板はバッテリースペース下に入れました。

トリガースイッチは、元の仕様通りフルセミの選択をトリガーストロークで行い、
セミをフォトリフレクタ、フルをマイクロスイッチで検出するようにしました。


そのほか、セーフティスイッチなどを取り付け。

これで実装完了ですが、テストしたところヒューズが飛ぶ問題が起こったので、
次回はその対処とプログラムの改良を行います。
電動ガンへの実装を行います。
今回取り付けるのは、G&GのF2000。
ETUが搭載されていない旧式です。
テスト基板を少し変更した実装基板を作りました。
この基板はメカボックスのスイッチが付いていた所に取り付けました。
マイコン基板はバッテリースペース下に入れました。
トリガースイッチは、元の仕様通りフルセミの選択をトリガーストロークで行い、
セミをフォトリフレクタ、フルをマイクロスイッチで検出するようにしました。
そのほか、セーフティスイッチなどを取り付け。
これで実装完了ですが、テストしたところヒューズが飛ぶ問題が起こったので、
次回はその対処とプログラムの改良を行います。