Hallo!
Ich habe eine Schaltung mit einem Atmega. An PortA Pin0 soll ein
Eingangssignal liegen. Das Signal liegt dort an mit ca.15-500
Signalen/Sekunde.
Diese Signale will ich Zählen. Ich kann ja keinen Interrupt-Event auf
pina0 schreiben (kann ich doch? wie?) und muss darum vermutlich so etwas
wie eine while-Schleife benutzen.
int fSig()
{
int iTime1=0; //store last timer value here
int iTime2=0; //store time value at tick here
int iCS=0; //count signals
int iCE=0; //count no signal to leave while if no signal
int iSig=0; //store time between two signals here
DDRA = (0 << DDA0); //pin a0 = input
TCCR0 |= (1<<CS00)|(1<<CS02); //start timer w clock/1024
while(iCS<10) //try and collect 10 signals
{
if ( PINA & (1<<PINA0)) //if pina0 is high
{
iTime2=TCNT0; //get current timer value
if(iTime1>iTime2)iTime1=255-iTime1+iTime2; //passed overflow?
else iTime1=iTime2-iTime1; //calculate diff
if(iSig<1)iSig=iTime1; //if first signal set iSig to iTime1
iSig=(iSig+iTime1)/2; //get average
iTime1=iTime2; //set iTime1 to last ticker Timer value
iCS++;
}
else
{
iCE++;
if(iCE>20)
{
iCS++;
iCE=0;
}
}
}
return iSig;
}
Was ich wollte ist vermutlich erkennbar. Wie ich dahinkomme für mich
noch fraglich. Zusätzlich verpasse ich doch die Genauigkeit immer um ein
paar Takte oder?
Hauptproblem im Moment ist, dass das Ding die ganze Zeit Werte ausspuckt
obwohl an pina0 nichts angeschlossen ist. Zunächst zeigt es mir artig
eine Null an aber nach einer Weile geht es los mit unsinnigen Werten
zwischen 1 und ein paar hundert. Und zwar ohne
Wäre nett, wenn mir jemand sagen könnte, was ich falsch mache, was an
dem Ansatz doof ist und wieso und wie ichs besser machen kann.
Grüße!
Wie sieht das Signal aus?
Digital? Welche Amplitude? Du musst bestimmte Pegel auf einen Eingang
geben.
Zu erst müssen die Pins/der Pin als digitaler Eingang konfiguriert
werden.
Dann besitzt der yC eine Funktion(müsste IOC heißen) Interrupt on Change
d.h. dass bei einem Flankenwechsel am Pin ein Interrupt ausgeführt wird.
Im Interrupt inkrementierst du dann den Zähler.
Das wars schon!
So brauchst du keine While-Schleife(du fragst ja nicht immer an den
"richtigen Stellen" im Signal ab) Dazü müsste das Signal eine feste
Frequenz haben und mit dem Polling synchronisiert werden. Viel zu
Aufwändig. Also nen Interrupt on Pinchange. Siehe Datenblatt des yCs
unter Interrupt enable und Interrupt on Change.
ps: das mit den Pegeln sollte heißen, dass ein yC erst ab einer
bestimmten Eingangsspannung ein HI-Signal interpretiert. Deswegen die
Frage nach der Amplitude deines Signals. ggf. brauchst du ja noch einen
Pegelwandler oder halt nen Vorwiderstand als Spannungsteiler.
bin ich doof. unbeschaltet ist nicht = low, oder?
grr ;)
wenn ich das ganze auf masse lege siehts schon besser aus.
ich schau mir mal das datasheet an und hoffe, ich kann einen interrupt
auf jedem beliebigen pin auslösen lassen. es ist nämlich leider so, dass
ich die pins 1-20 schon komplett unter verwendung habe...
das signal ist pseudo-digital. ein analogsignal, welches ich auf den
richtigen pegel gebracht habe. wenn das ungenau wird, dann muss noch n
optokoppler dazwischen. das soll aber erst mal nicht die sorge sein
sondern die software-seite...
danke & grüße!
pitti wrote:
> Hallo!> Ich habe eine Schaltung mit einem Atmega. An PortA Pin0 soll ein> Eingangssignal liegen. Das Signal liegt dort an mit ca.15-500> Signalen/Sekunde.> Diese Signale will ich Zählen. Ich kann ja keinen Interrupt-Event auf> pina0 schreiben (kann ich doch? wie?)
Das hängt davon ab, was für einen ATMega Du hast. Die neueren Modelle
haben sogenannte Pin Change Interrupts (@Latissimo: Beim AVR heißen die
nicht "interrupt on change" oder so...), die älteren (ATMega8, 16, 32)
haben das noch nicht.
> und muss darum vermutlich so etwas> wie eine while-Schleife benutzen.
Generell musst Du Flanken (also Pegelwechsel) zählen, d.h. wenn Du
keinen Interrupt verwenden kannst, dann musst Du bei der Abfrage jeweils
den letzten Zustand speichern und mit dem aktuellen vergleichen.
Wenn Du die Frequenz des Signals bestimmen willst, solltest Du
vielleicht besser die Input Capture-Funktion eines Timers verwenden
(dann ermittelst Du den Abstand zweier Flanken).
Hmm...
Ich geh dann an T1 oder ICP1 oder kann man beides relaisieren?
Das Problem ist, dass ICP1 schon belegt ist. Und wenn ich ihn
freischaufle, dann gibts noch immer ein Problem: ich muss zwei Sensoren
auswerten. Der zweite hat zwar eine noch niedrigere Frequenz - das
befreit mich aber nicht von der Strukturproblematik.
Es handelt sich um einen Atmega32. Ist also essig mit Interrupt on
Pinchange?!
Wie geh ich das Problem denn jetzt an? Ick steh uffem Schlauch...
Grüße!
Also den Interrupt on Pinchange würde ich nicht aus den Augen verlieren.
Wenn du die möglichkeit hast(also wenn noch ein so konfigurierbarer Pin
frei ist) dann solltest du das auf jeden Fall in Betracht ziehen. Dann
bekommste bei jedem pos ODER negativen Flankenwechsel nen Interrupt.
In diesem zählst du einfach nen Zähler hoch.
Falls die Frequenz bestimmt werden soll, dann geb ich dem "Johannes M"
recht, dann ist es sinvoll, wenn man die Inp. Capture Fkt. nutzt.
Weitere Fragen?
So. Ein bisschen googlen später hat sich mein Konzept geändert.
Es gibt 2 Pins um Timer mit externer Quelle laufen zu lassen und einen
ICP.
Alle drei taugen zur Frequenzmessung, wenn ich nicht schief gewickelt
bin.
Ich hab mich für pin1 (b0) entschieden. Das ist T0. Gute Idee?
Jedenfalls: diese Funktion gibt Werte >0 aus, wenn kein Signal angelegt
ist.
Wieso frag ich mich. Was sagt ihr denn sonst so zu dem Ansatz? Besser?
Der Atmega ist mit 4mhz quarzoszi getaktet. Der TCCR1B kann doch so hoch
zählen, oder?!
int sig()
{
uint16_t Sig=0;
TCNT0 = 0; //reset timer
TCNT1 = 0; //reset timer
TCCR0 |= (1 <<CS02) | (1 <<CS01) | (1 <<CS00); //external clock
TCCR1B |= (1<< CS12) | (0<<CS11) | (1 << CS10); //cpu/1024
while(TCNT1<4096) //chill a second and count
{
}
TCCR0 &= ~(1 <<CS02) | ~(1 <<CS01) | ~(1 <<CS00); //stop timer0
TCCR1B &= ~(1 << CS10); //stop timer1
Sig = TCNT0; // counted signals in 1 second
return Sig;
}
Achso. Auf ICP hängt derzeit mein LCD. Kann ich natürlich ändern aber
dann fehlt mir ja immer noch ein Messpin. Darum über T0/T1.
Überhaupt wieder posten tu ich übrigens, weil die Funktion da oben mir
völligen Quatsch ausspuckt.
Hilfe!
Grüße!
> TCCR0 &= ~(1 <<CS02) | ~(1 <<CS01) | ~(1 <<CS00); //stop timer0
Das macht aber nicht das, was im Kommentar steht. Die Bitmasken müssen
erst verODERt werden und am Ende wird das Bitkomplement von allem
gebildet!
> TCCR1B &= ~(1 << CS10); //stop timer1
Und was ist mit CS12?
> TCCR1B |= (1<< CS12) | (0<<CS11) | (1 << CS10); //cpu/1024
^^^^^^^^^
Das ist auch sinnfrei. Entweder das "|" vor dem "=" weg, oder den
Quatsch einfach weglassen. Eine Null kannst Du schieben sooft Du willst,
sie bleibt eine 0 und kann sich in so einem Fall als schöne Fehlerquelle
betätigen. Wenn Du "der Übersicht wegen" alle Bits unbedingt
hinschreiben willst, dann eben ohne verODERung, weil ODER mit einer 0
keinerlei Änderung bringt...
Übrigens: Wie ist der dazugehörige Portpin initialisiert? Wenn der
keinen Pull-Up hat und offen ist, dann ist auch klar, dass da alles
Mögliche passieren kann.
pitti wrote:
> Der Atmega ist mit 4mhz quarzoszi getaktet.
Bei 4 Millihertz musst Du aber erheblich länger als eine Sekunde
warten, bis das TCNT1 4096 erreicht!
Bedenke auch, dass TCNT0 nur bis 255 zählen kann und dann überläuft!
Desahlb reicht als Rückgabewert auch ein uint8_t.
Da hab ich wohl was übersehen. Kann mir vielleicht irgendjemand sagen,
wie der oben stehende Code aussehen muss, damit es funktioniert? Die
Messdauer soll natürlich später runtergesetzt werden.
Grüße!
Groß und Kleinschreibung... ist aber klar, dass ich keinen 4 milliHz
Quarzoszi habe, oder? Wär mir zumindest neu, dass es so etwas überhaupt
gibt ;)
Hab den Pin als Eingang initialisiert. Jetzt mit Pull up. Nun bekomm ich
0 raus. ist ja in etwa das, was ich wollte ;)
Kann ich zur Simulation einfach in der while Schleife ab und an nen
anderen Pin kurz high und wieder low schalten und diesen an T0 hängen um
die Signale zu zählen oder zählt der Atmega sich nicht gern selbst?
Wüsste gern, obs bis hierher klappt...
{
DDRB &= ~(1<<DDB0); //b0 als eingang
PORTB |= (1<<PB0); //internen pull up aktivieren
uint16_t Sig=0;
TCNT0 = 0; //reset timer
TCNT1 = 0; //reset timer
TCCR0 |= (1 <<CS02) | (1 <<CS01) | (1 <<CS00); //external clock
TCCR1B |= (1<< CS12) | (0<<CS11) | (1 << CS10); //cpu/1024
while(TCNT1<(4096*3)) //chill and count
{
}
TCCR0 &= ~(1 <<CS02) | ~(1 <<CS01) | ~(1 <<CS00); //stop timer0
TCCR1B &= ~(1 << CS10) | ~(1 << CS11) | ~(1 << CS12); //stop
Sig = TCNT0; // counted signals
return Sig;
}
Von Hand mit nem Kabel auf dem PIN rumwackeln bringts nicht ;)
> TCCR0 &= ~(1 <<CS02) | ~(1 <<CS01) | ~(1 <<CS00); //stop timer0> TCCR1B &= ~(1 << CS10) | ~(1 << CS11) | ~(1 << CS12); //stop timer1
Aua! Oben war nur die erste Zeile falsch, jetzt beide! Ich habe doch
oben genau beschrieben, wie das mit dem Bit-Löschen geht... Erst
verODERn, dann Bitkomplement! Kann doch nicht so schwer sein.
ääh codezeile muss natürlich PORTA &= ~(1<<PA0); //a0 low heissen
nun machts was. das problem ist nur, dass die funktion nie wieder 0
liefert, nachdem sie einmal was gemessen hat..
grüße!
pitti wrote:
> ääh codezeile muss natürlich PORTA &= ~(1<<PA0); //a0 low heissen> nun machts was. das problem ist nur, dass die funktion nie wieder 0> liefert, nachdem sie einmal was gemessen hat..> grüße!
Siehe oben....
Ehrlich gesagt doch. Ich wälze mal noch ein bisschen Literatur. Hab die
Zeilen übrigens wie von Dir vorgeschlagen geändert. Ändert leider nichts
am Problem.
Grüße!
Ich weiss auch gar nicht, wieso das Ergebnis davon betroffen sein
sollte. Ich kann den Timer auch so anhalten, oder?
TCCR0 |= (0 <<CS02) | (0 <<CS01) | (0 <<CS00); //stop timer
TCCR1B |= (0 << CS10) | (0 << CS11) | (0 << CS12);
Aber wie gesagt. Ich kann ja TCNT0 auch vorher auslesen. Zu Beginn der
Funktion wird TCNT0=0 gesetzt und wenn dann kein Signal am Pin anliegt,
wie kommt dann der Wert zustande? Versteh ich nicht...
pitti wrote:
> Ich weiss auch gar nicht, wieso das Ergebnis davon betroffen sein> sollte. Ich kann den Timer auch so anhalten, oder?>> TCCR0 |= (0 <<CS02) | (0 <<CS01) | (0 <<CS00); //stop timer> TCCR1B |= (0 << CS10) | (0 << CS11) | (0 << CS12);
Das habe ich doch auch oben schon geschrieben: Wenn Du irgendetwas mit
einer 0 verODERst, dann ändert sich gar nichts ! Schau Dir mal die
Wahrheitstabellen der grundlegenden logischen Operatoren an.
Im AVR-GCC-Tutorial steht eigentlich auch alles, was man wissen
muss.
Nun. Was Du mit dem or meinst, leuchtet mir langsam ein. Ich habe aber
trotzdem auch dann noch das Problem, wenn ich die Zeilen entsprechende
Deines Vorschlages einfüge.
Ich finde im Tutorial sind nicht ausreichend Code-Beispiele. Man
versteht zwar, wie etwas funktioniert aber dadurch noch lange nicht, wie
man es anwendet. Ist zu theoretisch für blutige Anfänger.
Ich finds auch super, dass Du mir zur Selbsthilfe verhelfen willst. Es
wäre nur vielleicht sinnvoll, mir in diesem Fall die Lösung meines
Problemes zu stellen.
Wie auch immer. Danke soweit!
Dann zeig doch mal Deinen aktuellen Code (mit den entsprechenden
Korrekturen). Am besten auch vernünftig formatiert, damit es besser
lesbar ist. Schließlich bietet dieses Forum extra die Möglichkeit dazu.
> PORTA |= (0<<PA0); //a0 low
Dass das genauso sinnfrei ist, wie das oben angesprochene ist denk ich
mal klar...
pitti wrote:
> Ich finde im Tutorial sind nicht ausreichend Code-Beispiele. Man> versteht zwar, wie etwas funktioniert aber dadurch noch lange nicht, wie> man es anwendet. Ist zu theoretisch für blutige Anfänger.
Welchen Teil des Abschnitts verstehst du nicht?
1
Verändern von Registerinhalten
2
Einzelne Bits setzt und löscht man "Standard-C-konform" mittels logischer (Bit-) Operationen.
1
x|=(1<<Bitnummer);// Hiermit wird ein Bit in x gesetzt
2
x&=~(1<<Bitnummer);// Hiermit wird ein Bit in x geloescht
1
Es wird jeweils nur der Zustand des angegebenen Bits geändert, der vorherige Zustand der anderen Bits bleibt erhalten.
2
3
Beispiel:
1
#include<avr/io.h>
2
...
3
#define MEINBIT 2
4
...
5
PORTA|=(1<<MEINBIT);/* setzt Bit 2 an PortA auf 1 */
6
PORTA&=~(1<<MEINBIT);/* loescht Bit 2 an PortA */
1
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.
2
3
Beispiel:
1
#include<avr/io.h>
2
...
3
DDRA&=~((1<<PA0)|(1<<PA3));/* PA0 und PA3 als Eingaenge */
4
PORTA|=(1<<PA0)|(1<<PA3);/* Interne Pull-Up fuer beide einschalten */
1
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden
2
einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.
Sieht erst mal nicht so schlecht aus.
> dass die funktion nie wieder 0> liefert, nachdem sie einmal was gemessen hat..
Woher weißt du das? Oder anders ausgedrückt, was machst du mit
dem Ergebnis?
Wenn ich resette Hab ich S:0 auf dem Display. Lege ich nun die Pins
aufeinander, dann bekomme ich einen Wert >700.
Dieser Wert verringert sich um 700, wenn ich die Verbindung wegnehme und
erhöht sich auch wieder um 700, wenn die Verbindung wieder da ist.
Dabei schwankt der Wert um ca. 70 (mal 012<->712, mal 032<->732,...).
Das passiert aber sicher beim Anlegen des Kabels, oder?
Grüße!
Hab noch was witziges festgestellt:
Wenn ich das Ding beschaltet in Betrieb nehme, dann zeigt er mir 7 an.
Nehm ich das Kabel dann weg, 0.
Das spiel kann ich ein paar mal wiederholen dann geht er wieder nicht
mehr auf 0 und macht die 70er Sprünge. Was er aber auch kann, sind 700er
Sprünge. Insgesamt fällt auf, dass es sich um 7*10^0, 7*10^1, 7*10^2
handelt.
Der Messwert von 7 als Faktor bleibt konstant. Lediglich die Potenz
ändert sich. Das finde ich merkwürdig.
Beim schreiben kam mir die Idee. Der Messwert ist 7 bzw 0.
Beim Anlegen des Signals wird er aber schon mal höher. Ich schreibe dann
zB. S:102 aufs Display. Dann wird der Messwert konstant 7 und ich
schreibe S:7 darüber. Nun steht dort also S:702. Wird der Messwert nun
0, dann steht immer noch 002 da. Ist also ein Fehler bei der Ausgabe.
Ich werde den Wert nun also noch bearbeiten und mit leading spaces
versehen sodass immer die maximal benötigten fünf Stellen geschrieben
werden.
Dummer Fehler. Typisch Anfänger. Vielen Dank an Alle, die mir geholfen
haben!!
Grüße!
pitti wrote:
> Dabei schwankt der Wert um ca. 70 (mal 012<->712, mal 032<->732,...).
Was hier passiert ist etwas völlig anderes.
Auf deinem Display kann nicht 012 stehen, weil itoa keine
führenden Nullen einbaut.
Was hier passiert ist Folgendes:
Dein Programm schreibt 712 hin.
Der nächste Messwert der geliefert wird sei meinetwegen 0.
Den schreibst du über die 712 drüber. Aber da 0 weniger Stellen
braucht als 712, überschreibst du nur die 7. Die 12 bleiben
stehen. Daher zeigt deine Anzeige 012.
Mach mal folgendes:
iSig=fSig();
itoa(iSig,sSig,10);
lcd_gotoxy(9, 0);
lcd_puts("S:");
lcd_puts(sSig);
lcd_puts( " " );
also einfach ein paar Leerzeichen zusätzlich ausgeben.
Klar, hatte ich ja oben schon geschrieben. Was ich jetzt gemacht habe
ist, strlen und dann ein while, welches spaces vor die Zahl packt, bis
sie 5 Zeichen lang ist.
Funktioniert soweit. Nur, dass sie bei 0 folgendes ausgibt: " 0 0" -
bekomm ich aber sicher noch in den griff.
Danke!