Hallo Leute, ich suche eine möglichst einfache Schaltung um die Frequenz eines Rechtecksignals (duty cycle 50%, 50-150Hz, Ampl ~3,6V) in der Art zu manipulieren dass ich einen positiven Offset von 20Hz habe. Also z.B. 120 Hz statt 100 Hz. Einen Microcontroller möchte ich möglichst vermeiden. Hat jemand einen Vorschlag? Danke
:
Bearbeitet durch User
Hallo, also in der HF-Technik würde man das mit einem Mischer machen, aber 50-150Hz sind da eher Gleichspannung. Eine PLL-Lösung scheidet aus, denn damit kannst die Frequenz nur um einen bestimmten Faktor ändern. folgendes würde gehen: 1) ein Frequenz-Spannung-Umsetzer macht aus deiner Eingangsfrequenz eine analoge Spannung 2) ein Spannungsgesteuerter Generator der dann deine neue Frequenz erzeugt Scheint mir aber recht aufwendig und mit Abgleich verbunden. Mit einem µC sollte das gehen, man müsste dann nur sehen (a) mit welcher Genauigkeit sich die Frequenz messen lässt und (b) wie die Genauigkeit bei der Erzeugung ist. Sascha
Ja vieles scheidet leider aus. Ich vermute fast dass die Realisierung mit einem µC wohl den besten Kompromiss darstellen wird. Die Erzeugung sollte noch relativ gut machbar sein, aber ob die Messung der Frequenz so genau sein wird? Es käme auch nur ein kleiner µC z.B. Attiny 13 in Frage.
Jürgen F. schrieb: > Ich vermute fast dass die Realisierung mit einem µC wohl den besten > Kompromiss darstellen wird. Wieso Kompromiss? Du weißt nach jeder Periode des Eingangssignals die aktuelle Frequenz, kannst daraus die Periodendauer des Ausgangssignals ausrechnen und verwendest den Wert zur Steuerung eines unabhängig von der Software laufenden Timers. Viele direkter geht das doch eigentlich nicht.
Jürgen F. schrieb: > Die Erzeugung sollte noch relativ gut machbar sein, aber ob die Messung > der Frequenz so genau sein wird? > > Es käme auch nur ein kleiner µC z.B. Attiny 13 in Frage. Ist problemlos auch mit noch kleineren µCs möglich. Und genau wird das auch, wenn man es richtig macht. Gruß Jonathan
Jürgen F. schrieb: > Die Erzeugung sollte noch relativ gut machbar sein, aber ob die Messung > der Frequenz so genau sein wird? Wie genau? Für einen µC mit einem Timer ist es doch ein leichtes, die Zeit zwischen zwei Flanken auszumessen. Und dann muß er halt mal rechnen. Wofür braucht man eigentlich so einen Frequenzverschieber?
Jürgen F. schrieb: > ich suche eine möglichst einfache Schaltung um die Frequenz eines > Rechtecksignals (duty cycle 50%, 50-150Hz, Ampl ~3,6V) in der Art zu > manipulieren dass ich einen positiven Offset von 20Hz habe. Also z.B. > 120 Hz statt 100 Hz. Addition von zwei Frequenzen. Exakt das gleiche Thema hatten wir doch vor nicht allzu langer Zeit hier schon einmal. Ist das an irgendeiner Schule jetzt eine Standard-Hausaufgabe? > Einen Microcontroller möchte ich möglichst vermeiden. Das ist aber blöd, denn das stellt die einfachste Lösung dar, denn schon die einfachste alternative Lösung kostet mindest zwei CMOS-ICs und etliches Hühnerfutter drum herum.
Ich merke schon hier wird keine Alternative zu einem µC mehr kommen. Mit einfache Schaltung war übrigens eine Schaltung mit möglichst wenigen Bauteilen gemeint. Also werde ich es mal mit einem Attiny 13 versuchen. Mal schauen wie ich da die Zeitmessung realisiere, einen Input Capture Pin hat er ja nicht. Vorschläge? Nein eine Hausaufgabe oder ähnliches ist es nicht, sondern für die "Manipulation" der Drehzahlauswertung eines Lüfters.
Wenn es nur um ein Lüftersignal geht, sollten die Anforderungen an die Genauigkeit wohl nicht so hoch sein. Da ginge dann wohl auch der Weg über die Spannung als zwischenwert, etwa mit 2 mal 4046. Der Tiny13 wird das auch schon irgendwie hinbekommen, aber ein Tiny25 mit 2 Timern ist vermutlich einfacher.
Ulrich schrieb: > Der Tiny13 wird das auch schon irgendwie hinbekommen, aber ein Tiny25 > mit 2 Timern ist vermutlich einfacher. Bei höheren Frequenzen seh ich das auch so. Aber bei der Anforderung von um die 150 Hz ist eine rein softwaretechnische Lösung wahrscheinlich sogar am einfachsten. Das heißt, man braucht gar keinen Timer. Es reicht ein Billigst-Mikrocontroller, z.B. ATtiny13A, ATtiny10, 8-Bit-PIC usw.
Würde da auch einen Tiny25 nehmen. - Mir täten 10 Cent mehr auch nicht so wehtun.... Timer0 um die Frequenz über ExtInt0 zu erfassen, ... etwas rechnen lassen ... Timer1 um die Frequenz-mit-Offset auszugeben. Man müsste sich jetzt Gedanken machen, ob man beim Einschalten mit der Ausgabe einer maximalen, oder einer minimalen (Vorgabe?) Frequenz anfängt... Müsste man sich aber auch bei einer µC-freien Konstruktion machen...
Bernie schrieb: > Man müsste sich jetzt Gedanken machen, ob man > beim Einschalten mit der Ausgabe einer maximalen, > oder einer minimalen (Vorgabe?) Frequenz anfängt... Und warum nicht mit einer mittleren?
Jürgen F. schrieb: > Was meinst du mit, man braucht keinen Timer? Dass man keinen Timer braucht. :-) Aber – wie Bernie schon schrieb – mit Timer ist es einfacher. Man braucht sich dann weniger Gedanken über die Ausführungszeiten von Befehlen zu machen und muss nicht mit einer Reihe von NOPs fürs Gleichgewicht sorgen. Ich würde dann aber nur einen einzigen Timer verwenden und den Rest in der Timer-ISR awickeln: Zeit zwischen zwei Flankenwechseln am Eingang mitzählen, diese umrechnen (z.B. *0,75) und damit den Zähler für die Flankenabstände des Ausgangs versorgen.
Markus Weber schrieb: > Jürgen F. schrieb: >> Was meinst du mit, man braucht keinen Timer? > > Dass man keinen Timer braucht. :-) > > Aber – wie Bernie schon schrieb – mit Timer ist es einfacher. Man > braucht sich dann weniger Gedanken über die Ausführungszeiten von > Befehlen zu machen und muss nicht mit einer Reihe von NOPs fürs > Gleichgewicht sorgen. > > Ich würde dann aber nur einen einzigen Timer verwenden und den Rest in > der Timer-ISR awickeln: Zeit zwischen zwei Flankenwechseln am Eingang > mitzählen, diese umrechnen (z.B. *0,75) und damit den Zähler für die > Flankenabstände des Ausgangs versorgen. Habe unter anderem mal Attiny10 bei Dir geordert. Mal sehen ob sich das damit zufriedenstellend realiseren lässt. Wäre halt von der Bauform optimal für meinen Fall.
:
Bearbeitet durch User
Ich habe mal etwas mit einem Attiny10 herumgespielt. Leider scheint da irgendwas nicht richtig zu funktionieren. Um sicher zu gehen keinen Fehler gemacht zu haben, habe ich jetzt mal einen fertigen Code zur Frequenzmessung hier aus dem Forum benutzt. Aber leider tritt hier auch ein Fehler auf. Ich möchte mit der If Abfrage ganz unten den Pin PB0 auf High setzen sobald die Frequenz größer als 50 Hz ist. Beim Compilieren mit dem aktuellen AVR Studio 6.1 erhalte ich allerdings folgende Fehlermeldung. ld returned 1 exit status collect2.exe Es scheint an der Variablen in der If Abfrage zu liegen. Weiße ich Freq einfach einen Wert und nicht Erg zu funktioniert es. Normal sollte er doch einfach einen Integer daraus machen?
1 | /* |
2 | |
3 | ///////////////////////////////////////////////////////////////////// |
4 | // Frequenzmesser |
5 | // |
6 | // Holger Brenner |
7 | // |
8 | // Das zu messende Signal wird an den Input Capture Pin des Prozessors |
9 | // angelegt. Zur Freqenzbestimmung wird mittels Timer1 die Perioden- |
10 | // dauer des Signals von einer steigenden Flanke bis zur naechsten |
11 | // steigenden Flanke ermittelt. Daraus laesst sich dann die Frequenz |
12 | // errechnen. |
13 | // |
14 | // Das Beispiel benutzt die LCD Funktionen von .... |
15 | // Für andere Ausgabemedien muss das entsprechend angepasst werden |
16 | // |
17 | // 8. Feb. 2007 |
18 | ///////////////////////////////////////////////////////////////////// |
19 | |
20 | // |
21 | // Prozessor Taktfrequenz einstellen sofern es nicht eine Vorgabe |
22 | // beim Aufruf des C Compilers gab. |
23 | // |
24 | #ifndef F_CPU |
25 | #define F_CPU 4000000 |
26 | #endif |
27 | |
28 | |
29 | #include <avr/io.h> |
30 | #include <avr/interrupt.h> |
31 | #include <util/delay.h> |
32 | |
33 | #include <stdlib.h> |
34 | |
35 | |
36 | #ifndef TRUE |
37 | #define TRUE 1 |
38 | #define FALSE 0 |
39 | #endif |
40 | |
41 | |
42 | volatile unsigned char NrOverflows = 0; // Anzahl der Timer Overflows die während der Messung passiert sind |
43 | volatile unsigned int StartTime = 0; // ICR-Wert bei 1.High-Flanke speichern |
44 | volatile unsigned int EndTime = 0; // ICR-Wert bei 2.High-Flanke speichern |
45 | volatile unsigned char JobFlag; // Job Flag |
46 | |
47 | |
48 | |
49 | ISR( TIM0_CAPT_vect ) |
50 | { |
51 | static unsigned char ErsteFlanke = TRUE; |
52 | |
53 | if( JobFlag ) // Das Display wurde mit den Ergebnissen der vorhergehenden |
54 | return; // Messung noch nicht upgedated. Die naechste Messung |
55 | // verzögern, bis die Start und EndTime Variablen wieder |
56 | // gefahrlos beschrieben werden koennen |
57 | |
58 | // |
59 | // Bei der ersten Flanke beginnt die Messung, es wird der momentane |
60 | // Timer beim Input Capture als Startwert gesichert |
61 | // |
62 | if( ErsteFlanke ) |
63 | { |
64 | StartTime = ICR0; |
65 | NrOverflows = 0; |
66 | ErsteFlanke = FALSE; // Die naechste Flanke ist das Ende der Messung |
67 | } |
68 | |
69 | // |
70 | // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt |
71 | // |
72 | else |
73 | { |
74 | EndTime = ICR0; |
75 | JobFlag = TRUE; // Eine vollständige Messung. Sie kann ausgewertet werden |
76 | ErsteFlanke = TRUE; // Bei der naechsten Flanke beginnt der naechste Messzyklus |
77 | } |
78 | } |
79 | |
80 | |
81 | ISR( TIM0_OVF_vect ) |
82 | { |
83 | NrOverflows++; |
84 | } |
85 | |
86 | int main() |
87 | { |
88 | |
89 | double Erg = 0.0; |
90 | uint8_t Freq; |
91 | |
92 | |
93 | TCCR0A = (1<<ICES0) | (1<<CS00); // Input Capture Edge, kein PreScale |
94 | TIMSK0 = (1<<ICIE0) | (1<<TOIE0); // Interrupts akivieren, Capture + Overflow |
95 | |
96 | DDRB |=(1<<DDB0); |
97 | |
98 | |
99 | sei(); |
100 | |
101 | while(1) |
102 | { |
103 | // |
104 | // liegt eine vollständige Messung vor? |
105 | // |
106 | if( JobFlag ) |
107 | { |
108 | |
109 | // |
110 | // Die Zeitdauer zwischen den Flanken bestimmen |
111 | // Da EndTime und StartTime unsigned sind, braucht nicht |
112 | // darauf Ruecksicht genommen werden, dass EndTime auch |
113 | // kleiner als StartTime sein kann. Es kommt trotzdem |
114 | // das richtige Ergebnis raus. |
115 | // Allerdings muss die Anzahl der Overflows in der Messperiode |
116 | // beruecksichtigt werden |
117 | // |
118 | // Die Zeitdauer wird als Anzahl der Taktzyklen zwischen den |
119 | // beiden gemessenen Flanken berechnet ... |
120 | Erg = (NrOverflows * 65536) + EndTime - StartTime; |
121 | |
122 | // ... mit der bekannten Taktfrequenz ergibt sich dann die Signalfrequenz |
123 | Erg = F_CPU / Erg; // f = 1 / t |
124 | |
125 | Freq = Erg; |
126 | |
127 | |
128 | if (Freq>50) |
129 | { |
130 | PORTB |= (1<<PORTB0); |
131 | } |
132 | |
133 | |
134 | |
135 | // die naechste Messung kann starten |
136 | // |
137 | JobFlag = FALSE; |
138 | } |
139 | } |
140 | } |
:
Bearbeitet durch User
Die fehlt die Floating Point Library. Hier müsste stehen wie es konfiguriert werden kann: http://www.mikrocontroller.net/articles/FAQ (Kapitel "Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio")
Ich habe mal versucht in Abhänigkeit der gemessenen Frequenz ein neues Signal mit 20Hz höherer Frequenz zu erzeugen. Leider funktioniert das so wie ich es gemacht habe nicht. 1. Die ausgegebene Frequenz schwankt dauernd sehr stark. 2. Die zusätzlichen Hz sind gar nicht bemerkbar. Hat jemand eine Idee wie ich das stabil hinbekommen kann?
1 | ///////////////////////////////////////////////////////////////////// |
2 | // Frequenzmesser |
3 | // |
4 | // Holger Brenner |
5 | // |
6 | // Das zu messende Signal wird an den Input Capture Pin des Prozessors |
7 | // angelegt. Zur Freqenzbestimmung wird mittels Timer1 die Perioden- |
8 | // dauer des Signals von einer steigenden Flanke bis zur naechsten |
9 | // steigenden Flanke ermittelt. Daraus laesst sich dann die Frequenz |
10 | // errechnen. |
11 | // |
12 | // Das Beispiel benutzt die LCD Funktionen von .... |
13 | // Für andere Ausgabemedien muss das entsprechend angepasst werden |
14 | // |
15 | // 8. Feb. 2007 |
16 | ///////////////////////////////////////////////////////////////////// |
17 | |
18 | // |
19 | // Prozessor Taktfrequenz einstellen sofern es nicht eine Vorgabe |
20 | // beim Aufruf des C Compilers gab. |
21 | // |
22 | //#ifndef F_CPU |
23 | #define F_CPU 1000000 |
24 | //#endif |
25 | |
26 | |
27 | #include <avr/io.h> |
28 | #include <avr/interrupt.h> |
29 | #include <util/delay.h> |
30 | |
31 | #include <stdlib.h> |
32 | |
33 | |
34 | #ifndef TRUE |
35 | #define TRUE 1 |
36 | #define FALSE 0 |
37 | #endif |
38 | |
39 | |
40 | volatile unsigned char NrOverflows = 0; // Anzahl der Timer Overflows die während der Messung passiert sind |
41 | volatile unsigned int StartTime = 0; // ICR-Wert bei 1.High-Flanke speichern |
42 | volatile unsigned int EndTime = 0; // ICR-Wert bei 2.High-Flanke speichern |
43 | volatile unsigned char JobFlag = FALSE; // Job Flag |
44 | volatile signed long int Erg = 0; |
45 | volatile signed long int Mittelwert = 0; |
46 | |
47 | |
48 | ISR( TIM0_CAPT_vect ) |
49 | { |
50 | static unsigned char ErsteFlanke = TRUE; |
51 | |
52 | if( JobFlag ) // Das Display wurde mit den Ergebnissen der vorhergehenden |
53 | return; // Messung noch nicht upgedated. Die naechste Messung |
54 | // verzögern, bis die Start und EndTime Variablen wieder |
55 | // gefahrlos beschrieben werden koennen |
56 | |
57 | // |
58 | // Bei der ersten Flanke beginnt die Messung, es wird der momentane |
59 | // Timer beim Input Capture als Startwert gesichert |
60 | // |
61 | if( ErsteFlanke ) |
62 | { |
63 | StartTime = ICR0; |
64 | NrOverflows = 0; |
65 | ErsteFlanke = FALSE; // Die naechste Flanke ist das Ende der Messung |
66 | } |
67 | |
68 | // |
69 | // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt |
70 | // |
71 | else |
72 | { |
73 | EndTime = ICR0; |
74 | JobFlag = TRUE; // Eine vollständige Messung. Sie kann ausgewertet werden |
75 | ErsteFlanke = TRUE; // Bei der naechsten Flanke beginnt der naechste Messzyklus |
76 | |
77 | // |
78 | // liegt eine vollständige Messung vor? |
79 | // |
80 | if( JobFlag ) |
81 | { |
82 | |
83 | |
84 | // |
85 | // Die Zeitdauer zwischen den Flanken bestimmen |
86 | // Da EndTime und StartTime unsigned sind, braucht nicht |
87 | // darauf Ruecksicht genommen werden, dass EndTime auch |
88 | // kleiner als StartTime sein kann. Es kommt trotzdem |
89 | // das richtige Ergebnis raus. |
90 | // Allerdings muss die Anzahl der Overflows in der Messperiode |
91 | // beruecksichtigt werden |
92 | // |
93 | // Die Zeitdauer wird als Anzahl der Taktzyklen zwischen den |
94 | // beiden gemessenen Flanken berechnet ... |
95 | Erg = (NrOverflows * 65536) + EndTime - StartTime; |
96 | |
97 | // ... mit der bekannten Taktfrequenz ergibt sich dann die Signalfrequenz |
98 | Erg = ((F_CPU/8) / Erg); // f = 1 / t |
99 | } |
100 | } |
101 | |
102 | } |
103 | |
104 | ISR( TIM0_OVF_vect ) |
105 | { |
106 | NrOverflows++; |
107 | |
108 | } |
109 | |
110 | int main() |
111 | { |
112 | int i=0; |
113 | int j=10; |
114 | |
115 | CCP=0xD8; |
116 | CLKMSR=0x00; |
117 | CCP=0xD8; |
118 | CLKPSR=0x03; |
119 | |
120 | TCCR0B |= ((1<<ICES0) | (1<<CS00)); // Input Capture Edge, kein PreScale |
121 | TIMSK0 |= ((1<<ICIE0) | (1<<TOIE0)); // Interrupts akivieren, Capture + Overflow |
122 | TCCR0A |= ((1<<WGM01) | (1<<WGM00) | (1<<COM0A0) | (0<<COM0A1)); |
123 | TCCR0B |= ((1<<WGM03) | (1<<WGM02)| (1<<ICNC0) | (1<<CS01)); |
124 | //Fast PWM Mode 15 auf Pin OC0A, Taktteiler 8, Noise Canceler |
125 | DDRB=0x00; |
126 | DDRB |=(1<<DDB0); |
127 | |
128 | |
129 | |
130 | |
131 | sei(); |
132 | |
133 | |
134 | |
135 | while(1) |
136 | { |
137 | for( i=0; i<j; i++) |
138 | { |
139 | Mittelwert = Mittelwert+Erg; |
140 | } |
141 | |
142 | Mittelwert = Mittelwert/j; |
143 | |
144 | OCR0A =(F_CPU/8)/(2*(Mittelwert+20)); |
145 | |
146 | |
147 | Mittelwert=0; |
148 | |
149 | JobFlag=FALSE; |
150 | // |
151 | // Das wars: Display ist wieder up to date |
152 | // die naechste Messung kann starten |
153 | // |
154 | |
155 | } |
156 | |
157 | } |
Jürgen F. schrieb: > Hat jemand eine Idee wie ich das stabil hinbekommen kann? Ich tendier bei sowas dazu, es in Assembler zu programmieren. Aber das ist Geschmacksache. Ich werde mir mal Gedanken drüber machen. Deine Anforderung "+20 Hz" - lässt sich die auch abändern in "Frequenz um 25 % höher" (oder so ähnlich)? Dann könnte man Multiplikationen und Divisionen einsparen.
Markus Weber schrieb: > Deine Anforderung "+20 Hz" - lässt sich die auch abändern in "Frequenz > um 25 % höher" (oder so ähnlich)? Dann könnte man Multiplikationen und > Divisionen einsparen. Wenn es stabil laufen würde ging das zur Not auch.
Ich habe herausgefunden dass die Frequenzmessung über den Capture Pin nicht mehr funktioniert sobald ich Fast PWM Mode 15 aktiviere. Kann mir jemand sagen weshalb das dann nicht mehr richtig arbeitet? Geht Fast PWM und Input Capture nicht gleichzeitig?
:
Bearbeitet durch User
Jürgen F. schrieb: > Geht Fast PWM und Input Capture nicht gleichzeitig? Habe ich nie probiert, müsste aber im Datenblatt ersichtlich sein... hoffentlich. :-) Ich hab grad ein kleines Assembler-Programm geschrieben, das muss ich aber erst noch ausprobieren. Und dazu brauche ich einen Breadboard-Adapter für den ATtiny10. Um den zu kriegen, muss ich eine Platine sägen. Um die Platine sägen zu können, muss ich die Säge aufbauen. Um die Säge aufbauen zu können, muss ich Platz schaffen. Und dazu schließlich muss ich aufräumen. Kann also noch ein paar Minuten dauern. ;-)
Ok, also man sollte das Datenblatt doch mal genau lesen :) Die Zeitmessung ging ja davon aus dass der Zähler erst bei 65536 überläuft. Da ich ja aber im Fast PWM Mode 15 mit OCR0A vorgebe bis wie weit er zählen soll muss ich natürlich auch für die Zeitmessung mit diesem Wert rechnen. Jetzt habe ich das Problem dass sich das ganze natürlich aufschauckelt, und dann bis hoch in den kHz ausgibt. Wie gehe ich da am besten vor um ein aufschauckeln zu vermeiden?
So, ATtiny10-Breadboard-Adapter ausgesägt und verwendet. :-) Jürgen F. schrieb: > Jetzt habe ich das Problem dass sich das ganze natürlich aufschauckelt, > und dann bis hoch in den kHz ausgibt. Ich geb zu, ich habe mich nicht getraut, Input Capture und PWM mit dem selben Timer zu erledigen. Zudem waren mir die 16 Bit Zählerbreite zu gering, jedenfalls dann, wenn man auch mal höhere oder niedrigere Frequenzbereiche nutzen will. Deswegen habe ich mit dazu entschlossen, grundsätzlich mit 24 Bits zu zählen. Der Timer dient bei meiner Lösung nur noch als Zeitbasis und gibt den Takt für die Verarbeitung vor (hier 100 kHz). Eine Abfrage musste noch ergänzt werden, damit beim plötzlichen Ausbleiben der Input-Flanken und damit nicht messbarer Einfangsfrequenz nicht weiter fröhlich der Ausgang vor sich hin togglet. Und eine weitere Abfrage, die dafür sorgt, dass bei plötzlicher Erhöhung der Eingangsfrequenz der Ausgang nicht erst noch auf das Ende der aktuellen Schwingung wartet. Deswegen wurde das Programm dann doch länger als gedacht...
1 | ; fq 2013-11-23 20:00 |
2 | ; version 0.1 |
3 | ; target: ATtiny10 |
4 | ; |
5 | ; This program reads the frequency of the input signal at PB1 |
6 | ; and provides a similar signal at PB2 with frequency which |
7 | ; has been increased by 25 %. |
8 | ; operating range: 0.02 Hz .. ca. 10 kHz (resolution is 10 µs) |
9 | ; |
10 | .include "tn10def.inc" |
11 | |
12 | ; register allocation |
13 | .def temp=r16 |
14 | .def zero=r18 ; constant 0 |
15 | .def one=r19 ; constant 1 |
16 | .def inlevel=r31 ; level of input signal |
17 | .def intime0=r20 ; 24-bit value for time between |
18 | .def intime1=r21 ; edges of input signal |
19 | .def intime2=r22 ; 0..0x3f0000 |
20 | .def outtime0=r24 ; 24-bit value for time between |
21 | .def outtime1=r25 ; edges of output signal |
22 | .def outtime2=r26 ; |
23 | .def cache0=r28 ; 24-bit buffer for last input timer |
24 | .def cache1=r29 ; 0..0xfeffff |
25 | .def cache2=r30 ; 0xffxxxx: marked as invalid |
26 | |
27 | rjmp init |
28 | .org OC0Aaddr ; timer ISR |
29 | |
30 | ; check if input signal has changed |
31 | in temp,PINB |
32 | eor temp,inlevel |
33 | andi temp,0b0010 |
34 | breq countup ; no change in input signal |
35 | eor inlevel,temp ; toggle singal level bit |
36 | |
37 | ; write decreased input counter value to buffer |
38 | ; we multiply by 0b0.11 (decimal 0.75) |
39 | mov cache0,intime0 |
40 | mov cache1,intime1 |
41 | mov cache2,intime2 |
42 | lsr cache2 |
43 | ror cache1 |
44 | ror cache0 |
45 | add cache0,intime0 |
46 | adc cache1,intime1 |
47 | adc cache2,intime2 |
48 | lsr cache2 |
49 | ror cache1 |
50 | ror cache0 |
51 | |
52 | ; initialize input counter |
53 | clr intime0 |
54 | clr intime1 |
55 | clr intime2 |
56 | |
57 | ; check if output counter value is too high |
58 | cp cache0,outtime0 |
59 | cpc cache1,outtime1 |
60 | cpc cache2,outtime2 |
61 | brsh countup ; output counter value <= buffer value |
62 | |
63 | ; cut output counter value (set it to buffer value) |
64 | mov outtime0,cache0 |
65 | mov outtime1,cache1 |
66 | mov outtime2,cache2 |
67 | |
68 | countup: |
69 | cpi intime2,0x3f |
70 | breq care_for_output ; counter already at maximum |
71 | |
72 | ; increment input counter |
73 | add intime0,one |
74 | adc intime1,zero |
75 | adc intime2,zero |
76 | |
77 | ; check if input counter value is too high |
78 | mov temp,cache2 |
79 | cpi temp,0xff |
80 | breq care_for_output ; buffer value is not valid |
81 | lsl cache0 ; temporarily multiply buffer by 4 |
82 | rol cache1 |
83 | rol cache2 |
84 | lsl cache0 |
85 | rol cache1 |
86 | rol cache2 |
87 | cp cache0,intime0 |
88 | cpc cache1,intime1 |
89 | cpc cache2,intime2 |
90 | brsh input_counter_ok ; input counter <= buffer*4 |
91 | ldi cache2,0xff ; mark buffer value as invalid |
92 | rjmp care_for_output |
93 | input_counter_ok: |
94 | lsr cache2 ; divide buffer by 4 |
95 | ror cache1 |
96 | ror cache0 |
97 | lsr cache2 |
98 | ror cache1 |
99 | ror cache0 |
100 | |
101 | care_for_output: |
102 | |
103 | mov temp,outtime0 |
104 | or temp,outtime1 |
105 | or temp,outtime2 |
106 | breq new_countdown |
107 | |
108 | ; countdown |
109 | sub outtime0,one |
110 | sbc outtime1,zero |
111 | sbc outtime2,zero |
112 | brne finished |
113 | |
114 | ; toggle output pin |
115 | ldi temp,0b0100 |
116 | out PINB,temp |
117 | |
118 | new_countdown: |
119 | ; check if buffer value is valid |
120 | mov temp,cache2 |
121 | cpi temp,0xff |
122 | breq finished ; buffer value is not valid |
123 | |
124 | ; reload timer counter from buffer |
125 | mov outtime0,cache0 |
126 | mov outtime1,cache1 |
127 | mov outtime2,cache2 |
128 | |
129 | finished: |
130 | sei ; shortcut for reti because there is no main program |
131 | rjmp pc |
132 | |
133 | init: |
134 | ; Port initialization |
135 | ldi temp,0b0000 |
136 | out PORTB,temp |
137 | ldi temp,0b0100 |
138 | out DDRB,temp |
139 | |
140 | ; initialization of constants and variables |
141 | ldi zero,0 |
142 | ldi one,1 |
143 | in inlevel,PINB |
144 | clr intime0 |
145 | clr intime1 |
146 | ldi intime2,0x3f |
147 | clr outtime0 |
148 | clr outtime1 |
149 | clr outtime2 |
150 | ldi cache2,0xff |
151 | |
152 | ; set CPU clock to 8 MHz |
153 | ldi temp,0b0000 ; CPU prescaler 1 |
154 | out CLKPSR,temp |
155 | |
156 | ; timer initialization |
157 | ldi temp,0 |
158 | out OCR0AH,temp |
159 | ldi temp,80-1 ; maximum number of cycles of ISR |
160 | out OCR0AL,temp |
161 | ldi temp,1<<OCIE0A |
162 | out TIMSK0,temp |
163 | ldi temp,1<<WGM02|1<<CS00 ; prescaler 1 |
164 | out TCCR0B,temp |
165 | sei |
166 | |
167 | ; let ISR do its job |
168 | rjmp pc |
Vielen Dank für das Programm. Es funktioniert natürlich sehr gut :) Lässt sich die Frequenz auch z.B. auch um 30% erhöhen? Oder wären dafür grundlegende Änderungen nötig? Ich bin aber trotzdem etwas enttäuscht weil ich das mit dem Aufschwingverhalten in C nicht hinbekomme. Wäre hier für weitere Tipps sehr dankbar.
Einen Spatz mit einer Kanone zu treffen scheint die Lieblingsbeschäftigung von einigen hier zu sein. Einen 08/15 555, auf einen Bereich (Poti) von 100 bis 300 Hz und dahinter einen einfachen Teiler durch zwei. Ist aber wahrscheinlich zu einfach und übersichtlich. Und dann erst die Ausgangsspannung mit einem Poti auf 3,6 V zu trimmen, ist wohl der totale Overkill.
Jürgen F. schrieb: > Lässt sich die Frequenz auch z.B. auch um 30% erhöhen? Oder wären dafür > grundlegende Änderungen nötig? Ich merk grad, dass das Assembler-Programm nicht die Frequenz um 25 % erhöht, sondern die Periodendauer um 25 % verkürzt. Das entspricht einer Frequenzerhöhung um 33 %. Im Prinzip sind auch andere Werte möglich, da aber die ATtinys keinen Multiplizierer besitzen, muss man sich mit Softwaremultiplikation behelfen. Und die klappt am besten, wenn es sich beim Faktor um eine Binärzahl mit möglichst wenig Nachkommastellen handelt (im Programm die Zahl 0b0.11, die dem Faktor 0,75 entspricht). Hier der verantwortliche Teil:
1 | ; write decreased input counter value to buffer |
2 | ; we multiply by 0b0.11 (decimal 0.75) |
3 | mov cache0,intime0 |
4 | mov cache1,intime1 |
5 | mov cache2,intime2 |
6 | lsr cache2 |
7 | ror cache1 |
8 | ror cache0 |
9 | add cache0,intime0 |
10 | adc cache1,intime1 |
11 | adc cache2,intime2 |
12 | lsr cache2 |
13 | ror cache1 |
14 | ror cache0 |
> Ich bin aber trotzdem etwas enttäuscht weil ich das mit dem > Aufschwingverhalten in C nicht hinbekomme. > Wäre hier für weitere Tipps sehr dankbar. Hier fällt mir leider nichts ein, aber vielleicht liest jemand mit, der einen Tipp geben kann...? P.S.: Wenn du einen anderen Faktor als 1,33 brauchst, sag Bescheid, ich schreib dir ein Code-Beispiel.
:
Bearbeitet durch User
Vielleicht ist das Thema noch aktuell. Da ich fertige Programme zur Frequenzmessung und Frequenzerzeugung für AVRs in der 'Schublade' habe, habe ich diese einmal kombiniert. Um nicht zu kleckern, kommt nur ein AVR mit input-capture-Funktion in Betracht, wobei ein ATmega48 schon ausreicht. Ferner ist es viel einfacher, ein Programm in C zu verwenden, bei dem die Eingangsfrequenz als float-Variable vorliegt und beliebig verrechnet werden kann. Ob jetzt die Eingangsfrequenz mit einem konstanten Offset versehen wird oder ein Teiler mit dem Faktor 2,5 bei beliebigen Tastverhältnis benötigt wird, in C kann man es sehr einfach formulieren und programmieren. Quellcode sowie ein fertiges Beispiel f2f_konverter.hex sind hier zu finden. http://www.mino-elektronik.de/Generator/takte_impulse.htm#bsp4
Markus Weber schrieb: > P.S.: Wenn du einen anderen Faktor als 1,33 brauchst, sag Bescheid, ich > schreib dir ein Code-Beispiel. Ich hätte noch eine kurze Frage, wäre es ohne große Änderung auch möglich dass nur die Frequenzen bis z.B. 90Hz verändert werden, und alles was darüber liegt einfach direkt 1:1 weitergegebn wird? Danke
Jürgen F. schrieb: > dass nur die Frequenzen bis z.B. 90Hz verändert werden, und > alles was darüber liegt einfach direkt 1:1 weitergegebn wird? Meinst du das wirklich so: 90 -> 120 91 -> 91 ?? Gruss Reinhard
Jürgen F. schrieb: > dass nur die Frequenzen bis z.B. 90Hz verändert werden, und > alles was darüber liegt einfach direkt 1:1 weitergegebn wird? ... if(f <= 90.0) f = f + 20.0; erzeuge(f); ... Reinhard Kern schrieb: > ?? :-)
Reinhard Kern schrieb: > Meinst du das wirklich so: > > 90 -> 120 > 91 -> 91 > > ?? > > Gruss Reinhard Ja das meine ich tatsächlich so ;) m.n. schrieb: > ... > if(f <= 90.0) f = f + 20.0; > erzeuge(f); > ... Ja, wenn ich jetzt nur wüsste wie ich das in das Assemblerprogramm einbauen muss.... :( Ich denke das C-Programm welches Du weiter oben als Beispiel genannt hast lässt sich nicht mit dem Attiny 10 realisieren.
:
Bearbeitet durch User
Moin! :-) Wie oben schon geschrieben, ist dieser Teil für die Umrechnung verantwortlich:
1 | ; write decreased input counter value to buffer |
2 | ; we multiply by 0b0.11 (decimal 0.75) |
3 | mov cache0,intime0 |
4 | mov cache1,intime1 |
5 | mov cache2,intime2 |
6 | lsr cache2 |
7 | ror cache1 |
8 | ror cache0 |
9 | add cache0,intime0 |
10 | adc cache1,intime1 |
11 | adc cache2,intime2 |
12 | lsr cache2 |
13 | ror cache1 |
14 | ror cache0 |
Für die Fälle, in denen die Periodenlänge kleiner als 10 ms ist und deswegen keine Frequenzänderung erfolgen soll, müsste die Multiplikation durch eine reine Zuweisung ersetzt werden:
1 | mov cache0,intime0 |
2 | mov cache1,intime1 |
3 | mov cache2,intime2 |
Wäre also noch eine entsprechende Abfrage notwendig. Ich kanns leider grad nicht probieren, weil ich den Versuchsaufbau nicht mehr da habe. Testen muss man das auf alle Fälle, denn wenn ich mich recht erinnere, begrenzt das Programm den Faktor auf den Bereich zwischen 1/4 und 1/1. Keine Ahnung, was passiert, wenn man dann direkt an der 1,0-Grenze dran ist. Wahrscheinlich funktionierts ohne Probleme - aber ich würds besser ausprobieren.
Jürgen F. schrieb: > Ich denke das C-Programm welches Du weiter oben als Beispiel genannt > hast lässt sich nicht mit dem Attiny 10 realisieren. So ist es. Minimal wäre ein ATtiny2313 notwendig. Dafür macht er dann alles, was Du möchtest. Es mit einem ATtiny10 zu machen ist auf jeden Fall sehr 'interessant'.
Jürgen F. schrieb: > Ich hätte noch eine kurze Frage, wäre es ohne große Änderung auch > möglich dass nur die Frequenzen bis z.B. 90Hz verändert werden, und > alles was darüber liegt einfach direkt 1:1 weitergegebn wird? Ach so, was mir grad noch einfällt, nach deiner Beschreibung wäre das z.B. so: aus 90 Hz wird 120 Hz aus 91 Hz wird 91 Hz Du hättest dann einen Sprung in der Kennlinie. Ist das so gewollt?
Markus Weber schrieb: > Wäre also noch eine entsprechende Abfrage notwendig. > > Ich kanns leider grad nicht probieren, weil ich den Versuchsaufbau nicht > mehr da habe. > > Testen muss man das auf alle Fälle, denn wenn ich mich recht erinnere, > begrenzt das Programm den Faktor auf den Bereich zwischen 1/4 und 1/1. > Keine Ahnung, was passiert, wenn man dann direkt an der 1,0-Grenze dran > ist. Wahrscheinlich funktionierts ohne Probleme - aber ich würds besser > ausprobieren. Testen wäre kein Problem, leider weiß ich nicht wie eine entsprechende Abfrage zu implementieren wäre. Markus Weber schrieb: > Ach so, was mir grad noch einfällt, nach deiner Beschreibung wäre das > z.B. so: > > aus 90 Hz wird 120 Hz > aus 91 Hz wird 91 Hz > > Du hättest dann einen Sprung in der Kennlinie. Ist das so gewollt? In diesem Fall ja, da auch das Eingangssignal nicht kontinuierlich ist sondern Sprünge hat. Die Eingangsfrequenz springt also z.B. von 70Hz auf 81Hz usw. m.n. schrieb: > Jürgen F. schrieb: >> Ich denke das C-Programm welches Du weiter oben als Beispiel genannt >> hast lässt sich nicht mit dem Attiny 10 realisieren. > > So ist es. Minimal wäre ein ATtiny2313 notwendig. Dafür macht er dann > alles, was Du möchtest. > Es mit einem ATtiny10 zu machen ist auf jeden Fall sehr 'interessant'. Ja, durch den von mir verwendeten Attiny10 ist das ganze natürlich programmiertechnisch nicht so einfach umzusetzen, zumindest nicht für mich. Aber das Programm von Markus funktioniert ja hervorragend mit dem Attiny10, das wäre ja jetzt nur eine Erweiterung zur ursprünglich genannten Anforderung. Danke
:
Bearbeitet durch User
Markus Weber schrieb: > Du hättest dann einen Sprung in der Kennlinie. Ist das so gewollt? Das hatte ich auch schon gefragt, aber m.n. fand das dann furchtbar lächerlich. Anscheinend ist genaueres Nachdenken und Hinterfragen nicht nur unerwünscht, sondern einfach unbekannt. Gruss Reinhard
Reinhard Kern schrieb: > Das hatte ich auch schon gefragt, aber m.n. fand das dann furchtbar > lächerlich. Anscheinend ist genaueres Nachdenken und Hinterfragen nicht > nur unerwünscht, sondern einfach unbekannt. Mach dir keine Gedanken, hier kursieren eben unterschiedliche Ansichten. Manche Leute bringen ihre Ansichten schüchtern rüber, manche weniger schüchtern. ;-) Und es ist ja wirklich so, dass der Weg, für diese Aufgabe einen ATtiny10 zu verwenden, eher ungewöhnlich ist, da hat m.n. schon Recht. Aber wenn die Größe eine Rolle spielt, dann ist SOT-23 sicher keine schlechte Wahl. Billig ist das Teil schließlich auch. Ich hab das Programm mal ergänzt, hier die Änderungen:
1 | *** fq_old.asm |
2 | --- fq.asm |
3 | *************** |
4 | *** 1,5 **** |
5 | ! ; fq 2013-11-23 20:00 |
6 | ! ; version 0.1 |
7 | ; target: ATtiny10 |
8 | --- 1,5 ---- |
9 | ! ; fq 2013-12-15 10:30 |
10 | ! ; version 0.2 |
11 | ; target: ATtiny10 |
12 | *************** |
13 | *** 8,15 **** |
14 | ; |
15 | ; This program reads the frequency of the input signal at PB1 |
16 | ; and provides a similar signal at PB2 with frequency which |
17 | ! ; has been increased by 25 %. |
18 | ! ; operating range: 0.02 Hz .. ca. 10 kHz (resolution is 10 µs) |
19 | ; |
20 | .include "tn10def.inc" |
21 | |
22 | --- 8,16 ---- |
23 | ; |
24 | ; This program reads the frequency of the input signal at PB1 |
25 | ; and provides a similar signal at PB2 with frequency which |
26 | ! ; has been increased by 33 % (if fq < 90 Hz). |
27 | ! ; operating range: 0.02 Hz .. ca. 10 kHz (resolution is |
28 | ! ; isrcpucycles/8MHz) |
29 | ; |
30 | .include "tn10def.inc" |
31 | |
32 | *************** |
33 | *** 28,33 **** |
34 | --- 29,37 ---- |
35 | .def cache1=r29 ; 0..0xfeffff |
36 | .def cache2=r30 ; 0xffxxxx: marked as invalid |
37 | |
38 | + ; constant |
39 | + .equ isrcpucycles= 100 ; maximum number of cycles of ISR |
40 | + |
41 | rjmp init |
42 | .org OC0Aaddr ; timer ISR |
43 | |
44 | *************** |
45 | *** 40,48 **** |
46 | --- 44,57 ---- |
47 | |
48 | ; write decreased input counter value to buffer |
49 | ; we multiply by 0b0.11 (decimal 0.75) |
50 | + ; if fq< 90 Hz |
51 | mov cache0,intime0 |
52 | mov cache1,intime1 |
53 | mov cache2,intime2 |
54 | + cpi cache0,LOW(8000000/isrcpucycles) |
55 | + ldi temp,HIGH(8000000/isrcpucycles) |
56 | + cpc cache1,temp |
57 | + brlo mul_end |
58 | lsr cache2 |
59 | ror cache1 |
60 | ror cache0 |
61 | *************** |
62 | *** 52,57 **** |
63 | --- 61,67 ---- |
64 | lsr cache2 |
65 | ror cache1 |
66 | ror cache0 |
67 | + mul_end: |
68 | |
69 | ; initialize input counter |
70 | clr intime0 |
71 | *************** |
72 | *** 160,166 **** |
73 | ; timer initialization |
74 | ldi temp,0 |
75 | out OCR0AH,temp |
76 | ! ldi temp,80-1 ; maximum number of cycles of ISR |
77 | out OCR0AL,temp |
78 | ldi temp,1<<OCIE0A |
79 | out TIMSK0,temp |
80 | --- 170,176 ---- |
81 | ; timer initialization |
82 | ldi temp,0 |
83 | out OCR0AH,temp |
84 | ! ldi temp,isrcpucycles-1 |
85 | out OCR0AL,temp |
86 | ldi temp,1<<OCIE0A |
87 | out TIMSK0,temp |
Aber ungetestet, also Vorsicht! :-)
Reinhard Kern schrieb: > aber m.n. fand das dann furchtbar > lächerlich. Nein, so war das garnicht gemeint. Manche Aufgaben mögen einem Aussenstehenden so merkwürdig vorkommen, dass die Stirn Falten wirft. Da ist es dann besser, mit einem Lächeln darüber hinweg zu sehen, als ernsthaft nachzufragen und es am Ende doch nicht zu verstehen. Du weißt doch sicherlich aus dem Maschinenbau, dass dort teilweise Sachen so gemacht werden wie vor zig Jahren: Das machen wir immer so. Da hat es dann keinen Sinn, groß zu diskutieren, sondern man 'fügt' sich den Gegebenheiten und die Anwender sind glücklich.
Markus Weber schrieb: > Aber ungetestet, also Vorsicht! :-) Ich habe es mal getestet, leider werden nun alle Frequenzen ungeändert 1:1 durchgereicht. Ich habe den Code nochmal angehängt, vielleicht ist ja noch ein Fehler drin.
Jürgen F. schrieb: > Ich habe es mal getestet, leider werden nun alle Frequenzen ungeändert > 1:1 durchgereicht. > > Ich habe den Code nochmal angehängt, vielleicht ist ja noch ein Fehler > drin. Ja, da ist ein Fehler drin, ich hab vergessen, die 90 Hz mit in die Berechnung reinzunehmen, sorry. :-) Bitte ändere in beiden Fällen das "8000000/isrcpucycles" um zu "8000000/isrcpucycles/90".
Markus Weber schrieb: > Ja, da ist ein Fehler drin, ich hab vergessen, die 90 Hz mit in die > Berechnung reinzunehmen, sorry. :-) > > Bitte ändere in beiden Fällen das "8000000/isrcpucycles" um zu > "8000000/isrcpucycles/90". Hat leider noch immer keine Auswirkung auf das Ausgangssignal.
Jürgen F. schrieb: > Markus Weber schrieb: >> Bitte ändere in beiden Fällen das "8000000/isrcpucycles" um zu >> "8000000/isrcpucycles/90". > > Hat leider noch immer keine Auswirkung auf das Ausgangssignal. OK, Programmierung auf Raten. ;-) Du redest von der Frequenz (90 Hz), das Programm aber misst die Zeit zwischen zwei Flankenwechseln. Und diese Zeit beträgt dann nicht 1/90 s, sondern nur die Hälfte davon, weil eine volle Schwingung ja zwei Flankenwechsel hat. Folglich: 8000000/isrcpucycles/90/2
Jürgen F. schrieb: > Markus Weber schrieb: >> 8000000/isrcpucycles/90/2 > > Leider immer noch 1:1 :-( Dann müsste man nun Schritt für Schritt nach einem Fehler suchen. Zuerst z.B. statt "brlo mul_end" ein "nop" schreiben, um den Zustand des alten Programms zu simulieren usw.
Markus Weber schrieb: > Dann müsste man nun Schritt für Schritt nach einem Fehler suchen. Zuerst > z.B. statt "brlo mul_end" ein "nop" schreiben, um den Zustand des alten > Programms zu simulieren usw. Ja mit einem "nop" werden aus 100Hz wieder 134Hz. Ich habe spaßeshalber hier mal 80 statt 100 probiert.
1 | .equ isrcpucycles= 100 |
Ändert rein gar nichts.
:
Bearbeitet durch User
Seltsam... Das hier wäre noch eine Ecke ausführlicher, hätte gedacht, dass die Abfrage funktioniert. Vielleicht fällt jemand anderem noch etwas auf?
1 | mov cache0,intime0 |
2 | mov cache1,intime1 |
3 | mov cache2,intime2 |
4 | cpi cache0,LOW(8000000/isrcpucycles/90/2) |
5 | ldi temp,HIGH(8000000/isrcpucycles/90/2) |
6 | cpc cache1,temp |
7 | cpc cache2,zero |
8 | brlo mul_end |
Läuft das Teil wirklich mit 8 MHz? Testhalber könntest du noch ein "/8" anhängen...
Ha! Das CLKPSR-Register ist geschützt - wie bei den anderen AVRs auch. Beim ATtiny10 jedoch über das CCP-Register.
1 | ; set CPU clock to 8 MHz |
2 | ldi temp,0xd8 |
3 | out CCP,temp |
4 | ldi temp,0b0000 ; CPU prescaler 1 |
5 | out CLKPSR,temp |
Funktioniert nun wie gewünscht, super. Vielen Dank für Deine Bemühungen :)
Jürgen F. schrieb: > Funktioniert nun wie gewünscht, super. Freut mich. :-) Durch so kleine Projekte lern ich selber auch immer wieder etwas. Mit dem Zwerg ATtiny10 hab ich noch nicht viele Erfahrungen, aber er gefällt mir immer besser.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.