1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
#include <Arduino.h>
#include <Bounce2.h>
// relais board:
// A1 = potentiometer
// SDA = 24V+
// SCL = direction in
// PIN A1 potentiometer
// PIN 0
// PIN 1 relay 1
// PIN 2 relay 2
// PIN 3
// PIN 4 direction in
// PIN 5 motor frequency out
// PIN 6 motor direction out
// PIN 7 CAN interrupt
#define POTI_PIN 1
#define DIRECTION_PIN 4
#define MOTOR_FREQUENCY_PIN 5 // hardcoded in SetupTimer()
#define MOTOR_DIRECTION_PIN 6
#define POT_JITTER_THRESHOLD 20
void SetupTimer();
void StopTimer();
void RestartTimer();
void SetFrequencyOutput(uint16_t frequency);
void SetMotorOutput(uint16_t rpm);
float fscale(int inputValue, float originalMin, float originalMax,
float newBegin, float newEnd, float curve);
Bounce direction_button;
const int frequency_max = 6000;
struct {
int direction = 0;
int frequency = 0;
int potValue = 1023;
} current, target;
bool timer_enabled = false;
void setup()
{
Serial.begin(9600);
pinMode(MOTOR_FREQUENCY_PIN, OUTPUT);
direction_button.attach(DIRECTION_PIN, INPUT_PULLUP);
target.direction = current.direction = !direction_button.read();
pinMode(MOTOR_DIRECTION_PIN, OUTPUT);
digitalWrite(MOTOR_DIRECTION_PIN, target.direction);
SetupTimer();
}
void loop()
{
direction_button.update();
if (direction_button.changed())
{
target.direction = !direction_button.read();
Serial.print("target direction: ");
Serial.println(target.direction, DEC);
}
int newPotValue = 1023 - analogRead(POTI_PIN); // inverse input values
if (newPotValue < 30)
newPotValue = 0;
else if (newPotValue > 1000)
newPotValue = 1023;
if (abs(newPotValue - target.potValue) > POT_JITTER_THRESHOLD)
{
target.potValue = newPotValue;
Serial.print("newPotValue: ");
Serial.println(newPotValue, DEC);
//target.frequency = (int)fscale(target.potValue / 10, 0, 102, 0, frequency_max, -4);
target.frequency = map(target.potValue / 10, 0, 102, 0, frequency_max);
Serial.print("target frequency: ");
Serial.println(target.frequency, DEC);
}
const int step_delay = 300;
if (current.frequency != target.frequency || current.direction != target.direction)
{
if (!timer_enabled && target.frequency != 0)
{
RestartTimer();
timer_enabled = true;
Serial.println("enable motor");
delay(step_delay);
}
/*int step = (current.potValue < 20) ? 1
: (current.potValue < 100) ? 2
: (current.potValue < 300) ? 3
: (current.potValue < 400) ? 5
: (current.potValue < 500) ? 10
: 20;*/
int step = (current.frequency > 5000) ? 1
: (current.frequency > 4500) ? 2
: (current.frequency > 4000) ? 5
: (current.frequency > 3000) ? 10
: (current.frequency > 1000) ? 25
: 50;
int frequency = (current.direction == target.direction) ? target.frequency : 0;
step = min(abs(frequency - current.frequency), step);
if (frequency < current.frequency)
step *= -1;
current.frequency += step;
SetFrequencyOutput(current.frequency);
//Serial.print("current potValue: ");
//Serial.print(current.potValue, DEC);
Serial.print("current frequency: ");
Serial.println(current.frequency, DEC);
delay(step_delay);
if (current.frequency == 0 && current.direction != target.direction)
{
StopTimer();
timer_enabled = false;
Serial.println("disable motor");
current.direction = target.direction;
digitalWrite(MOTOR_DIRECTION_PIN, target.direction);
Serial.print("changed direction to: ");
Serial.println(current.direction, DEC);
delay(step_delay);
}
}
else if (timer_enabled && newPotValue == 0)
{
StopTimer();
timer_enabled = false;
Serial.println("disable motor");
delay(step_delay);
}
}
void SetupTimer()
{
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Enable the port multiplexer for port D5
PORT->Group[g_APinDescription[5].ulPort].PINCFG[g_APinDescription[5].ulPin].bit.PMUXEN = 1;
// Connect the TCC timers to the port outputs - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2
PORT->Group[g_APinDescription[5].ulPort].PMUX[g_APinDescription[5].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;
// Feed GCLK4 to TCC0 and TCC1
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Single slope PWM operation
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->PER.reg = 4799; // Set the frequency of the PWM on TCC0 to 10kHz
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[2].reg = 2399; // TCC0 CC2 - 50% duty-cycle on D10
while (TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
TCC0->CC[3].reg = 2399; // TCC0 CC3 - 50% duty-cycle on D12
while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
// Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the timer
TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
//Stop TCC0 counter on MCU initialization (stepper initially stopped)
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_STOP; // Force the TCC0 timer to STOP
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
void StopTimer()
{
//Stop TCC0 counter on MCU initialization (stepper initially stopped)
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_STOP; // Force the TCC0 timer to STOP
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
void RestartTimer()
{
// put your main code here, to run repeatedly:
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Force the TCC0 timer to START
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
void SetFrequencyOutput(uint16_t frequency)
{
uint32_t stepperFrequencyDivisor = round(48000000 / frequency);
TCC0->PERB.reg = stepperFrequencyDivisor - 1; // Set the starting frequency of the PWM on TCC0 to 1Hz
while (TCC0->SYNCBUSY.bit.PERB); // Wait for synchronization
TCC0->CCB[1].reg = (stepperFrequencyDivisor / 2) - 1; // TCC0 CC2 - 50% duty-cycle
while (TCC0->SYNCBUSY.bit.CCB2); // Wait for synchronization
}
void SetMotorOutput(uint16_t rpm)
{
const float stepperHzToRPM = 1; //6.67hz runs the stepper at 1 RPM
SetFrequencyOutput(rpm * stepperHzToRPM);
}
float fscale(int inputValue, float originalMin, float originalMax,
float newBegin, float newEnd, float curve)
{
// condition curve parameter
curve = constrain(curve, -10, 10);
curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output
curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
// Check for out of range inputValues
inputValue = constrain(inputValue, originalMin, originalMax);
// Check for originalMin > originalMax - the math for all other cases i.e. negative numbers seems to work out fine
if (originalMin > originalMax)
return 0;
// Zero Refference the values
float originalRange = originalMax - originalMin;
float zeroRefCurVal = inputValue - originalMin;
float normalizedCurVal = zeroRefCurVal / originalRange; // normalize to 0 - 1 float
float rangedValue = 0;
if (newEnd > newBegin)
{
float newRange = newEnd - newBegin;
rangedValue = (pow(normalizedCurVal, curve) * newRange) + newBegin;
}
else
{
float newRange = newBegin - newEnd;
rangedValue = newBegin - (pow(normalizedCurVal, curve) * newRange);
}
return rangedValue;
}
|