Moin zusammen,
ich will einen Schrittmotor einfach so drehen lassen. Habe aktuell zum
testen einen TB6600 Treiber mit einem 3Nm 4,2A Schrittmotor.
Jetzt will ich mit einem Attiny 85 dem Timer 0 als CTC verwenden um das
Pulssignal für den Schrittmotortreiber zu erzeugen.
Mein Problem ist, das die PWM absolut "unstabil" läuft.
Rein theoretisch müsste ich ja von ca. 15-3900Hz "einstellen".
Aber es läuft wie im Bild zu sehen. Kann mir jemand sagen wo mein Fehler
ist?
1
#define F_CPU 8000000L
2
#include<avr\io.h>
3
#include<stdlib.h>
4
#include<avr/interrupt.h>
5
6
#define PWM_PIN 0
7
#define PWM_DDR DDRB
8
#define PWM_PORT PORTB
9
10
volatileuint8_ttemp=0;
11
12
uint8_tadc_8bit(uint8_tchannel)
13
{
14
ADMUX=channel;// Analog Kanal einstellen
15
ADMUX|=(1<<ADLAR);//
16
ADCSRA|=(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);// ADC Register einstellen und Messung starten
17
loop_until_bit_is_clear(ADCSRA,ADSC);// Warten bis die Messung abgeschlossen ist
18
returnADCH;// Mittelwert zurück in Ergebnis schreiben
19
}
20
21
voidtimer0_init(uint16_tprescaler)
22
{
23
//Prescaler Timer 0 Attiny85
24
TCCR0A|=(1<<WGM01)|(1<<COM0A0);//WGM für CTC Modus COM0A0 für Toggle on Compare Match
Hallo Peter,
Bevor du dich mit dem Thema Programm auseinandersetzt solltest du dich
doch erst einmal mit dem Thema PWM auseinandersetzen und dir ansehen wie
man die Hardware überhaupt benutzt bzw. ansteuert.
Hier ein link zum Thema PWM:
www.elektronik-kompendium.de/sites/kom/0401111.htm
Zur Ansteuerung benötigst du kein PWM sondern nur einen kurzen Puls am
am Pulse-eingang des Treibers. In deinem Programm veränderst du aber
nicht die Frequenz sondern den Tastgrad (Verhältnis der Impulsdauer zur
Pausendauer).
Den Impuls kannst du auch mit Timer0 erzeugen jedoch würde ich einfach
einen Timer Overflow Interrupt nehmen und in der Funktion eine Variable
hochzählen. mit der Variable und if-Abfragen kannst du dann einen
Ausgang schalten und somit deinen Puls generieren, sowie auch die
Frequenz deines Pulses verändern.
Hallo Lukas,
Ich weiß worauf du hinaus willst. Das habe ich übrigens auch schon
probiert.
Aber deine Aussage ist so nicht korrekt.
Ich bin mit dem Timer durch setzen des WGM01 Bit im CTC Modus. Clear on
Compare Match. Heißt mein Tcnt0 zählt nur bis der Wert den im Ocr0a
eingestellten Wert erreicht hat.
Mit dem Com0A Bit wird der Ausgang beim erreichen des Compare Match
getoggelt.
Dadurch ist mein Tastgrad wenn sich mein OCr0a nicht ändert immer 50%.
Das funktioniert auch Einwand frei wenn ich den Ocr0a einmalig beim
initialisieren Setzte. Dann kann ich meine Frequenz von etwa 15 Hz
(ocr0a=255) bis ca 3900 Hz (Ocr0a =1) verändern.
Die Pwm läuft dann auch sauber durch wenn der Ocr0a nur beim
initialisieren gesetzt wird und nicht in der While Schleife.
Das es mit dem Tastgrad von 50% klappt weiß ich daher, das es hiermit
funktioniert(Natürlich mit einem Arduino Nano nicht dem attiny85).
https://github.com/hausen8/cold-end
Der ADC gibt auch die richtigen Werte raus. Die habe ich mir mit nem
S-Uart an den PC gesendet und überprüft.
Nur die Kombi sobald ich mit dem ADC den OCR0A setze funktioniert nicht.
Laufen noch weitere Interrupts, oder ist das oben das vollständige
Programm? Wenn mehrere IR-Quellen aktiv sind, ist es essentiell, die
nicht-unterbrechbaren Routinen so kurz wie möglich zu machen. Den Rest
kann man durch Setzen von Flags und bedingtes Abarbeiten in der Mainloop
erledigen.
Mit freundlichen Grüßen
Thorsten Ostermann
Das Setzen vom OCR0A setzt offenbar den Timer zurück, d.h. der beginnt
neu zu zählen. Das muss man in der while-Schleife mit dem Timer
synchronisieren. Datenblatt lesen.
rµ schrieb:> Das Setzen vom OCR0A setzt offenbar den Timer zurück, d.h. der beginnt> neu zu zählen.
Da zählt nichts neu, aber der Timer verpasst sein durch OCR0A
vorgegebenes TOP und läuft bis 255 durch.
Der ADC-Wert wackelt sicher ein wenig und wenn OCR0A dann in der
Hauptschleife massiv zugehauen wird, dann ergeben sich in Kombination
mit der fehlenden Doppelpufferung interessante Schwebungseffekte.
> Das muss man in der while-Schleife mit dem Timer> synchronisieren.
Einsynchronisieren ist richtig, in der Hauptschleife könnte man es
blockierend machen, ist aber selbst dort bei Prescaler 1 und TOP von 1
aufgrund der Latenz der Sync-Warteschleife schwierig bis unmöglich ohne
Glitch.
Wenn der neue Wert kleiner als der alte Wert ist, würde OCR0A im
Interrupt setzen, indem man versucht den Doppelpuffer nachzubilden. Aber
selbst im Interrupt gilt bei kleinem Prescaler und kleinem TOP obiges
aufgrund der Interrupt-Latenz.
Bei Werten von Prescaler 1 TOP 1 -> TOP 0 wird's so und so schwierig :D
Da hilft dann nur Timer anhalten, auf 0 setzen, neuen OCR0A-Wert setzen,
Timer wieder starten.
Thorsten O. schrieb:> Laufen noch weitere Interrupts, oder ist das oben das vollständige> Programm?
Nein laufen keine, und ja, war das ganze Programm.
MWS schrieb:> Wenn der neue Wert kleiner als der alte Wert ist, würde OCR0A im> Interrupt setzen, indem man versucht den Doppelpuffer nachzubilden.
Doppelpuffer nachbilden?
Würde das nicht reichen?
1
ISR(TIM0_COMPA_ISR)
2
{
3
OCR0A=temp;
4
}
m.n. schrieb:> Es ist zwar der "Holzhammmer" aber als funktionierendes Beispiel für den> Anfang auch nützlich:> http://www.mino-elektronik.de/Generator/takte_impulse.htm#bsp5
So ähnlich wollte ich es basteln.
Ich will es nicht vorgekaut bekommen. Schonmal danke für die vielen
Hilfreichen Tips. Bin meiner Lösung schon um einiges näher
MWS schrieb:> Probier's so, bei Schrittmotor-üblichen Frequenzen dürfte das> reichen.
Super vielen lieben dank. Das funktioniert tip top. Wie oben schon
gefragt, ob man es nicht einfach direkt im Interrupt so übertragen kann
oder ob da was schiefgehen kann?
Peter schrieb:> Wie oben schon> gefragt, ob man es nicht einfach direkt im Interrupt so übertragen kann> oder ob da was schiefgehen kann?
Nö, was soll groß schiefgehen? Schlimmer als zu Beginn kann's nicht
werden.
Die ISR vom Aufruf über den Interruptvektor bis zum Übertragen von temp
nach OCR0A wird 20-30 Takte dauern, die ISR insgesamt geschätzte 40-50.
Die Aufrufrate der ISR mal benötigte Takte darf (@8MHz) 8.000.000
Takte/s nicht überschreiten, geteilt durch 50 für die ISR wären 160.000
Aufrufe/s. Da der Portpin toggelt, wird's die Hälfte, also OC0A 80kHz
max. Der Tiny85 ist dann bei 100% Auslastung, die Hauptschleife wird
nicht mehr abgearbeitet, weil nach dem vorigen Interrupt gleich der
nächste kommt.
Wenn die angenommenen 50 Takte unterschritten würden, also Prescaler 1
und OCR0A 30, dann kommt Verhau raus, weil die ISR stets "nachhinkt" und
einen neuen OCR0A-Wert nur mehr zufällig zum richtigen Zeitpunkt setzt.
Daraus ergibt sich:
1) Die ISR-Methode belastet mehr als die Hauptschleifen-Methode
2) Die ISR-Methode sollte bei einem Prescaler >= 64 mit Werten >= 0 für
TOP arbeiten. Bei TOP = 0 müsste man u.U. aufpassen, ob laufendes
Schreiben von 0 auf OCR0A in der ISR zu einem Problem führt.
3) Bei der Hauptschleifen-Methode kommt's nur auf die Latenz der paar
Zeilen Code an, optimal übersetzt würde das zur Opcode-Sequenz: SBI, IN,
ANDI, BRNE & OUT. Ergibt unter Annahme des ungünstigsten Zeitpunktes in
dem Timer0 das OCF0A setzt, ein TOP von 9 bei Prescaler 1 = 400kHz.