Forum: Mikrocontroller und Digitale Elektronik usb_serial - Interrupt - Problem


von Christian B. (chbalnuweit)


Angehängte Dateien:

Lesenswert?

Guten Abend,

Ich habe mich hier neu angemeldet weil ich ein Problem mit meinem 
AT90USB162 habe, mit dem ich alleine nicht weiterkomme.

Ich nutze die usb_serial -Bibliothek von www.prjc.com und komme auch 
ganz gut damit zurecht.

Nun möchte ich über ein Terminalprogramm eine Frequenz eingeben, mit dem 
µC einlesen und eine LED in der Frequenz blinken lassen. Das ganze über 
einen Timer Overflow Interrupt.

Ich habe den 16bit Timer so konfigueriert, dass jede ms der Interrupt 
"TIMER1_OVF_vect" aufgerufen wird und der Softwarecounter inkrementiert 
wird.

Vielleicht ist es an dieser Stelle sinnvoller wenn ich den Code poste:
1
#include <avr/io.h>
2
#include <math.h>
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <util/delay.h>
6
#include <string.h>
7
#include <avr/pgmspace.h>
8
#include "usb_serial.h"
9
#include <avr/interrupt.h>
10
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
11
12
//=======================================================================================
13
// Globale Variablen
14
//=======================================================================================
15
volatile uint16_t freq = 0;
16
17
uint16_t counter_1 = 0;                // Software-Counter initialisieren  
18
19
//=======================================================================================
20
// Prototypen
21
//=======================================================================================
22
void timer_init();
23
24
//=======================================================================================
25
// MAIN
26
//=======================================================================================
27
int main(void)
28
{
29
  char data[2];
30
  DDRB = (1<<PB6);
31
  CPU_PRESCALE(0);
32
  usb_init();
33
  timer_init();
34
  while(1)
35
  {  
36
    
37
    while (!usb_serial_available()) {;}      // Warte auf Daten im Puffer
38
    usb_serial_readline(data,2);             // Lese Daten aus Puffer in Char-Array
39
    usb_serial_write(data,2);
40
    freq = atoi(data);                 // Wandle Wert aus data in int um
41
    usb_serial_flush_input();          // Leere Puffer
42
    sei();
43
   }
44
}
45
46
47
48
49
//=======================================================================================
50
// Timer einstellen
51
//=======================================================================================
52
void timer_init()
53
{
54
TCCR1B =  (1<<CS10);                // Prescale 1 (kein)
55
TIMSK1 |= (1<<TOIE1);                 // Overflow Interupt erlauben
56
TCNT1 = 49536;                    // Timer vorladen --> 1 Interupt/ms
57
sei();                        // Interrupt aktivieren
58
}
59
60
//=======================================================================================
61
// Interrupt definieren
62
//=======================================================================================
63
ISR (TIMER1_OVF_vect)
64
{
65
counter_1++;                                // 1ms hochzählen
66
  if (counter_1 == round(1000/(2*freq)) && speed_freq != 0)        // LED mit eingegebener Frequenz togglen
67
  {  
68
    PORTB ^= (1<<PB6);                           // Toggle LED
69
    counter_1 = 0;
70
  }
71
TCNT1 = 49536;                                // Timer vorladen --> 1 Interupt/ms
72
}

Wenn ich eine Frequenz fest im Programm vorgebe, dann funktioniert das 
super. Nutze ich allerdings die Funktion usb_serial_readline, dann 
dauert es ca 1 min bis die LED anfängt mit der eingegebenen Frequenz zu 
blinken.

Ich vermute das es etwas mit usb_serial_getchar und den deaktivierten 
Interrupts zu tun hat, komme da jedoch nicht weiter.

Meine aktuelle usb_serial.c befindet sich im Anhang!

Ich hoffe ihr könnt mir weiterhelfen!

Viele Grüße

Christian

von Guest (Gast)


Lesenswert?

>Ich nutze die usb_serial -Bibliothek von www.prjc.com und komme auch
>ganz gut damit zurecht.

Wenn Du zurecht kommst, wo ist dann das Problem?

Über Deinen virtuellen COM-Post schickst Du vom PC an den uC Daten - 
z.B. 64 Bytes. Du sendest jetzt in Byte 1 einen Identifier für Deine 
Frequenz und liest dieses eine Byte in der Firmware aus.

Es ist also absolut unötig, den USB-Treiber irgendwie auch nur im 
Geringsten zu ändern!

von Christian B. (chbalnuweit)


Lesenswert?

Das Problem ist das Zusammenspiel aus usb_serial und meinem Interrupt. 
Das Empfangen und Senden von Zahlen funktioniert ja, da mir der µC die 
eingegebenen Werte wieder ins Terminal zurückschreibt.

Ich weiß jetzt allerdings nicht wo ich den USB-Treiber geändert haben 
soll?!
Meinst du die usb_serial_readline-Funktion?

Gruß

Christian

von Stefan E. (sternst)


Lesenswert?

1
freq = atoi(data);
Da der Inhalt von data nicht null-terminiert ist, funktioniert das nur 
mit Glück.

1
  if (counter_1 == round(1000/(2*freq)) && speed_freq != 0)
1) Was ist denn das Ergebnis von "1000/(2*freq)", wenn freq 0 ist?
2) Was ist "speed_freq"?
3) "1000/(2*freq)" ist eine Integer-Operation mit einem 
Integer-Ergebnis. Da gibt es rein gar nichts für round() zu tun.

von Christian B. (chbalnuweit)


Lesenswert?

oh, speed_freq = freq. Da ist mir beim Anpassen ein Fehler unterlaufen. 
Damit klärt sich auch was passiert, wenn freq=0.

Mit dem round-Befehl war ich mir auch nicht sicher. Beim Integer werden 
meines Wissens nach die Nachkommastellen einfach abgeschnitten. So 
ergibt 2.9  = 2 und round(2.9) = 3 oder nicht?

von Stefan E. (sternst)


Lesenswert?

Christian B. schrieb:
> oh, speed_freq = freq. Da ist mir beim Anpassen ein Fehler unterlaufen.
> Damit klärt sich auch was passiert, wenn freq=0.

Es wäre allerdings deutlich sinnvoller, vor der Rechnung auf 0 zu 
testen.

Christian B. schrieb:
> Mit dem round-Befehl war ich mir auch nicht sicher. Beim Integer werden
> meines Wissens nach die Nachkommastellen einfach abgeschnitten. So
> ergibt 2.9  = 2 und round(2.9) = 3 oder nicht?

Ja, aber du hast ja nie 2.9.
Wie gesagt,"1000/(2*freq)" ist eine Integer-Operation mit einem 
Integer-Ergebnis. Nirgendwo tauchen da jemals Nachkommastellen auf.

Und noch was:

Christian B. schrieb:
> Nutze ich allerdings die Funktion usb_serial_readline, dann
> dauert es ca 1 min bis die LED anfängt mit der eingegebenen Frequenz zu
> blinken.

Das liegt an dem "==". Was ist denn, wenn counter_1 gerade 100 ist und 
freq wird 20? Dann muss counter_1 erst einmal "ganz rum", bevor es 
wieder bei 20 ankommt. Und "ganz rum" dauert im Maximalfall ca. 65 
Sekunden.

von Christian B. (chbalnuweit)


Lesenswert?

Ja das klingt in der Tat plausibel! Das werde ich morgen mal testen! Es 
wäre wohl sinnvoll den counter nach dem Einlesen der Frequenz auf 0 zu 
setzen, richtig?

Zum Thema "Terminierter String": Es müsste funktionieren wenn ich den 
Char-Array erweitere und z.B. schreibe:
1
char data[3];
2
...
3
data[3] = '\0';
Richtig?

Danke schonmal für die Tipps und Gute Nacht!

Christian

von Christian (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Das liegt an dem "==". Was ist denn, wenn counter_1 gerade 100 ist und
>
> freq wird 20? Dann muss counter_1 erst einmal "ganz rum", bevor es
>
> wieder bei 20 ankommt. Und "ganz rum" dauert im Maximalfall ca. 65
>
> Sekunden.

Das war genau der richtige Tipp! Danke!
Das Problem umgehe ich jetzt auf zwei Wege:
Zum einen wird counter_1 im Main direkt nach dem Einlesen der Frequenz 
auf 0 gesetzt und zum anderen habe ich im Interrupt aus dem "==" ein 
">=" gemacht.

Vielen Dank für die Hilfe!

Schöne Grüße

Christian

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.