2019年7月20日土曜日

CO2レーザー その18(Z軸可動化Ⅴ:制御回路設計)

前回Ⅳで、やっと、メカ部分が実用的になってきたので
制御回路を作ることにします。
テストでは、CNC2418のWoodpeckerでやってて
これは、1号機の時ですが、こんなんじゃ組み込めませんからね~
組み込めるよう、小さいDigisparkか、ATTINY85にしようかな!
Digisparkは、ここでちょっと噛じって
Windowsに専用のUSB DriverとArduino IDEの開発環境Installして
ここで、一発スクリーンショットを作って、
その後、キャパシティブセンサーもやって以来であります。

このDigispark Cloneが在庫ってます。
US$ 2.68/2個

やっぱ、この元祖タイプの直挿Digispark Cloneだな!
USBの差し込みが甘く、ちょっと押すと接触不良起こすので
PCで抜き差しする用途には向かないと思います。
今回は、スケッチ書込時だけで、後は、スタンドアローンで動かすので
こっちを使うことにします。
一時、売り切れでしたが、今見ると、$1.3で復活してます。

Digisparkに乗っているATtiny85の機能は、GitHubのここにあります。
「AVR 8bit Microcontroller」なのです。

この「みら太な日々」師のブログでは、
Digispark+A4988でステッピングモーターを動かして
カメラスライダーを作られていますので
参考にさせていただきましたm(_ _)m

まずは、ボタンで上下させるだけ
その後、Limit SWを付けることにします。

Digisparkボード内には、色々パーツがぶら下がっているので注意です。
PB5は、RESET専用にしなければなりません。
PB1にもLチカ確認用のLEDがあるので要注意です。
Digistump Createdの回路図は、このPDFです。

で、ピンアサインは、これ。
PIN0:Direction
PIN1:Step(ボード内のLEDは問題ないでしょう)
PIN2:UP BUTTON
PIN3:DOWN BUTTON
PIN4:Limit SW(PullUP、Normal Open)(ボードにPullUP抵抗あり)
PIN5:NC(RESET専用)

では、スケッチを設計していきます。

まず、STEP PLUSEの周期は、
20step/回転、3mmリード=ピッチ、1/4 Microstepなので
20 ÷ 1/4 ÷ 3 = 26.667step/mm
F500 = 500mm/minだと
1分間に
26.667 x 500 = 13333step/min
1秒間のSTEP数が、A4988のStepパルス周期になるので
13333 ÷ 60 = 222.22step/sec = 222.22Hz
= 4.5 msec(HIGH/LOWの1周期)

Arduinoのスケッチは、昨年6月以来、久々です。
すっかり忘れてるので、過去のブログを覗きます。
ブログ書いてて良かっと感じる時です^^;

実体配線図を作っておかないと確実に間違えるので(-_-;)
久しぶりにFritzingを立ち上げます。
「A4988」を検索すると
おっ!Pololu A4988がありますね~
Digisparkのライブラリは、この時
ここから直挿DigisparkのFritzing PartsをImportしてます。

ステッピングモーターの電源は、定格 3~6Vなのですが
A4988(定格 8V~)の実力でも 6.2V~しか動かないので
6.2Vで動かしています。

MS1/MS3:100KΩで、MS2:50KΩでPULL-DOWNされてるので
MS2だけHIGHでいいでしょう。
A4988のDatasheetには、SLEEPとRESETを直結する記述がないけど
CNC2418のWoodpeckerで動いてるので、直結でいくとして...
A4988のENABLEは、どうしようか?
このステッピングモーター、静止状態で電流を流し続けたくないし
PB5(RESET)をOUTPUTで使えるだろうか?
ATTINY85のDatasheetは、このPDF
有志の方々で作成された日本語訳は、これです。
それを見ると、
PB5を使うには、RESETをDISABLEすればできるのですが、
Datasheetの10.3.1に
RESETをINPUT/OUTPUTとして使う場合は、
RSTDISBL Fuse(非プログラミング領域)を変更しないといけないのです。
Table 20-4 にありました。
一番上の「RSTDISBL」です。
(2)の注釈が下にあり、書換は、高電圧シリアルプログラミングだけ。と
どうもこれをやると弊害の方が大きいので止めておきます(T_T)
A4988のENABLEは、取り敢えずGNDに落としとくしかないな~
後で考えることにしよっと^^;

Arduino IDE v.1.8.3を起動します。
「Digisparkの製作元Digistump LLC(合同会社)のパッケージ」は
この前インストールしています。
まだ、Digispark(ATtiny85)は、接続しないで
[ツール]-[ボード]-[Digispark(Default - 16.5mhz)]を選択します。
[ツール]-[書込装置]-[Micronucleus]を選択します。
Fusion360の3Dの後にソフト考えるのは、頭が~
切り替えるのに時間がかかります(-_-;)
...
できたみたいなので、書き込みです。
まだ、Digisparkは接続しません!つい、先に繋ぎそうになるんですよね~
[スケッチ]-[マイコンボードに書き込む]します。
コンパイルが終わり、下の欄に
Plug in device now...(will timeout in 60 seconds)」とでたら
Digispark(ATtiny85)をUSBに接続します。
書き込み時は、Digisparkは、何も配線していない方がいいかと思われます。
ボードへの書き込みが完了しました。」と
Micronucleus done. Thank you!」の表示がでます。
DigiSparkのLEDが点灯したままになります。
これは、PB1:STEPにしている端子です。
まあ、色々低レベルなミスを乗り越えて^^;
まずは、上下だけ
動いたけど、モーターから変なキュイ~んって音が出て、ややぎこちない動き
OPENしているMS1、MS3がノイズを拾っているのかなあ?
***最初に動いたスケッチ**
// CO2 Laser Cutter Z-Axis Controller
// DigiSpark
// 基本動作の確認

const int Direction = 0; // PB0 for Direction
const int Step = 1;      // PB1 for STEP
const int UpBtn = 2;     // PB2 for UP Button
const int DownBtn = 3;   // PB3 for DOWN Button
const int LimitSW = 4;   // PB4 for Limit Switch

void setup() {
  // initialize the digital pin as an output.
  pinMode(0, OUTPUT);       //DIRECTION
  pinMode(1, OUTPUT);       //STEP
  pinMode(2, INPUT_PULLUP); //UP Button
  pinMode(3, INPUT_PULLUP); //DOWN Button
  pinMode(4, INPUT_PULLUP); //LimitSW
  pinMode(5, INPUT);        //Reset
}

void loop() {
  // UP & DOWN
  if (digitalRead(UpBtn) == LOW) { // UP Button
    digitalWrite(Direction, HIGH); // DIRECTION UP
      move();
  }
  if (digitalRead(DownBtn) == LOW) { // DOWN Button検出
    digitalWrite(Direction, LOW); // DIRECTION DOWN
      move();
  }
}

// STEP信号を1Step Pluse送出 F500固定
// 20step/回転、3mmリード=ピッチ、1/4 Microstepなので
// 20 ÷ 1/4 ÷ 3 = 26.667step/mm
// F500 = 500mm/min
// 26.667 x 500 = 13333step/min(1分間)
// 1秒間のSTEP数が、A4988のStepパルス周期になるので
// 13333 ÷ 60 = 222.22step/sec = 222.22Hz = 4.5 msec(HIGH/LOWの1周期)
// 4.5msec ÷ 2 = 2250μsec
void move() {
  digitalWrite(Step, HIGH);
  delayMicroseconds(2250);
  digitalWrite(Step, LOW);
  delayMicroseconds(2250);
}
*****

少し動かしていると、動かなくなりました(T_T)
UP/DOWN SWを押すと点灯していたDigispark内のLED(PB1:Step)が
点灯しなくなったのです。
Digisparkからの空中配線ケーブルの接触不良っぽいな~
配線し直そうかな~

そうだ!
ATTINY85のソケット付きのDigisparkボードと
DIPの単体チップを持ってるのすっかり忘れてました!
US$ 0.49

このDIPのATTINY85を乗せて書き込みするのです。
これをブレッドボードに直挿しすれば、空中配線がスッキリするハズです。
Digisparkボード内のパーツも気にしなくてよくなります。
(後でATTINY85に振り回されることになるとは、知る由もないマーティー)
US$ 1.15(15% OFF)

ATTINY85のDIPタイプのピンアサイン
VBB(Mortor系)、VDD(Logic系)の立上げも
サンケン電気のDatasheet 8.16に、この記述がありました。
つまりどっちを電源ON/OFFでの順番は気にしなくていいようです。
今回は、Step UP Converterしか持ってないので
VBB:6.2Vは、VDD:5Vから作ることにします。

Friztingで実体配線図!
早速、書き込んでみます。
忘れてました~
これは、生チップなのでブートローダーが入ってないですね~(T_T)
所が、そう簡単にはいかず、長~いATTINY85地獄の始まりだったのです(-_-;)
この後、
ATTINY85(Digispark)との格闘に陥るわけであります。
すでに投稿してますが、格闘三部作になっております(-_-;)
格闘Ⅰ(生チップへBootloader書込)
格闘Ⅱ(謎のPULL-UP)
格闘Ⅲ(妙なパルス)

<教訓>
① 内部PULL-UPは、信用しないこと!
  Digisparkでは「pinMode(INPUT_PULLUP);」でPULL-UPされない
  「digitalWrite(x, HIGH);」でPULL-UPされるが、されないPINがある
  なので、必ず、外部PULL-UP抵抗をつける。

② DigisparkボードのPB1、PB3、PB4にぶら下がっているパーツに注意!
・PB1:GND間に「LED+1KΩ」あるので
  LED 2Vとすると4.7KΩでPULL-UPしても2.5Vまでしか上がらない。
  2KΩ以下でPULL-UPしないとHIGHレベルにならないでしょう。
・PB3:66.5Ωを挟んで3.6Vツェナー ~ GND、と1.5KΩ PULL-UP
・PB4:66.5Ωを挟んで3.6Vツェナー ~ GND
  これだけでATTINY85から20mAも出すことになる。
  実際にはドライブ不足でHIGHレベルが大きく低下する。

③ 単体のATTINY85は、挙動不審なので何が起こるかわからない。
  動作が変な時は、必ずオシロで波形確認する。

ということで、思わぬ所でATTINY85地獄に落ちました(T_T)
抜け出すのに数日、とても苦労しました(-_-;)

さて、ATTINY85との格闘に三連勝して^^;
上下の基本動作がや~~~っと動いた所で
どうしてもA4988のENABLEが気になるのであります。
ADCを使って1pinでUP、DOWN、
折角なので、もう一つKEYを増やして、HOMEを追加!
1pin空くので、ENABLEに当てようと思いつきました!
このモーターは、常時電流流すのはキツそうなのです。
ENABLEでモーター電流OFFしても、ほとんど位置がズレそうにないし、
CO2レーザーなので1mm位ズレても問題ないでしょう。

1pinで3KEYに対応するには、
抵抗分割してADCで電圧を読むという手があります。
まあ、解像度が数段階のジョイスティックってところです。
しばし頭を捻って、このタイプA
同時押ししても高優先順位のが有効になりますが、
抵抗値の選定が面倒(まあExcelとかで計算すればいいですが)
と、このタイプB
SW2とSW3の同時押しで中間の電圧になりますが
抵抗値選定は、とても簡単!
ということで、後者のタイプBに!
UPはGNDで変動せず、±20%の変動を見ても、
DOWN: 1.76V±20% ・・・ 1.4~2.2V
HOME:  3.0V±20%   ・・・ 2.4~3.6V
まあ、範囲が被らないから大丈夫でしょう。
というか、被らないように抵抗値を決めた結果ですが...
現時点の実体配線図!

できたスケッチです。
・Limit SWは、まだ使いません。
・KEY無操作が2sec続くと、ENABLE HIGHでモーター電流OFFにします。
・ADCを使った、1pinで3 Buttonの検出も動きました!
***CO2_Z-Axis_Move_E.ino***
// CO2 Laser Cutter Z-Axis Controller
// DigiSpark (ATTINY85)
// 抵抗分割して3-KEYをADCで検出
// PB1(Step):470KΩ+Diode(Pluse 立下りなまり対策)
// PB4(LimitSW):4.7KΩ程度のPullUp必須(内部Pull-Up不安定対策)


// 注意 see https://digistump.com/wiki/digispark/quickref
// Digital 2 is analog (ADC channel) 1
// Digital 3 is analog (ADC channel) 3
// Digital 4 is analog (ADC channel) 2
// Digital 5 is analog (ADC channel) 0

#define Direction 0 // PB0 for Direction HIGH: UP、LOW: DOWN 
#define Step 1 // PB1 for Step Pluse
#define KEY_Detect 1 // For Analog ch1 = digital ch2(PB2)
#define nENABLE 3 // PB3 for nENABLE 
#define LimitSW 4 // PB4 for Limit-SW(Upper & Lower)
#define nRESET 5 // PB5 for nRESET

int DontMoveCount = 0; // 非動作期間カウント
int MaxDontMoveCount = 2000; // これで約2秒

void setup() {
  // initialize the digital pin as an output.
  pinMode(Direction, OUTPUT);       // DIRECTION
  pinMode(Step, OUTPUT);       // STEP
  pinMode(KEY_Detect, INPUT); // ADC ch1(PB2)でKEY検出
  pinMode(nENABLE, OUTPUT); // nENABLE
  pinMode(LimitSW, INPUT); // LimitSW ("_PULLUP"は効かない)
  digitalWrite(LimitSW, HIGH); // 一応これで内蔵PullUpをActiveにする
  pinMode(nRESET, INPUT);        // Reset(固定だが覚書)
  // 初期値設定
  digitalWrite(Direction, HIGH); // UP
  digitalWrite(Step, LOW);
  digitalWrite(nENABLE, HIGH);  // Motor Disable
  analogReference(DEFAULT);  // Reference = VCC(5V) (念の為)
}

// 抵抗分割のKEYは、ADCで検出(1pinキー回路)
// UP  : 0.0V(0~1.0V)
// DOWN: 1.76V±20% (1.4~2.2V)
// HOME: 3.0V±20% (2.4~3.6V) ・・・ 最上部に移動
// INPUT: 5V ・・・ ADC: 1023 なので
// UP  : 0 to 205
// DOWN: 286 to 451
// HOME: 491 to 738

void loop() {
  // 0 以上 ~ 205以下:UP
  if ( (0 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 205)) {
    DontMoveCount = 0;
    digitalWrite(nENABLE, LOW); // ENABLEをACTIVEする
    digitalWrite(Direction, HIGH);
// ここにLimit SW判定
    move();
  }

  // 286 以上 ~ 451以下:DOWN
  if ( (286 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 451)) {
    DontMoveCount = 0;
    digitalWrite(nENABLE, LOW); // ENABLEをACTIVEする
    digitalWrite(Direction, LOW);
// ここにLimit SW判定
    move();
  }

    // 491 以上 ~ 738以下:HOME ・・・ Limit SW 直前まで上がる
  if ( (491 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 738)) {
    DontMoveCount = 0;
    digitalWrite(nENABLE, LOW); // ENABLEをACTIVEする
    digitalWrite(Direction, HIGH);
    // ここにHoming動作が入る
    }
    
    // KEYを押してない時は、5V(VCC)になる
    // 一定カウント超えたらnENABLEをHIGHにしてモーター電流停止
    // SW ALL OFF 739~1023
  if ((739 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 1023)) {
    // 非動作期間カウントUP処理
    DontMoveCount = ++DontMoveCount;
    if ( DontMoveCount >= MaxDontMoveCount ) {
      digitalWrite(nENABLE, HIGH); // ENABLEをDISABLEする
      DontMoveCount = 0; // カウントリセット
    }
  }
}

void move() {
  digitalWrite(1, HIGH);
  delayMicroseconds(2250);
  digitalWrite(1, LOW);
  delayMicroseconds(2250);
}
*****

が、SWを素早く押しても1mm程動き、微妙な操作ができません。
素早く押すといっても、100msec位は、押してるので、
1step 4.5msecが22 Pluseも出て、0.0375 x 22=0.825mm
動いてしまうというわけです。

「短押し:チョン」と「長押し:0.3sec以上」で
動きを変えることにします。

0.1sec未満のKEY押しは、ノイズとみなして無視することにして、
<短押し>
・0.1~0.3sec 同じKEYを押すとSTEP信号を出す
・1STEP毎に0.1secのDelayを入れる。
<長押し>
・0.3sec以上 同じKEYを押してるとDelayをなくして連続移動にします。

これは、この時にとても苦労して作った
JOYSTICKで上下左右3段階検出のスケッチが役立ちました^^;

一応、ADC時間は、
Arduino.ccのここに、analogRead()は、100μsecとあるので
0.1secに比べ十分無視できます。

やや格闘したスケッチ「CO2_Z-Axis_Move_F」バージョンは、
動作したけど、ちとゴチャゴチャなので整理してます。
チョンチョンで1step、長押しで連続移動
時間は正確ではないけど、動作は、良い感じです\(^o^)/
***CO2_Z-Axis_Move_G.ino***
// CO2 Laser Cutter Z-Axis Controller
// DigiSpark (ATTINY85)
// 抵抗分割して3-KEYをADCで検出
// PB1(Step):470KΩ+Diode(Pluse 立下りなまり対策)
// PB4(LimitSW):4.7KΩ程度のPullUp必須(内部Pull-Up不安定対策)
// 1 PUSHで1mm以上移動するので
// 0.1~0.3sec同一KEY押下で、1STEP送出
// 0.3sec以上の同一KEY押下で連続移動

// 注意 see https://digistump.com/wiki/digispark/quickref
// Digital 2 is analog (ADC channel) 1 今回は、これ
// Digital 3 is analog (ADC channel) 3
// Digital 4 is analog (ADC channel) 2
// Digital 5 is analog (ADC channel) 0

#define Direction 0 // PB0 for Direction HIGH: UP、LOW: DOWN 
#define Step 1 // PB1 for Step Pluse
#define KEY_Detect 1 // For Analog ch1 = digital ch2(PB2)
#define nENABLE 3 // PB3 for nENABLE 
#define LimitSW 4 // PB4 for Limit-SW(Upper & Lower)
#define nRESET 5 // PB5 for nRESET

int DontMoveCount = 0; // 非動作期間カウント
int MaxDontMoveCount = 500; // これで約0.5秒
short ContenuousMode = 0; // 0:シングル動作、1:連続動作
#define None_Key 4
#define UP_Key 1
#define DOWN_Key 2
#define HOME_Key 3
short KeyPosition = None_Key; //  今押したKEY 4:None Key、1:UP、2:DOWN、3:Home
short last_KeyPosition = None_Key; // 前押したKEY 4:None Key、1:UP、2:DOWN、3:Home
int SameKeyCount = 0; // 同一KEY押下時間カウント
short WaitCount = 0;   // Single Step移動時の無効化時間カウント
short WaitCountIncrease = 10; // 1LOOPの時間が約10msecになるようにする

void setup() {
  // initialize
  pinMode(Direction, OUTPUT);       // DIRECTION
  pinMode(Step, OUTPUT);       // STEP
  pinMode(KEY_Detect, INPUT); // ADC ch1(PB2)でKEY検出
  pinMode(nENABLE, OUTPUT); // nENABLE
  pinMode(LimitSW, INPUT); // LimitSW ("_PULLUP"は効かない)
  digitalWrite(LimitSW, HIGH); // 一応これで内蔵PullUpをActiveにする
  pinMode(nRESET, INPUT);        // Reset(固定だが覚書)
  // 初期値設定
  digitalWrite(Direction, HIGH); // UP
  digitalWrite(Step, LOW);
  digitalWrite(nENABLE, HIGH);  // Motor Disable
  analogReference(DEFAULT);  // Reference = VCC(5V) (念の為)
}

void loop() {
  KeyDetect(); // ADCのKEY状態をScan
  MoveProcess(); // KEY押下時間判定して移動処理
}

// 抵抗分割のKEYは、ADCで検出(1pinキー回路)
// UP  : 0.0V(0~1.0V)
// DOWN: 1.76V±20% (1.4~2.2V)
// HOME: 3.0V±20% (2.4~3.6V) ・・・ 最上部に移動
// INPUT: 5V ・・・ ADC: 1024 なので
// UP  : 0 to 205
// DOWN: 286 to 451
// HOME: 491 to 738

void KeyDetect() {
   // 0 以上 ~ 205以下:UP
  if ( (0 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 205)) {
    KeyPosition = UP_Key;
  }
  // 286 以上 ~ 451以下:DOWN
  if ( (286 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 451)) {
    KeyPosition = DOWN_Key;
  }
  // 491 以上 ~ 738以下:HOME ・・・ Limit SW 直前まで上がる
  if ( (491 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 738)) {
    KeyPosition = HOME_Key;
  }
   // KEYを押してない時は、5V(VCC)になる
   // 一定カウント超えたらnENABLEをHIGHにしてモーター電流停止
   // キーを押してもカウントリセット必要 
   // SW ALL OFF 739~1023
  if ((739 <= analogRead(KEY_Detect)) && (analogRead(KEY_Detect) <= 1023)) {
    KeyPosition = None_Key;
  }
}

void MoveProcess(){
    if (last_KeyPosition == KeyPosition) { // 前KEYと同一KEY押下の場合
     SameKeyCount = SameKeyCount + WaitCountIncrease; // ① 1loop分増加
     // 同一Positionに最低閾値~300msec定位していればシングルstep移動命令
     // 同一KEY押下が約100~300msecであれば、シングルstep動作
       if ( 1000 <= SameKeyCount && SameKeyCount < 3000 ) {
          if ( WaitCount == 0 ) { // KEY変化の最初
            ContenuousMode = 0;  // 連続モードOFF
            MoveControl();  // 
            WaitCount = WaitCount + WaitCountIncrease;  // 1loop分増加
          } else { // 2回めに同一KEY検出したら290 Wait
          // 一定期間(300msec弱)不感時間を設ける為のカウンタ
            WaitCount = WaitCount + WaitCountIncrease;  // 1loop分増加
              if( WaitCount > 2900) {
                 WaitCount = 0;
              }
          }
          // KEY押下時間SameCountが100未満の場合、①のカウントUPのみ
        }
      // 同一Positionに約300msec以上定位していれば連続移動命令
        if (SameKeyCount >= 3000) {
          // 一定間隔で出力する連続モード
          ContenuousMode = 1;
          MoveControl(); // delayなしでMoveする
        }
     } else { // 前KEYと異なるKEY押下の場合
    // 初めてのKeyScan時の初期化処理
    // ノイズも含め全ての検出は、最初にここを通る
       last_KeyPosition = KeyPosition;
       SameKeyCount = 0;
       WaitCount = 0;
    }
}

void MoveControl() {
    switch ( KeyPosition ) {  // KEYに応じて処理
    case 1:  // UP
      DontMoveCount = 0;
      // UP処理
      // ここにLimit SW判定
      digitalWrite(nENABLE, LOW); // ENABLEをACTIVEする
      digitalWrite(Direction, HIGH);
      SendStepPluse();
    break;
    case 2:  // DOWN
      DontMoveCount = 0;
      // DOWN処理
      // ここにLimit SW判定
      digitalWrite(nENABLE, LOW); // ENABLEをACTIVEする
      digitalWrite(Direction, LOW);
      SendStepPluse();
    break;
    case 3:  // HOME・・・上側Limit SWまでUP
      DontMoveCount = 0;
      digitalWrite(nENABLE, LOW); // ENABLEをACTIVEする
      digitalWrite(Direction, HIGH);
      // ここにHOMING処理
      delay(1000); // HOMEの後は、少しDelayした方がいいだろう
    break;
    case 4:  // None KEY(KEY押下なし)
      // nENABLEをDISABLEしてMOTOR OFF処理
      // 非動作期間カウントUP処理
      DontMoveCount = ++DontMoveCount;
      if ( DontMoveCount >= MaxDontMoveCount ) {
        digitalWrite(nENABLE, HIGH); // ENABLEをDISABLEする
        DontMoveCount = 0; // カウントリセット
      }
    break;
  }
}

void SendStepPluse() {
  digitalWrite(1, HIGH);
  delayMicroseconds(2250);
  digitalWrite(1, LOW);
  delayMicroseconds(2250);
}
***

ATTINY85にかなりもて遊ばれました~(-_-;)
やっとLimit Switchに行けそうです。
PICに乗り換えたくなってきましたが
一旦、最後までATTINY85で頑張ってみるつもりですが...^^;

0 件のコメント: