mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ICP auf PB0 bei Tiny13


Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ich möchte bei meinem Tiny13 auf PB0 auf fallende Flanke triggern und 
die Zeit messen, bis dieses Ereignis eintritt.

Mein Code:
#include <avr/interrupt.h>
#include <avr/io.h>

int main(void)
{
  TIMSK0 = 1 << 0CIE0B;
  TCCR0B = ((1 << ICNC1) | (1 << CS10));
  sei();
  while (1){ }
  return 0;
}

ISR(TIMER1_CAPT_vect)
{
  DDRB = (1 << PB3) | (1 << PB4);
  PORTB |= (1 << PB3);
  PORTB &=~(1 << PB3);
}

Was mache ich falsch?

Autor: dummy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
  TIMSK0 = 1 << 0CIE0B;

Die Syntaxhervorhebung sagt mir das rechts neben dem
<< eine Null steht und kein Ohh.

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann es sein, dass der Tiny13 kein ICP kann? Wenn dem so ist, wie kann 
ich mein Problem dann lösen?

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

also ich denke jetzt, dass der Tiny13 wohl kein ICP beherrscht. Daher 
würde ich gerne einen Timer möglichst schnell laufen lassen, um Zeit zu 
messen (der Tiny läuft auf 1MHz).

Hier ist mein Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

long runtime = 0;
int on = 0;

ISR (TIM0_OVF_vect)
{
  runtime++;
  if (runtime > 1024)
  {
    runtime = 0;
    if (on == 0)
    {
      PORTB &= ~(1 << PB3);
      on = 1;
    }
    else
    {
      PORTB |=  (1 << PB3);
      on = 0;
    }
  }
  TCNT0 = 255;
}

int main()
{
  TCCR0B = ( 1 << CS00 );
  TIMSK0 = ( 1 << TOIE0 );

  DDRB = (1 << PB3);
  PORTB &= ~(1 << PB3);

  sei();

  while (1) { }
}

Leider zeigt mir das Oszi, dass im Moment der PB3 nur mit etwa 65ms 
getogglet wird. Wenn ich diesen Wert durch 1024 Teile beudetet das, dass 
ich die Runtime nur auf etwa 60µs genau messen kann. Ich würde die 
Genauigkeit gerne steigern. Wie kann ich das machen?

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> also ich denke jetzt, dass der Tiny13 wohl kein ICP beherrscht.

Das ist korrekt, ein Blick ins Datenblatt hätte diese Vermutung 
bestätigt.

> Daher würde ich gerne einen Timer möglichst schnell laufen lassen,

Das geht mit Vorteiler 1:1. Da der Tiny13 nur einen 8-Bit-Timer (mit 
Zählumfang von 256) hat, ist schnell aber nicht immer gut, denn damit 
kann man dann längere Impulse nicht mehr so ohne weiteres messen.

>  um Zeit zu messen (der Tiny läuft auf 1MHz).

Das (1MHz) halte ich für ein Gerücht. Der Tiny13 läuft üblicherweise mit 
9,6MHz und Systemtaktvorteiler 1:8, was einen Takt von 1,2MHz ergibt. 
Der Systemvorteiler kann vom Programm verändert werden, wie das geht, 
steht im Datenblatt.

Wenn Du Zeiten mit dem Tiny13 messen willst, dann kläre estmal ab, 
welchen Bereich die Zeiten haben können. Daraus ermittels Du den 
benötigten Timer-Vorteiler und notfalls auch noch den benötigten 
Systemvorteiler. Nun kannst Du den externen Interrupt oder einen 
Pinchange-Interrupt benutzen, um die Flanke zu dedektieren. Der Timer 
läuft dabei frei durch. In der ISR des ext. Interrupts prüfst Du, ob es 
Impulsbeginn oder Impulsende ist. Bei Impulsbeginn merkst Du Dir den 
aktuellen Timerstand (Zeitstempel), bei Impulsende liesr Du den 
Timerstand ein und subtrahierst davon den gemerkten Stand des 
Impulsbeginns. Wenn Du dieses Grundprinzip ausbaust, kannst Du 
Impulsdauer, Impulspause und Periodendauer in (fast) einem Rutsch 
ermitteln. In ASM ist das kein Akt, C ist allerdings nicht mein Ding, da 
kann ich Dir kein Beispiel geben.

...

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde gerne auf 1µs genau messen. Dazu muss ich wohl auf 9,6MHz 
takten. Aber obwohl ich TCNT0 immer auf 255 setze bekomme ich nur alle 
10µs einen Interrupt?

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich würde gerne auf 1µs genau messen. Dazu muss ich wohl auf 9,6MHz
> takten.

Es interessiert nicht nur die Auflösung, sondern vor allem die der 
Wertebereich der Dauer der zu erwartenden Impulse. Die Impulszeit muss 
ja in den Zählbereich des Timers passen, und der ist nur 256, denn der 
Tiny13 hat nur einen 8-Bit-Timer. Mit einem AVR, der über einen 
16-Bit-Timer verfügt, geht ICP besser, einerseits wegen des größeren 
Wertebereiches von 65536, andererseits wegen der verfügbaren 
Hardware-ICP-Einheit.

> Aber obwohl ich TCNT0 immer auf 255 setze bekomme ich nur alle
> 10µs einen Interrupt?

Das verstehe ich jetzt nicht...

Wiso setzt Du TCNT0 auf 255? Wenn Du Impulsdauer messen willst, dann 
brauchst Du den Timer überhaupt nicht auf irgendeinen Wert zu setzen. Du 
brauchst den Timer doch nur im externen Interrupt zu lesen.

Den Timer lässt man frei durchlaufen, das hat den angenehmen 
Nebeneffekt, dass man nebenher noch einen (oder auch beide) 
Compare-Interrupts benutzen kann, um Zeiten zu erzeugen, z.B. für 
serielle Ausgabe.

Ich glaube fast, Du hast eine völlig falsche Vorstellung von der 
Funktionsweise des ICP (egal ob in Hardware oder Software) und 
versuchst, die Impulsdauer durch Pollen des Portpins und Zählen im 
Timer-Interrupt zu realisieren. Das ist für langsamere Impulse zwar auch 
ein praktikabler Weg, ist aber kein ICP.

Unter dem Aspekt, dass Du vielleicht im Timer-Interrupt pollen und 
zählen willst, bekommt Deine Aussage:

> Ich würde gerne auf 1µs genau messen. Dazu muss ich wohl auf 9,6MHz
> takten.

eine andere Bedeutung. Ein Interrupt-Intervall von 1ms bekommst Du mit 
9,6MHz Controllertakt nicht hin, da alleine der Aufruf des Interrupts 
und dessen Rücksprung etwa 10 Takte braucht. Dazu kommt noch 
SREG-Sicherung und die eigentliche Arbeit. Unter 20-30 Takte wirst Du 
(in ASM) nicht auskommen, in einer Hochsprache vermutlich mehr.

...

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

also ich dachte eher daran eine Variable "runtime" in der ISR hochzählen 
zu lassen und somit die Zeit zu messen. Aber du hast recht: Einfach den 
Timer Zählen lassen und den Timerstand auszulesen ist natürlich deutlich 
schneller -- wenn auch weniger genau. Ich probiere es... Ich möchte 
Zeitspannen im Bereich 10µs bis 250µs messen.

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> also ich dachte eher daran eine Variable "runtime" in der ISR hochzählen
> zu lassen und somit die Zeit zu messen.

Das ist auch eine akzeptable Methode, ist aber kein ICP. 8-(

Software-ICP mit externem Interrupt als Flankentrigger und einem 
Timer-Vorteiler von 1:8 (bei 9,6MHz Controllertakt) erlaubt einen 
Messbereich bis 213µs. Vielleicht kannst Du ja Deine Ansprüche 
korregieren.

Ergänzend kannst Du ja auch eine Compare-Einheit auf den ICP-Zeitstempel 
des Impulsbeginns setzen und mit dem Compare-Interrupt den Übertrag 
zählen. Dann macht es vielleicht auch Sinn, mit Timer-Vorteiler 1:1 zu 
arbeiten. Die ermittelten Zahlen sind dann allerdings nicht im 
1ms-Raster, das Ergebnis müsste also noch skaliert werden.

Besser fährst Du mit einem AVR, der mit 1MHz bzw. 8MHz intern läuft 
(z.B. Tiny2313). Der hat dann (meist) auch Hardware-ICP am 16-Bit-Timer. 
Mit 8MHz Controllertakt und Timer-Vorteiler 1:8 erhält man das Ergebnis 
im Raster von 1µs. Dann sind auch noch genug Pins vorhanden, das 
Ergebnis auf einem LCD oder einer mehrstelligen gemultiplexten 
Siebensegmentanzeige anzuzeigen.

...

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

danke für die vielen Tipps! :-)

Ich habe mich jetzt für die Methode mit dem Analog-Comparator-Interrupt 
(interne Referenzspannung) mit Timer entschieden. Da ich aber noch nie 
mit dem Analog-Comparator gearbeitet habe wollte ich nun erst einmal den 
Interrupt betrieb zu laufen bringen. Einstellen möchte ich: Interrupt 
bei fallender Flanke auf ADC1. Der Interrupt soll dann eine LED blitzen 
lassen. Leider funktioniert es nicht. Hier mein Code:
ISR (ANA_COMP_vect)
{
  cli();              // Interrupts aus
  PORTB |=  (1<<PB3); // LED ein
  _delay_ms(100);     // Leuchtzeit
  PORTB &= ~(1<<PB3); // LED aus
  sei();              // Interrupts ein
}

int main()
{
  DDRB   =  (1 << PB3);
  PORTB &= ~(1 << PB3);

  ADCSRA =  (0 << ADEN);
  ADCSRB =  (1 << ACME);
  ADMUX  =  (0 << MUX0) | (1 << MUX1);
  ACSR   =  (0 <<  ACD) | (1 << ACBG) | (1 << ACI) | (1 << ACIE) | (1 << ACIS1) | (1 << ACIS0);

  sei();
  while (1) { }
}

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kann zwar kein C, aber 100ms Delay in der ISR ist tödlich. Auch das 
CLI und SEI ist Humbug, das macht die Hardware des AVRs alleine.

...

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

leider habe ich das Problem noch nicht gelöst bekommen. :-(

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Ich habe mich jetzt für die Methode mit dem Analog-Comparator-Interrupt
>(interne Referenzspannung) mit Timer entschieden.

Wo ist der Timer? "Delay" zählt nicht!

In der ANA_COMP-ISR musst du je nach eingestellter Flankenrichtung den 
Timer starten oder stoppen (bei steigender Flanke starten, bei fallender 
stoppen).
Wenn der Timer gestoppt ist, liest man den Timer-Wert aus, speichert ihn 
in einer Varibalen und setzt ein Flag, damit das Hauptprogramm weiß, 
dass es eine neue Pulslänge gibt., die verarbeitet werden kann.

Die Flankenrichtung kann man anhand der ACIS-Bits im ACSR feststellen 
und ein (Abfrage und Änderung von ACIS0 sollte reichen).

Den Code mußt du dir jetzt aber selber ausdenken...

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Problem ist nicht das Timing... im ersten Schritt möchte ich 
eigentlich nur die den Interrupt auslösen und die LED aufblitzen lassen 
(bei fallender Flanke an PB0). Leider wird der Interrupt nicht ausgelöst 
oder zumindest die ISR nicht aufgerufen.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Ich habe mich jetzt für die Methode mit dem Analog-Comparator-Interrupt...

Und wo wird der initialisiert?

MfG Spess

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Und wo wird der initialisiert?

>ACSR   =  (0 <<  ACD) | (1 << ACBG) | (1 << ACI) | _(1 << ACIE)_ | (1 << ACIS1) | 
(1 << ACIS0);


Oder nicht?

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Entschuldige. Übersehen.

MfG Spess

Autor: Mr. X (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leider habe ich das Problem immer noch. Wo liegt der Fehler in meinem 
Code?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.