Edgar99
Aktives Mitglied
Hallo,
für ein neues Projekt habe ich die SW meines Motor Fokus komplett neu, in Arduino Umgebung geschrieben. Ich mache dazu mal hier einen neuen Thread auf (hier der alte Thread).
Die Software ist natürlich wieder frei verwendbar und kann beliebig verändert werden.
Gesteuert wird das ganze über die RoboFokus Software und ist somit auch wieder ASCOM kompatibel.
Neu hinzugekommen ist:
Die Software ist nun komplett in Arduino (statt in Bascom) geschrieben.
Ein Temperatursensor, ein AD22100, ist hinzugekommen.
Die Anzahl der max. Schritte sind auf 999999 erweitert.
Wenn man außerdem kurz hintereinander auf IN oder OUT klickt, wird jeweils eine weitere Schrittlänge verfahren. Wenn man ständig klickt, kann man so kontinuierlich verfahren. Das ist praktisch, wenn man mit dem Fokus weit daneben ist und lange Wege fahren muss.
Außerdem sind die 4 „User-Buttoms“ verwendbar, um z.B. die Taukappenheizungen zu schalten.
Eine Handsteuerung ist allerdings immer noch nicht implementiert. Vielleicht hat jemand Zeit/Lust, das nachzurüsten. Da ich ausschließlich fotografiere, brauche ich das nicht.
Endlagenschalter werde ich evtl. später noch hinzufügen.
Es gibt zwei Besonderheiten in den RoboFokus Einstellmenüs:
In der „Motor-Configuration“ kann man mit StepSize 1..5 den Mikroschrittbetrieb von 2..32-fach einstellen. Stellt man größere Werte als 5 ein, bleibt es bei 32 Mikroschritten (der DRV8825 kann nur 32 Schritte, was aber eh mehr als genug ist).
Stellt man Duty-Cycle kleiner als 50 ein, wird der Motor nach dem Verfahren ausgeschaltet - wenn der Wert größer/gleich 50 ist, bleibt er an und erzeugt so ein Haltemoment (das dürfte die sinnvollere Betriebsart sein). Normalerweise kann man mit Duty-Cycle den Haltestrom stufenlos von 0-100% einstellen. Das kann aber leider der DRV8825 nicht.
Vor dem Kompilieren muss man die TimerOne Library einbinden: Hier das Zip runterladen und in der Arduino IDE, unter Sketch > Library importieren > Add Library und dann auf das Zip File zeigen.
Ein Freund von Arduino bin ich trotzdem nicht geworden. RoboFokus wird komplett verwirrt, wenn sich nach dem Öffnen des USB Ports plötzlich der Bootloader meldet.
Dem kann man abhelfen, indem man, nach dem Upload der Firmware, den Reset Kondensator aus dem Nano-Board entfernt. Ein erneuter Upload funktioniert danach allerdings nur noch mit Hilfe des Reset Taster (nach dem klicken auf Upload den Reset Taster kurz drücken).
Bevor man den Motor anschließt, sollte man an dem Poti des DRV8825 einen sinnvollen Motorstrom einstellen. Die Messspannung an dem Messpunkt (s. Anhang) mal 2 ergibt den eingestellten Motorstrom (0,25V am Messpunkt entspricht also 0,5A Motorstrom).
Den Temperatursensor werde ich noch mit Wärmeleitkleber in der Flachfräsung einkleben. Dann misst er die Temperatur des Alu Gehäuses. Die Lufttemperatur wäre evtl. durch die Abwärme des Motors verfälscht.
Auf der 12V Versorgungsleitung sollte man noch einen fetten Elko (so 47µF/63V), zum Abblocken von Spikes, vorsehen.
Die Baudrate muss in RoboFokus auf 19200, 8Bit, non parity, 1 Stop Bits eingestellt werden.
Den richtigen COM Port erfährt man im Fenster Windows > Startmenü > Geräte und Drucker: Wenn man den Arduino-Nano einsteckt, poppt ein neues „Gerät“ auf. Mit Doppelklick darauf wird im Reiter Hardware der neu vergebene COM Port angezeigt (falls man Win7 User ist...)
für ein neues Projekt habe ich die SW meines Motor Fokus komplett neu, in Arduino Umgebung geschrieben. Ich mache dazu mal hier einen neuen Thread auf (hier der alte Thread).
Die Software ist natürlich wieder frei verwendbar und kann beliebig verändert werden.
Gesteuert wird das ganze über die RoboFokus Software und ist somit auch wieder ASCOM kompatibel.
Neu hinzugekommen ist:
Die Software ist nun komplett in Arduino (statt in Bascom) geschrieben.
Ein Temperatursensor, ein AD22100, ist hinzugekommen.
Die Anzahl der max. Schritte sind auf 999999 erweitert.
Wenn man außerdem kurz hintereinander auf IN oder OUT klickt, wird jeweils eine weitere Schrittlänge verfahren. Wenn man ständig klickt, kann man so kontinuierlich verfahren. Das ist praktisch, wenn man mit dem Fokus weit daneben ist und lange Wege fahren muss.
Außerdem sind die 4 „User-Buttoms“ verwendbar, um z.B. die Taukappenheizungen zu schalten.
Eine Handsteuerung ist allerdings immer noch nicht implementiert. Vielleicht hat jemand Zeit/Lust, das nachzurüsten. Da ich ausschließlich fotografiere, brauche ich das nicht.
Endlagenschalter werde ich evtl. später noch hinzufügen.
Es gibt zwei Besonderheiten in den RoboFokus Einstellmenüs:
In der „Motor-Configuration“ kann man mit StepSize 1..5 den Mikroschrittbetrieb von 2..32-fach einstellen. Stellt man größere Werte als 5 ein, bleibt es bei 32 Mikroschritten (der DRV8825 kann nur 32 Schritte, was aber eh mehr als genug ist).
Stellt man Duty-Cycle kleiner als 50 ein, wird der Motor nach dem Verfahren ausgeschaltet - wenn der Wert größer/gleich 50 ist, bleibt er an und erzeugt so ein Haltemoment (das dürfte die sinnvollere Betriebsart sein). Normalerweise kann man mit Duty-Cycle den Haltestrom stufenlos von 0-100% einstellen. Das kann aber leider der DRV8825 nicht.
Vor dem Kompilieren muss man die TimerOne Library einbinden: Hier das Zip runterladen und in der Arduino IDE, unter Sketch > Library importieren > Add Library und dann auf das Zip File zeigen.
Ein Freund von Arduino bin ich trotzdem nicht geworden. RoboFokus wird komplett verwirrt, wenn sich nach dem Öffnen des USB Ports plötzlich der Bootloader meldet.
Dem kann man abhelfen, indem man, nach dem Upload der Firmware, den Reset Kondensator aus dem Nano-Board entfernt. Ein erneuter Upload funktioniert danach allerdings nur noch mit Hilfe des Reset Taster (nach dem klicken auf Upload den Reset Taster kurz drücken).
Bevor man den Motor anschließt, sollte man an dem Poti des DRV8825 einen sinnvollen Motorstrom einstellen. Die Messspannung an dem Messpunkt (s. Anhang) mal 2 ergibt den eingestellten Motorstrom (0,25V am Messpunkt entspricht also 0,5A Motorstrom).
Den Temperatursensor werde ich noch mit Wärmeleitkleber in der Flachfräsung einkleben. Dann misst er die Temperatur des Alu Gehäuses. Die Lufttemperatur wäre evtl. durch die Abwärme des Motors verfälscht.
Auf der 12V Versorgungsleitung sollte man noch einen fetten Elko (so 47µF/63V), zum Abblocken von Spikes, vorsehen.
Die Baudrate muss in RoboFokus auf 19200, 8Bit, non parity, 1 Stop Bits eingestellt werden.
Den richtigen COM Port erfährt man im Fenster Windows > Startmenü > Geräte und Drucker: Wenn man den Arduino-Nano einsteckt, poppt ein neues „Gerät“ auf. Mit Doppelklick darauf wird im Reiter Hardware der neu vergebene COM Port angezeigt (falls man Win7 User ist...)
Code:
// MotorFok2 Arduino Version
// ohne Enlagenschalter, ohne Handsteuerung von Edgar99
// General Public, kann beliebig verbreitet und verändert werden
#include <TimerOne.h>
#include <EEPROM.h>
unsigned long StepDelayFaktor = 1000; // 1 StepDelay = 1000 µs
//********************************************
//************ define I/O pins ***************
//********************************************
// pins for Arduino Nano // pins for Arduino Pro Micro
int led = 13; //for test only
int tempSensor = A7; //A3 // temperature sensor
int Mot_dir = 10; //9; //Portb.2 Direction 1 = move out
int Mot_stp = 9; //8; //Portb.1 Step on rising edge
int Mot_sleep = 8; //7; //Portb.0 Sleep L = Sleep
int Mot_reset = 7; //6; //Portd.7 Reset L = RESET
int Mot_m3 = 6; //5; //Portd.6 µ-Step Bit2
int Mot_m2 = 5; //4; //Portd.5 µ-Step Bit1
int Mot_m1 = 4; //3; //Portd.4 µ-Step Bit0
int Mot_en = 3; //2; //Portd.3 Enable L = Enable
int Pwr_1 = A2; //A2 // power switch 1
int Pwr_2 = A3; //A1 // power switch 2
int Pwr_3 = A4; //A0 // power switch 3
int Pwr_4 = A5; //10 // power switch 4
//************ define serial strings ***************
unsigned long serialValue;
char serialBuffer[10];
char versionStr[10] = "FV003.14"; // response string for firmware version
char actualPositionStr[10] = "FD000000"; // response string for actual Position
char maxTravelStr[10] = "FL000000"; // response string for max. travel
char backlashStr[10] = "FBN00000"; // response string for backlash
char configStr[10] = "FC000000"; // response string for config
char tempStr[10] = "FT00NNNN"; // response string for temperature
char powerStr[10] = "FP00NNNN"; // response string for power switches
//****** define EEPROM map **********
struct param_t {
byte test;
unsigned long MaxTravel;
unsigned int Backlash;
unsigned int BacklashDir;
unsigned int DutyCycle;
unsigned int StepDelay;
unsigned int StepSize;
}param = { // default values
0, // dummy = 0
999999, // max. travel range 0..999.999
20, // Backlash 0..255
2, // BacklashDir 2 = add to IN; 3 = add to OUT
55, // DutyCycle < 50: motor power turn OFF; >= 50: motor power remains ON after movements
1, // StepDelay 1..20 (1 = max Speed)
7, // StepSize 1..7 (microsteps)
};
//******* default motor params
unsigned int ActualDir = 0; // 0 = Non; 2 = IN; 3 = OUT
unsigned long ActualMovement= 0; // 0..999999
unsigned long ActualPosition= 500000; // 0 = inward, 999999 = outward
float Temperature = 586.3; // 0..1024 represents 0..512 Kelvin
unsigned long PowerSwitch = 0; // status of power switches
unsigned long MoveCNT = 0; // motor step counter
//********** Flags *****************
byte MotorRun = 0;
byte MotorRequ = 0;
byte MotorBacklash = 0;
byte PositionRefresh = 0;
byte MotorDir = 0;
//***********************************************
//************ Timer1 interrupt *****************
//********* moves motor by one step *************
//***********************************************
void Timer1Int() {
if (MoveCNT == 0){
MotorRun = 0; // movement finished
}
else {
digitalWrite(Mot_stp, HIGH); // motor step
MoveCNT--; // dec step CNT
if (MotorDir == 2){ActualPosition--;}
else {ActualPosition++;}
digitalWrite(Mot_stp, LOW); // minimum H time is 1.9µs !!
}
}
//***********************************************
// *********** abort motor **************
//***********************************************
void abortMotor(){
MoveCNT = 0; // motor stop
MotorRun = 0;
MotorRequ = 0;
MotorBacklash = 0;
// Timer1.stop();
}
//***********************************************
// *********** calculate check sum **************
//***********************************************
char calcCheckSum(char Buffer[]){
char checkSum = '\0';
for (int i = 0; i < 8; i++){
checkSum = checkSum + Buffer[i];
}
return checkSum;
}
//************************************************
// ******** check serialBuffer == "..000.." ******
//************************************************
int zeroStr(int start, int end){
for (int i = start; i <= end; i++){
if (serialBuffer[i] != '0'){
return 0;
}
}
return 1;
}
//****************************************************************
// ******* convert unsigned long to ASCII incl. leading '0' ******
//****************************************************************
void uitoa(char Buffer[], int start, int end, unsigned long intValue) {
for (int i = 0; i < (end - start); i++){
Buffer[end - 1 - i] = 48 + (intValue % 10);
intValue = intValue / 10;
}
}
//************************************************
// ******* convert ASCII to unsigned long ********
//************************************************
// Buffer kann serialBuffer sein ??
unsigned long atoui(char Buffer[], int start, int end){
Buffer[end + 1] = '\0';
return atol(&Buffer[start]);
}
//************************************************
// ******** sent actual position ********
//************************************************
void sentActualPosition(){
uitoa(actualPositionStr, 2, 8, ActualPosition); // write ASCII value to string
actualPositionStr[8] = calcCheckSum(actualPositionStr); // add check sum
Serial.write(actualPositionStr);
}
//************************************************
// ******** sent max. travel ********
//************************************************
void sentMaxTravel(){
uitoa(maxTravelStr, 2, 8, param.MaxTravel); // write ASCII value to string
maxTravelStr[8] = calcCheckSum(maxTravelStr); // add check sum
Serial.write(maxTravelStr);
}
//************************************************
// ******** sent backlash ********
//************************************************
void sentBacklash(){
uitoa(backlashStr, 3, 8, param.Backlash); // write ASCII value to string
backlashStr[2] = char(param.BacklashDir + 48);
backlashStr[8] = calcCheckSum(backlashStr); // add check sum
Serial.write(backlashStr);
}
//************************************************
// ******** sent config ********
//************************************************
void sentConfig(){
configStr[2] = char(param.DutyCycle); // Error in Robofocus specs !!
configStr[3] = char(param.StepDelay); // Error in Robofocus specs !!
configStr[4] = char(param.StepSize); // Error in Robofocus specs !!
configStr[8] = calcCheckSum(configStr); // add check sum
Serial.write(configStr);
}
//************************************************
// ******** sent temperature ********
//************************************************
void sentTemp(){
Temperature = analogRead(tempSensor)/2.304 + 424.078;
uitoa(tempStr, 4, 8, int(Temperature));
tempStr[8] = calcCheckSum(tempStr); // add check sum
Serial.write(tempStr);
}
//************************************************
// ******** sent power switches ********
//************************************************
void sentPowerSwitch(){
uitoa(powerStr, 4, 8, PowerSwitch); // write ASCII value to string
powerStr[8] = calcCheckSum(powerStr); // add check sum
Serial.write(powerStr);
if (powerStr[4] == '2'){digitalWrite(Pwr_1, HIGH);} else {digitalWrite(Pwr_1, LOW);} // set power switches
if (powerStr[5] == '2'){digitalWrite(Pwr_2, HIGH);} else {digitalWrite(Pwr_2, LOW);}
if (powerStr[6] == '2'){digitalWrite(Pwr_3, HIGH);} else {digitalWrite(Pwr_3, LOW);}
if (powerStr[7] == '2'){digitalWrite(Pwr_4, HIGH);} else {digitalWrite(Pwr_4, LOW);}
// if (powerStr[7] == '2'){digitalWrite(led, HIGH);} else {digitalWrite(led, LOW);} // Test
}
//************************************************
// ******** set motor StepSize ********
//************************************************
void setStepSize(){
if (param.StepSize > 7){param.StepSize = 7;}
if (bitRead(param.StepSize, 0)){digitalWrite(Mot_m1, HIGH);}
else {digitalWrite(Mot_m1, LOW);}
if (bitRead(param.StepSize, 1)){digitalWrite(Mot_m2, HIGH);}
else {digitalWrite(Mot_m2, LOW);}
if (bitRead(param.StepSize, 2)){digitalWrite(Mot_m3, HIGH);}
else {digitalWrite(Mot_m3, LOW);}
}
//************************************************
// ******** write EEPROM ********
//************************************************
void writeEEprom(struct param_t * value, unsigned int size){
for (unsigned int i = 0; i < size; i++){
EEPROM.write(i, *((char*)value + i));
}
}
//************************************************
// ******** read EEPROM ********
//************************************************
void readEEprom(struct param_t * value, unsigned int size){
for (unsigned int i = 0; i < size; i++){
*((char*)value + i) = EEPROM.read(i);
}
}
//***********************************************
//****************** setup **********************
//***********************************************
void setup() {
pinMode(led, OUTPUT);
pinMode(Mot_dir, OUTPUT);
pinMode(Mot_stp, OUTPUT);
pinMode(Mot_sleep, OUTPUT);
pinMode(Mot_reset, OUTPUT);
pinMode(Mot_m3, OUTPUT);
pinMode(Mot_m2, OUTPUT);
pinMode(Mot_m1, OUTPUT);
pinMode(Mot_en, OUTPUT);
pinMode(Pwr_1, OUTPUT);
pinMode(Pwr_2, OUTPUT);
pinMode(Pwr_3, OUTPUT);
pinMode(Pwr_4, OUTPUT);
digitalWrite(Mot_dir, LOW);
digitalWrite(Mot_stp, LOW);
digitalWrite(Mot_sleep, HIGH); // awake motor
digitalWrite(Mot_reset, LOW); // reset
digitalWrite(Mot_m3, LOW);
digitalWrite(Mot_m2, LOW);
digitalWrite(Mot_m1, LOW);
digitalWrite(Mot_en, HIGH); // motor disable
digitalWrite(Mot_reset, HIGH); // non reset
analogReference(DEFAULT);
if (EEPROM.read(0) == param.test){ // if EEPROM values are valid
readEEprom(¶m, sizeof(param)); // read EEPROM values
}
setStepSize();
Serial.setTimeout(500);
Serial.begin(19200, SERIAL_8N1); // 19200Baud, 8Bit, non parity, 1 Stop
while (!Serial) {} // wait for serial port to connect (needed for Leonardo only)
}
//***********************************************
//****************** Loop **********************
//***********************************************
void loop() {
// Handsteuerung fehlt
//*********** sent actual position until motor is running ***********
if (PositionRefresh){
Serial.flush();
sentActualPosition(); // sent actual position
}
if (!MotorRun && !MotorBacklash && PositionRefresh){
PositionRefresh = 0; // reset position refresh
}
//*********** read serial interface *************
if (Serial.available() > 0) {
if (Serial.readBytes(serialBuffer, 9) == 9) {
if (serialBuffer[0] == 'F') {
if (serialBuffer[8] == calcCheckSum(serialBuffer)){
switch (serialBuffer[1]) {
case 'V': // CMD = request firmware version
versionStr[8] = calcCheckSum(versionStr); // add check sum
Serial.write(versionStr);
break;
case 'G': // CMD = goto command
if (zeroStr(2, 7)){
sentActualPosition(); // if "0" sent actual Position
break;
}
serialValue = atoui(serialBuffer, 2, 7); // read serial value
if (serialValue <= ActualPosition) {
ActualMovement = ActualPosition - serialValue;
ActualDir = 2; // move in
}
else {
ActualMovement = serialValue - ActualPosition;
ActualDir = 3; // move out
}
MotorRequ = 1; // Start move
break;
case 'I': // CMD = move in
ActualMovement = atoui(serialBuffer, 3, 7);
ActualDir = 2;
MotorRequ = 1; // Start move
break;
case 'O': // CMD = move out
ActualMovement = atoui(serialBuffer, 3, 7);
ActualDir = 3;
MotorRequ = 1; // Start move
break;
case 'S': // CMD = set current position
if (zeroStr(2, 7)){
sentActualPosition(); // if "0" sent actual Position
break;
}
abortMotor(); // motor stop
ActualPosition = atoui(serialBuffer, 2, 7);
break;
case 'L': // CMD = set maximum travel
if (zeroStr(2, 7)){
sentMaxTravel(); // if "0" sent travel range
break;
}
abortMotor(); // motor stop
param.MaxTravel = atoui(serialBuffer, 2, 7);
sentMaxTravel();
writeEEprom(¶m, sizeof(param)); // save to EEprom
break;
case 'P': // CMD = power switch
if (zeroStr(4, 7)){
sentPowerSwitch(); // if "0" sent power switches
break;
} // CMD = set remote power module
PowerSwitch = atoui(serialBuffer, 4, 7);
sentPowerSwitch();
break;
case 'B': // CMD = backlash compensation
if (zeroStr(2, 7)){
sentBacklash(); // if "0" sent backlash
break;
}
abortMotor(); // motor stop
param.BacklashDir = int(serialBuffer[2] - 48);
param.Backlash = atoui(serialBuffer, 3, 7);
sentBacklash();
writeEEprom(¶m, sizeof(param)); // save to EEprom
break;
case 'C': // CMD = set configuration
if (zeroStr(2, 7)){
sentConfig(); // if "0" sent Config
break;
}
param.DutyCycle = serialBuffer[2];
param.StepDelay = serialBuffer[3];
param.StepSize = serialBuffer[4];
abortMotor(); // motor stop
writeEEprom(¶m, sizeof(param)); // save to EEprom
setStepSize();
sentConfig();
break;
case 'T':
sentTemp(); // CMD = temperature reading
break;
}
}
}
else {
if (serialBuffer[0] == 'V') {
abortMotor();
PositionRefresh = 0;
}
}
}
}
//*********************************************
//********* state machine to move motor *******
//*********************************************
//************* Start Motor *******************
if (!MotorRun && MotorRequ && !MotorBacklash){
MotorRun = 1;
MotorRequ = 0;
MotorDir = ActualDir;
PositionRefresh = 1;
if (param.BacklashDir == ActualDir){
MotorBacklash = 1; // add Backlash
MoveCNT = param.Backlash + ActualMovement;
}
else {
MotorBacklash = 0;
MoveCNT = ActualMovement;
}
if (MotorDir == 2){digitalWrite(Mot_dir, LOW);}
else {digitalWrite(Mot_dir, HIGH);} // set Motor Dir
digitalWrite(Mot_en, LOW); // motor Enable
Timer1.initialize(StepDelayFaktor * param.StepDelay); // set motor speed
Timer1.attachInterrupt(Timer1Int); // interrupt enable
}
//************* Backlash *******************
if (!MotorRun && MotorBacklash){ // TEST !MotorRequ &&
MotorRun = 1;
//MotorRequ = 0;
MotorBacklash = 0;
if (MotorDir == 2){ // reverse direction
MotorDir = 3;
digitalWrite(Mot_dir, HIGH);
}
else {
MotorDir = 2;
digitalWrite(Mot_dir, LOW);
}
MoveCNT = param.Backlash; // init counter
}
//************* Motor finish *******************
if (!MotorRun && !MotorRequ && !MotorBacklash){
Timer1.stop();
if (param.DutyCycle < 50){
digitalWrite(Mot_en, HIGH); // motor disable
}
else {
digitalWrite(Mot_en, LOW); // motor enable
}
}
}