パルスをカウントしようとすると奇妙な結果

私は今、絶望的に立ち往生しており、助けてください。

私は自動化された天文台を建設していますが、天文台の回転を制御しようとしています。ドームはステップモーターを使用して回転し、正常に動作します。ドームの回転はロータリーエンコーダを回して、ドームが動いていることを独立して確認します。 REは直交型ですが、方向情報が不要なので、両方のチャネルからREティックを数えています。

私が今やろうとしていることは、REティックの各ペアの間にモータ駆動パルスがいくつ発生するのかを数えることです。この背後にある理由は、ドームの動きを監視し、妨害を検出することです。実際のモーターパルスはすべてのドーム位置計算に使用されます。

これを達成するために、ULN2803A反転バッファーとArduino Nanoを備えた小型ボードを構築しました。 NanoはピンD11とピンD12のピンチェンジ割り込みを使用してREティックをカウントします。これは存在しませんが、ピンD7はホームポジションセンサーのモニターにも使用されます。モータパルスはバッファを介してピンD2(INT0)に送られ、モータの方向はバッファを介してピンD3(INT1)に送られます。その後、外部割込みを使用して、モータの方向に応じてモータのパルスをカウントアップまたはカウントダウンします。

詳細については、Velleman VM110 USBブレークアウトボードもあります。このボードには、ロータリーエンコーダのティックが入っています。ロータリーエンコーダーのシャフトを手で回転させると、私のボードが同じ答えを出すかどうかを簡単に確認することができます。

OK - 申し訳ありませんが、これは非常に長い巻きですが、今は変です。

モーターを動かさずにREを回転させると、すべてが正常です。エンコーダのフルサイクルではなく、それぞれの立ち上がりと立ち下がりでトリガするPin Changesを使用しているので、Vellemanボードの4倍のパルスが得られます。これは予測どおりに行われ、すべてが素晴らしく安定しています。

しかし、私がモーターを動かすと、エンコーダを動かすたびに何百ものREティックが得られます。エンコーダに触れることなくREティックが増えることがあります。

明らかな答えは何らかの形のRFIですが、私はこれを排除するためにできることはすべて行っています。モータードライバとそれを駆動するNanoボードはシールドされたエンクロージャ内にあり、モーターへのケーブルはシールドケーブルであり、4つのモーターリードのそれぞれに10uHのチョークがあります。最後に、モーターボックスに入ってくる電源にフィルタを取り付けて、RFIが電力線に戻るのを最小限に抑えました。

ULN2803Aバッファの使用は私の最近の試みでありました。これまでは、信号が直接ナノピンに送られていました。バッファでは、入力側に20kプルアップ、出力側に10kプルアップを使用しました。これは私が問題なく動作していたVellemanボード入力回路の直接コピーです。

私は自分のスコープでNanoへの入力ピンでモーターパルスを見てきました。そして、彼らは、持続時間70μSと497Hzの鋭い鋭いエッジのあるパルスです。 Accelstepperライブラリを使用して500Hzまでの脈拍数を設定しても悪くありません。

今はこれがソフトウェアの問題だと思う。これは私がこのすべてに非常に新しいので、私が必要とすることを行うために各段階で十分に勉強しようとしているので、私を驚かせることはありません。私はコードを添付しました - これは、私の問題に関連しない多くのI2Cのものを除いた削除されたバージョンです。

情報については、もう一度。私は attachInterrupt()関数を使用して、関連するレジスタを直接設定してみました - 違いはありません!

とにかく、私は本当に本当に誰かがこれを整理するのを助けることを願っています。

よろしくね、ヒュー

/*          
ABoard3  
ROTATION MONITORING AND POSITION
Version AB3_stripped
*****************************PURPOSE*****************************************
This sketch is used on an Arduino Nano to count the motor pulses from ABoard1
and the Rotary Encoder ticks. The motor pulse count between encoder ticks is
used to detect dome jamming.
****************************INPUTS*******************************************
PIN CHANGE INTERRUPTS
**********ROTARY ENCODER INPUT*********
The rotary encoder is a Bourns ENA1J-B28 L00064L 1139M MEX 64 cycle per turn
optical encoder. This is connected to ABoard3 Pins D11 and D12. These pins
connect to Channel A and Channel B respectively. Pin change interrupts are used
to receive the rotary encoder output.
(The output pulses from the rotary encoder is also sent to the Velleman board
for use by LesveDomeNet for finding dome position)
*********HOME POSITION INPUT*********
The home position sensor is an Omron EESX671 Optical Sensor.
The sensor output is connected to ABoard3 Pin D7.
Pin change interrupt PCINT21 records activation/deactivation of the sensor.
EXTERNAL INTERRUPTS
*********MOTOR PULSE INPUT***********
The pulses sent to the G251X stepper driver are also sent to Aboard3 Pin D2.
This pin is the External Interrupt pin, vector INT0
*********MOTOR DIRECTION INPUT********
Motor direction is input to ABoard3 from ABoard2. ABoard2 pin, pnVmInRotMotor
(AB2:A0{54}) is connected to Velleman pins DI4 and DO2 and also to AB3:D3.
AB3:D3 is an External Interrupt pin, vector INT1.
*/

#include                        
#include "streaming.h"                        
#include "I2C_AnythingHEG.h"   
#include                     

//CONSTANTS                       
//PIN ASSIGNMENTS For ABOARD3
const uint8_t pnMotorPulse = 2;      //Port PD2, INT0, ISR(INT0_vect){}
const uint8_t pnMotorDirection = 3;  //Port PD3, INT1, ISR(INT1_vect){}
const uint8_t pnDomeAtHome = 7;      //Port PD7, PCINT23,ISR(PCINT2_vect){}
const uint8_t pnREChanA = 11;        //Port PB3, PCINT3, ISR(PCINT0_vect){}
const uint8_t pnREChanB = 12;        //Port PB4, PCINT4, ISR(PCINT0_vect){}

//*****************EXPERIMENTAL STUFF FOR PULSE COUNTING*******************************                  
uint16_t volatile myTest = 0;
int32_t volatile ratioCount = 0L;
int32_t volatile totalCount = 0L;
int32_t volatile tickCount = 0L;
uint8_t volatile halftickCount = 0;
int32_t volatile addPulse = 0L; 

void setup() {
  //**********************************SERIAL FOR DEBUG ONLY************************
  Serial.begin(115200);
  //*************************INPUT PIN MODE SETUP**********************************
  //NOTE Set all UNUSED PCINT0:5 pins (D8, D9, D10, D11) to OUTPUT.
  //and set the value to LOW
  //The actual pins used to receive interrupts have external 10k pull-ups.
  //This is to reduce susceptibility to interference causing false triggering.
  pinMode(pnMotorPulse, INPUT); //D2
  pinMode(pnMotorDirection, INPUT); //D3
  pinMode(pnDomeAtHome, INPUT); //D7
  pinMode(pnREChanA, INPUT); //D11
  pinMode(pnREChanB, INPUT); //D12
  pinMode(4, OUTPUT); //D4
  digitalWrite(4, LOW);
  pinMode(8, OUTPUT); //D8
  digitalWrite(8, LOW);
  pinMode(9, OUTPUT); //D9
  digitalWrite(9, LOW);
  pinMode(10, OUTPUT); //D10
  digitalWrite(10, LOW);
  //******************SET UP AddPulse According to Motor Direction******************
  //This is needed to make sure the pulse counting starts in the correct direction
  //as the direction is only checked in the ISR after a change has occurred.
  //If Motor Direction is AntiClockwise, change to Subtract a pulse
  if( digitalRead(pnMotorDirection)) {
    addPulse = 1L;
  } else {
    addPulse = -1L;
  }
  //**************************SET UP PIN CHANGE INTERRUPTS**************************
  //Set the Pin Change Interrupt Masks
  //Clear all bits to start with
  PCMSK0 &= 0b00000000; //Clear all bits
  PCMSK1 &= 0b00000000; //Clear all bits
  PCMSK2 &= 0b00000000; //Clear all bits
  //Mask for PCINTs 0 through 7 is PCMSK0 (Rotary Encoder Pulses)
  //Need to allow interrupts on PCINT3 and PCINT4, so set bits 3 and 4
  //PCINT3 is Nano  pin D11 and PCINT4 is Nano pin D12
  //Use a compound bitwise OR to set the bits
  PCMSK0 |= 0b00011000; //Enable PCINT3 ONLY (bit 3)
  //Interrupt on pins 11 and 12, RE Ticks
  //Mask for PCINTs 16through 23 is PCMSK2 (Home Position)
  //Need to allow interrupts on PCINT23 so set bit 7
  PCMSK2 |= 0b10000000; //Interrupt on pin 7, Home Position Sensor activated
  //Now enable the interrupts (TURN THEM ON) by setting bits in PCICR
  //PCICR is Pin Change Interupt Control Register.Set bit 0 (PCIE0)  
  //to enable interrupts PCINT0:7 This catches PCINT3 and 4 - RE Ticks
  //Set bit 2 (PCIE2) to enable interrupts PCINT16:23. Catches PCINT21 - Home Position Sensor
  PCICR &= 0b00000000; //Clear PCICR register 
  PCICR |= 0b00000001; //Set bit 0 - Catches PCINT3 & PCINT4 - RE Ticks
  PCICR |= 0b00000100; //Set bit 2 - Catch PCINT21 - Home Position Sensor
  //**************************SET UP 'EXTERNAL INTERRUPTS'**************************
  //Interupts on External Interrupt Pins D2 (INT0) and D3 (INT1).
  //INT0 (Nano pin D2)occurs when a stepper motor driver pulse is received
  //INT1 (Nano pin D3)occurs when the ABoard2 pin, pnVmInRotMotor toggles 
  //indicating a motor direction change.
  //To use the 'External Interrupts' the following registers need to be set-up:         
  //EICRA External Interrupt Control Register A       
  //Need to set Interrupt Sense Control bits ISC11 .. ISC00 (bits 3:0 in EICRA)
  //ISC01     ISC00 (INT0)    Interrupt
  //ISC11     ISC01 (INT1)    Generated by
 // 0         0             Low level on Pin
 // 0         1             Any Logical Change
 // 1         0             Falling Edge
 // 1         1             Rising Edge
  //First clear all bits, then set as follows:  
  //For INT1 - Motor Direction - Generate on ANY LOGICAL CHANGE     
  //bit 3 ISC11 0     
  //bit 2 ISC10 1 This combination = Any logical change causes interrupt 
  //For INT0 - Stepper Motor Pulses  - Generate on RISING EDGE      
  //bit 1 ISC01 1     
  //bit 0 ISC00 1 This combination = Rising edge of pulse causes interrupt
  //NOTE: To provide some immunity to RFI, Aboard3:Pins 2 & 3 are pulled high
  //using 10k resistors. 
  //So, code is
  EICRA &= 0b00000000; //Clear EICRA register
  EICRA |= 0b00000111;//Set bits 0,1 and 2 in EICRA register
  //EIMSK External Interrupt Mask Register        
  //Need to set External Interrupt Request Enables INT1 & INT0  (bits 1:0)          
  //First clear both bits, then set as follows: 
  //bit 1  INT1  1 External interrupt pin (D3) enabled   
  //bit 0  INT0  1 External interrupt pin (D2) enabled   
  //So, code is
  EIMSK &= 0b00000000; //Clear EIMSK register
  EIMSK |= 0b00000011;//Set bits 0 and 1 in EIMSK register
  //NOTE: Setting EIMSK TURNS ON THE EXTERNAL INTERRUPTS
  //************VARIABLE INITIALISATION*********
  myCommand = 0;
  myTest = 0;
  tickCount = 0L;
  totalCount = 0L;
} //END of setup

void loop() {
  //******************************COMMAND ACTIONS******************************
  if (myTest == 3) (
    //RE tick
    Serial << "Tick Count = " << tickCount << "  totalCount = " << totalCount << "\n";
    myTest = 0;
  }
}
//*************************FUNCTIONS/ISRs FROM HEREON*************************
ISR(INT0_vect) {
  //Triggered by stepper motor drive pulse from ABoard1
  totalCount = totalCount + addPulse;
}
ISR(INT1_vect) {
  //Triggered by a change in motor direction
  if(digitalRead(pnMotorDirection)) {
    addPulse = 1L;
  } else {
    addPulse = -1L;
  }
}
ISR(PCINT0_vect) {
  //Triggered by a ROTARY ENCODER TICK
  halftickCount++;
  if (halftickCount == 2) {
    //Make count same as Velleman
    tickCount++;
    halftickCount = 0;
    myTest = 3;
  }
}
ISR(PCINT2_vect) {
  //Triggered by activation of Home Position Sensor
  myTest = 4;
}
0
1)あなたはスコープを持っています、あなたはそれを使ってモーターパルスを見ました。エンコーダパルスも見ましたか?彼らはどのように見えるのですか? 2)直角位相エンコーダでは、たいていの場合、たいてい1つのチャンネルが「弾力ある」ものであると予想されます。次に、通常の方法でエンコーダーを使用する場合は、+1、-1、+1、-1のシーケンスを取得してゼロになるはずです。 3)AVR I/Oレジスタを設定するための標準的なイディオムは、 PCMSK0 = _BV(PCINT3)|です。 _BV(PCINT4); は、 PCMSK0&= 0b00000000よりもはっきりしています。少なくともAVRプログラミングに精通している人には、PCMSK0 | = 0b00011000;
追加された 著者 Sprogz,
PCMSK0&= 0b00000000; //すべてのビットをクリアする - PCMSK0 = 0; ははるかに明確になりますか?一方私はGerbenの答えに同意します。割り込みによって設定されたマルチバイト変数へのアクセスを "保護"してください。 割り込みを参照してください。
追加された 著者 Nick Gammon,
直角位相エンコーダ出力とArduinoとの間に適切な共通基盤がありますか?もしそうなら、@ EdgarBonetは正しいです。直交エンコーダは狂ったようにバウンスすることができます。エンコーダを正しく読み取って、ステップの方向を決定し、すべての読み取りごとにすべてを読み取る必要があります。こうすることで、+ 1、-1、または他のシーケンスが連続して100回バウンスすると、新しい場所が何であるかを知ることになります。各直交出力に2.2uFのキャップを追加して、ハードウェアのデバウンスに少しでも役立ててください。
追加された 著者 meepsh,
エンコーダを正しく読み込むことは、あなたが本当に必要とするデバウンスソフトウェアです。
追加された 著者 meepsh,
ロータリーエンコーダは電気的または光学的ですか?私はあなたがスイッチのバウンスやおそらく光レシーバの境界にあるかどうかに問題があるのだろうかと疑問に思っています。これが当てはまる場合、それを解決するのに役立つ1つの方法は、4進符号化パルスを適切に復号することである。このブログ記事は、4つの符号化されたパルスをデコードする信頼できる方法を示しています。 thewanderingengineer.com/2014/08/11/… Regads、
追加された 著者 CMaster,
プロセッサでデバウンスを実行する代わりに、その目的のために設計されたチップを使用することもできます。以下は適切なものです: elmelectronics.com/ic/elm402 よろしくお願いします。
追加された 著者 CMaster,

1 答え

いくつかの変数にint32_tを使用する必要はありません。 8ビットプロセッサで8ビット以上の変数を使用する場合の問題は、プロセッサがその値を取得するために4回の読み込みが必要であることです。これらの読み取りの途中で割り込みが発生し、古い値のいくつかのビットと新しいビットのいくつかのビットを持つ値が得られます。

変数によっては8ビットしか必要ありません。

uint8_t volatile myTest = 0;
int32_t volatile totalCount = 0L;
int32_t volatile tickCount = 0L;
uint8_t volatile halftickCount = 0;
int8_t volatile addPulse = 0L; 

次のことは、値のアトミックをアトミックにすることです。

void loop()                                             
{                                               
//******************************COMMAND ACTIONS******************************  

if (myTest == 3)   //RE tick
{
    noInterrupts();
    int32_t tickCountCopy = tickCount;
    int32_t totalCountCopy = totalCount;
    interrupts();
    Serial << "Tick Count = " << tickCountCopy << "  totalCount = " << totalCountCopy << "\n"; 
    myTest = 0; 
}
}   

PS。これは必ずしも答えではありませんが、それはコメントに収まりませんでした。

1
追加された
ここには本当のバウンスはありませんが、あなたが期待していたほどの変化はないでしょう。私の推測はシュミットトリガだけで動作します。私の答えがあなたの問題を解決するわけではありませんが、それはあなたのコードについて説明するかもしれない問題を指摘しています。
追加された 著者 Al.,
こんにちは、GerbenとNickに感謝します。私はあなたが推し進めた変更を加えましたが、実際にはステップモーターが動かなくてもREの各チャンネルで複数のトリガーが得られていたので、実際には物事を悪化させてしまいました。
追加された 著者 SeaDrive,
おっと - 新しいラインと押されたリターンが欲しかったが、私が終わる前にコメントを送った。だから、今からフォーマットの欠如を許してください。私は今、ロータリーエンコーダーをデバウンスする必要があるという結論に達しました。光学式エンコーダのように、デバウンシングは必要ないと思っていましたが、私の仮定は間違っていたと思います。私はRCタイミングと74HC14六角反転SchmittトリガーICを使って小さなハードウェアデバウンサーを作るつもりです。うまくいけば、帽子は問題を解決します。良いニュースは、実際のステップモータパルスが問題なくカウントされることです。ありがとう、ヒュー。
追加された 著者 SeaDrive,
こんにちは、エンコーダの出力を見るためにスコープを使用しようとしましたが、正しく使用する方法がわからず、意味のある結果が得られませんでした。私は各エンコーダチャンネルで約5mSの遅延を使ってデバウンサーボードを作りました。そして、最終的にはそれが想定どおりに動作します。私はまずこの問題を12月初旬に抱いていましたので、この時間をすべて取って解決しました。実際にこの特定の問題に役立つ私の範囲を買った。回答しコメントをいただいたすべての方々に感謝します。ヒューに感謝
追加された 著者 SeaDrive,
@HughGilhespie、問題が今解決された場合は、自分の質問への回答を投稿し、解決済みとマークしてください。
追加された 著者 meepsh,