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
Bitte beachten, was über **jeder** Texteingabebox hier steht:
1
Antwort schreiben
2
Wichtige Regeln - erst lesen, dann posten!
3
4
* ...
5
* Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
Mein Vorschlag: häng den Code an den nächsten Post einfach als ino Datei
an und ich lösche den oben raus.
Kai W. schrieb:> Trotzdem habe ich den Verdacht, dass beim Takt/Teiler etc. ggf. etaws> nicht stimmt.
Passt die clkdiv8 Fuse?
Kai W. schrieb:> ich hoffe ihr könnt mir weiterhelfen, da ich nach vielen vergeblichen> Teststunden nicht mehr weiterweiß.
Variablen, die in einem Interrupthandler und in main() genutzt werden,
sollten als volatile deklariert werden.
Grüßle,
Volker
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.
Hallo Kai,
Kai W. schrieb:> const uint8_t zeitWerteSekundaer[50] = {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,> 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,> 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2};
Die Daten für die Konstante werden in den RAM geschaufelt, lege sie in
den Flash, dann hast du fast den gesamten RAM frei.
Schau mal hier rein:
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Flash_mit_PROGMEM_und_pgm_read
Etwas weiter unten gibt es auch noch die Beschreibung von "__flash", ist
IMO einfacher in der Anwendung, nutze ich häufig für Konstanten.
Reinhard
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.
MfG Kai
Ich blicke durch den Code nicht durch.
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.
Kai W. schrieb:> 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.
Grüßle,
Volker
Volker B. schrieb:> Tja, wenn ich jetzt wüsste, was diese Oberfläche meint?
Sie meint die Fuses im default Zustand, also 1,2 MHz Taktfrequenz.
Die hexadezimale Darstellung unten drunter ist unmissverständlich.
Steve van de Grens schrieb:> Volker B. schrieb:>> Tja, wenn ich jetzt wüsste, was diese Oberfläche meint?>> Sie meint die Fuses im default Zustand, also 1,2 MHz Taktfrequenz.>> Die hexadezimale Darstellung unten drunter ist unmissverständlich.
...wenn man die ausgewählte MCU kennen würde...
Ah, ok, steht im Titel. Korrigiere:
...wenn man nicht zu faul wäre, das Datenblatt zu suchen... :-)
Grüßle,
Volker
Volker B. schrieb:> wenn man die ausgewählte MCU kennen würde.
Siehe links oben im Screenshot Fuses.JPG des Eröffnungsbeitrages. Oder
im Titel dieser Diskussion.
Die Fuses sind im Datenblatt beschrieben. Ich empfehle das Tool
https://www.engbedded.com/fusecalc/
Hallo,
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.
Vielen Dank für Eure Antworten!
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.
MfG Kai
Hallo,
_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.
Hallo,
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
links Tooolchain:
AVR/GNU C++ Compiler >
Optimization: Level auf (-Os) setzen
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.
Vielen Dank und viele Grüße
Kai
Hallo,
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.