初めに
Arduino UnoとCNCシールドで4軸を動かしたく調べてた備忘録
⇒結局はMega2560とCNCシールドに補助シールドを起こすことで対応
使用したもの
ライブラリは下記を使用
https://github.com/mlambm/grbl
ライブラリの問題点
上記ライブラリで4軸目のA軸も無事に動いたが A軸用のリミットスイッチがない
確認した内容
A軸用のリミットスイッチを設けるためソースを見ていくと digitalWrite や analogWrite でなく ポートを直接いじっている模様
太刀打ちできんのでChatGPTに聞くも毎回珍回答ばかりで調べた結果が次の通り
ATmega328pのピン番号 | ポート番号 | Arduino Unoのピン番号 | CNCシールド v3.0のピン名 | 備考 |
---|---|---|---|---|
1 | PC6(RESET/PCINT14) | |||
2 | PD0(PCINT16) | D0 | RXのため使用しない | |
3 | PD1(PCINT17) | D1 | TXのため使用しない | |
4 | PD2(PCINT18) | D2 | X-STEP | |
5 | PD3(PCINT19) | D3 | Y-STEP | |
6 | PD4(PCINT20) | D4 | Z-STEP | |
7 | VCC | 電源電圧 | ||
8 | GND | グランド | ||
9 | PB6(XTAL1/PCINT6) | クリスタル1 | ||
10 | PB7(XTAL2/PCINT7) | クリスタル2 | ||
11 | PD5(PCINT21) | D5 | X-DIR | |
12 | PD6(PCINT22) | D6 | Y-DIR | |
13 | PD7(PCINT23) | D7 | Z-DIR | |
14 | PB0(PCINT0) | D8 | EN | ドライバーの有効・無効 |
15 | PB1(PCINT1) | D9 | X-EndStop | Limit X-Axis |
16 | PB2(PCINT2) | D10 | Y-EndStop | Limit Y-Axis |
17 | PB3(PCINT3) | D11 | Z-EndStop | Limit Z-Axis |
18 | PB4(PCINT4) | D12 | SpinEnable(A.STP) | |
19 | PB5(PCINT5) | D13 | SpinDir(A.DIR) | |
20 | AVCC | |||
21 | AREF | |||
22 | AGND | |||
23 | PC0(PCINT8) | D14 / A0 | ABORT | |
24 | PC1(PCINT9) | D15 / A1 | HOLD | |
25 | PC2(PCINT10) | D16 / A2 | RESUME | |
26 | PC3(PCINT11) | D17 / A3 | Coolant | |
27 | PC4(PCINT12) | D18 / A4 | for I2C(SDA)? | |
28 | PC5(PCINT13) | D19 / A5 | for I2C(SCL)? |
CNCシールドのピン | 説明 |
---|---|
Step[XYZ] | [XYZ]軸の回転方向 |
Probe | ワークピースの位置を確認するためのセンサー |
Spindle | 主軸(カッター)の回転を制御するためのピン |
Coolant | 冷却液を制御するためのピン |
Coolant が使えるのかな
⇒使えないわけではない様だが外部割込みは一つのポートで完結しないとプログラムが大変らしい
結論その1
結論Unoだとピンが足りない!
Mega2560 に鞍替え
では沢山ポートがある Mega2560 でならので何とかなるだろう!と鞍替え
...リミットスイッチがPin Change Interrupt register に属してないといけないから自分の知識では結構難しかった💦
これの 13. 外部割り込み に詳細が書かれている
PCIE0, PCIE1, PCIE2は54頁
Pin Change Interrupt Enable # | PIN | Pin Change Enable Mask # |
---|---|---|
PCIE0 | PCINT0~PCINT7 | PCMSK0 |
PCIE1 | PCINT8~PCINT15 | PCMSK1 |
PCIE2 | PCINT16~PCINT23 | PCMSK2 |
なるほど
ATmega2560 には Pin Change Interrupt Register は3つあるが PCMSK1 は Mega2560 では使用できない事が判明
ということで次の様になった
CNCシールドのものを直接使用するのは断念しサブシールドを作成して対応
Mega2560 | ポート | 役割 | 備考 |
---|---|---|---|
D22 | PA0 | X-step | |
D24 | PA2 | Y-step | |
D26 | PA4 | Z-step | |
D28 | PA6 | A-step | |
D31 | PC6 | X-dir | |
D33 | PC4 | Y-dir | |
D35 | PC2 | Z-dir | |
D37 | PC0 | A-dir | |
A8 | PK0 | X-limit | PCMSK2 PCINT16 |
A9 | PK1 | Y-limit | PCMSK2 PCINT17 |
A10 | PK2 | Z-limit | PCMSK2 PCINT18 |
A11 | PK3 | A-limit | PCMSK2 PCINT19 |
D50 | PB3 | Reset | PCMSK0 PCINT3 |
D51 | PB2 | Feed Hold | PCMSK0 PCINT2 |
D52 | PB1 | Sycle State | PCMSK0 PCINT1 |
D53 | PB0 | Safty Door | PCMSK0 PCINT0 |
いじったのは次のファイル
#define CPU_MAP_ATMEGA2560
#define HOMING_CYCLE_2 ((1<<A_AXIS))
N_AXIS3 -> N_AXIS4
// Returns limit pin mask according to Grbl internal axis indexing.
uint8_t get_limit_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); }
if ( axis_idx == Z_AXIS ) { return((1<<Z_LIMIT_BIT)); }
return((1<<A_LIMIT_BIT));
}
#if defCPU_MAP_ATMEGA328P
uint8_t step_bits[2]; // Stores out_bits output to complete the step pulse delay
#else
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
#endif
---
ISR(TIMER1_COMPA_vect)
{
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
#ifdef CPU_MAP_ATMEGA328P
// Set the direction pins a couple of nanoseconds before we step the steppers
DIRECTION_PORT0 = (DIRECTION_PORT0 & ~DIRECTION_MASK0) | (st.dir_outbits & DIRECTION_MASK0);
// Then pulse the stepping pins
#ifdef STEP_PULSE_DELAY
st.step_bits[0] = (STEP_PORT0 & ~STEP_MASK0) | (st.step_outbits & STEP_MASK0); // Store out_bits to prevent overwriting.
#else // Normal operation
STEP_PORT0 = (STEP_PORT0 & ~STEP_MASK0) | st.step_outbits & STEP_MASK0;
#endif
// Set the direction pins a couple of nanoseconds before we step the steppers
DIRECTION_PORT1 = (DIRECTION_PORT1 & ~DIRECTION_MASK1) | ((st.dir_outbits<<4) & DIRECTION_MASK1);
// Then pulse the stepping pins
#ifdef STEP_PULSE_DELAY
st.step_bits[1] = (STEP_PORT1 & ~STEP_MASK1) | ((st.step_outbits<<4) & STEP_MASK1); // Store out_bits to prevent overwriting.
#else // Normal operation
STEP_PORT1 = (STEP_PORT1 & ~STEP_MASK1) | (st.step_outbits<<4) & STEP_MASK1;
#endif
#else
// Set the direction pins a couple of nanoseconds before we step the steppers
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);
// Then pulse the stepping pins
#ifdef STEP_PULSE_DELAY
st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting.
#else // Normal operation
STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits;
#endif
#endif
---
ISR(TIMER0_OVF_vect)
{
// Reset stepping pins (leave the direction pins)
#ifdef CPU_MAP_ATMEGA328P
STEP_PORT0 = (STEP_PORT0 & ~STEP_MASK0) | (step_port_invert_mask & STEP_MASK0);
STEP_PORT1 = (STEP_PORT1 & ~STEP_MASK1) | (step_port_invert_mask & STEP_MASK1);
#else
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
#endif
TCCR0B = 0; // Disable Timer0 to prevent re-entering this interrupt when it's not needed.
}
#ifdef STEP_PULSE_DELAY
// This interrupt is used only when STEP_PULSE_DELAY is enabled. Here, the step pulse is
// initiated after the STEP_PULSE_DELAY time period has elapsed. The ISR TIMER2_OVF interrupt
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
// The new timing between direction, step pulse, and step complete events are setup in the
// st_wake_up() routine.
ISR(TIMER0_COMPA_vect)
{
#ifdef CPU_MAP_ATMEGA328P
STEP_PORT0 = st.step_bits[0]; // Begin step pulse.
STEP_PORT1 = st.step_bits[1]; // Begin step pulse.
#else
STEP_PORT = st.step_bits; // Begin step pulse.
#endif
}
#endif
---
// Initialize and start the stepper motor subsystem
void stepper_init()
{
// Configure step and direction interface pins
#ifdef CPU_MAP_ATMEGA328P
STEP_DDR0 |= STEP_MASK0;
STEP_DDR1 |= STEP_MASK1;
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
DIRECTION_DDR0 |= DIRECTION_MASK0;
DIRECTION_DDR1 |= DIRECTION_MASK1;
#else
STEP_DDR |= STEP_MASK;
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
DIRECTION_DDR |= DIRECTION_MASK;
#endif
#define X_STEP_BIT 0 // MEGA2560 Digital Pin 22
#define Y_STEP_BIT 2 // MEGA2560 Digital Pin 24
#define Z_STEP_BIT 4 // MEGA2560 Digital Pin 26
#define A_STEP_BIT 6 // MEGA2560 Digital Pin 28
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)|(1<<A_STEP_BIT)) // All step bits
// Define step direction output pins. NOTE: All direction pins must be on the same port.
#define DIRECTION_DDR DDRC
#define DIRECTION_PORT PORTC
#define DIRECTION_PIN PINC
#define X_DIRECTION_BIT 6 // MEGA2560 Digital Pin 31
#define Y_DIRECTION_BIT 4 // MEGA2560 Digital Pin 33
#define Z_DIRECTION_BIT 2 // MEGA2560 Digital Pin 35
#define A_DIRECTION_BIT 0 // MEGA2560 Digital Pin 37
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)|(1<<A_DIRECTION_BIT)) // All direction bits
// Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR DDRH
#define STEPPERS_DISABLE_PORT PORTH
#define STEPPERS_DISABLE_BIT 5 // MEGA2560 Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port
#define LIMIT_DDR DDRK
#define LIMIT_PORT PORTK
#define LIMIT_PIN PINK
#define X_LIMIT_BIT 0 // MEGA2560 Analog Pin A8
#define Y_LIMIT_BIT 1 // MEGA2560 Analog Pin A9
#define Z_LIMIT_BIT 2 // MEGA2560 Analog Pin A10
#define A_LIMIT_BIT 3 // MEGA2560 Analog Pin A11
#define LIMIT_INT PCIE2 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT2_vect
#define LIMIT_PCMSK PCMSK2 // Pin change interrupt register
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)|(1<<A_LIMIT_BIT)) // All limit bits
// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB
#define SPINDLE_ENABLE_BIT 6 // MEGA2560 Digital Pin 12
#define SPINDLE_DIRECTION_DDR DDRB
#define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 7 // MEGA2560 Digital Pin 13
// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
// #define COOLANT_FLOOD_DDR DDRH
// #define COOLANT_FLOOD_PORT PORTH
// #define COOLANT_FLOOD_BIT 5 // MEGA2560 Digital Pin 8
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR DDRF
#define COOLANT_MIST_PORT PORTF
#define COOLANT_MIST_BIT 3 // MEGA2560 Analog Pin 3
#endif
// Define user-control CONTROLs (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
#define CONTROL_DDR DDRB
#define CONTROL_PIN PINB
#define CONTROL_PORT PORTB
#define RESET_BIT 3 // MEGA2560 Digital Pin 50
#define FEED_HOLD_BIT 2 // MEGA2560 Digital Pin 51
#define CYCLE_START_BIT 1 // MEGA2560 Digital Pin 52
#define SAFETY_DOOR_BIT 0 // MEGA2560 Digital Pin 53
#define CONTROL_INT PCIE0 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT0_vect
#define CONTROL_PCMSK PCMSK0 // Pin change interrupt register
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
結論その2
これでホーミングもできるようになりましたとさ