最初はそれを再アップロードしたかったのですが、この目的のために、GrblCNCプロジェクトのソースコードを入手しました。しかし、好奇心が克服され、私はこれらの情報源の研究に飛び込みました...
それらは非常にシンプルで論理的ですが、
実際、CNCマシン用のコントローラーのアイデアは非常にシンプルで興味深いものです。いくつかの処理スレッドがあります。1つはデータ(gcode)を読み取って解析し、2つ目はコマンドを実行ブロックに変換し、3つ目(ステッパー)は実際にこれらのブロックを実行します。この3番目のストリームについて説明します。
ステッパーは、次の形式の個々のコマンドのリストを処理します-3つすべての(少なくとも)ステッパーモーターに対して、指定された時間と方向に(X、Y、Z)ステップを実行します(これは非常に単純です)。ドライバー付きのステッパーモーターは、制御が非常に簡単であると言わなければなりません。回転方向を設定(0または1)すると、モーターは正の入力差(0-> 1)で1ステップを実行しようとします(通常、1回転あたり200ステップあります)。データはすでに準備されているので、3つの整数を指定された時間と何らかの方法で相関させる必要があります。
オリジナルでは、作者はatmega328pコントローラーを使用しましたが、実質的に変更されていないため、すべてがアームに簡単に転送されます(たとえば、stm32)。しかし、アルゴリズム自体は疑問を投げかけるしかない。
一方では、非常に完璧なBresenhamのアルゴリズムが使用され、むしろそのバージョンのAdaptive Multi-AxisStep-Smoothingが使用されます。しかし一方で、どういうわけかそれはすべて複雑であり、最も重要なのは、ステッパーモーターの滑らかさとルーターの精度が制御信号の精度に直接依存していることです。この場合、これはタイマーが動作する周波数と割り込み処理時間によるものであり、これはせいぜい40〜50 kHz以下、通常はそれ以下です。つまり、制御設定の精度は20〜50マイクロ秒です。
しかし、バッファから1つのコマンドを処理するとき、出力ポートでの信号切り替えの瞬間とこれらの瞬間を計算して切り替えを行う必要があることは明らかです。
cortex-m(より正確には、私が大好きで非常に安価になったstm32h750)への切り替えを検討していたので、2つのDMAチャネルと1つの32ビットカウンターを使用するだけで、CPUを使用せずにこのようなタスクを完全に解決できます。
アイデアはとてもシンプルです。1つのチャネルがカウンタオーバーフロー時にポートに新しいデータを書き込み、2番目のチャネルが新しい最大カウンタ値を書き込むようにします(これは、カウンタの最初のクロックサイクルで行うのが妥当です)。次に、リストからコマンドを処理するには、ポートの変更値の配列とそれらの間のタイムアウトを準備する必要があります。
こんな感じになります。
割り込み処理-新しいバッファーへの切り替え(ダブルバッファー)。
#define MAX_PGM 32
typedef struct _pgm_buffer {
uint32_t data[MAX_PGM];
uint32_t delta[MAX_PGM];
} pgm_buffer;
pgm_buffer buf[2];
uint32_t current_buf = 1;
uint32_t flags = 0;
void program_down(DMA_HandleTypeDef *_hdma) {
TIM2->CR1 &= ~TIM_CR1_CEN;
if ((flags & BUF_RUNNING) == 0)
return;
current_buf ^= 1;
DMA1_Channel5->CCR &= ~1;
DMA1_Channel2->CCR &= ~1;
DMA1_Channel5->CNDTR = MAX_PGM;
DMA1_Channel2->CNDTR = MAX_PGM;
DMA1_Channel5->CMAR = (uint32_t) (buf[current_buf].delta);
DMA1_Channel2->CMAR = (uint32_t) (buf[current_buf].data);
DMA1_Channel5->CCR |= 1;
DMA1_Channel2->CCR |= 1;
TIM2->CNT = 0;
TIM2->ARR = 8;
TIM2->EGR |= TIM_EGR_UG;
TIM2->CR1 |= TIM_CR1_CEN;
}
次の方法で開始できます。
HAL_DMA_RegisterCallback(&hdma_tim2_up, HAL_DMA_XFER_CPLT_CB_ID,
program_down);
HAL_DMA_Start_IT(&hdma_tim2_up, buf, &GPIOA->BSRR, MAX_PGM);
DMA1_Channel5->CCR &= ~1;
DMA1_Channel5->CPAR = &TIM2->ARR;
DMA1_Channel5->CCR |= 1;
TIM2->CCR1 = 1;
TIM2->DIER |= TIM_DIER_UDE | TIM_DIER_CC1DE;
flags |= BUF_RUNNING;
さて、開始は次のとおりです。
program_down(NULL);
それは何をするためのものか?同じstm32h750の例を使用して計算してみましょう。タイマー(TIM2)は200 MHzの周波数で動作し、最小遅延は2クロックサイクルですが、DMAは50 MHzより速くデータを送信できません。つまり、ポートを切り替えるための2つのコマンドの間に、(可能なバス占有を考慮して)40 nsec(25 MHz)を置くことができます-これは元の実装よりも1000倍優れています!
一方、ポート幅は16ビットなので、3つではなく8つのステッパーモーターを同時に制御できるので、
この場合、データ自体を入力しても問題は発生しません(このような解像度で!)-各モーターの単純な線形補間(最適化のために)40nsecより近いイベントを組み合わせることによって。
実際の結論。
ワークショップには、モーターとドライバーを備えた1.2メートル×0.8メートルの完成したCNCマシンがありますが、コントローラーはありません。仕事を終えて、それがどれほど壮大になるか試してみる必要があるようです。もしそうなら、私は間違いなく続編を書きます。それまでの間、コントローラーがatmegaでこれを実行し、これらの大まかな割り込みですべての3Dプリンターとcncルーターできしむ理由がわかりません...
そしてもちろん、おそらくCortex-M7のパワーを備えているので、すべての制限付きでよりスムーズな軌道制御を実装できます、しかしそれは完全に異なる記事です。
PSどうやら、なぜこんなに短い時間が重要なのか、いくつかの仮説的な例を示す必要があります。
マシンがXで100mm、Yで11 mm移動する必要があり、ソフトウェアがすべてを加速と均一な移動のセクションに分割したとします。11ステップで100ステップのセクションが多数あり、最大速度でトラバースされます。10kHzに対応します。ええと、Yの10ステップはXの100ステップに正確に適合しますが、11番目に問題が発生する可能性があります。周波数が2倍になるため、スキップできます。その結果、動きはXで100 mm、Yで10〜11 mmになります。これは直線的な動きであり、実際、許容される加速度と速度の制限さえも単純です。そして、それがジグザグで行われる場合はどうなりますか?ええと、例えば、10回のパスで100×110mmの領域のスイープがあります-それから私たちは一般的に非常に見逃します...
提案されたアルゴリズムはこのエラーを排除するために使用され、スーパーミルなどにはまったく使用されません。