For your information I have developped a sketch to perform microstepping using a uno R4, a TB6612 shield and a 17HS4401 microstep motor. The goal was to achieve slow and very quiet rotation using 256x50 steps per rotation and reducing the cosumption to a minimum. I use four PWM outputs with a frequency of 10KHZ. It gives me 4800 different levels of PWM.
Achievment : the motor is perfectly quiet up to 0.5 r/s. The maximum speed is 4 turn/second. It consumes 100mA with a 9V (8V in fact) battery (Vin) with raisonnable (low) torque. I can reduce the torque and consume down to 50mA. This does not include the UNOR4.
I use the EventLinkInterrupt library from David Caldwell. Thanks.
So It works on UNOR4 only, a 10 Khz interrupt is only possible with this fast processor.
The rotation period is perfectly precise in terms of timing, in multiple of 1,041 µs ( see code).
(but do'nt forget that the 48Mhz of the card is not precise at all )
My final need is to make it turn once every 70.558 seconds to build a DOY equatorial stand for my camera.
If someone wants to turn it into a library, no problem, I dont own it.
The wiring :
and the demo program :
//______________________________________________________________________________________________________
#include "EventLinkInterrupt.h"
// Writen by Eric AZELART march 2024
// 256 steps of sinusoid with 4800 max amplitude
int sina[256] = {
0, 118, 236, 353, 470, 588, 704, 821, 936, 1052, 1166, 1280, 1393, 1506, 1617, 1727, 1837, 1945, 2052, 2158, 2263, 2366, 2468, 2568, 2667, 2764,
2859, 2953, 3045, 3135, 3223, 3310, 3394, 3476, 3557, 3635, 3710, 3784, 3855, 3924, 3991, 4055, 4117, 4176, 4233, 4287, 4339, 4388, 4435, 4478,
4519, 4558, 4593, 4626, 4656, 4683, 4708, 4729, 4748, 4764, 4777, 4787, 4794, 4799, 4800, 4799, 4794, 4787, 4777, 4764, 4748, 4729, 4708, 4683,
4656, 4626, 4593, 4558, 4519, 4478, 4435, 4388, 4339, 4287, 4233, 4176, 4117, 4055, 3991, 3924, 3855, 3784, 3710, 3635, 3557, 3476, 3394, 3310,
3223, 3135, 3045, 2953, 2859, 2764, 2667, 2568, 2468, 2366, 2263, 2158, 2052, 1945, 1837, 1727, 1617, 1506, 1393, 1280, 1166, 1052, 936, 821, 704,
588, 470, 353, 236, 118, 0, -118, -236, -353, -470, -588, -704, -821, -936, -1052, -1166, -1280, -1393, -1506, -1617, -1728, -1837, -1945, -2052,
-2158, -2263, -2366, -2468, -2568, -2667, -2764, -2859, -2953, -3045, -3135, -3223, -3310, -3394, -3476, -3557, -3635, -3710, -3784, -3855, -3924,
-3991, -4055, -4117, -4176, -4233, -4287, -4339, -4388, -4435, -4478, -4519, -4558, -4593, -4626, -4656, -4683, -4708, -4729, -4748, -4764, -4777,
-4787, -4794, -4799, -4800, -4799, -4794, -4787, -4777, -4764, -4748, -4729, -4708, -4683, -4656, -4626, -4593, -4558, -4519, -4478, -4435, -4388,
-4339, -4287, -4233, -4176, -4117, -4055, -3991, -3924, -3855, -3784, -3710, -3635, -3557, -3476, -3394, -3310, -3223, -3135, -3045, -2953, -2859,
-2764, -2667, -2568, -2468, -2366, -2263, -2158, -2052, -1945, -1837, -1728, -1617, -1506, -1393, -1280, -1166, -1052, -936, -821, -704, -588,
-470, -353, -236, -118
};
#define FREQ 4800 // for 10 000 Hz IRQ
#define MAX_SPEED 240000L // 4 rotation per second (48000000/(50*4 ))
#define SEC_ 1000000L // in µs
#define uc unsigned char
// variables to be set (those variables can be read by interrupt any time)
boolean sens; // gives the rotation
unsigned long tick = 0; // number of clocks (*256) between two micro steps, 0 meaning "stop". tick = ( 48000000 / (speed * 50))
unsigned long gain = 0; // tension to be proportionate to speed min : 0 , max 1024 ;
// variables to be read (those variables can be changed by interrupt any time)
volatile unsigned long count = 0; // compared to tick
volatile long step = 0; // number of micro steps since reset, négative and positive.
unsigned long count_irq; // number of irq served since reset, not very useful
// variables to be untouched
unsigned char kk = 0; // pointer in sinusoid
int pwm1 = 0, pwm2 = 0; // actual pmw values
unsigned int IrqIndex; // interrupt vector
void micro_step_setup() {
// pinMode(2, OUTPUT); // if pin2 used for spying irqs
analogWrite(6, 255);
analogWrite(7, 255);
analogWrite(8, 255);
analogWrite(9, 255); // this forces pwms to be programmed
Serial.begin(9600);
R_GPT7->GTWP = 0xA500; // allow to write in timer registers
R_GPT7->GTCR = 0x00000001; // start counter, saw wave, clk = 48 Mhz
R_GPT7->GTPBR = FREQ - 1; // set to 10 000 Hz
R_GPT7->GTBER = 0x1F0000; // double buffer operation to give time to irq
R_GPT3->GTWP = 0xA500;
R_GPT3->GTCR = 0x00000001;
R_GPT3->GTPBR = FREQ - 1;
R_GPT3->GTBER = 0x1F0000;
R_GPT3->GTCLR = 0x0000088; // clear counter 3 and 7
IrqIndex = attachEventLinkInterrupt(0x95, IrqHandler); // 0x95 means GPT7 overflow
}
void IrqHandler(void) {
// digitalWrite(2, 1); // to evaluate time in ITQ
if (tick != 0) {
count += FREQ * 256;
if (tick < MAX_SPEED) { // max speed 5 rot/s
while (count > MAX_SPEED) { // if speed > 0.7 rot/s, steps are skipped
if (sens) kk++;
else kk--;
count -= MAX_SPEED;
step++; // number steps are ok even if steps are skipper
}
} else {
while (count > tick) { // if speed > 0.7 rot/s, steps are skipped
if (sens) kk++;
else kk--;
count -= tick;
step++; // number steps are ok even if steps are skipper
}
}
}
pwm1 = FREQ / 2400 * ((long)(gain * sina[(uc)(kk + 00) & 0xFF]) / 2048); // gain should be 0 to 1024 for no saturation
pwm2 = FREQ / 2400 * ((long)(gain * sina[(uc)(kk + 64) & 0xFF]) / 2048); // 64 for 90° phasing
// 0xff stands for micro stepping : 0xFF:256, 0xFE:128, 0xFC:64, 0xF8:32, 0xF0:16, 0xE0:8, 0xC0:4. less will not work
if (pwm1 > FREQ) pwm1 = FREQ;
if (pwm2 > FREQ) pwm2 = FREQ;
count_irq++;
if (pwm1 >= 0) {
R_GPT7->GTCCR[2] = FREQ - pwm1; // pin 8
R_GPT7->GTCCR[5] = FREQ; // pin 9
} else {
R_GPT7->GTCCR[2] = FREQ; // pin 8
R_GPT7->GTCCR[5] = FREQ + pwm1; // pin 9
}
if (pwm2 >= 0) {
R_GPT3->GTCCR[2] = FREQ - pwm2; // pin 6
R_GPT3->GTCCR[5] = FREQ; // pin 7
} else {
R_GPT3->GTCCR[2] = FREQ; // pin 6
R_GPT3->GTCCR[5] = FREQ + pwm2; // pin 7
}
resetEventLink(IrqIndex);
// digitalWrite(2, 0); // to evaluate time in ITQ
}
void setup() {
micro_step_setup();
}
void loop() {
// minimum to make it work : set tick, sens and gain ;
unsigned int jj = 0;
unsigned long ii, iii, ll, lll, hh, hhh, tick1;
int speed = 0, gain1 = 0;
bool sens1;
iii = ii = micros();
lll = ll = step;
hhh = hh = count_irq;
while (1) {
while ((micros() - ii) > (SEC_ / 100)) { // we are here every second/100, even during long overflow
ii += SEC_ / 100;
speed = (analogRead(A0)) - 512; // speed from -5.12 to 5.11
if (speed >= 0) {
sens1 = true;
} else {
sens1 = false;
}
if (speed != 0) {
tick1 = (96000000L / abs(speed));
if (tick1 < MAX_SPEED) tick1 = MAX_SPEED;
} else {
tick1 = 0;
}
gain1 = 200 + (35000 * FREQ) / tick1; // this gives a balance between torque and comsumption
// 200 and 35000 are OK for 8V Vin. motor draws about 100mA
// higher numbers for more torque and noise and current
// lower numbers for more silence
if (gain1 > 1024) gain1 = 1024;
sens = sens1;
tick = tick1;
gain = gain1; // change values of sens, gain, tick only when computed
}
while (micros() - iii > SEC_) { // we are here every second, even during long overflow
iii += SEC_;
lll = step - ll; // how many steps in one second
ll += lll;
hhh = count_irq - hh;
hh += hhh; // how may irq_s in one second
Serial.print(speed); // Speed set
Serial.print(",");
Serial.print((lll * 10000) / (256 * 50)); // measured absolute speed (*1000) if no rotation inversion
Serial.print(",");
Serial.print(lll); // number of steps
Serial.print(",");
Serial.print(hhh); // number of irqs
Serial.print(",");
Serial.print(tick * 1000 / (256 * 48)); // actual period between steps in ns
Serial.print(",");
Serial.println(gain);
}
}
}
//_____________________________________________________________________________________________________
1 post - 1 participant