Forum: Mikrocontroller und Digitale Elektronik Atmega Timer 1 PWM und ICP gleichzeitig


von Steffen H. (stef_fen)


Lesenswert?

Hallo Zusammen,

ich möchte mit dem Timer 1 ein PWM Signal erzeugen und gleichzeitig ein 
PPM Signal einer Fernsteuerung über den ICP auswerten. Ist das überhaupt 
möglich? Des Weiteren soll die Frequenz und die Pulsbreite einstellbar 
sein. Wie kann ich das machen? Einzeln funktionieren die 
Programmabschnitte. Wenn ich aber beide Teile benutzen möchte scheint es 
am ICR1 zu liegen. Da dieser von beiden genutzt wird.

1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include <avr/io.h>
4
#include <stdlib.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
#include "i2cmaster.h"
8
#include "LCD.h"
9
10
int flanke;
11
uint16_t flankeStart;
12
volatile uint16_t stick;
13
float deltaStick;
14
float rechenwert;
15
int sollwert;
16
17
ISR(TIMER1_CAPT_vect) {
18
  if (flanke==0) {
19
    flankeStart = ICR1;
20
    TCCR1B &= ~(1<<ICES1); // fallende Flanke zur Auswertung des ICP
21
    flanke = 1;
22
  }
23
  else {
24
    stick = ICR1 - flankeStart;
25
    flanke = 0;
26
    TCCR1B |= (1<<ICES1); // steigende Flanke zur Auswertung des ICP
27
  }
28
  TIFR = ( 1 << ICF1 );
29
}
30
31
int main (void) {
32
sei();          //erlaubt globale Interrupts
33
34
i2c_init();
35
i2c_start_wait(0x40 | 0);
36
lcd_init();
37
lcd_print("Test V1.0");
38
39
DDRD |= (1<<PD5);
40
TCCR1A = (1<<COM1A1) | (1<<WGM11);
41
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
42
ICR1 = 30000; // = (8MHz / Prescaler 8) / 20000 = 50Hz
43
OCR1A = 1000; // = (8MHz / Prescaler 8) * 1500 = 1,5ms
44
45
DDRD &= ~(1<<PD6);    //Impuls von Fernsteuerung ICP1 = Eingang
46
PORTD |= (1<<PD6);    //Pullup aktiviert
47
TCCR1B |= (1<<ICES1);  //steigende Flanke zur Auswertung des ICP
48
TCCR1B |= (1<<CS11);  //Quelle für Timer/Counter = CPU-Takt/8
49
TIMSK |= (1<<TICIE1);  //Capture Interrupt Enable
50
51
while (1) 
52
{
53
  DDRD &= ~(1<<PD7); //als Eingang -> PWM ein / LED blinkt
54
  sollwert = stick;
55
  lcd_clear();
56
  lcd_print_integer(sollwert);
57
  _delay_ms(1000);
58
}
59
60
return 0;
61
}

Vielen Dank. Gruß Steffen

von STK500-Besitzer (Gast)


Lesenswert?

Steffen Ha schrieb:
> Ist das überhaupt
> möglich?

Ja. Schon gemacht.

Steffen Ha schrieb:
1
 TIFR = ( 1 << ICF1 );

Finger weg vom TIFR! Darum kümmert sich der Controller ganz alleine.

Steffen Ha schrieb:
1
 TCCR1A = (1<<COM1A1) | (1<<WGM11);
2
 TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
3
 ICR1 = 30000; // = (8MHz / Prescaler 8) / 20000 = 50Hz
4
 OCR1A = 1000; // = (8MHz / Prescaler 8) * 1500 = 1,5ms

Wenn man einen Timer-Modus verwendet, der als TOP-Wert das ICR 
verwendet, dann kann man natürlich kein Input Capture mehr betreiben.
Du musst wohl oder übel den Normalmode verwenden und die Ausgabepins 
manuell in der Timer-Overflow-ISR setzt.
Das Zurücksetzen kann per OC-Hardware erfolgen.
In der TimerOverflow-ISR kannst/musst du dann auch die OC-Werte setzen, 
da in diesem Modus die beiden Register nicht gepuffert sind.

von Karl H. (kbuchegg)


Lesenswert?

Steffen Ha schrieb:
> Hallo Zusammen,
>
> ich möchte mit dem Timer 1 ein PWM Signal erzeugen und gleichzeitig ein
> PPM Signal einer Fernsteuerung über den ICP auswerten. Ist das überhaupt
> möglich?

Sicher.
Warum soll das nicht möglich sein?

Nur wirds ein bischen schwierig werden (tm), wenn du das ICR1 Register 
gleichzeitig zum Einstellen der PWM-Frequenz im Fast-PWM Modus ...

> TCCR1A = (1<<COM1A1) | (1<<WGM11);
> TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
> ICR1 = 30000; // = (8MHz / Prescaler 8) / 20000 = 50Hz

... benutzen willst, und du andererseits das ICR1 Register zurm 
Ausmessen einer Pulslänge benutzen willst.

> Des Weiteren soll die Frequenz und die Pulsbreite einstellbar
> sein. Wie kann ich das machen? Einzeln funktionieren die
> Programmabschnitte.

Das sagt dir eigentlich nur eines.
Du hast ein Programm geschrieben, welches Servo-Pulse erzeugen kann
Du hast ein Programm geschrieben, welches Pulse ausmessen kann

Jetzt musst du ein 3-tes, komplett neues Programm schreiben, welches den 
Timer so benutzt dass du gleichzeitig Servopulse UND Pulslängen messen 
kannst. Die Betonung liegt auf "NEUES". Deine ersten beiden Programme 
sind nett, nur helfen sie dir jetzt nur noch bedingt weiter. Es ist ein 
Trugschluss zu glauben, man können Teilfunktionalitäten aus 2 Programmen 
immer und in jedem Fall einfach so in ein drittes Programm 
zusammenmischen. Dein Problem liegt nicht so sehr im Zusammenmischen vom 
Code, sondern darin, dass die beiden Einzelkonzepte für sich alleine 
zwar funktionieren. Aber miteinander funktionieren sie so eben nicht 
mehr. Also muss man sich was neues bzw. anderes überlegen.

(PS: der Timer kennt ja auch noch andere Modi als den Modus 14. Zb. den 
Modus 15, bei dem OCR1A die Rolle übernimmt, die in deinem Servo-Code 
ICR1 spielte. Dann kannst du zwar OCR1A nicht mehr zur Einstellung der 
Pulsbreite der Servopulse benutzen. Aber: Du hast ja noch OCR1B)

von Steffen H. (stef_fen)


Lesenswert?

@ Karl Heinz Buchegger (kbuchegg) (Moderator)

Wie muss ich das mit OCR1B verstehen? Mit OCR1A das habe ich verstanden. 
Der Wert ist dann für die Frequenz verantwortlich.


@ STK500-Besitzer (Gast)

"Finger weg vom TIFR! Darum kümmert sich der Controller ganz alleine." 
Benötige ich diese Zeile nicht? Das löscht doch das Input Capture Flag.

1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include <avr/io.h>
4
#include <stdlib.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
#include "i2cmaster.h"
8
#include "LCD.h"
9
10
int flanke;
11
uint16_t flankeStart;
12
volatile uint16_t stick;
13
float deltaStick;
14
float rechenwert;
15
int sollwert;
16
17
ISR(TIMER1_CAPT_vect) {
18
  if (flanke==0) {
19
    flankeStart = ICR1;
20
    TCCR1B &= ~(1<<ICES1); // fallende Flanke zur Auswertung des ICP
21
    flanke = 1;
22
  }
23
  else {
24
    stick = ICR1 - flankeStart;
25
    flanke = 0;
26
    TCCR1B |= (1<<ICES1); // steigende Flanke zur Auswertung des ICP
27
  }
28
  TIFR = ( 1 << ICF1 );
29
}
30
31
int main (void) {
32
sei();          //erlaubt globale Interrupts
33
34
i2c_init();
35
i2c_start_wait(0x40 | 0);
36
lcd_init();
37
lcd_print("Test V1.0");
38
39
DDRD |= (1<<PD5);
40
TCCR1A = (1<<COM1A1) | (1<<WGM11) | (1<<WGM10);
41
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
42
OCR1A = 128;
43
OCR1B = ???;
44
45
DDRD &= ~(1<<PD6);    //Impuls von Fernsteuerung ICP1 = Eingang
46
PORTD |= (1<<PD6);    //Pullup aktiviert
47
TCCR1B |= (1<<ICES1);  //steigende Flanke zur Auswertung des ICP
48
TCCR1B |= (1<<CS11);  //Quelle für Timer/Counter = CPU-Takt/8
49
TIMSK |= (1<<TICIE1);  //Capture Interrupt Enable
50
51
while (1) 
52
{
53
  DDRD &= ~(1<<PD7); //als Eingang -> PWM ein / LED blinkt
54
  sollwert = stick;
55
  lcd_clear();
56
  lcd_print_integer(sollwert);
57
  _delay_ms(1000);
58
}
59
60
return 0;
61
}

von Karl H. (kbuchegg)


Lesenswert?

Steffen Ha schrieb:
> @ Karl Heinz Buchegger (kbuchegg) (Moderator)
>
> Wie muss ich das mit OCR1B verstehen? Mit OCR1A das habe ich verstanden.
> Der Wert ist dann für die Frequenz verantwortlich.

Und mit OCR1B kannst dann eine PWM aufziehen.


                 Modus 14              Modus 15

Die Rolle von      ICR1    übernimmt    OCR1A
                   OCR1A      -"-       OCR1B

Damit ist ICR1 frei geworden und kann die Aufgabe des Capturen 
übernehmen.


> "Finger weg vom TIFR! Darum kümmert sich der Controller ganz alleine."
> Benötige ich diese Zeile nicht? Das löscht doch das Input Capture Flag.

Das macht der AVR schon ganz von alleine, sobald die ISR aufgerufen 
wird.


Und tu dir noch selbst einen klitzekleinen Gefallen
1
int main (void) {
2
sei();          //erlaubt globale Interrupts
3
4
...


den sei() macht man als letzte Aktion vor der Haupt-while Schleife. 
Zuerst wird alles sauber eingestellt, alle Hardware-Komponenten 
konfiguriert, alle Variablen sofern notwendig initialisiert, kurz und 
gut das komplette System in einen vernünftigen Ausgangszustand gebracht 
und erst dann gehts los, dass Interrupts ihre Arbeit verrichten können.
Aber nicht umgekehrt.

von Steffen H. (stef_fen)


Lesenswert?

Dann müsste ich ja für OCR1A = 20000 und für OCR1B = 1000 eintragen 
damit ich eine Frequenz von 50Hz und eine Pulsbreite von 1,5ms hätte.

Für das Ergebnis des ICP zeigt mir aber das Display manchmal negative 
Zahlen wie z.B. -18800 an. Im Grunde kommt aber das richtige bei raus 
1100 = 1,1ms  für die untere Knüppelposition und 1900 = 1,9ms für die 
obere Knüppelposition.
1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include <avr/io.h>
4
#include <stdlib.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
#include "i2cmaster.h"
8
#include "LCD.h"
9
10
int flanke;
11
uint16_t flankeStart;
12
volatile uint16_t stick;
13
float deltaStick;
14
float rechenwert;
15
int sollwert;
16
17
ISR(TIMER1_CAPT_vect) {
18
  if (flanke==0) {
19
    flankeStart = ICR1;
20
    TCCR1B &= ~(1<<ICES1); // fallende Flanke zur Auswertung des ICP
21
    flanke = 1;
22
  }
23
  else {
24
    stick = ICR1 - flankeStart;
25
    flanke = 0;
26
    TCCR1B |= (1<<ICES1); // steigende Flanke zur Auswertung des ICP
27
  }
28
}
29
30
int main (void) {
31
  
32
i2c_init();
33
i2c_start_wait(0x40 | 0);
34
lcd_init();
35
lcd_print("Test V1.0");
36
37
DDRD |= (1<<PD5);
38
TCCR1A = (1<<COM1A1) | (1<<WGM11) | (1<<WGM10);
39
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
40
OCR1A = 20000; // = (8MHz / Prescaler 8) / 20000 = 50Hz
41
OCR1B = 1000; // = (8MHz / Prescaler 8) * 1500 = 1,5ms
42
43
DDRD &= ~(1<<PD6);    //Impuls von Fernsteuerung ICP1 = Eingang
44
PORTD |= (1<<PD6);    //Pullup aktiviert
45
TCCR1B |= (1<<ICES1);  //steigende Flanke zur Auswertung des ICP
46
TCCR1B |= (1<<CS11);  //Quelle für Timer/Counter = CPU-Takt/8
47
TIMSK |= (1<<TICIE1);  //Capture Interrupt Enable
48
49
sei();          //erlaubt globale Interrupts
50
51
while (1) 
52
{
53
  DDRD &= ~(1<<PD7); //als Eingang -> PWM ein / LED blinkt
54
  sollwert = stick;
55
  lcd_clear();
56
  lcd_print_integer(sollwert);
57
  _delay_ms(1000);
58
}
59
return 0;
60
}

von Karl H. (kbuchegg)


Lesenswert?

Tja.
Wenn jetzt der B-Kanal die PWM erzeugt, dann wird das hier

DDRD |= (1<<PD5);
TCCR1A = (1<<COM1A1) | (1<<WGM11) | (1<<WGM10);

wohl nicht mehr stimmen.
Das war ja auf OCR1A als PWM-Erzeuger ausgelegt.
Deine PWM kommt jetzt aber nicht mehr am OC1A Pin raus, sondern am OC1B 
Pin.

>  sollwert = stick;

da stick ein unsigned int ist, hat der per Definition kein Vorzeichen, 
kann also auch nicht negativ sein.

Aber sollwert ist ein int. Wertebereich -32768 bis +32767. Da passt zb 
ein stick_wert von 40000 gar nicht mehr rein, sondern es kommt zu einer 
Fehlinterpretation. 40000 wird als int als negative Zahl repräsentiert.

Dazu kommt ...
    stick = ICR1 - flankeStart;
Das könnte man machen, wenn der Timer von 0 bis 65535 durchlaufen würde. 
Nur ... deiner tut das nicht mehr. Und das musst du berücksichtigen.
Wenn der eine Input Capture bei 19990 kommt und der andere bei 10, dann 
lagen da keine 45556 Takte dazwischen, sondern nur 20. Weil ja dein 
Timer bei 20000 bereits auf 0 resettet wurde. Das berücksichtigst du 
hier aber nirgends.

von Steffen H. (stef_fen)


Lesenswert?

Das Ergebnis des ICP setzt sich doch so zusammen:
Timer zählt vor sich hin. Kommt eine positive Flank wird der Wert 
gespeichert. Kommt eine negative Flanke so wird der Wert mit der 
positiven Flanke verrechnet. Ein Timer Zählen dauert 1us. Also könnte es 
jetzt passieren das z.B. die positive bei 10 kommt und dann die negative 
bei 30. Das ganze verrechnet würde dann eine negative Zahl geben?

Warum gibt jetzt OCR1B das PWM Signal aus? Wäre es möglich das OCR1A 
trotzdem noch das PWM Signal ausgeben könnte?

Vielen Dank für die Erklärungen und die Geduld.

von Karl H. (kbuchegg)


Lesenswert?

Steffen Ha schrieb:
> Das Ergebnis des ICP setzt sich doch so zusammen:
> Timer zählt vor sich hin. Kommt eine positive Flank wird der Wert
> gespeichert.

zb bei einem Zählerstand von 19990

> Kommt eine negative Flanke

zb bei einem Zählerstand von 10

> so wird der Wert mit der
> positiven Flanke verrechnet. Ein Timer Zählen dauert 1us. Also könnte es
> jetzt passieren das z.B. die positive bei 10 kommt und dann die negative
> bei 30. Das ganze verrechnet würde dann eine negative Zahl geben?

30 - 10 ergibt doch keine negative Zahl.

> Warum gibt jetzt OCR1B das PWM Signal aus? Wäre es möglich das OCR1A
> trotzdem noch das PWM Signal ausgeben könnte?

Wenn du den Timer Modus 14 benutzen würdest/könntest.
Kannst du aber nicht, weil du ICR1 für was anderes brauchst.

Wo liegt denn das Problem den B-Kanal für die PWM zu benutzen? Anstatt 
mit den COM-Bits den Compare-Match vom A-Kanal auf den OC1A Ausgang zu 
legen, benutzt du die COM-Bits für den B-Kanal und lässt dir am OC1B die 
PWM erzeugen. Kabel vom Servo noch an den anderen Anschluss umlöten und 
gut ists. Das ist doch alles keine Raketentechnik. Das ist eine Sache 
auf 10 Sekunden, wenn du weißt was du tust.

von Steffen H. (stef_fen)


Lesenswert?

Ein Problem ist es nicht. Später soll das mal einen BLDC Regler geben. 
Ein bisschen läuft er schon. Nur will ich keinen Mega 1284p nehmen. Der 
hätte ja zwei 16Bit Timer mit denen ich die Sachen getrennt erledigen 
kann. Hab nur bei z.B. auf dem Schaltplan vom Mikrokopter BL-Ctrl 
gesehen, dass das PWM Signal von OC1A  kommt und die den ICP auch 
nutzen.

von Karl H. (kbuchegg)


Lesenswert?

Steffen Ha schrieb:

> Hab nur bei z.B. auf dem Schaltplan vom Mikrokopter BL-Ctrl
> gesehen, dass das PWM Signal von OC1A  kommt und die den ICP auch
> nutzen.

:-)
Es gibt ja auch noch andere Möglichkeiten.
Da musst du halt mal deren Programm studieren, wie die das machen.

Sowas, Studium fremder Programme und ergründen wie und warum sie 
funktionieren, soll sehr lehrreich sein.

von Steffen H. (stef_fen)


Lesenswert?

Das Programm des BL-Ctrl V2.0 habe ich leider nur als Hex File gefunden. 
Jetzt schau ich erst noch mal wie die negative Zahl Zustande kommt. 
Danke.

von Replacer (Gast)


Lesenswert?

Steffen Ha schrieb:
> Hab nur bei z.B. auf dem Schaltplan vom Mikrokopter BL-Ctrl
> gesehen, dass das PWM Signal von OC1A  kommt und die den ICP auch
> nutzen.

Ohne mir das angesehen zu haben oder das Projekt zu kennen, ich weiß von 
anderen Copter Projekten (Naze32 baseflight), dass die Kanäle entweder 
PWM Ausgang oder InputCapture sein können. Vermutlich wird da also 
nichts gleichzeitig gemacht. Ist nur so ein Gedanke.
Bei dem genannten Projekt werden die Kanäle umgemappt, je nachdem, ob 
ein Summensignal vom RC-Empfänger kommt, oder jeder Kanal einzeln. Mit 
Summensignal kann man dann mehr Motoren (z.B. Octocopter) oder 
zusätzliche Servos ansteuern, ansonsten müssen an den Pins per ICP die 
Signale der Kanäle eingelesen werden.

von STK500-Besitzer (Gast)


Lesenswert?

Willst du einen Bl-Motor direkt ansteuern (BL-Ctrl V2.0) oder dazwischen 
noch einen Drehzahlregler einsetzen, wie es z.B. die DJI Naza macht?

von Peter D. (peda)


Lesenswert?

Mit ICP kann man die PWM nur 16 bittig benutzen. Ansonsten liest man 
Mumpitz bei der Differenz von 2 Capture-Messungen.
OCR1A muß also immer 0xFFFF sein. Die PWM macht OCR1B.

von Steffen H. (stef_fen)


Lesenswert?

Ich möchte mir meinen eigenen Brushless Regler bauen. Das Signal was ich 
von der Fernsteuerung erhalte ist ein PPM Signal.

Die Erfahrung das man mumpitz liest musste ich jetzt auch machen. Bei 
50Hz funktioniert die Sache aber bei höheren Frequenzen nicht mehr. 
Nehme ich jetzt als Beispiel 8kHz dann müsste ich für OCR1A = 1000 (8Mhz 
Clock ohne Prescaler) einstellen. Er zählt dann nur noch bis 1000 und 
beginnt dann von vorne. Der Timer braucht dann aber nur noch 0,125us für 
einen Zählschritt was bei 1100us (untere Knüppelposition Fernsteuerung) 
einem Wert von 8800 entprechen würde.

Die PWM Frequenz möchte ich gerne Variabel haben. 8, 9, 10kHz. Wie kann 
ich das jetzt am geschicktesten lösen? Ich brauch später noch Timer für 
den Startup Speed und den Kommutierungszeitpunkt.
1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include <avr/io.h>
4
#include <stdlib.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
#include "i2cmaster.h"
8
#include "LCD.h"
9
10
int flanke;
11
uint16_t flankepositiv;
12
uint16_t flankenegativ;
13
int16_t hilfsnenner;
14
volatile uint16_t stick;
15
float deltaStick;
16
float rechenwert;
17
int16_t sollwert;
18
19
ISR(TIMER1_CAPT_vect) {
20
  if (flanke==0) {
21
    flankepositiv = ICR1; // orig flankeStart = ICR1
22
    TCCR1B &= ~(1<<ICES1); // fallende Flanke zur Auswertung des ICP
23
    flanke = 1;
24
  }
25
  else {
26
    flankenegativ = ICR1;
27
    if (flankepositiv < flankenegativ)
28
    {
29
      stick = flankenegativ - flankepositiv; //orig stick = ICR1 - flankepositiv
30
    }
31
    else
32
    {
33
      hilfsnenner = 2000 - flankepositiv;
34
      stick = flankenegativ + hilfsnenner;
35
    }  
36
    flanke = 0;
37
    TCCR1B |= (1<<ICES1); // steigende Flanke zur Auswertung des ICP
38
  }
39
}
40
41
int main (void) {
42
  
43
i2c_init();
44
i2c_start_wait(0x40 | 0);
45
lcd_init();
46
lcd_print("Test V1.0");
47
48
DDRD |= (1<<PD4);
49
TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
50
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
51
OCR1A = 20000; // = (8MHz / Prescaler 8) / 20000 = 50Hz
52
OCR1B = 1500; // = (8MHz / Prescaler 8) * 1500 = 1,5ms
53
54
DDRD &= ~(1<<PD6);    //Impuls von Fernsteuerung ICP1 = Eingang
55
PORTD |= (1<<PD6);  //Pullup aktiviert
56
TCCR1B |= (1<<ICES1);  //steigende Flanke zur Auswertung des ICP
57
TCCR1B |= (1<<CS11);  //Quelle für Timer/Counter = CPU-Takt/8
58
TIMSK |= (1<<TICIE1);  //Capture Interrupt Enable
59
60
sei();          //erlaubt globale Interrupts
61
62
while (1) 
63
{
64
  DDRD &= ~(1<<PD7); //als Eingang -> PWM ein / LED blinkt
65
  sollwert = stick;
66
  lcd_clear();
67
  lcd_print_integer(sollwert);
68
  _delay_ms(500);
69
}
70
71
return 0;
72
}

von Steffen H. (stef_fen)


Lesenswert?

Oder könnte ich es auch so machen. Das ich das Programm so lasse und 
einen zweiten Timer hinzuziehe. Der in dem Timer1_Capt_Vect gestartet 
wird wenn die positive Flanke kommt und wenn die negative kommt gestoppt 
wird. So könnte ich vielleicht auch sinnvoll überprüfen ob das PPM 
Signal von der Fernsteuerung noch korrekt ist, z.B. wenn der Empfänger 
keinen Strom mehr hat. Dieses würde bei der jetzigen Variante keinen 
Interrupt mehr auslösen und der letzt gültige Wert würde in der 
Variablen verbleiben was in ganz ungünstigem Fall Vollgas ist.

Gruß Steffen

von STK500-Besitzer (Gast)


Lesenswert?

Steffen Ha schrieb:
> if (flankepositiv < flankenegativ)

Was soll das denn?
Bloß weil dein Display negative Zahlen anzeigt, und du den Grund dafür 
nicht gefunden hast?
Es ist nur ein Interpretationsproblem deiner LCD-Ausgabe.

Warum musst du die Geschichte eigentlich unbedingt mit nur einem Timer 
realisieren?
Ein anderer Timer (welche von den beiden weiß ich gerade nicht) des 
ATMega8 hat auch eine OnCapture-Funktion. Allerdings mit nur einem 
Capture-Register.
Der Timer würde problemlos funktionieren, auch wenn er nur 8Bit 
Auflösung hat.

von Steffen H. (stef_fen)


Lesenswert?

Ich weiss schon warum das Display negative Zahlen anzeigt. Das liegt 
daran wenn z.B. der ICP einmal bei der positiven Flanke bei dem 
Zählerwert 19900 auslöst und der bei der negativen bei 100 kommt es 
durch die Rechnung im ISR zur negativen Zahl. Da der Timer ja nur bis 
20000 zählt bei 50Hz.

Nur lässt sich das Problem auch nicht umgehen denn wenn ich jetzt noch 
eine andere Frequenz wähle:
Nehme ich jetzt als Beispiel 8kHz dann müsste ich für OCR1A = 1000 (8Mhz
Clock ohne Prescaler) einstellen. Er zählt dann nur noch bis 1000 und
beginnt dann von vorne. Der Timer braucht dann aber nur noch 0,125us für
einen Zählschritt was bei 1100us (untere Knüppelposition Fernsteuerung)
einem Wert von 8800 entprechen würde. Dieses wäre dann noch schlimmer da 
der Timer mehr als einmal neu gestartet wird.

von STK500-Besitzer (Gast)


Lesenswert?

Steffen Ha schrieb:
> Ich weiss schon warum das Display negative Zahlen anzeigt. Das liegt
> daran wenn z.B. der ICP einmal bei der positiven Flanke bei dem
> Zählerwert 19900 auslöst und der bei der negativen bei 100 kommt es
> durch die Rechnung im ISR zur negativen Zahl. Da der Timer ja nur bis
> 20000 zählt bei 50Hz.

In einem begrenzten Zahlenraum wie er hier vorliegt (16bit), gibt es 
keine negativen Zahlen, sondern einen Überlauf.
Hier kann man Zahlen zwischen 0 und 65535 darstellen.
Oder man arbeitet mit der Darstellung, dass das höchstwertige Bit das 
Vorzeichen beschreibt, und bekommt einen Zahlenraum von -32767 bis 
+32768

https://de.wikipedia.org/wiki/Dualsystem


Steffen Ha schrieb:
> Nehme ich jetzt als Beispiel 8kHz dann müsste ich für OCR1A = 1000 (8Mhz
> Clock ohne Prescaler) einstellen. Er zählt dann nur noch bis 1000 und
> beginnt dann von vorne. Der Timer braucht dann aber nur noch 0,125us für
> einen Zählschritt was bei 1100us (untere Knüppelposition Fernsteuerung)
> einem Wert von 8800 entprechen würde. Dieses wäre dann noch schlimmer da
> der Timer mehr als einmal neu gestartet wird.


Nimm zwei Timer.

von Steffen H. (stef_fen)


Lesenswert?

Ich habe es jetzt so gelöst das das Timer 1 das PWM Signal erzeugt und 
der ICP nur noch zum Starten und Stoppen des Timers 0 genutzt wird. Der 
gibt mir dann die Impulsdauer aus. Gleichzeitig nutze ich den Timer0 
Overflow um zu überprüfen ob das Empfangssignal noch da ist. Ist das so 
ok? Was kann man verbessern?

1
#define F_CPU 8000000
2
#include <avr/io.h>
3
#include <stdlib.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
#include "i2cmaster.h"
7
#include "LCD.h"
8
9
int flanke;
10
uint16_t flankepositiv;
11
uint16_t flankenegativ;
12
int16_t hilfsnenner;
13
volatile uint16_t stick;
14
float deltaStick;
15
float rechenwert;
16
int16_t sollwert;
17
volatile uint8_t i = 0;
18
19
ISR(TIMER1_CAPT_vect) {
20
  if (flanke==0) {
21
    TCNT0 = 0;
22
    TCCR1B &= ~(1<<ICES1); // fallende Flanke
23
    flanke = 1;
24
  }
25
  else {
26
    stick = TCNT0;
27
    flanke = 0;
28
    TCCR1B |= (1<<ICES1); // steigende Flanke
29
    i = 0;
30
  }
31
}
32
33
ISR(TIMER0_OVF_vect) {
34
  if (i == 10) {
35
    stick = 0;
36
  }
37
  i++;
38
}
39
40
int main (void) {
41
  
42
i2c_init();
43
i2c_start_wait(0x40 | 0);
44
lcd_init();
45
lcd_print("Test V1.0");
46
_delay_ms(1000);
47
48
TCCR0 |= (1<<CS01) | (1<<CS00);
49
TCNT0 = 0;
50
TIMSK |= (1<<TOIE0);
51
52
DDRD |= (1<<PD4);
53
TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
54
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
55
OCR1A = 2400; // = (8MHz / Prescaler 8) / 20000 = 50Hz
56
OCR1B = 1500; // = (8MHz / Prescaler 8) * 1500 = 1,5ms
57
58
DDRD &= ~(1<<PD6);    //Impuls von Fernsteuerung ICP1 = Eingang
59
PORTD |= (1<<PD6);    //Pullup aktiviert
60
TCCR1B |= (1<<ICES1);  //steigende Flanke zur Auswertung des ICP
61
TCCR1B |= (1<<CS11);  //Quelle für Timer/Counter = CPU-Takt/8
62
TIMSK |= (1<<TICIE1);  //Capture Interrupt Enable
63
64
sei();          //erlaubt globale Interrupts
65
66
while (1) 
67
{
68
  DDRD &= ~(1<<PD7); //als Eingang -> PWM ein / LED blinkt
69
  sollwert = stick;
70
  lcd_clear();
71
  lcd_print_integer(sollwert);
72
  _delay_ms(500);
73
}
74
75
return 0;
76
}

von Replacer (Gast)


Lesenswert?


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.