Hallo liebes Forum,
ich hoffe ihr könnt mir weiterhelfen, da ich nach vielen vergeblichen Teststunden nicht mehr weiterweiß. Eigentlich sollte mein Vorhaben recht einfach sein...
Ziel: 4x 2xLEDs "zufällig" an/aus-schalten und einen Servo ebenfalls "zufällig" einen kurzen hin-und-her-Zyklus (Endstellungen) durchlaufen lassen.
Problem: Wenn alle LEDs-Codeabschnitte akiv sind, flackern die LEDs 2-4 (scheinbar unregelmäßiges 2ms und 3,5 ms-An/aus). LED 1 flackert zwar nicht, hat aber einen falschen Takt (8, 18 Sek....) und der Servo wird zwar synchron zur LED 3 angesteuert, aber nur mit einem 1ms-Signal anstatt eines Zyklus (Soll:1ms, 1 Sek. warten, ~2ms).
Wenn nur der Code für die LED 3 (inkl. Servoaktivierung) aktiv ist, funktioniert es.
Vermutung: Da ich den Schaltungsaufbau & myC und Fuses bereits erfolgreich mit einem nur 4-LEDs-"zufällig"-an/ausschalten-lassen-Programm und einem Servo-Testprogramm (sekündlich zwischen Endstellungen umschalten) verwendet habe, verdächtige ich diese eher nicht.
Ich vermute, dass ich bei meiner Timer-Konstruktion etwas nicht beachtet/falsch gemacht habe oder dass es im myC irgendwelche Abhängigkeiten gibt, die mir nicht bewusst sind. Das Servosignal funktioniert prinzipiell mit 1+16 ms ganz gut. Trotzdem habe ich den Verdacht, dass beim Takt/Teiler etc. ggf. etaws nicht stimmt. Mit dem Datenblatt habe ich mich zwar bereits recht intensiv bei der Timer-Programmierung beschäftig, aber ich hoffe, dass ihr mir noch hilfreiche Tipps geben könnt.
MfG
Chilibit
myC: ATTiny 13A
Zu mir: ambitionierter Anfänger, habe aber schon 2 andere Projekte mit dem 13A umgesetzt (aber noch nichts mit Servos) ;-)
Servo: no name...
Ich habe Bilder der Fuses, Signalen an den Ausgängen und der Schaltung
angehängt.
Microchip Studio 7.0.2594
Program Memory Usage: 540 bytes 52,7 % Full
Data Memory Usage: 59 bytes 92,2 % Full
Programm:
1
#define F_CPU 9600000 //CPU auf 9,6MHz
2
#include<avr/io.h>
3
#include<stdlib.h>
4
#include<avr/interrupt.h>
5
6
//Augen-Zeitvariablen:
7
uint8_tzeitAugenFst=0;
8
uint8_tzeitAugenEinsSek=0;
9
uint8_tzeitAugenZweiSek=0;
10
uint8_tzeitAugenDreiSek=0;
11
uint8_tzeitAugenVierSek=0;
12
13
//Servo-Code:
14
uint8_tphase=0;
15
uint8_tZeitZaehlerServo=0;
16
// Timer ist übergelaufen (alle 0,213ms (=256*0,000833ms)), neue Phase beginnt (?? Irgendwie fehlt hier noch der Faktor 10x um auf 17ms zu kommen?!)
17
// Jede 8. Phase (~17,1ms) wird PB0=1 gesetzt und dann bei Erreichen des Vergleichswertes (OCR0A in der Main-Schleife löst "ISR(TIM0_COMPA_vect)" aus),s.u., wieder auf 0 gesetzt
18
ISR(TIM0_OVF_vect)
19
{
20
// Servo nur in Phase 0 ansteuern
21
if(phase==0)
22
{
23
PORTB|=(1<<PB0);//Servo-Signal an
24
25
ZeitZaehlerServo=ZeitZaehlerServo+1;//alle 8 Phasen (17 ms) eins hochzählen
26
27
//Reduziere alle Zeitvariablen um 1 (59 => 0,97Sek)
28
zeitAugenFst=zeitAugenFst+1;
29
30
}
31
32
phase=phase+1;// Nächste Phase 1... 2...8 => 0
33
if(phase>8)
34
{
35
phase=0;
36
}
37
}
38
39
// Timer hat Vergleichswert erreicht
40
ISR(TIM0_COMPA_vect)
41
{
42
PORTB&=~(1<<PB0);//Servo-Signal aus (=>~ 1 - 2 ms)
43
}
44
45
46
47
uint8_tlaufZeit=0;
48
uint8_tservoZyklus=0;
49
//Zur Info: Max-Werte für Zeit: 245 (x+10<256), Anzahl: 50
90% RAM Auslastung sind u.U. zu viel. Der Compiler kann Heap/Stack nicht korrekt berücksichtigen. Definiere doch die zeitWertesekundaer als Progmem und passe die Zugriffe entsprechend an. Das gibt etwas Luft.
Vielen Dank für eure zahlreichen Antworten!
Der Reihe nach:
Da ich nicht weiß, wie ich mit dem Microchip Studio ino-Dateien erzeuge, habe ich mal die main.c angehängt. Sorry, ich dachte, dass mein MiniProg nicht in die Kategorie "lang" fällt.
Bzgl. des "clkdiv8 Fuse":
Ich habe im ersten Post ein Bild der Fuses angehängt - der LOW.CKDIV8 ist gesetzt oder was ist gemeint?
volatile:
Alle im Interrupt und main verwendeten Variablen sind jetzt als "volatile" deklariert.
RAM/Flash:
Vielen Dank für den Hinweis - ich habe versucht ihn umzusetzen. Wäre nett, wenn jemand meine Umsetzung kontrollieren würde.
Ich habe das in der main01.c angehängte Programm auf dem myC getestet und bin etwas ratlos, da:
die LEDs flackern nicht mehr, bleiben aber sehr lange an bzw. aus, z.T. gehen sie nur kurz an und direkt wieder aus (was ungefähr der vorgesehenen 1 Sek. entsprechen könnte).
Immerhin Durchläuft der Servo wie geplant einen Links-Rechts-Zyklus, wenn die LED 3 angeht.
Die Servo-Ansteuerung per Interrupt bin ich so gewohnt, da habe ich nichts zu meckern. Aber die Ansteuerung der LEDs würde ich im Zustandsautomaten verpacken. Falls dir das nichts sagt, schau dir das an: http://stefanfrings.de/multithreading_arduino/index.html
Kleinvieh macht auch Mist... warum nicht so? Ist kürzer und schneller.
uint8_t zufallsZeitWert(){ //gibt Zufallswert für Zeit zurück
laufZeit++;
if (laufZeit > 49) {laufZeit = 0;}
return pgm_read_byte (zeitWerteSekundaer[laufZeit]);
Ansonsten scheint die Funktion des Programms ja deutlich näher am Soll zu liegen. Das deutet darauf hin, dass der RAM Verbrauch tatsächlich zu hoch war.
Bzgl. des "clkdiv8 Fuse":
Ich habe im ersten Post ein Bild der Fuses angehängt - der LOW.CKDIV8
ist gesetzt oder was ist gemeint?
Tja, wenn ich jetzt wüsste, was diese Oberfläche meint? Ich würde das so interpretieren, dass CKDIV8 aktiv ist, also auf 0 gesetzt wird. Damit wird der Takt der MCU, also auch der Takt, der deren Timer speist, durch 8 dividiert.
geh nochmal einen großen Schritt zurück.
Laut deiner Konfig nutzt du den internen 9,6MHz RC.
Zusätzlich ist CKDIV8 Fuse aktiv.
Das heißt dein µC taktet mit 1,2MHz.
Teste einmal ob die 2 Pins sauber takten?
Ungetestet, kompiliert jedoch.
Danach überlegst du dir nochmal wie du den Timer konfigurieren musst um vernünftig ein Servosignal zwischen 1-2ms zu erzeugen und dabei eine Periodendauer von 20ms einzuhalten. Kannst auch auf 10ms Periodendauer runtergehen. Das kommt der Auflösung zu Gute bei der Rechnerei.
Da nun wieder genug RAM zur Verfügung steht, habe ich den LED-Zustand mit vier bool-Variablen erfasst, anstatt ihn jedes mal abzufragen. Für meinen einfachen Fall genügt aber, denke ich, die einfache if-else-Konstruktion oder würdet ihr dringend zu der switch-case-Variante raten? Aber danke für den Hinweis auf den Zustandsautomaten, werde ich mir merken.
Die Optimierung von Georg G. an der zufallsZeitWert()-Funktion habe ich übernommen.
Danke für die Hinweise bzgl. CKDIV8 - das hatte ich nicht berücksichtigt.
Ich habe Dein Programm, Veit D., am MCU getestet. Heraus kam eine Frequenz von 275 Hz (Signallänge an PB1 bzw. PB2: 1,820ms).
Zugegebener Maßen, verstehe ich das Programm mit den "_BV()"-Ausdrücken nicht so ganz und hätte eigentlich eher unterschiedliche Frequenzen an den beiden Pins erwartet... Wie auf den Bildern zu sehen ist, trat auch eine wiederkehrende Störung auf... diese ist mir aber beim aktuellen Programm nicht mehr begegnet.
Nach ein wenig herumrechnen, habe ich mich dann für die folgende Variante entschieden:
CKDIV8 deaktiviert (also 9,6 MHz) und den Timer-Prescaler auf 256.
Das ergibt dann 1ms: 37 Takte (1,072ms gemessen), 2ms: 70 Takte (nachgemessen & korrigiert => 2,0ms) und einen Zyklus von 21,6ms(gemessen)
Servo und LEDs werden nun korrekt angesteuert und funktionieren wie erwartet... zumindest wenn ich nicht die zufallsZeitWert()-Funktion verwende. Wenn ich feste Werte vorgebe, werden diese korrekt umgesetzt. Die LEDs, die ihre Zeitwerte über die Funktion beziehen ändern ihren Zustand gar nicht oder nur sehr selten. Hat jemand eine Idee, voran das liegen könnte? Ich habe den aktualisierten Code angehängt.
_BV() ist ein Makro.
avr/sfr_defs.h>
void _BV(bit_number);
Andere Schreibweise statt Bit << schubsen.
Das beide Pins den gleichen Takt haben stimmt. Da hatte ich einen Denkfehler. Den OCRA Pin müßte der Timer selbst schalten damit die Pulsweite wie Anfangs gedacht verwendet wird. Dann hätte er den doppelten Takt. Ist jetzt auch egal.
Übrigens, das Oszi hat links eine Tastenreihe. Damit kann man im Bildschirm unten Meßwerte einblenden lassen. Zwecks deiner Frequenz und Duty Messung.
Mit Störungen meinst du sicherlich das "Rauschen" auf dem Low/High-Pegel?
Das werden Störungen durch schlechte Kontakte/Masse sein. Steckbrett usw.
habe mich mal rangemacht erstmal den Code aufzuräumen. Damit steigen die Chancen das jemand durchblickt. Und du auch um den Fehler zu finden, weil die Code Dopplung entfällt.
Dafür müßtest du das aber als Cpp Projekt neu anlegen und wenigstens unter
Projekt > Properties >
etwas oberhalb > Configuration: > All Configuration
C++ Miscellaneous: "other flags" auf -std=c++11 einstellen,
Haken bei verbose kannste auch setzen
C kann leider nicht mit der Struktur "struct Auge" umgehen.
Du kannst weiterhin C programmieren, abgesehen von der Struktur.
Wenn das erstmal wie bisher funktioniert kann man weiter schauen ...
Vielen Dank für Eure Antworten und Hinweise! Und vielen Dank Veit D., dass Du Dir die Mühe gemacht hast, das Programm umzuschreiben. Dadurch hast Du für andere User sicherlich ein besseres Beispielprogramm erstellt, als mein Anfängerproggi. Zugegeben, mit struct kenne ich mich noch nicht aus, aber ich sehe ein, dass es in diesem Anwendungsfall wohl Sinn macht. Ich werde Eure zahlreichen Hinweise bei den nächsten Projekten versuchen zu berücksichtigen - vielen Dank dafür!
Nach der Ergänzung des fehlenden Adressoperators beim Zugriff auf das Array im Flash funktioniert das Programm nun wie erwartet, womit meine Frage beantwortet wäre. Ich hänge das endgültige Prog. noch an, falls noch anderen Anfängern die struct-Variante zu kompliziert ist.
Abschließend noch der Sinn des Programms:
Wer mag, kann mit seinen Kindern zu Halloween Tischtennisbälle als Augen bemalen (Edding, Alufolie als Maske, Klarsichtfolie als Feuchteschutz), mit LED versehen und diese dann in einen kugeligen Busch im Vorgarten platzieren. Je ein Augenpaar wird dann zufällig an-/ausgeschaltet. Der Servo soll dann einen Ast schütteln.
Hauptsache ist das es funktioniert. :-)
Je mehr du programmierst und je mehr Codedopplungen man hat, umso eher kommt man dann ins Themengebiet Arrays, Strukturen usw. Das ergibt sich dann fließend mit der Zeit. Aber schön ist das du eine Lösung hast.