PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : mega168 - ICP vs. Port als Ausgang?!



0tes_Gesetz
30.03.2006, 14:29
Hi.

- ATmega168
- AVR-Studio 4.12 SP1
- AVR-GCC 3.4.5
- AVR-DUDE 1.72 mit GUI 0.2.0

Ich suche nach einer Möglichkeit, wie ich die oberen 4 Bit (7-4) eines Ports beschreiben kann, OHNE auf den unteren 4 Bit (3-0)/Pins Interrupts auszulösen..
Wozu?:.. Ich will das Input-Capture-Register auf einer LED-Zeile ausgeben (8 Stück).

Zuordnung der LED's zu den Pins/Ports: |PB7|PB6|PB5|PB4|PC3|PC2|PC1|PC0|

Weshalb diese Zerstückelung? Weil:...
- PB0 wird für den Input Capture Pin benötigt.
- Port C hat nur 7 Bit.
- Port D wird auch schon durch Interrupt belegt..

Die Auszugebende Variable ist folgendermassen definiert und wird durch Zuweisung aus dem ICR1 (Input-Capture-Register) gewonnen, bei einem solchen Interrupt:

volatile unsigned short int icp;
icp = ICR1;

Ports sind folgendermassen initiiert:
DDRB |= (1<<DDB7)|(1<<DDB6)|(1<<DDB5)|(1<<DDB4); /* PinB7-4 als AUSGANG */
DDRB |= (0<<DDB0); /* Eingang für ICP1 */
DDRC |= (1<<DDC3)|(1<<DDC2)|(1<<DDC1)|(1<<DDC0); /* PinC3-0 als AUSGANG */
DDRD |= (0<<DDD3)|(0<<DDD2); /* Eingänge für INT0 und INT1 */

Nun will ich die binäre Information der Variablen 'icp' auf den Pins 7-4 des Port B ausgeben und hab dies bisher folgendermassen getan:

PORTB = ~(icp);

Gibt es eine Möglichkeit, wie ich das Beschreiben der unteren 4 Bits von Port B bei dieser Zuweisung "schützen" kann, so dass dort garantiert keine Interrupts ausgelöst werden?
Ist dies schon mit Hilfe der Richtungsangabe im Data-Direction-Register erledigt?

Danke und Grüße
Nico

Bernhard.Erfurt
30.03.2006, 14:39
>Gibt es eine Möglichkeit, wie ich das Beschreiben der unteren 4 Bits >von Port B bei dieser Zuweisung "schützen" kann, so dass dort >garantiert keine Interrupts ausgelöst werden?

ja, ganz einfach,

1. den jeweiligen PORTx erstmal einlesen durch "in temp, PINx"
2. dann mit and bzw or die jeweiligen PINS herausfiltern und schützen
jeweils vom PORTx und von Deinem Wert
3. das ganze wieder zurückschreiben "out PORTx, temp"


PS: der zweite Schritt kann etwas knifflig sein, aber Dir fällt sicherlich was dazu ein ?

Bernhard

ogni42
30.03.2006, 14:43
Ja das geht:


uint8_t icp; // icp ist ein 8bit wert
uint8_t pb;

pb = PINB & 0x0f; // nur die unteren vier bit
icp &= 0xf0; // nur die oberen vier bit
PORTB = icp | pb;

0tes_Gesetz
30.03.2006, 15:41
Ok, Danke ihr Zwei. Nun weiß ich wenigstens, dass diese Sache nicht das Problem ist.

Ich werd mal nen neuen Thread aufmachen.. :(

Grüße
Nico

SprinterSB
30.03.2006, 16:02
Du musst erst das icp auseinanderfriemeln:


#include <avr/interrupt.h>
...
uint8_t sreg = SREG;
cli();
uint16_t icp1 = ICP1;
uint8_t portc = PORTC & 0xf0;
uint8_t portb = PORTB & 0x0f;
PORTB = portb | (icp1 & 0xf0); // bits 4..7
PORTC = portc | (icp1 & 0xf); // bits 0..3
SREG = sreg;

Offenbar verwendest du Interrupts. Das SREG mit cli() brauchst du, falls du PORTC oder PORTB in einer ISR änderst, damit die Änderungen nicht durch die Sequenz überschrieben werden.

ogni42
30.03.2006, 18:17
Es muss IMHO aber auf der rechte Seite PINC heissen.

0tes_Gesetz
31.03.2006, 02:17
Ist ok.. die Tipps waren ok und es funktioniert auch damit..

Das Problem lag aber woanders. Ich will einen Impuls messen (2µs, 4µs, 8µs...). INT0 nimmt die steigende Flanke und resettet Timer1 (16Bit) mit einem nackten Assembler Interrupt (11cycles lang). Die fallende Flanke wird dann vom ICP über das ICR1 Register des Timer1 aufgenommen.
Laut Datenblat des ATmega168 kein Thema und im Bereich des Möglichen.

Mein Problem war nun, das dieser Impuls im Bereich von 2µs noch erfassbar sein sollte.. deswegen wollte ich mir (was jetzt funktioniert - auch ohne die oben angegebene Interrupt-sichere Beschreibung der Ausgabepins) den Wert von ICR1 binär ausgeben lassen..
Zu erwarten sind ungefähr Werte von 16, 32, 64.. (16cyclesx0,125µs/cycle sind grade 2µs) usw.. für die Impulsbreite, aber die kamen nicht..
Auf der LED Zeile war irgendwie nichts stimmig.. weswegen ich hier Probleme mit zufällig ausgelösten Interrupts vermutete, ansonsten sah ja alles gut aush.. naja..

..4h später - eher zufällig...

Gelegen hats an folgendem:
Der Clock des Prozessors muss für diese Messung natürlich auf 8MHz laufen - Standard ist 1MHz - Prescaler=8.. also muss man auch den Clock_IO Prescaler verstellen..
Ich dachte das hätte ich!..
(hätte eigentlich schon vorher stutzig werden müssen, da die kleinste mögliche Messung 32µs betrug!)

Nun ja:
CLKPR |= (1<<CLKPCE);
CLKPR = 0;
funktionierte offensichtlich nicht, wie ich feststellen musste.
Der CLK_IO blieb bei 1MHz. /den Schnipsel hatte ich glaube aus dem Forum... ;)

Verwendet hab ich nun:
CLKPR = (1<<CLKPCE)|(0<<CLKPS3)|(0<<CLKPS2)|(0<<CLKPS1)(0<<CLKPS0);
CLKPR = (0<<CLKPCE)|(0<<CLKPS3)|(0<<CLKPS2)|(0<<CLKPS1)(0<<CLKPS0);

Damit funktioniert alles wie ich es wollte...
2µs Impulse sind damit erfassbar.. INTO resettet Timer1 und ICP1 liest den Wert vom Timer1 bei fallender Flanke.

Ich denke mal, ich bräuchte nur bei der oberen Variante die erste Zeile durch 'CLKPR = 0x80;' ersetzen und es würde laufen.. vielleicht teste ich das morgen ;)
Die Vorschrift zum setzen des CLK_IO Prescalers sagt ja, zuerst CLKPCE auf 1 und alle anderen auf Null und dann danach gleich CLKPCE auf Null und die anderen entsprechend dem Prescale... der '|' wird wohl der Übeltäter sein, da durch seinen Einsatz die CLKPS-Bits nicht gesetzt werden (IMHO) ;)

Grüße und vielen Dank
Nico

PS: wenn der Code noch gewünscht wird, bitte bescheid sagen, dann poste ich den noch..

SprinterSB
31.03.2006, 08:28
Es muss IMHO aber auf der rechte Seite PINC heissen.
Wirklich?

Mit PORTX liest man die Werte aus PORTX zurück, wie man sie geschrieben hat. Wenn man PINx liest und es ist ein Eingang, würde man mit Lesen von PINX und Schreiben nach PORTX die Pullups verstellen, wenn sich das Signal ändert!

Bernhard.Erfurt
31.03.2006, 10:11
>...das dieser Impuls im Bereich von 2µs noch erfassbar sein sollte..

Ich kenn das Problem mit den kurzen Impulsen, deshalb schalte ich gern

ein einfaches FLIP-FLOP-GATTER vor den Eingang, somit ist die

Impulslänge total egal ;)

SprinterSB
31.03.2006, 10:49
Und wie unterscheidest du dann z.B. zwischen 2µs und 4µs-Pulsen?

Bernhard.Erfurt
31.03.2006, 11:42
>Und wie unterscheidest du dann z.B. zwischen 2µs und 4µs-Pulsen?

Das geht mit diesem Verfahren nicht,

es wird nur 100%ig erkannt, dass ein Impuls vorhanden war

ogni42
31.03.2006, 14:00
@Sprinter: Laut Datenblatt (Mega168) ist es genau umgekehrt:


12.2.1
If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated. To switch the pull-up resistor off, PORTxn has to be written logic zero or the pin has to be configured as an output pin.

...

12.2.4 Reading the pin value
Independent of the setting of the Data Direction bit DDxn, the port pin can be readt through the PINxn Register bit. ...
[/quote]

SprinterSB
31.03.2006, 14:38
Schon klar, aber man will die Pullups an den Ports doch nicht verändern. Also muss PORTX so geschrieben werden (zumindest für die nichtbetroffenen Bits) wie es war. Dazu liest man es einfach aus. PINX hingegen liest die Eingänge.

Beispiel: PORTx=1, also ein IN mit Pullup. Dieser Port soll so bleiben, also nicht zu den Ports gehören, an denen ICP1 angezeigt werden soll.

Nun lesen wir PINx, es sei 0.
Schreiben wir nun 0 nach PORTx, wird der Pullup deaktiviert!!!

Liest man stattdessen PORTx, liest man 1 und beim Schreiben ändert man der Wert des PORTx nicht (zumindest dieses Bits), was man hier haben will.

ogni42
31.03.2006, 14:56
Nein,
wenn Du PORTx = 0 schreibst, wird nur der Pullup abgeschaltet, wie es im Datenblatt auch steht. Um den Port von Eingang auf Ausgang zu schalten müsste das Datenrichtungsregister beschrieben werden.

IOPortX als Ausgang: DDRx = 0xff
IOPortX als Eingang ohne Pullups: DDRx = 0x00; PORTx = 0x00;
IOPortX als Eingang mit Pullups: DDRx = 0x00; PORTx = 0xff;

IOPortX als Ausgang schreiben: PORTx = value;
IOPortX als Ausgang lesen: value = PINx;
IOPortX als Eingang lesen: value = PINx;

Beim Lesen ist egal, ob der IOPort als Ausgang oder Eingang definiert ist. Es wird immer über das PINx Register gelesen.

SprinterSB
31.03.2006, 15:02
Üsch glaub, wir reden aneinander vorbei...

Was passiert denn deiner Meinung nach, wenn der PU (=PullUp) aktiv ist (PORT=1) und wir an PIN eine 0 lesen?

Was willst du dann nach PORT schreiben? 1 (wie es war) oder willst du ein einem Port, der nix mit der Ausgabe zu tun hat, an den PUs drehen?

An PIN liest man den Eingang, richtig. Aber der Wert am Eingang interessiert ja gar nicht! Was interessiert, ist wie die PUs einzustellen sind. Und das liest (und schreibt) man via PORT.

Oder man merkt sich ein einer Variablen, wie man die PUs einstehen hat (was aber mehr Overhead ist.)

ogni42
31.03.2006, 17:47
Das hatte ich auch schon vermutet (mit dem aneinander vorbei reden :) )

Wenn's nur um die PUs geht hast Du natürlich recht.

SprinterSB
02.04.2006, 13:41
Um die PUs geht's doch, oder? bzw darum, daß sich an den nichtbeteiligten Ports nix ändert. Dazu müssenb die PUs gleich bleiben (falls es IN sind) bzw die Port-Werte müssen gleich bleiben (diese liest man auch via PORT, falls es OUT sind).
:-)