This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
projects:cnc:4th_axis [2025/04/11 17:02] – [Arduino software using the FastAccelStepper library] admin | projects:cnc:4th_axis [2025/04/15 00:07] (current) – [A manual controller for a spindle motor] admin | ||
---|---|---|---|
Line 4: | Line 4: | ||
Movement control is done using a potentiometer and button. With the potentiometer the speed and direction can be set, while the button allows to rotate at a specific angle, after which the motor stops. | Movement control is done using a potentiometer and button. With the potentiometer the speed and direction can be set, while the button allows to rotate at a specific angle, after which the motor stops. | ||
- | After powering up, it is required to either rotate the potentiometer to the center position (speed: 0) or press the button to rotate exactly | + | After powering up, it is required to either rotate the potentiometer to the center position (speed: 0) or press the button to rotate exactly |
Project summary: | Project summary: | ||
- | * Code runs on an arduino | + | * Code runs on an arduino |
* Connected to an [[https:// | * Connected to an [[https:// | ||
* With a 4k7 potentiometer on A0, direction and speed can be set. | * With a 4k7 potentiometer on A0, direction and speed can be set. | ||
- | * When using a button on D8 (to GND), the axis rotates certain degrees (currently hard-coded as 90 degrees) in the direction it was rotating previously and stops afterwards, regardless of the potentiometer setting. | + | * When using a button on D8 (to GND), the axis rotates certain degrees (which can be configured with a BCD switch) in the direction it was rotating previously and stops afterwards, regardless of the potentiometer setting. |
* To start motor movement, either press the switch, or move the potentiometer to the center position, after which the motor will rotate continuously when moving the potentiometer any further. | * To start motor movement, either press the switch, or move the potentiometer to the center position, after which the motor will rotate continuously when moving the potentiometer any further. | ||
- | * If the indicator led is blinking fast, it will indicate that the potentiometer need to be positioned at the center first. This is to prevent any unwanted | + | * If the indicator led is blinking fast, it will indicate that the potentiometer need to be positioned at the center first. This is to prevent any motor rotation |
+ | * As a latest addition, a 2-digit BCD-switch is connected to inputs A1-A5, | ||
The documentation of the stepper controller can be found here: [[https:// | The documentation of the stepper controller can be found here: [[https:// | ||
Line 24: | Line 25: | ||
| CN4,5 | /Off | Power off stepper drivers | | CN4,5 | /Off | Power off stepper drivers | ||
| CN4,6 | / | | CN4,6 | / | ||
- | | CN4,7 + CN4,8 | +5v | This pin is not connected | + | | CN4,7 + CN4,8 | +5v | This pin was originally |
| CN4,9 + CN4, | | CN4,9 + CN4, | ||
| CN7.5 | / | | CN7.5 | / | ||
==== Arduino software using the FastAccelStepper library ==== | ==== Arduino software using the FastAccelStepper library ==== | ||
- | < | + | < |
// | // | ||
// https:// | // https:// | ||
// https:// | // https:// | ||
+ | // FastAccelStepper documentation: | ||
+ | // https:// | ||
// | // | ||
// Arduino Hardware settings: | // Arduino Hardware settings: | ||
// Processor: ATmega328P (regular, not the Old bootloader) | // Processor: ATmega328P (regular, not the Old bootloader) | ||
- | // Board: Arduino Nano | + | // Board: Arduino Nano 3.0 |
// Port: / | // Port: / | ||
// | // | ||
// Dependency: | // Dependency: | ||
- | // Install the FastAccelStepper library by Jochen Kiemes (currently version 0.31.5) | + | // Install the FastAccelStepper library by Jochen Kiemes (currently version 0.31.6) |
Line 54: | Line 57: | ||
- | // pins numbers | + | // pin numbers |
+ | // Number corresponds to name Dxx as on silkscreen and used in the function | ||
+ | // digitalWrite(pin_number). For example: | ||
+ | // 13 corresponds to D13, 9 corresponds to D9 | ||
const uint8_t rst_pin = 13; // rst pin for HP-Step.pro | const uint8_t rst_pin = 13; // rst pin for HP-Step.pro | ||
const uint8_t rxd_pin = 12; // rx pin for some kind of tty communication | const uint8_t rxd_pin = 12; // rx pin for some kind of tty communication | ||
Line 66: | Line 72: | ||
const uint8_t led_pin = 7; // signal led to indicate angular mode connected between D7 and GND | const uint8_t led_pin = 7; // signal led to indicate angular mode connected between D7 and GND | ||
- | const uint8_t adj_pin = A0; //Analog pin with 4k7 lin. potentiometer | + | // A0 corresponds with a #define to decimal 14, PIN_A0 is similar but a const uint instead. |
+ | // All input pins use the internal pull-up. | ||
+ | const uint8_t adj_pin = PIN_A0; // Analog pin A0 with 4k7 lin. potentiometer | ||
const uint8_t rot_pin = 8; // connect a button (switch to ground) to rotate (90) degrees | const uint8_t rot_pin = 8; // connect a button (switch to ground) to rotate (90) degrees | ||
- | + | ||
+ | // 2-digit bcd-wheel to set rotation angle. Common is connected to ground | ||
+ | const uint8_t bcd10 = PIN_A1; | ||
+ | const uint8_t bcd20 = PIN_A2; | ||
+ | const uint8_t bcd40 = PIN_A4; | ||
+ | const uint8_t bcd80 = PIN_A3; | ||
+ | const uint8_t bcd01 = PIN_A5; | ||
+ | const uint8_t bcd02 = 2; | ||
+ | const uint8_t bcd04 = 10; | ||
+ | const uint8_t bcd08 = 3; | ||
// stepper definitions | // stepper definitions | ||
const uint8_t stp360 = 200; // number of steps for stepper motor to rotate 360 degrees | const uint8_t stp360 = 200; // number of steps for stepper motor to rotate 360 degrees | ||
// connected geometry (if stepper has a gear with another gear) | // connected geometry (if stepper has a gear with another gear) | ||
- | const uint8_t | + | const uint8_t |
- | const uint8_t sg = 10; // number of teeth of small gear | + | const uint8_t sg = 10; // number of teeth of secondary |
+ | const uint8_t steps_for_3deg = stp360 * 4 * (pg/sg) / 120; // smallest exact step is 3 degrees and corresponds with 40 clk pulses | ||
// storage for potentiometer samples, rotation direction, blink time | // storage for potentiometer samples, rotation direction, blink time | ||
static uint16_t pos[NUM_SAMPLES]; | static uint16_t pos[NUM_SAMPLES]; | ||
- | static uint8_t n; // samples array index | + | static uint8_t n; // samples array index |
- | static | + | static |
enum patterns {LED_OFF, BLINK_SLOW, BLINK_FAST, LED_ON}; | enum patterns {LED_OFF, BLINK_SLOW, BLINK_FAST, LED_ON}; | ||
static enum patterns led_ptrn = BLINK_FAST; | static enum patterns led_ptrn = BLINK_FAST; | ||
Line 90: | Line 109: | ||
// button debounce | // button debounce | ||
- | const uint8_t min_debounce_ms = 100; // 100 ms debounce time | + | const uint8_t min_debounce_ms = 20; // 20 ms debounce time |
uint32_t btnPrevMillis; | uint32_t btnPrevMillis; | ||
Line 113: | Line 132: | ||
digitalWrite(off_pin, | digitalWrite(off_pin, | ||
pinMode(rot_pin, | pinMode(rot_pin, | ||
- | + | ||
+ | // initialization for bcd wheel | ||
+ | pinMode(bcd10, | ||
+ | pinMode(bcd20, | ||
+ | pinMode(bcd40, | ||
+ | pinMode(bcd80, | ||
+ | pinMode(bcd01, | ||
+ | pinMode(bcd02, | ||
+ | pinMode(bcd04, | ||
+ | pinMode(bcd08, | ||
Engine.init(); | Engine.init(); | ||
stepper = Engine.stepperConnectToPin(clk_pin); | stepper = Engine.stepperConnectToPin(clk_pin); | ||
Line 132: | Line 161: | ||
void move_degrees(uint32_t angle) { | void move_degrees(uint32_t angle) { | ||
+ | static int8_t step_counter = 0; // compensation counter. Based on consecutive triple calls give integer accuracy. | ||
stepper-> | stepper-> | ||
while (stepper-> | while (stepper-> | ||
- | | + | |
- | | + | // formula: angle * 200 * 4 * (ratio) / 360 = 40 * angle / 3 |
+ | // where ratio = 60/10 | ||
+ | uint16_t steps = steps_for_3deg * angle / 3; | ||
+ | | ||
+ | |||
+ | // Motor cannot step 13.3333 or 26.6666 steps. Therefore only integer values | ||
+ | // are used and a compensation is added once every three calls. | ||
+ | // 0 -> 13.333 -> 26.666 -> 40 will then become: 0 -> 13 -> 27 -> 40 | ||
+ | // for 1 degree angle rotations. All larger | ||
+ | // a multiple of this schema. | ||
+ | switch (remainder) { | ||
+ | case 0: | ||
+ | // leave result unchanged | ||
+ | break; | ||
+ | case 1: | ||
+ | steps += (stp_direction | ||
+ | break; | ||
+ | case 2: | ||
+ | | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // precise movement based on natural numbers | ||
stepper-> | stepper-> | ||
- | stepper-> | + | stepper-> |
- | while ( stepper-> | + | |
+ | while (stepper-> | ||
+ | |||
+ | // correction counter since we are not using floating point | ||
+ | if (step_counter >= 2 && stp_direction == FORWARD) { | ||
+ | step_counter = 0; | ||
+ | } else if (step_counter <= 0 && stp_direction == BACKWARD) { | ||
+ | step_counter = 2; | ||
+ | } else { | ||
+ | step_counter += (stp_direction == FORWARD); | ||
+ | } | ||
} | } | ||
Line 177: | Line 239: | ||
return calc_average(); | return calc_average(); | ||
} | } | ||
- | + | ||
+ | uint8_t read_bcd_wheel(void) { | ||
+ | return 10 * (!digitalRead(bcd10) | | ||
+ | | ||
+ | | ||
+ | | ||
+ | (!digitalRead(bcd01) | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
uint16_t calc_average(void) { | uint16_t calc_average(void) { | ||
uint16_t sum = 0; | uint16_t sum = 0; | ||
Line 240: | Line 313: | ||
} | } | ||
- | void handle_angular_rotation(uint16_t angle, | + | void handle_angular_rotation(uint16_t *avg, uint16_t *avg_prev) { |
led_ptrn = LED_ON; | led_ptrn = LED_ON; | ||
handle_led_ptrn(); | handle_led_ptrn(); | ||
Line 247: | Line 320: | ||
uint16_t pos = analogRead(adj_pin); | uint16_t pos = analogRead(adj_pin); | ||
if (get_direction(pos) != STOP) stp_direction = get_direction(pos); | if (get_direction(pos) != STOP) stp_direction = get_direction(pos); | ||
- | move_degrees(90); // use direction from last movement | + | move_degrees(read_bcd_wheel()); // use direction from last movement |
rotation_mode = ANGULAR; | rotation_mode = ANGULAR; | ||
} | } | ||
Line 262: | Line 335: | ||
{ | { | ||
uint16_t blink_interval; | uint16_t blink_interval; | ||
- | |||
- | if (led_ptrn == LED_OFF || led_ptrn == LED_ON) { | ||
- | digitalWrite(led_pin, | ||
- | return; | ||
- | } | ||
switch (led_ptrn) { | switch (led_ptrn) { | ||
+ | case LED_OFF: | ||
+ | digitalWrite(led_pin, | ||
+ | return; | ||
+ | case LED_ON: | ||
+ | digitalWrite(led_pin, | ||
+ | return; | ||
case BLINK_SLOW: | case BLINK_SLOW: | ||
blink_interval = 1000; | blink_interval = 1000; | ||
Line 302: | Line 376: | ||
} | } | ||
- | /* debounce + rotate | + | /* debounce + rotate |
if (millis() - btnPrevMillis > min_debounce_ms) { | if (millis() - btnPrevMillis > min_debounce_ms) { | ||
if (!digitalRead(rot_pin)) { | if (!digitalRead(rot_pin)) { | ||
- | handle_angular_rotation(90, &avg, & | + | handle_angular_rotation(& |
while (!digitalRead(rot_pin)); | while (!digitalRead(rot_pin)); | ||
btnPrevMillis = millis(); | btnPrevMillis = millis(); |