Bin noch ein ziemlicher Noob in C
Bisher die Schaltung nur in tinkercad.com simuliert (vormals autodesk
circuits.io)
Funktioniert soweit alles bis auf timing Problemchen und Farb Glitches.
Aufgabe:
3 x 5 LEDs 120 Grad vesetzt auf einem Rad. Abfrage der Drehzahl über
Hall Sensor auf PB2 (INT0)
Strips sollen an einer Position recht synchron an einer Stelle
aufleuchten, auch bei Änderung der Drehzahl.
Drehzahl ist recht gering 1-6 Hz.
Ich zähle per Interrupt die Drehzahl und berechne damit weiterführend
die Leuchtdauer und Abschaltzeit der LEDs sowie mit einem kleineren
Delay den Abstand der Strips zueinander.
Problem ist nur das die Drehzahlberechnung anfangs zu hohe Werte
ausgibt, da sie auf verstrichene Zeit zwischen Interrupts basiert und
das Ergebnis als Teiler dient.
Wie bekomm ich es hin das ich ab dem zweiten Interrupt anfange den Code
auszuführen?
Auch rufe ich die Funktion aus dem Interrupt heraus auf. Glaube das ist
auch problematisch. Läuft aber besser als wenn ich den Code direkt im
Interrupt ausführe.
Hier ist mein Programm.
1
#include<Adafruit_NeoPixel.h>
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
5
#define NUMPIXELSA 5
6
#define NUMPIXELSB 5
7
#define NUMPIXELSC 5
8
9
Adafruit_NeoPixelstrip_A=Adafruit_NeoPixel(NUMPIXELSA,0,NEO_GRB+NEO_KHZ800);// 3 Strips designed to be offset by 120 degrees on a wheel
unsignedlongrefreshInterval=10;// in ms, for LCD refresh timing
29
unsignedlongpreviousMillis=0;
30
volatileunsignedintrpm=0;
31
volatileunsignedintlast_rpm=0;
32
unsignedintrotDelay=0;// Calculated time based variable to control the duration and delay between LEDs switching on and off, integer instead of float to test and save processing time
strip_A.setBrightness(230);// Set Strips to approx 90% Brightness
48
strip_B.setBrightness(230);
49
strip_C.setBrightness(230);
50
strip_A.show();// Send empty data to strips to reset floating output turning all Neopixels off
51
strip_B.show();
52
strip_C.show();
53
54
// for (int a = 0; a < NUMPIXELSA; a++) { // Strip Test
55
// strip_A.setPixelColor(a, whiteA );
56
// strip_A.show(); // Send data
57
// delay(100); // Delay to test each strip
58
// }
59
// for (int a = 0; a < NUMPIXELSA; a++) {
60
// strip_A.setPixelColor(a, offA );
61
// strip_A.show();
62
// }
63
// for (int b = 0; b < NUMPIXELSB; b++) {
64
// strip_B.setPixelColor(b, whiteB);
65
// strip_B.show();
66
// delay(100);
67
// }
68
// for (int b = 0; b < NUMPIXELSA; b++) {
69
// strip_B.setPixelColor(b, offB );
70
// strip_B.show();
71
// }
72
// for (int c = 0; c < NUMPIXELSC; c++) {
73
// strip_C.setPixelColor(c, whiteC);
74
// strip_C.show();
75
// delay(100);
76
// }
77
// for (int c = 0; c < NUMPIXELSA; c++) {
78
// strip_C.setPixelColor(c, offC );
79
// strip_C.show();
80
// }
81
sei();// Enable Interrupts
82
}
83
voidupdate_rpm(){
84
rpm=60*(1000000/(time*1));
85
rotDelay=360000/(rpm*20);
86
rotDelayA=((rotDelay/3)/3);
87
}
88
voidstrips(){
89
for(inta=0;a<NUMPIXELSA;a++){
90
strip_A.setPixelColor(a,whiteA);
91
strip_A.show();
92
delay(rotDelay);
93
}
94
for(inta=0;a<NUMPIXELSA;a++){
95
strip_A.setPixelColor(a,offA);
96
strip_A.show();
97
delay(rotDelay);for(inta=0;a<NUMPIXELSA;a++){
98
strip_A.setPixelColor(a,whiteA);
99
strip_A.show();
100
delay(rotDelay);
101
}
102
for(inta=0;a<NUMPIXELSA;a++){
103
strip_A.setPixelColor(a,offA);
104
strip_A.show();
105
delay(rotDelay);
106
}
107
delay(rotDelayA);
108
for(intb=0;b<NUMPIXELSB;b++){
109
strip_B.setPixelColor(b,whiteB);
110
strip_B.show();
111
delay(rotDelay);
112
}
113
for(intb=0;b<NUMPIXELSB;b++){
114
strip_B.setPixelColor(b,offB);
115
strip_B.show();
116
delay(rotDelay);
117
}
118
delay(rotDelayA);
119
for(intc=0;c<NUMPIXELSC;c++){
120
strip_C.setPixelColor(c,whiteC);
121
strip_C.show();
122
delay(rotDelay);
123
}
124
for(intc=0;c<NUMPIXELSC;c++){
125
strip_C.setPixelColor(c,offC);
126
strip_C.show();
127
delay(rotDelay);
128
}
129
delay(rotDelayA);
130
}
131
delay(rotDelayA);
132
for(intb=0;b<NUMPIXELSB;b++){
133
strip_B.setPixelColor(b,whiteB);
134
strip_B.show();
135
delay(rotDelay);
136
}
137
for(intb=0;b<NUMPIXELSB;b++){
138
strip_B.setPixelColor(b,offB);
139
strip_B.show();
140
delay(rotDelay);
141
}
142
delay(rotDelayA);
143
for(intc=0;c<NUMPIXELSC;c++){
144
strip_C.setPixelColor(c,whiteC);
145
strip_C.show();
146
delay(rotDelay);
147
}
148
for(intc=0;c<NUMPIXELSC;c++){
149
strip_C.setPixelColor(c,offC);
150
strip_C.show();
151
delay(rotDelay);
152
}
153
delay(rotDelayA);
154
}
155
voidloop(){
156
update_rpm();// sensor ISR will look for time period
157
if(last_rpm-rpm!=0){// update LCD only if there is a change of rpm
158
last_rpm=rpm;
159
// need to check the case that there is 0 rpm (full stop)
160
unsignedlongcurrentMillis=millis();
161
if(currentMillis-previousMillis>refreshInterval){// at a decent refresh rate
162
previousMillis=currentMillis;
163
}
164
}
165
166
}
167
168
ISR(INT0_vect)//Capture Hall via Interrupt
169
{
170
time=(micros()-time_last);
171
time_last=micros();
172
strips();
173
174
}
Selbstest auskommentiert, der Simulator ist mir zu langsam, vllt lass
ihn auch ganz weg.
"Läuft" simuliert auf einem ATTiny 85, passt Speichertechnisch ganz gut,
3900 Bytes belegt.
Weiss leider nicht mit welcher Mhz der Simulator für den MC nimmt, die
Beschreibung sagt 8 Mhz, also läuft der Code mit 1 Mhz richtig?
So wie es jetzt ist spucken die LEDs ab und zu andere Farben aus die
nicht programmiert sind.
Hab leider noch keine Möglichkeit es selbst zusammenzulöten, mir fehlen
noch viele Komponenten, unter anderem der MC und die LEDs ༼ つ ◕_◕ ༽つ
Uh, also ich habs nur überflogen und als ich die ISR sah...also
Funktionen in einer ISR würde ich zu vermeiden versuchen und du ruftst
direkt mehrere bzw. mehrmals Funktionen in ner ISR auf.
Du könntest z.B. einen Timer parallel mitlaufen lassen und immer in der
INT0-ISR auswerten/zurücksetzen. Damit kannst du den Abstand von Int0-
zu Int0-Interrupt messen und von da aus auf die Drehzahl schließen.
Eine andere Idee wäre es, einen Input-Capture-Interrupt (Timer) hierfür
zu verwenden. Der erste Capture-Interrupt startet den Timer, der zweite
stoppt ihn. Auch hier kann man dann auf die Drehzahl schließen.
Die Abarbeitung deiner Funktion strips(); würde ich entweder komplett
neu überdenken oder aber Flag-gesteuert in den Mainloop legen.
Ich hatte strips() erst garnicht, das stand direkt so im main loop, aber
dann läufts ja einfach los.
Mit flags mach ich zb einfach eine bool variable die ich über den
Interrupt auf high setze und im main loop solange warte bis die auf high
ist?
Das Zählen funktioniert ja so ganz gut, und bei den Variablen muss ich
mich auchnicht ums zurücksetzen kümmern. Der interne Timer ist ja quasi
schon drinne mit micros() oder?
Hatte schon Mühe den Interrupt zu programmieren, viel copy paste und
lesen :D
Wie gesagt bin ein Vollnoob, bissl Basic (C64, ewig her), bissl Pascal
und nie was in java oder python, html zählt ja nich unbedingt :P
Die LEDs werden ja jetzt billiger, quasi Auslaufmodell, die neuen
heissen ATA 101 soweit ich gelesen habe, 4 strippig, die in der Lage
sind kaputte LEDs in der Mitte zu überspringen^^
Dann les ich mich mal in states und Timer ein. Grösste Hindernis ist und
bleibt wohl der Simulator :D
Fam B. schrieb:> Mit flags mach ich zb einfach eine bool variable die ich über den> Interrupt auf high setze und im main loop solange warte bis die auf high> ist?
Richtig, und wenn die Funktion in der Main-Loop abgearbeitet ist setzt
du das Flag wieder auf Low. Könnte z.B. so aussehen:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
// Flag "konfigurieren"
5
volatileuint8_tmyFlag=0;
6
7
intmain(void){
8
// Ausgang einstellen
9
DDRB|=(1<<PB0);
10
// Timer0 einstellen
11
TIMSK0|=(1<<TOIE0);
12
// und starten
13
TCCR0B|=(1<<CS00);
14
// Globale Interrupts aktivieren
15
sei();
16
// main-loop, endlos-schleife
17
for(;;){
18
if(myFlag==1){
19
PORTB^=(1<<PB0);
20
myFlag=0;
21
}
22
}
23
// wird nie erreicht
24
return0;
25
}
26
//Overflow-Interrupt-Vektor für Timer0
27
ISR(TIMER0_OVF_vect){
28
myFlag=1;
29
}
Kurzbeispiel für ne Flagnutzung. Timer0 läuft mit CPU-Takt,
Overflow-Interrupt für Timer0 eingeschaltet, in dessen ISR wird
lediglich das Flag gesetzt. Ist das Flag gesetzt wird der Ausgang PB0
getoggelt. Klar, hier könnte man auch im ISR den Ausgang toggeln, das
Beispiel soll aber nur das Prinzip von Flags zeigen. Man könnte hier das
Flag auch so ändern, dass nur bei jeden 3 Overflow der Ausgang getoggelt
wird, kannst du dir ja mal überlegen wie man dann den Code ändern
müsste. ;)
Fam B. schrieb:> Hatte schon Mühe den Interrupt zu programmieren, viel copy paste und> lesen :D
Das ist nicht unüblich. Es fällt einem halt nix in den Schoß und Übung
macht den Meister.
Dieses Flag gibt es schon in Hardware, das muß man nicht vie ISR
simulieren.
TIFR0 Bit TOV0 zeigt den Zählerüberlauf an, ein Schreiben von "1" in
dieses Bit löscht es wieder. Latenz:0.
Carl D. schrieb:> TIFR0 Bit TOV0 zeigt den Zählerüberlauf an, ein Schreiben von "1" in> dieses Bit löscht es wieder. Latenz:0.
Nana...
1
if(TIFR0&TOV0)
2
{
3
4
/* User Code */
5
6
TIFR0&=~(1<<TOV0);
7
}
Das ist nun nicht "0 Latenz" :-D Aber du hast Recht, schneller geht es
nicht und ist selbstverständlich auch nicht blockend. Sollte man die ISR
aber dennoch benötigen, wegen anderen Dingen, muss man halt damit aber
auch aufpassen. Da kann man sich schnell ins Bein schießen damit.
DraconiX schrieb:> Carl D. schrieb:>> TIFR0 Bit TOV0 zeigt den Zählerüberlauf an, ein Schreiben von "1" in>> dieses Bit löscht es wieder. Latenz:0.>> Nana...>>
1
>if(TIFR0&TOV0)
2
>{
3
>/* User Code */
4
>TIFR0&=~(1<<TOV0);
5
>}
Nein, sondern:
1
if(TIFR0&TOV0)
2
{
3
/* User Code */
4
TIFR0=1<<TOV0;
5
}
Außerdem sehe ich nicht wo das IRQ-Latenz erzeugen sollte.
Hallo,
hier ist noch eine kleine und wichtige Korrektur:
Beitrag #5061482:
> if (TIFR0 & (1<<TOV0))> {> /* User Code */> TIFR0 = (1<<TOV0);> }
Ich mag die Klammern, in der ersten Zeile zeigen sie, was noch fehlt und
in der vorletzten, was zusammen evaluiert wird.
Nachtrag:
Die Arduino-Umgebung nutzt den Timer0 schon selber, d.h. versucht es,
denn wenn dieser bei Setup() umkonfiguriert wird und man selbst eine ISR
schreibt (die der Linker zuerst findet und dann benutzt), dann werden
manche Dinge nicht mehr funktionieren. Z.B. millis(), micros() und
delay().
Der Timer1 wird (ohne Zusatzt-Libs) wohl nur für Servos benutzt, ist
also einfacher "frei" zu halten.
Wobei man das Eingangsproblem per Timer1 gut lösen kann. PinChangeInt
auf irgend einen Eingangspin (oder für höhere Genauigkeit InputCapture),
Timer0 auf 0, OCR1x auf den (zuvor ermittelten Timerwert * gewünschter
Winkel) /360, Klammern wegen Rundung und in der OC1x-ISR die LEDs
feuern.
Wie kann ich denn PB0 der mein EIngang ist in einen Ausgang verwandeln?
Wo soll ich dann den Hall Sensor anschliessen :P
Das ist alles over the top für dieses Projekt. Der Interrupt wird
HÖCHSTENS 10 mal pro Sekunde angesteuert, ich glaub nich das ich da ein
Latenzproblem bekomme.
Im Normalfall läuft es auf 1-5 mal pro Sekunde.
Ich hab jetzt bisschen was geändert, die rpm Berechnung findet im Loop
statt.
Aber wo er vorher richtig gerechnet hat spuckt er jetzt wieder völlig
falsche Werte aus, und zwar ist rpm wieder komplett am integer Anschlag.
Wenn ich die Zeitübergabe im ISR mache sind die RPM immernoch zu hoch
anstatt.
Hier meine RPM Berechnung
1
voidupdate_rpm(){
2
3
rpm=60*(1000/(time*1));
4
rotDelay=360000/(rpm*20);
5
rotDelayA=((rotDelay/3)/3);
6
}
Hier der Teil im Loop
1
voidloop(){
2
T=(millis()-time_last);
3
time_last=millis();
4
update_rpm();
5
if(last_rpm-rpm!=0){
6
last_rpm=rpm;
7
}
8
// Hier folgen dann die strips mit if (strips == 1)
Hier der ISR
1
ISR(INT0_vect){
2
time=T;
3
strips=1;
4
}
Das ganze Programm wieder als Anhang.
Zwischendurch hat alles wunderbar funktioniert, die Pausen zwischen den
LEDs usw.
Auch die RPM waren korrekt auf 60, doch jetzt läuft alles auf Anschlag
und anstatt 1000 ms für eine Umrundung läuft es konstant auf ca 150ms
(schwer zu sehen) und bleibt konstant und ändert sich bei
Frequenzänderung nichtmehr.
Gut das ich meinen vorherigen Code oben noch habe. Jetzt funktioniert es
wieder.
Und "strips" Setze ich gleich im Beginn der if Schleife wieder auf 0,
hatte es erst am Ende aber das hat auch Probleme verursacht.
In der Simulation hängt sich das Programm nach einer Minute bei 1 Hz
auf, bzw die Delays werden auf Schlag ein paar Sekunden lang.
Muss ich noch genau kucken. Vllt füge ich eine Schleife ein die alle 50
Interrupts alle relevanten Variablen und den Interrupt Counter wieder
auf 0 setzt. Ein Reset quasi.
Carl D. schrieb:> Dieses Flag gibt es schon in Hardware, das muß man nicht vie ISR> simulieren.> TIFR0 Bit TOV0 zeigt den Zählerüberlauf an, ein Schreiben von "1" in> dieses Bit löscht es wieder. Latenz:0.
Das weiß ich aber vielleicht liest du meinen Post noch mal genau nach.
Fam B. schrieb:> Muss ich noch genau kucken. Vllt füge ich eine Schleife ein die alle 50> Interrupts alle relevanten Variablen und den Interrupt Counter wieder> auf 0 setzt. Ein Reset quasi.
Suche lieber den Fehler statt ihn so zu umgehen ;)
Fam B. schrieb:> In der Simulation hängt sich das Programm nach einer Minute bei 1 Hz> auf, bzw die Delays werden auf Schlag ein paar Sekunden lang.
Läuft da vielleicht was über? Irgendeine Variable, die nach ca. 1 Minute
über alle Grenzen wächst (z.B. ein uint16_t, der über 2^16 groß wird).
Verändere mal den Clock und schau mal ob sich diese Minute im gleichen
Verhältnis ändert.
Hi
Delays sind im Simulator ein ganz blödes Thema.
Teilweise setze ich mir im Code eine Konstante, daß die Delays nur 'zum
Teil' abgewartet werden, damit das Simulieren schneller geht - muß man
dann natürlich für die HEX zum Brennen wieder draußen haben, was öfter
vergessen wird und dann im Target seltsame Funktionen beobachtet werden
können ;)
MfG
Es gibt wohl soviele Arduino Emulatoren/Simulatoren, aber die die ich
bis jetzt gefunden habe haben entweder keine Neopixel/LEDs mit Treibern,
sind webseitenbasiert und man kann nur zur Verfügung gestellte Bauteile
benutzen und keine libraries importieren.
Der java basierte Simulator den ich benutze läuft halt nicht auf
konstantem Speed, also kuck ich mit einem Auge oben auf den Timer und
mit dem anderen Auge auf die LEDs.
Hier ist mein "Spinnennetz". Man kann das Timing recht gut erkennen,
auch wenn der Simulator stottert. Natürlich funktioniert das nur bei
niedrigen Frequenzen.
Wenn die Frequenz länger gleich bleibt läuft es etwas ruckelfreier.
https://youtu.be/LI4IeZW22iA
Im Anhang der aktuelle/finale Code