/* * Silent clock driver using CNC Shield V4 driver board * * Build a minimum hardware stepper motor clock driver using * CNC Shield V4, TMC2208 stepper driver, and DS3231 RTC * * CNC Shield V4 is modified to fix jumper connections * Jumpers are removed from lower right TMC2208 port * All 3 pins hard wired to VIO (5V) * TMC2208 motor supply voltage hard wired to 5V * One motor header is hacked to connect DS3231 RTC * Pin 1 connected to Vss * (RTC NC and Vss pins shorted together) * Pin 2 connected to SCL * Pin 3 connected to SDA * Pin 4 connected to VIO (5V) * * Use a DS3231 precision RTC as a reference time beat * Accuracy 2PPM = about 1 minute per year drift * * Plug TMC2208 into lower right port * ENN = D8 (PB0) Low = motors enabled * DIR = D4 (PD4) * STEP = D7 (PD7) * * Jumpers in the 8 pin header set speed and direction * CoolEN changes motor speed * Resume, Hold, and Abort set speed to one of 8 speeds * Parameters calculated any time jumpers are changed * * * Note: This sketch uses DS3231.h to access the real time clock * Type to install libraries * Search for "DS3231" and install DS3231.h * * * Download this sketch from https://www.stevesclocks.com/sp6 * Cut and paste the code into the Arduino IDE * (Or change the file extension from .txt to .ino) * * * Revision History: * 05-Jan-2023 1.00 Original release * 06-Jan-2023 1.01 Switched from RTC.h to DS3231.h */ #include #include static DS3231 RTC; // RTC register definition // define pins used #define MOTOR_ENN_PIN 8 #define MOTOR_DIR_PIN 4 #define MOTOR_STEP_PIN 7 #define STATUS_LED_PIN 13 #define MOTOR_JUMPER_PIN A3 #define MOTOR_SPEED_PIN_2 A2 #define MOTOR_SPEED_PIN_1 A1 #define MOTOR_SPEED_PIN_0 A0 // make most variables global (accessible during setup() and loop() int motor_delay; // delay value sent to motor int min_motor_delay; // short delay value int max_motor_delay; // long delay value int motor_steps; // number of steps per second int extra_step_time; // how often an extra step is needed int extra_steps; // how many extra steps get added int extra_step_count = 0; // counter for when to add extra steps long seconds_counted = 0; // total count of seconds elapsed long seconds_moved = 0; // total count of seconds motor has moved int char_counter = 0; // character display count (for CR/LF every minute) int motor_speed_pins; // current reading of 3 motor speed pins int last_motor_speed = -1; // previous reading of 3 motor speed pins int rtc_seconds; // actual seconds value returned by RTC int last_seconds; // previous seconds from RTC to compare against void setup() { // put your setup code here, to run once: int i; pinMode(STATUS_LED_PIN, OUTPUT); // status LED pinMode(MOTOR_ENN_PIN, OUTPUT); // motor enable# pinMode(MOTOR_DIR_PIN, OUTPUT); // motor direction pinMode(MOTOR_STEP_PIN, OUTPUT); // motor step pinMode(MOTOR_JUMPER_PIN, INPUT_PULLUP); // input to set motor direction pinMode(MOTOR_SPEED_PIN_2, INPUT_PULLUP); // input #2 to set motor speed pinMode(MOTOR_SPEED_PIN_1, INPUT_PULLUP); // input #1 to set motor speed pinMode(MOTOR_SPEED_PIN_0, INPUT_PULLUP); // input #0 to set motor speed digitalWrite(STATUS_LED_PIN, LOW); // turn off status LED // debug message at program start Serial.begin(9600); Serial.println(""); Serial.println(""); Serial.println("CNC Shield V4 clock movement - Rev 1.00"); // wait for seconds to change at beginning of algorithm // abort check if RTC appears to be missing (25000 tests without any response) rtc_seconds = RTC.getSecond(); // initialize rtc_seconds setting last_seconds = rtc_seconds; while ((rtc_seconds == last_seconds) && (i++ < 25000)) { rtc_seconds = RTC.getSecond(); } last_seconds = rtc_seconds; // make these match at program start } void loop() { // put your main code here, to run repeatedly: // local variables int step_count; // counter for one second of movement // reset motor direction at every loop // (to allow changing direction anytime) digitalWrite(MOTOR_DIR_PIN, digitalRead(MOTOR_JUMPER_PIN)); // read motor speed pins and determine if they have changed motor_speed_pins = 4 * !digitalRead(MOTOR_SPEED_PIN_2) + 2 * !digitalRead(MOTOR_SPEED_PIN_1) + 1 * !digitalRead(MOTOR_SPEED_PIN_0); if(motor_speed_pins != last_motor_speed) { // determine new speed based on speed pins switch (motor_speed_pins) { case 0: // 2.4:1 (24:10, 36:15, etc.) motor_steps = (int)((long)3200 * 24 / 10 / 60); extra_step_time = 0; break; case 1: // 2.667:1 (32:12, etc.) motor_steps = (int)((long)3200 * 32 / 12 / 60); extra_step_time = 9; extra_steps = 2; // 2 extra steps every 9 seconds break; case 2: // 3:1 (30:10, 45:15, etc.) motor_steps = (int)((long)3200 * 30 / 10 / 60); extra_step_time = 0; break; case 3: // 3.6:1 (36:10, 54:15, etc.) motor_steps = (int)((long)3200 * 36 / 10 / 60); extra_step_time = 0; break; case 4: // 4.5:1 (36:8, 54:12, etc.) motor_steps = (int)((long)3200 * 36 / 8 / 60); extra_step_time = 0; break; case 5: // 5.4:1 (54:10, etc.) motor_steps = (int)((long)3200 * 54 / 10 / 60); extra_step_time = 0; break; case 6: // 6:1 (48:8, 60:10, etc.) motor_steps = (int)((long)3200 * 48 / 8 / 60); extra_step_time = 0; break; default: // 10:1 (60:6, 80:8, etc.) motor_steps = (int)((long)3200 * 60 / 6 / 60); extra_step_time = 3; extra_steps = 1; // 1 extra step every 3 seconds break; } // end switch() // calculate min/max steps based on parameters set during switch min_motor_delay = (int)((long) 960000 / motor_steps / 2); max_motor_delay = (int)((long)1010000 / motor_steps / 2); Serial.println(""); Serial.print("speed "); Serial.println(motor_speed_pins); Serial.print("steps "); Serial.println(motor_steps); Serial.print("min_delay "); Serial.println(min_motor_delay); Serial.print("max_delay "); Serial.println(max_motor_delay); if (extra_step_time) { // only print this value if extra_step time is set Serial.print("extra_steps = "); Serial.print(extra_steps); Serial.print("/"); Serial.println(extra_step_time); } Serial.println(""); // start with minimum motor delay (will probably miss first time) motor_delay = min_motor_delay; seconds_counted = seconds_moved = 0; char_counter = 0; last_motor_speed = motor_speed_pins; // save latest speed setting } // end if(motor_speed_pins != last_motor_speed){} // move motor for approximately one second for (step_count=0; step_count= seconds_moved) { // delay is just right (or slightly long) if seconds match // decrease motor_delay to speed up slightly motor_delay = min_motor_delay; Serial.print("-"); } else { // delay is too short if seconds do not match // increase motor delay to max motor_delay = max_motor_delay; Serial.print("+"); } // check if extra delay needs to be added if (extra_step_time) { if (++extra_step_count >= extra_step_time) { for (step_count = 0; step_count < extra_steps; step_count++) { // pulse one complete cycle (up and down) digitalWrite(MOTOR_STEP_PIN, HIGH); delayMicroseconds(motor_delay); digitalWrite(MOTOR_STEP_PIN, LOW); delayMicroseconds(motor_delay); } extra_step_count = 0; // reset extra step counter } } // add a CR/LF once per minute if (++char_counter >= 60) { // extra debug info every minute Serial.print(" ("); if ((seconds_moved / 3600) >= 24) { // print days Serial.print(seconds_moved / 3600 / 24); Serial.print("d"); } if (seconds_moved >= 3600) { // print hours Serial.print((seconds_moved / 3600) % 24); Serial.print("h"); } // print minutes Serial.print((seconds_moved / 60) % 60); Serial.println("m)"); char_counter = 0; } } // end loop()