Hi Leute.
Ich möchte eine unkomplizierte Methode zum Überprüfen, wie lange ein
bestimmter Portpin auf low/high ist.
Meine Idee wäre eine for-Schleife, die eine Variable hinaufzählt. Da ich
aber nicht weiß wieviele Clock-Schläge die Schleife benötigt um einmal
durchzulaufen, stehe ich vor einem Problem.
1
for(i=0;PB0==0;i++);
Auch für das Problem hätte ich einen Lösungsvorschlag. Im Datenblatt des
ATmega32 stehen so ziemlich am Ende die Dauer aller Assembler-Befehle.
Da ich in Assembler nun gar nicht bewandert bin, wollte ich euch fragen
wieviele Clock-Schläge der obige Code pro Durchlauf braucht.
Ich hoffe ihr versteht mein Anliegen und könnt mir helfen =)
Mfg
Christoph
A. K. schrieb:
> Christoph A. schrieb:>>> for(i=0;PB0 == 0;i++);>> Da PB0 fest für den Wert 0 steht dauert diese Schleife ewig.
Ändert sich der Wert des Pins nicht wenn der Eingang sich ändert?
Sollte ich PORTB verwenden?
Ja ginge natürlich schon, aber da müsste ich mich in die Interrupts
einearbeiten. Ich suche hier eine unkomplizierte Lösung.. ^^
Christoph A. schrieb:
> A. K. schrieb:>> Christoph A. schrieb:>>>>> for(i=0;PB0 == 0;i++);>>>> Da PB0 fest für den Wert 0 steht dauert diese Schleife ewig.>> Ändert sich der Wert des Pins nicht wenn der Eingang sich ändert?
Doch, das tut er.
> Sollte ich PORTB verwenden?
PINB wäre sinnvoller.
> Ja ginge natürlich schon, aber da müsste ich mich in die Interrupts> einearbeiten. Ich suche hier eine unkomplizierte Lösung.. ^^
Du wirst dich wohl auch in 'Grundlagen AVR' einarbeiten müssen.
Schleifen sind unberechenbar, daher eigenen sie sich nicht zur
Zeitmessung.
Christoph A. schrieb:
> Ändert sich der Wert des Pins nicht wenn der Eingang sich ändert?
Tut er. Aber PB0 ist nur die Nummer des Pins, nicht dessen Zustand.
Folglich steht da
for(i=0;0 == 0;i++);
A. K. schrieb:
> Christoph A. schrieb:>>> Ändert sich der Wert des Pins nicht wenn der Eingang sich ändert?>> Tut er. Aber PB0 ist nur die Nummer des Pins, nicht dessen Zustand.> Folglich steht da> for(i=0;0 == 0;i++);
Sry, keine Ahnung was ich da für einen Blödsinn gedacht habe.
Hallo,
kenne mich mit Assembler zwar nicht aus, deshalb wirst du wohl selbst im
datenblatt nachsehen, wie lange so ein einzelner befehl dauert, aber
lange zeit abstände wirst du damit nicht messen können, bevor die
variable überläuft, würde mal bei 1Mhz takt auf eine knappe millisekunde
tippen.
while(PINB&0x01)i++;
44: 01 96 adiw r24, 0x01 ; 1
46: b0 99 sbic 0x16, 0 ; 22
48: fd cf rjmp .-6 ; 0x44 <__SREG__+0x5>
Danke schonmal für die Ideen!
Ich mache es folgendermaßen:
Ich schaue zuerst ob die Flanke nach unten geht ( ich möchte die Dauer
eines low-levels messen). Wird dieses Interrupt ausgelöst, so fängt der
Counter an zu zählen. Geht die Flanke nach oben wird wieder ein
INterrupt ausgelöst. Die Zeit des Counters wird danach abgelesen und
fertig.
Eine Frage hätte ich noch: Was genau ist das I/O Clock? Im Datenblatt
steht, dass ich diese Art von Clock benötige um diverse Interrupts
möglich zu machen.
Mfg
Christoph
Christoph A. schrieb:
> Ich schaue zuerst ob die Flanke nach unten geht ( ich möchte die Dauer> eines low-levels messen). Wird dieses Interrupt ausgelöst, so fängt der> Counter an zu zählen. Geht die Flanke nach oben wird wieder ein> INterrupt ausgelöst. Die Zeit des Counters wird danach abgelesen und> fertig.
das sagt dir ja aber nicht viel über die Zeit, da du ja nicht weißt,
wieviele Takte ein Schleifendurchlauf braucht. Man könnte sich
höchstens das assembly anschauen und nachzählen.
es kommt auch immer ganz darauf an wie genau man eine Messung
durchführen will.
man kann das auch ohne weiteres in einer Pollingschleife machen.
Um die Zeit von einem Polling-aufruf zum nächsten zu messen, nimmt man
einfach den AVR-Simulator, setzt sich einen Haltepunkt beim
Polling-aufruf und schaut wieviel Taktzyklen zwischen den
Polling-aufrufen liegen.
Deine Zeitliche Auflösung ist dann halt diese Zeit und messen kannst du
so lange, bis deine Zählvariable überläuft.
Compileroptminierung würde ich ausschalten, weil die manchmal auch gerne
schleifen optimiert in einer Weise, die nicht optimal ist :P
Vlad Tepesch schrieb:
> Christoph A. schrieb:>> Ich schaue zuerst ob die Flanke nach unten geht ( ich möchte die Dauer>> eines low-levels messen). Wird dieses Interrupt ausgelöst, so fängt der>> Counter an zu zählen. Geht die Flanke nach oben wird wieder ein>> INterrupt ausgelöst. Die Zeit des Counters wird danach abgelesen und>> fertig.>> das sagt dir ja aber nicht viel über die Zeit, da du ja nicht weißt,> wieviele Takte ein Schleifendurchlauf braucht. Man könnte sich> höchstens das assembly anschauen und nachzählen.
Bei dieser Methode gibt es keine Schleifen^^
Es werden nur Interrupts angewendet. Natürlich gehen für den Aufruf des
Interrupts auch ein paar Clock-Schläge drauf, aber so extrem genau muss
das Ergebnis nicht sein. Im 100 µs Bereich bei 8 Mhz reicht das locker
=)
> Eine Frage hätte ich noch: Was genau ist das I/O Clock? Im Datenblatt> steht, dass ich diese Art von Clock benötige um diverse Interrupts> möglich zu machen.
Der Takt, der für die I/O-Hardware da ist. In manchen Stromsparmodi ist
dieser ausgeschaltet.
> Compileroptminierung würde ich ausschalten, weil die manchmal auch gerne> schleifen optimiert in einer Weise, die nicht optimal ist :P
Würde ich nicht tun. Besser ist es, zu verstehen, warum diese
Optimierungen gemacht werden und - sofern man das wirklich mal brauchen
sollte - wie man sie umgeht.
jetzt habe ich glaube verstanden was du willst, das Zauberwort ist ICP,
Input Capture Pin. Der Zählerstand von einem freilaufenden Counter (per
internem Hardwaretakt) wird bei Flanken am ICP in einem Register
eingefroren. Das ist dann die genaueste Möglichkeit mit dem µC
Pulse/Pausenzeiten zu messen.
JojoS schrieb:
> jetzt habe ich glaube verstanden was du willst, das Zauberwort ist ICP,> Input Capture Pin. Der Zählerstand von einem freilaufenden Counter (per> internem Hardwaretakt) wird bei Flanken am ICP in einem Register> eingefroren. Das ist dann die genaueste Möglichkeit mit dem µC> Pulse/Pausenzeiten zu messen.
Exakt genau das habe ich gesucht!
Gibts das schon fertig wie du es beschrieben hast, oder muss man sich
das so wie ich es beschrieben habe selbst mit Interrupts
"zusammenbasteln"?
hängt von dem µC ab den du nutzt, welcher ist es denn? Die meisten
ATMegas haben das in der Hardware drin, ein Blick ins Datenblatt hilft.
Ist in der Timer Beschreibung drin.
JojoS schrieb:
> hängt von dem µC ab den du nutzt, welcher ist es denn? Die meisten> ATMegas haben das in der Hardware drin, ein Blick ins Datenblatt hilft.> Ist in der Timer Beschreibung drin.
Ich nutze den ATmega32.
Laut Datenblatt hat der sowas wie ICP.
Nja, da werde ich mich jetzt einarbeiten ;)
Falls es nicht auf Anhieb klappt, werde ich hier weiterfragen. Wäre nett
wenn ein paar diesen Thread sich abonnieren könnten.
Vielen Dank für eure Hilfe!
Mfg
Christoph
Hier bin ich wieder^^
Ich hätte da eine ganz kleine Frage, bezogen auf das Datenblatt des
ATmega32.
Dieser besitzt ja einen 16bit Counter, und dess Register müsste man laut
Datenblatt in Assembler geteilt auslesen. Bei "C" ist das anscheindend
nicht der Fall?
"Note that when using “C”, the compiler handles the 16-bit
access."
Ist das für jeden Compiler gleich bzw. funktioniert das auf meinem?
(AVR GCC)
Vielen Dank im Voraus!
Mfg
>"Note that when using “C”, the compiler handles the 16-bit
access."
Das heisst auf deutsch: Bei "C" kümmert sich der Compiler darum.
Dadurch ist es EINFACHER.
Klaus
Klaus schrieb:
>>"Note that when using “C”, the compiler handles the 16-bit> access.">> Das heisst auf deutsch: Bei "C" kümmert sich der Compiler darum.>> Dadurch ist es EINFACHER.>> Klaus
Ja ich kann auch Englisch ;)
Meine Frage war aber ob das jetzt bei jedem C-Compiler so ist?
Mfg
>Meine Frage war aber ob das jetzt bei jedem C-Compiler so ist?
schwer zu sagen, wer kennt schon 'jeden' Compiler? Aber die meisten
Compiler haben die Option ein Assemblerlisting zu generieren, das ist
meist sehr hilfreich und gibt hier eine eindeutige Antwort. Der gcc zum
Beispiel 'kennt' den AVR und macht es richtig.
hier ist mal ein Schnippsel um die Zeiten zwischen Flanken in einem
Array zu sammeln (ohne Gewähr, und verwendet noch die _BV() Makros die
jetzt nicht mehr in sind):
Hier mein erster Code mit Interrupts. Die folgenden Fehler bringe ich
nicht weg. Mein erster Verdacht war, dass der Interrupt-Vector falsch
ist, aber dieser stimmt laut Datenblatt...
Die Zeile ist mit einem Kommentar markiert, um euch die Suche zu
erleichtern.
../DS1820.c:102: error: expected identifier or '(' before numeric
constant
../DS1820.c:102: error: expected identifier or '(' before numeric
constant
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<avr/interrupt.h>
4
5
#define TRANS PB3 //Transistorsteuerung
6
#define BUS PB2 //Busleitung
7
#define PORTx PORTB //PORT
8
#define DDRx DDRB //I/O Richtungssteuerung
9
10
11
#ifndef F_CPU
12
#define F_CPU 8000000 //Taktfrequenz definieren
13
#endif
14
15
16
voidResetPulse();
17
voidEnableInterrupt();
18
voidDisableInterrupt();
19
voidConfigInterruptICP();
20
voidConfigInterruptExt();
21
22
volatilecharhelpvar=0;
23
//Ist die Periode abgeschlossen?
24
25
uint16_ttime;
26
//Zeit, in der das Signal low ist
27
28
29
voidmain()
30
{
31
DDRx=0x00;
32
DDRx=(1<<TRANS)|(1<<BUS);
33
// Anfangszustand
34
35
DDRD=0xFF;
36
PORTD=0xFF;
37
//Alle STK500 LED's bleiben aus
38
39
40
PORTx=0xFF;
41
//LED's
42
43
PORTx|=(1<<BUS);
44
//Bus wird anfangs nicht auf low gezogen
45
46
ConfigInterruptExt();
47
//Interrupt wird richtig konfiguriert
48
49
EnableInterrupt();
50
51
ResetPulse();
52
//Gibt einen Reset Pulse
53
54
while(1);
55
}
56
57
voidEnableInterrupt()
58
{
59
sei();
60
}
61
voidDisableInterrupt()
62
{
63
cli();
64
}
65
66
/*void ConfigInterruptICP()
67
{
68
TCCR1A = 0x00;
69
//Alles in diesem Register ist überflüssig =)
70
71
TCCR1B = (1<<ICNC1) | (1<<CS10);
72
//Reaktion auf negative Flanke
73
//Interner Clock Prescaler auf 1
74
}
75
*/
76
voidConfigInterruptExt()
77
{
78
MCUCSR&=~(1<<ISC2);
79
//Auf negative Flanke wird reagiert
80
GICR=(1<<INT2);
81
//Ext.Interrupt wird auf INT2 aktiviert
82
}
83
84
voidResetPulse()
85
{
86
inti;
87
88
PORTx&=~(1<<BUS);
89
//Bus auf low ziehen
90
91
for(i=0;i<50;i++)
92
_delay_us(10);
93
//Reset Impuls
94
95
PORTx|=(1<<BUS);
96
//Bus auf wieder high
97
98
DDRx&=~(1<<BUS);
99
//Buspin als Eingang
100
}
101
102
ISR(INT2)//Hier meldet der Compiler 2 Fehler im Code
>> was soll INT2 bedeuten?>> Der InterruptVector für den Externen Interrupt am Pin INT2. Stimmt der> nicht?
Wie der Compiler dir schon sagt, stimmt der nicht. In der Doku der
avr-libc steht, wie dieser Interrupt-Handler eigentlich heißen muß.
Rolf Magnus schrieb:
>>> was soll INT2 bedeuten?>>>> Der InterruptVector für den Externen Interrupt am Pin INT2. Stimmt der>> nicht?>> Wie der Compiler dir schon sagt, stimmt der nicht. In der Doku der> avr-libc steht, wie dieser Interrupt-Handler eigentlich heißen muß.
Dankesehr!
Der Handler heisst wirklich: "EXT_INT2"
Noch eine Anmerkung:
Des Delay ist soo ein Schwachsinn, dass macht echt was es will..
Ich habe mit meinem Oszilloskop nachgemessen, der Wert stimmt weit
nicht.
Mfg
Christoph
Christoph A. schrieb:
> Noch eine Anmerkung:> Des Delay ist soo ein Schwachsinn, dass macht echt was es will..> Ich habe mit meinem Oszilloskop nachgemessen, der Wert stimmt weit> nicht.
Hast du auch brav die Optimierung eingeschaltet, die richtige
Prozessorfrequenz eingetragen und die richtige Clock-Source ausgewählt??
Klaus schrieb:
> Christoph A. schrieb:>> Noch eine Anmerkung:>> Des Delay ist soo ein Schwachsinn, dass macht echt was es will..>> Ich habe mit meinem Oszilloskop nachgemessen, der Wert stimmt weit>> nicht.>> Hast du auch brav die Optimierung eingeschaltet, die richtige> Prozessorfrequenz eingetragen und die richtige Clock-Source ausgewählt??
Die Optimierung ist standartmäßig eingeschaltet oder?
Prozessorfrequenz wurde definiert (siehe Code oben)
Würde nicht die richtige Clock-Source ausgwählt, so würde der ATmega
garnicht laufen oder?^^
Ein weiterer Fehler in meinem Code:
Der Interrupt-Handler ist falsch. Er sollte heissen: "INT2_vect".
Nun funktioniert auch mein Programm hervvoragend =)
Und ich glaube der Grund warum dass Delay bei mir nicht funktioniert hat
ist folgender:
Ich habe F_CPU erst nach dem Einbinden der Delay-Bibliothek definiert.
Jetzt habe ich util/delay erst nachher eingebunden. Ich kann leider
nicht mehr überprüfen ob es funktioniert, man braucht ja auch seinen
Schlaf.
Christoph A. schrieb:
> Die Optimierung ist standartmäßig eingeschaltet oder?
Wenn man einen Fehler sucht, besser alle Fehlermöglichkeiten
ausschließen, als sich auf Standardwerte zu verlassen ;)
> Würde nicht die richtige Clock-Source ausgwählt, so würde der ATmega> garnicht laufen oder?^^
Kommt drauf an. Es gibt Fälle, wo der uC dann einfach zu langsam läuft
und es einem deshalb erstmal nicht auffällt: Wenn man nämlich einen
externen Oszillator oder Quarz von z.B. 16Mhz anschließt, aber den
internen 8MHz Oszillator auswählt.
> Ich habe F_CPU erst nach dem Einbinden der Delay-Bibliothek definiert.> Jetzt habe ich util/delay erst nachher eingebunden. Ich kann leider> nicht mehr überprüfen ob es funktioniert
Richtig, das ist auch ein möglicher Fehler. Allerdings sollte in dem
Fall auch eine Warnung kommen.
> man braucht ja auch seinen Schlaf.
Ich wünsche eine gute Nacht ;)
Dieser Tag hat schonmal gut angefangen =). Das Delay-Problem ist gelöst
(es war tatsächlich der von mir genannte Fehler schuld) und die
Flankenerkennung funktioniert wie sie soll.
Für alle die es noch nicht erraten haben: Es geht um den DS1820 ^^
Mein nächster Schritt ist die Messung der Zeit in der das Signal low
ist. Dafür werde ich den Input-Capture-Pin verwenden, der laut
Datenblatt einen Timer losrennen lässt, bis das Signal ein bestimmtes
Flankenverhalten aufweist.
Mfg
Christoph
Klaus schrieb:
>> Ich habe F_CPU erst nach dem Einbinden der Delay-Bibliothek definiert.>> Jetzt habe ich util/delay erst nachher eingebunden. Ich kann leider>> nicht mehr überprüfen ob es funktioniert>> Richtig, das ist auch ein möglicher Fehler. Allerdings sollte in dem> Fall auch eine Warnung kommen.>
Es ist auch eine Warnung gekommen, ich habe sie nur ignoriert und dachte
das wird schon passen^^.
> den Input-Capture-Pin verwenden, der laut Datenblatt einen Timer> losrennen lässt, bis das Signal ein bestimmtes Flankenverhalten> aufweist.
Nicht so ganz, zumindest kenne ich die Funktion so nicht. Den Timer
lässt du einfach frei rennen. Ein Input-Capture-Ereignis löst dann ein
Kopieren des aktuellen Timer-Werts in ein separates Register aus. Da
kannst du dann taktzyklengenau auslesen, wann das Ereingis passiert ist.
> Es ist auch eine Warnung gekommen, ich habe sie nur ignoriert und dachte> das wird schon passen^^.
Dann hast du jetzt eine wichtige Lektion zum Thema Warnungen gelernt,
hoffe ich. ;-)
Rolf Magnus schrieb:
>> den Input-Capture-Pin verwenden, der laut Datenblatt einen Timer>> losrennen lässt, bis das Signal ein bestimmtes Flankenverhalten>> aufweist.>> Nicht so ganz, zumindest kenne ich die Funktion so nicht. Den Timer> lässt du einfach frei rennen. Ein Input-Capture-Ereignis löst dann ein> Kopieren des aktuellen Timer-Werts in ein separates Register aus. Da> kannst du dann taktzyklengenau auslesen, wann das Ereingis passiert ist.
Reicht diese Funktion um den Counter und ICP zu konfigurieren?
Wie erreiche ich dass der Counter zu zählen beginnt oder habe ich dass
mit der Prescaler Einstellung schon gemacht?
1
voidConfigInterruptICP()
2
{
3
TCCR1A=0x00;
4
//Ganz normaler Modus
5
6
TCCR1B=(1<<ICNC1)|(1<<CS10)|(1<<ICES1);
7
//Reaktion auf positive Flanke
8
//Interner Clock Prescaler auf 1
9
}
>> Es ist auch eine Warnung gekommen, ich habe sie nur ignoriert und dachte>> das wird schon passen^^.>> Dann hast du jetzt eine wichtige Lektion zum Thema Warnungen gelernt,> hoffe ich. ;-)
Habe ich definitiv =)
Mfg
Christoph