Hello. I need help please. I need the stepper motor to rotate at constant RPM (10 RPM in the attached example). It is used to drive a gear clock so the RPM needs to be accurate.
I am using NEMA 17 Stepper Motor with TMC2208 driver. the HW connections are as in the picture. I also connected DS3231 Real Time Clock.
As you can see in the code, I turn the stepper motor at 10RPM but I do not know how to correct the RPM via reference to the RTC.
Can anyone help with the code needed for using the RTC for achieving clock-grade RPM accuracy?
#define stepPin 2
#define dirPin 5
void setup() {
// Sets the two pins as Outputs
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
}
void loop() {
digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
// Makes 3200 pulses for making one full cycle rotation
for(int x = 0; x < 3200; x++) {
digitalWrite(stepPin,HIGH);
delayMicroseconds(937.5);
digitalWrite(stepPin,LOW);
delayMicroseconds(937.5);
}
}
Three possibilities for making an accurate clock are to calibrate the CPU oscillator, correct the clock (stepper motor position) periodically using the RTC, or use an RTC with a pulse-per-second output to advance the clock hands.
Clocks are a popular project, so check on line for posted tutorials.
Thanks for the info.
I would like to correct the clock (stepper motor position) periodically using the RTC but I don’t know how to do it and couldn’t find tutorial or reference.
Could you please either help with the code or refer me to other reference?
You can use the TimeLib.h library to keep time on the Arduino, which depends on the CPU clock frequency, and may lose or gain several seconds every day.
Compare that time with the RTC time, say at midnight, and calculate a correction.
You forgot to explain how well your clock currently performs.
One way to do this is to run the stepper motor slightly faster than 10 RPM, and then wait for a new second to start. But, since your stepper motor does not make a whole number of steps for every second, maybe better to do the correction once every six seconds (once per revolution of your motor).
I would try something like this:
#include <Wire.h>
#define stepPin 2
#define dirPin 5
byte ss = 99;
byte last_ss = 99;
unsigned long lastMicros = 0UL;
bool keepWaiting = false;
void setup() {
Wire.begin();
// Sets the two pins as Outputs
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
readTheSeconds();
last_ss = ss;
lastMicros = micros();
}
void loop() {
digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
// Makes 3200 pulses for making one full cycle rotation
// this should take 5.9712 seconds
for(int x = 0; x < 3200; x++) {
digitalWrite(stepPin,HIGH);
while (micros() - lastMicros < 933) { ; }
digitalWrite(stepPin,LOW);
while (micros() - lastMicros < 1866) { ; }
lastMicros += 1866;
}
keepWaiting = true;
while (keepWaiting) {
readTheSeconds();
switch (((60 + ss) - last_ss) % 60) {
case 5:
delay(10);
break;
case 6:
keepWaiting = false;
break;
default:
// signal an error condition
while (1) {
for(int x = 0; x < 267; x++) {
digitalWrite(stepPin,HIGH);
delayMicroseconds(933);
digitalWrite(stepPin,LOW);
delayMicroseconds(933);
}
delay(1000);
}
}
}
last_ss = ss;
lastMicros = micros();
}
void readTheSeconds() {
// ask RTC for the seconds
// send request to receive data starting at register 0
Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
Wire.write((byte)0); // start at register 0
Wire.endTransmission();
Wire.requestFrom(0x68, 1); // request one byte (seconds)
// check for a reply from the RTC, and use it if we can
if (Wire.available()) {
// if we're here, we got a reply
// so now we read the seconds
ss = bcd2bin(Wire.read()); // get seconds
}
else {
// if we're here, that means we were unable to read the time
ss = 99;
}
}
byte bcd2bin(byte x) {
// converts from binary-coded decimal to a "regular" binary number
return ((((x >> 4) & 0xF) * 10) + (x & 0xF)) ;
}
My code does not require the use of INT or SQW pins. It just uses SDA and SDL to read the time from the RTC chip, and you said you already have SDA and SDL connected to the Arduino.
Just so you know:
The way my code is meant to behave, your clock's hands will move forward 6 seconds in about 5.97 seconds real time, then will wait for about 0.03 second and then will start moving again. Hopefully, a pause of 0.03 second is too short to notice.
The 5.97 seconds are timed using the Arduino's internal timer, which, as you know, is not meant to be extremely accurate. So it might in fact be 5.95 seconds or 5.99 seconds, for example. The aim is to make it slightly less than 6 seconds.
If, according to the RTC, only 5 seconds have passed, then the clock must pause slightly to wait for the next second to begin. If the RTC says that 6 seconds have passed, then it is time for the clock's motor to start turning again. If, according to the RTC, the number of seconds that have passed is neither 5 nor 6, it indicates a malfunction.
I hope that this helps explain my code and how it is meant to behave.
If you used a module with INT/SQW output, you could use that output to trigger and time the steps. It should be more accurate than the clock in the Arduino.
Just found this amazing item on AliExpress. Check it out! ₪5.75 5% Off | DS3231 AT24C32 IIC Module Precision Clock Module DS3231SN Memory module DS3231 mini module Real Time 3.3V/5V for Raspberry Pi https://a.aliexpress.com/_oEn1APS
You could use the accurate INT/SQW signal to make continuous fine adjustments to the number of millis() counted to represent one second (or any fraction of a second you desire). You could do the same thing with micros() to get even finer resolution.