Forum: Projekte & Code Reziproker Frequenzzähler+ Optimierte 64bit uint Routinen


von Matthias H. (mshopf)


Angehängte Dateien:

Lesenswert?

Es gibt hier schon einige Implementierungen von Frequenzzählern, aber 
ich habe noch keine reine Software-Implementierung eines reziproken 
Zählers gesehen. Also habe ich mich selber daran gemacht :-)
Das ganze ist natürlich nur für eng begrenzte Anwendungsbereiche 
sinnvoll.

Zu finden unter  http://www.mshopf.de/proj/avr/freq_meter.html

Läuft auf einem ATtiny2313 mit einem Frequenzbereich von 0Hz..10MHz (bei 
20MHz Takt), Auflösung bis 1e-6Hz bei maximal 10ppm 
Wiederholungsgenauigkeit (relativer Fehler, absoluter Fehler hängt nur 
vom Quarz ab).


Ein reziproker Frequenzzähler zählt sowohl die Zyklen des Systemtakts, 
als auch die der 1-0-Übergänge des Eingangssignals. Der Zählerstart 
erfolgt an einem 1-0-Übergang, und endet nach einer minimalen 
Samplingzeit (500ms hier) wieder an einem 1-0-Übergang. Da das ganze in 
Software realisiert wird, kann ein Interrupt zu genau dem falschen 
Zeitpunkt die Genauigkeit reduzieren - das README erklärt, wie ich auf 
die 10ppm komme. Die Berechnung ist nicht trivial, da könnten also auch 
noch Fehler enthalten sein.

Nach dem Zählen hat man zwei Zahlen: n_CLK (Taktzahl bei einer 
Taktfrequenz f_CPU) und n_EVT (Anzahl an 1-0-Übergängen). Die Frequenz 
ergibt sich dann als:

   f = n_EVT * f_CPU / n_CLK


Das ganze muss in Fixpoint-Arithmetik gerechnet werden - was mal eben 
locker den Bereich einer 32-Bit Zahl sprengt.  Die libgcc-Routinen für 
64 bit Zahlen sind leider viel zu groß für einen ATtiny, deshalb habe 
ich noch ein paar Spezialroutinen in Assembler geschrieben.

Zu finden unter  http://www.mshopf.de/proj/avr/uint64_ops.html

* Bit Shifts (Ersparnis: ca. 200bytes vs. libgcc)
* Multiplikation 64bit x 32bit -> 64bit (Ersparnis über 200bytes in
  Vergleich zu C, viele KB zu libgcc)
* Division 64bit / 32bit -> 64bit (Ersparnis über 150bytes in Vergleich
  zu C, viele KB zu libgcc)

Der Code muss sich leider auf die Registerkonvention von 
Funktionsaufrufen verlassen, weil gcc's asm() noch nicht mit 
64bit-Registern klar kommt.

von Uwe (Gast)


Lesenswert?

Hallo Matthias,

das finde ich ein klasse Projekt !

Wirst Du auch noch Frequenzteiler einbauen?

Z.B.
- MB506 2,4GHz Prescaler
- MB510 2,7GHz Prescaler
- U891BS 1,3 GHz 1: 64
- U813BS 1,1 GHz 1: 64/128/256
- TBB512AG 1:64/ 1:65

Man könnte den Teilungsfaktor über eine Spannungsbereich  0 - 5V an 
einem ADC Port nach dem Reset einlesen.

z.B.: die Faktoren 1, 2, 4, 8, 10, 16, 32, 64, 100, 128, 144, 256, 272

Die Spannungsbereich wären dann U_bereich = 5V / (N+1) 'breit'.

Mit N=13 dann U_bereich = 0,3571V, das entspricht +73dec bei 10 Bit ADC 
Auflösung.

Faktor 1: 0 - 73    ADC-Wert
Faktor 2: 74 - 147
Faktor 4: 148 - 221
usw.


Hier noch ein Beispiel:
http://www.liebl-net.dtdns.net/hard/frequ_counter/frequ_counter.php

Uwe

von Matthias H. (mshopf)


Lesenswert?

Bis jetzt habe ich noch nicht an Frequenzteiler gedacht, weil ich das 
Projekt vor allem als Basis für andere verwenden will (z.B. 
kapazitativen Füllstandsmelder etc.).

Sollte sehr einfach sein, einen Teiler anzuflanschen - die Auto-Range 
Funktion wird dann noch etwas Arbeit vertragen.  ;-)

Durch das Messverfahren sind auch gar nicht mehrere Teilungsfaktoren 
nötig, einer (z.B. 256) reicht völlig - solange die Frequenz nicht unter 
ca. 10Hz fällt und damit das Sampling verlangsamt :-P

Man braucht dann noch einen Switch der schnell genug ist um 10MHz 
Signale durchzulassen - HCT sollte das sein. Es sei denn, Dir fällt ein 
Teiler ein, den man auf Durchzug stellen kann.

Theoretisch(!) könnte man die Unterscheidung auch in Software machen 
(die Funktionalität der beiden Counter vertauschen indem man das 
ungeteilte Signal an T0/INT0 und das geteilte Signal an T1/INT1 
anschliesst und nur eine Kombination auswertet), aber das würde die 
Interrupt-Händler doch deutlich verkomplizieren. Hm, vielleicht doch 
machbar. Mal gucken. Ich wollte die eh' noch in Assembler schreiben, um 
die Wiederholgenauigkeit noch weiter hochzudrehen :-)

von Ales D. (testus)


Angehängte Dateien:

Lesenswert?

Hallo,
ein klasse Modul, schön zu sehen, dass man es auch mit einer reinen 
Softwarelösung in sehr brauchbarer Genauigkeit hinbekommt.
Wegen der Geschichte mit einem Vorteiler würde ich gerne meine Idee 
beisteuern. Ich bin gerade dabei einen Frequenzzähler nach gleichem 
Messprinzip aufzubauen, allerdings habe ich die reine Softwarelösung 
wegen der angesprochenen IRQ Problematik für mich verworfen. Bei meinem 
Zähler wird die Synchronisation zwischen der Messzeit und dem 
Eingangssignal mit TTL gemacht. Danach kommt ein "Vorzähler", der nach 
der Messung ausgelesen wird. Dadurch kann man ohne Umschaltung und mit 
nur einer SW Routine den gesammten Bereich von 0,1 HZ bis ca. 100MHz 
(bisher nur 66MHz ausprobiert) messen. Ich bekomme mit dieser Schaltung 
sehr stabile Ergebnisse, z.B. 6 Stellen Stabil bei 0,1 s Messzeit über 
den ganzen Messbereich.

Habs leider nur als pdf dabei.

Grüße
Ales

von Matthias H. (mshopf)


Lesenswert?

Klar, wenn man eh' zusätzliche Hardware wie Teiler braucht kann man auch 
noch die zwei Logikchips hinzufügen. Ich wollte halt alles vermeiden 
soweit möglich, und für meine Anwendungszwecke sind 10MHz meistens 
ausreichend |-)

Das mit "auslesbaren Teiler" ist auch 'ne nette Idee :-)
Für meinen Fall nur übertrieben, weil die Quarze eh' genügend Drift 
haben (auch wenn ich aus den Datenblättern bzgl. Kurzzeitstabilität und 
Jitter noch nicht schlau geworden bin).

von Ales D. (testus)


Lesenswert?

ist klar, wollte Dein Projekt auch keineswegs schlecht reden, im 
Gegenteil;-)

von Matthias H. (mshopf)


Lesenswert?

Noch ein Hinweis: Mir ist gerade aufgefallen, dass die Berechnung der 
Wiederholgenauigkeit noch einen entscheidenden Faktor vermisst - 10ppm 
sind bei der momentanen Implementierung zu optimistisch (wenn auch die 
Fehler nur sporadisch wg. Interrupts auftauchen).

Ich muss das noch anpassen, und dann die Interrupt-Handler in Assembler 
schreiben (und weitere Interrupts auch dort erlauben - in dem Fall ist 
das möglich).

von Matthias H. (mshopf)


Angehängte Dateien:

Lesenswert?

So, Berechnung gefixed - es hat sich herausgestellt, dass ich sogar zu 
pessimistisch war, der Worst Case konnte so gar nicht eintreten 
(Nebenbedingungen konnten nicht erfüllt werden).
Fehler war schon immer < 8ppm.

Außerdem habe ich jetzt noch die Timer Overflow Interrupts in Assembler 
geschrieben und kann daher Interrupts gleich wieder erlauben. Das 
reduziert die potentiell fehldetektierte Phase, und der maximale 
relative Fehler ist jetzt bei 2.5ppm :-)

von Matthias H. (mshopf)


Lesenswert?

Ach ja, es ist kein Fehler dass das .tgz jetzt kleiner ist - ich habe 
beim 1.0er anscheinend die Kompression vergessen... Oops!

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

so jetzt geht auch mein Accout wieder.

Ich habe hier noch einen ATMega32 Board mit etwas anders beschalteten 
LCD-Display Pins.

Und so habe ich mich dran gemacht die Quellen an zu passen.

Ich hänge mal ein diff der beiden Quellen an und hoffe man kann den Code 
noch korrigieren.

Danke.

von Jupp (Gast)


Lesenswert?

Das Prinzip hat Peter Dannegger schon  2005 vorgeschlagen:

"Man mißt einfach die Perioden über eine gewisse Zeiteinheit und hat
somit immer die gewünschte Auflösung völlig unabhängig von der
Frequenz.


Ich starte die Messung mit einer 1-0-Flanke des Eingangssignals, dann
zähle ich die Perioden über eine Mindestzeit (z.B. 0,5s) und dann
stoppe ich mit der nächsten 1-0-Flanke. Nun habe ich die Periodenanzahl
und die Anzahl der CPU-Zyklen über diese Periodenanzahl:

f_X = f_CPU-Zyklus * Periodenzahl / Zyklenzahl

Und fertig ist die Eingangsfrequenz."

Siehe Beitrag "Frequenzzähler eigenbau"

von Matthias H. (mshopf)


Lesenswert?

@Jupp:
Ja, das Messprinzip ist uralt - Mitte letztes Jahrhundert würde ich 
vermuten. Ich habe keine gute Erklärung im Forum auf die Schnelle 
gefunden, danke für die Referenz!

@Uwe:
Die ersten paar Deiner Änderungen sind ja Konfigurationsänderungen, also 
lokal für Dich. Die Änderungen für den ATmega muss ich mir nochmal 
genauer angucken, danke schon mal! Ich hatte ja schon vermutet, dass 
ATmega (noch) nicht geht.
P.S. Nächstes mal den diff bitte mit -up erstellen - das erleichtert die 
Kontext-Suche ungemein und unified diff ist einfacher zu lesen.

Ausserdem ist es nie zu spät für Fixes :-)

BTW - wenn jemand eine bessere Idee hat als nach AVR_ATtiny2313 und 
AVR_ATtiny2313A zu überprüfen (z.B. Familie?), immer her mit der 
Information.

von Matthias H. (mshopf)


Angehängte Dateien:

Lesenswert?

@Uwe:
Ich habe Deinen Patch als Basis genommen, das Teil fuer ATmega8 und 
ATmega48 kompatibel zu machen. War zwar eigentlich gar nicht schwer, 
aber...

Da bei den ATmegas ja nicht 7 pins an einem Port frei sind, habe ich die 
LCD-Routinen noch aufgebohrt, um zwei verschiedene Ports für Daten- und 
Kontroll-Pins benutzen zu können. Natürlich habe ich die Änderung erst 
gemacht nachdem ich die Schaltung für den ATmega geändert habe.

Dabei habe ich es dann geschafft, einen Pin des ATmega8 bei Stecken auf 
das Steckbrett umzubiegen - das Debugging hat dann Stunden gekostet 
:-/

von Uwe S. (de0508)


Lesenswert?

Hallo Matthias,

vielen Dank !


Ich habe gerade noch andere Projekte ab zu schließen und werde dann 
sofort durchstarten, erst mal mit dem ATmega32 Board, dann später mit 
einer ATmega8x-20 Lösung.

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

hier ein Schaltplan für den ATmega88-20PU mit den neuen Anschlüssen für 
ein LCD-Display.

LCD R/#W braucht man i.a. gar nicht.
Verwendet Du die Steuerleitung ?

Ich lasse sie häufig einfach weg, wenn ich nur das LCD-Display 
beschreiben will.

Das Busy-Flag des HD44780 Kontrollers werte ich nicht aus, sondern 
arbeite mit Wartezeiten.

von Matthias H. (mshopf)


Lesenswert?

Die LCD-Routinen sind lose auf Dick Streefland's Routinen basiert - und 
er wartet auf das BUSY flag. Theoretisch haben die den Vorteil, dass 
sie weniger Zeit brauchen. Praktisch sollte es egal sein.

Ich hatte auch schon mal nur Timeout-basierte Routinen, vielleicht 
benutze ich die auch mal wieder. Könnten auch weniger Platz benötigen.

von Stephan H. (stephan-)


Lesenswert?

Ich mache es gern so das das LCD 1:1 im RAM liegt. Das Display wird dann 
in einer ISR aktualisiert. Einfach den RAM Wert +30h auf das Display 
augeben.
Sind bei 4x16 alle 4ms ein Zeichen bei 4 Aktualisierungen/Sekunde.
Sofern man genügend RAM hat.

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

ich habe jetzt meine Schaltung auf eine Platine gelötet und mit einem 
ATmega48-20 mit 20Mhz SMD Quarz bestückt.

Entgegen meinem Schaltplan ist ein AVR ISP-10 Pol Stecker montiert, da 
mein usbasp auch damit ausgestattet ist.

Zur LED; die ist doch nun an Port B Pin 0 an zuschließen ?
1
// main.c
2
#define LED_PORT PORTB
3
#define LED_DDR  DDRB
4
#define LED_BITS (1<<0)

Ich verwende ein LCD-Display mit 2x 16 Zeichen.

Was muss denn in
1
// main.c
2
#define LCD_LINES  2  // 1 or 2 lines
3
#define LCD_COLS  16  // 10col display (2*5) - only required for wrap

stehen?

Mit diesen Angaben sehe ich viele blinkende Zeichen in der zweiten Zeile 
?!
In der ersten Zeile steht an Pos 15 [0,..15] auch ein "?" - komisch.

Mit offenen Eingang zählt er schön die 50. Hz Schwingungen...

Ich sehe immer in der zweiten Zeile:
1
Hz_s0.19305____O

"_" sind Leerzeichen und unter dem "O" seht der blinke Kursor.

Soll das so sein ?

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

ich habe noch einige kleine Änderungen am code vorgenommen, so dass nun 
meine Anzeige nicht mehr spinnt!

Mit 20MHz Takt ist wohl das LCD-Display aus dem tritt gekommen.

Mehr siehe Anlage.

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

ich habe noch einige kleine Änderungen am code vorgenommen, mehr in der 
Anlage.

.

von Matthias H. (mshopf)


Lesenswert?

Stephan Henning schrieb:
> Ich mache es gern so das das LCD 1:1 im RAM liegt. Das Display wird dann
> in einer ISR aktualisiert. Einfach den RAM Wert +30h auf das Display
> augeben.

Klar. Nur sind die Interrupts in diesem Fall schon gut belegt :-)

> Zur LED; die ist doch nun an Port B Pin 0 an zuschließen ?

Wo Du willst. B0 war bei mir noch frei :-)
Die LED ist eh' nur fuers Debugging.

> Ich verwende ein LCD-Display mit 2x 16 Zeichen.

In dem Fall kannst Du Dir überlegen, ob Du nicht Hz noch an die Zahl in 
der 1. Zeile angehängt haben willst, und die 2. Zeile für was anderes 
aufhebst.

> Mit 20MHz Takt ist wohl das LCD-Display aus dem tritt gekommen.

Ich hatte es befürchtet :-(
Zu meiner Schande muss ich gestehen, dass ich 20MHz mangels Quarz nie 
getestet habe. Die Zeiten in den LCD-Routinen werden wohl noch nicht 
alle eingehalten. Kann auch an parasitären Kapazitäten liegen.

> ich habe noch einige kleine Änderungen am code vorgenommen,
> mehr in der Anlage.

Zumindest die Protokolländerung ist definitiv falsch. Tipp: schau Dir 
das 0x32 mal an, wenn Du annimmst, dass das Interface im 8bit Modus 
initialisiert ist. -> 0x30, 0x20 -> Interface ist jetzt(!) im 4bit 
Modus.
Das setzen in den 8bit Modus (0x30) mehrmals davor ist nötig, weil sonst 
die Synchronisation nicht funktioniert (low- und hi-nibble können nicht 
unterschieden werden).
Im Übrigen ist das die Standard-Initialisierungssequenz, und steht so 
sogar im Datenblatt.

Ich verstehe nicht, warum Deine Sequenz überhaupt funktioniert. Die 
sollte entsprechen:
write4 0x3  -> Interpretiert im 8bit-Mode: 0x30  -> Switch in 8bit
write  0x28 -> 0x2 Interpretiert im 8-bit-Mode: 0x20 -> Switch in 4bit
               0x8 Interpretiert im 4-bit-Mode als Hi Nibble
-> off by one Nibble.
Und davon erholt sich das Protokoll auch nicht mehr. Warum es trotzdem 
geht? Keinen blassen Schimmer...

Das 1. lcd_clear() sollte nicht nötig sein, laut Datenblatt wird beim 
init das RAM gelöscht. Kannst Du das mit Deinem Display verifizieren? 
Kann natürlich sein, dass sich verschiedene Kontroller verschieden 
verhalten...

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

Das ist ja auch kein Glaubenskrieg mit der LCD-Initialisierung.

Ich verwende dieses Schema, siehe Seite 46.

Mir ist das aufgefallen, als ich ein 1x16 Zeichen Display angeschlossen 
hatte und dies - nach Anpassung der Firmware - doch im Modus 2 zeiliges 
Display initialisiert wurde.

Dafür habe ich dann in der Funktion
1
void lcd_init (void)
 die Zeile
1
lcd_write (0x32); // Switch to 4-bit mode
 ausgemacht.

Mein LCD-Display hat den HD44780 von Hitachi drauf !

Auch kann die Zeile
1
/* Init LCD */
2
lcd_init();

entfernt werden.
Steht schon in der Funktion
1
void lcd_init (void)
.

Vielleicht übernimmst Du die wichtigsten Änderungen in die nächste 
Version?
1
static inline void lcd_write4 (..)
2
3
asm volatile("rjmp 0f\n0:");
4
5
static inline uint8_t lcd_read4 (void)
6
7
asm volatile("rjmp 0f\n0:");

.

von Matthias H. (mshopf)


Lesenswert?

Uwe S. schrieb:
> Das ist ja auch kein Glaubenskrieg mit der LCD-Initialisierung.
> Ich verwende dieses Schema, siehe Seite 46.

Tja, genau das machst Du ja leider nicht, siehe mein Kommentar oben :-)

Auf Seite 46 steht:
wait 15ms, 4bit 0x3, wait 4.1ms, 4bit 0x3, wait 100us, 4bit 0x3, 4bit 
0x2,
- Ab jetzt busy flag checken
2x4 bit 0x28, alle weiteren Befehle 2x4bit von nun an.

0x28 nur bei 2-zeiligen Displays. Bei einzeiligen 0x20.

Das macht genau
1
    _delay_ms  (15.0);
2
    lcd_write4 (3);
3
    _delay_ms  (4.1);
4
    lcd_write4 (3);
5
    _delay_ms  (0.1);
6
    lcd_write  (0x32);          // Switch to 4-bit mode
7
    lcd_write  (0x20 +          // Function set: 4-bit mode + #lines
8
                (LCD_LINES-1)*0x08);

das lcd_write(0x32) macht lcd_write4(0x3), lcd_write4(0x2), 
wait-for-busy.

> Mir ist das aufgefallen, als ich ein 1x16 Zeichen Display angeschlossen
> hatte und dies - nach Anpassung der Firmware - doch im Modus 2 zeiliges
> Display initialisiert wurde.

Na kein Wunder, wenn Du LCD_LINES nicht auf 1 setzt ;-)
Oder hast Du das mit Anpassung gemeint?

Das einzige, was mir einfällt wenn das bei Dir immer noch nicht gehen 
sollte: Probiere:
1
     lcd_write4 (0x3);
2
     _delay_ms  (0.1);
3
     lcd_write4 (0x2);
4
     _delay_ms  (0.1);
statt dem lcd_write (0x32);
Das verhindert die Busy-Flag-Abfrage für diesen einen Befehl.

> Mein LCD-Display hat den HD44780 von Hitachi drauf !

Standard. Ich wage nur zu bezweifeln, dass er wirklich von Hitachi ist 
- da gibt es sooo viele Nachbauten :-)

> Auch kann die Zeile
1
/* Init LCD */
2
lcd_init();
> entfernt werden.
> Steht schon in der Funktion
1
void lcd_init (void)
.

?!? Das eine ist die Definition, das andere der Aufruf...

> Vielleicht übernimmst Du die wichtigsten Änderungen in die nächste
> Version?

Die Zeiten in lcd_{read,write}4 überprüfe ich auf alle Fälle nochmal! 
Deine Änderungen sind schon mal ein guter Start, aber ich befürchte, 
dass andere Timings auch nicht eingehalten werden und nur zufällig gehen 
:-]

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

ich habe den Code Version 1.2 noch ein wenig erweitert.
Wie schon in einer E-Mail berichtet, gehen meine Bestrebungen in 
Richtung Amateurfunk, deshalb ist die Skalierung der Anzeige in "kHz", 
"MHz" und "GHz" durch die Definition der folgender Konstanten möglich:
1
#define FREQ_KHZ
2
// #define FREQ_MHZ
3
// #define FREQ_GHZ

Werden die Defines nicht gesetzt, so wird wieder "Hz" angezeigt und 
gerechnet.

Die benötigten Definitionen und Strings sind in
1
int main(void)
 definiert:
1
enum {FREQ_ID_HZ=0, FREQ_ID_KHZ, FREQ_ID_MHZ, FREQ_ID_GHZ};
2
const char *a_freq_text[] = {" Hz","kHz","MHz","GHz"};
3
const char *c_freq_text = a_freq_text[FREQ_ID_HZ];

Des weiteren habe ich noch die Funktion
1
extern double pow(double __x, double __y);
gegen das Makro
1
_POW_10_FUNC(n)
ersetzt.
Das spart viel Zeit, die sonst durch den Aufruf der Funktion
1
pow(10,n)
 benötigt würde.

Für einzeilige 16 Zeichen Displays habe ich einen eigenen Abschnitt 
eingefügt:
1
#if LCD_LINES == 1  // 20100824 -us 
2
:
3
#else  // LCD_LINES = 2

Die weiteren Anpassungen an den 20MHz Quarz in lcd_hd44780u.h sind aus 
dem Diff ersichtlich.

Für die Messungen von HF Signalen plane ich geraden einen HF-Verstärker 
und die Umsetzung von Signalen >10MHz durch einen Frequenzteiler.
Mit liegen noch Exemplare des U6064BS vor, das sollte ein 1:64 Teiler 
sein und U664BS ähneln.

Bild U6064BS - http://fa-nwt.akadns.de/dl/2010-08-24/U6064BS-350px.jpg

Datenblatt U664BS - 
http://www.elektronika.ba/forum/uploadz/1207428634_U664BS_Prescaler.pdf

.

von m.n. (Gast)


Lesenswert?

Seinerzeit gab es nur AT90S2313 mit 10MHz, sodass darauf basierend 
folgende Schaltung entstand:
http://www.mino-elektronik.de/fmeter/fmeter.htm

Meines Erachtens war (und ist) es sinnvoller, einen externen Vorteiler 
automatisch hinzuzuschalten, der nichts kostet und gleich zig MHz 
verarbeiten kann.
Heute kann man bequem einen ATmega168-20 nehmen und braucht sich um 
Speicherplatz keinen Kopf zu machen.

Für einfache Anwendungen reicht auch das Beispielprogramm:
http://www.mino-elektronik.de/fmeter/fm_software.htm

Viel Spatz!

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe für meine Anwendung eine erweiterte Schaltung entwickelt und 
eben so die Basissoftware von Matthias angepasst und erweitert.

Es gibt 2 Kanäle, K1 misst bis 39 Mhz, K2 bis 2.700Mhz.

Ein 1:4 Vorteiler 74HC74 und eine Kanalumschaltung mit 
Signalaufbereitung 74HC132 sitzt vor dem ATmega48.

Der 74HC74 sorgt, mit seiner 1:4 (Vor-)Teilung, für ein sauberes 50%:50% 
Signal am ATmega48 20Mhz, daraus ergibt sich dann auch die maximale 
Messfrequenz von K1.

Die maximale Messfrequenz von 2.700Mhz werden mit deinem MB510, 1:128 
Teiler erreicht und er verkraftet mit einem 3dB Pad +13dBm Pegel am 
Eingang. Von den 2.700Mhz liegen am ATmega48 nach Teilung nur 2.700Mhz/ 
512 = 5,2734MHz an!

Software Funktionen

* Das Anzeigenformat kann mit dem 2-pol Dip-Schalter zwischen Hz, kHz, 
MHz und GHz gewählt werden.

* Die Kanalumschaltung erfolgt mit einem Taster.

* Die Voreinstellung der Teilungsfaktoren mit zwei der drei 10kR 
Trimmer.

* Der dritte Trimmer ist für die Anzeige von Mode 
("FAX","FSK","LSB","USB","CW ","FM ","AM ") vorgesehen.


Ihr seht in den Bildern die nicht kalibrierte Messungen bis 200 MHz und 
der Eingangspegel beträgt dabei zwischen +6dBm und 0dBm.

Einige der HF-Bauteile sind SMD1206 und auf der Platinenunterseite zu 
finden.

.

von Peter D. (peda)


Lesenswert?

Uwe S. schrieb:
> Ich verwende dieses Schema, siehe Seite 46.

Da ist leider ein Fehler drinn.
Vor dem letzten Kasten mit den 5 Instruktionen muß auch nochmal 100µs 
gewartet werden, damit der Befehl ausgeführt wird.


Peter

von Stephan H. (stephan-)


Lesenswert?

Matthias Hopf schrieb:
> Stephan Henning schrieb:
>> Ich mache es gern so das das LCD 1:1 im RAM liegt. Das Display wird dann
>> in einer ISR aktualisiert. Einfach den RAM Wert +30h auf das Display
>> augeben.
>
> Klar. Nur sind die Interrupts in diesem Fall schon gut belegt :-)

klar, aber dafür gibts ja ein Interrupt Prioritätenregister.
Wenn man eins hat. :-)

von Uwe S. (de0508)


Lesenswert?

Hallo Matthias,

gibt's bei diesem Projekt von Dir noch Neuigkeiten ?

von Matthias H. (mshopf)


Lesenswert?

Noch immer leider EBUSY. :-/

Wenn Du einen tarball mit Deinem Source hier einstellen willst, nur zu!
Deine Patches sind nicht verloren, nur muss ich nur mal wieder einen 
Timeslot fuers Hardware- und Softwarebasteln finden, und da sieht's bis 
zum Jahresende schlecht aus :-(

von Besucher (Gast)


Lesenswert?

Hallo allerseits,

beim kompilieren der oben genannten main.c erscheint jedesmal die 
Warnung: internal compiler error: in start_function, at c-decl.c:6035

Kann mir jemand helfen?

Danke schon mal im voraus.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Besucher schrieb:

> Warnung: internal compiler error: in start_function, at c-decl.c:6035
>
> Kann mir jemand helfen?

Mit diesen Angeben: Nein.

Geraten: wirf das Attribut von main() raus.

Falls das nicht hilft: übersetzte zusätzlich mit den Compiler-Schaltern
 -v -save-temps -g3
und poste hier das main.i sowie die Kommandozeilen-Ausgabe vom avr-gcc.

von Besucher (Gast)


Lesenswert?

Hallo Johan,

hier die main():

extern int main (void)
{
    uint8_t i;
    char buf[16];
    enum {FREQ_ID_HZ=0, FREQ_ID_KHZ, FREQ_ID_MHZ, FREQ_ID_GHZ};
    const char *a_freq_text[] = {" Hz","kHz","MHz","GHz"};// 20100825 
-us
    const char *c_freq_text = a_freq_text[FREQ_ID_HZ];

usw.

Übrigens die komplette Datei ist unter 'freq_meter-20100825.zip' von Uwe 
S. zu finden

Gruß

von Bingo (Gast)


Lesenswert?

Die datei compiliert ok hier (Linux - Ubuntu 10.04).

avr-gcc v 4.3.4 , avr-libc 1.7.0
build mit diser script (incl. Jörgs FReeBSD patches)
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=95328&start=0&postdays=0&postorder=asc&highlight=
1
freq_meter-20100825$ make
2
avr-gcc -mmcu=atmega48  -Os -g -Wall -I. -c main.c
3
avr-gcc -mmcu=atmega48   -Os -g -Wall -I. -Wl,-Map,freq_meter.map,--cref -o freq_meter.out main.o
4
   text     data      bss      dec      hex  filename
5
   1898       22        6     1926      786  freq_meter.out
6
avr-objcopy -O ihex -j .text -j .data freq_meter.out freq_meter.hex

Probiere die "avr-gcc -v" kommando , funktioniert auch unter Win.
1
freq_meter-20100825$ avr-gcc -v
2
Using built-in specs.
3
Target: avr
4
Configured with: ../../source/gcc-4.3.4/configure -v --target=avr --disable-nls --prefix=/usr/local/avr --with-gnu-ld --with-gnu-as --enable-languages=c,c++ --disable-libssp --with-dwarf2
5
Thread model: single
6
gcc version 4.3.4 (GCC)

mfg
Bingo

von Besucher (Gast)


Lesenswert?

Hallo Bingo,

ich habe nicht ganz verstanden was du mit
>Probiere die "avr-gcc -v" kommando
meinst. Ich kompiliere mit 'Programmer's Notepad' v2.0.7.667-devel

mfg

von Uwe S. (de0508)


Lesenswert?

Hallo Gast, ein Name wäre schön.

es geht uns um die gcc-avr version, so heisst der AVR "C" Compiler und 
mit dem Aufruf "avr-gcc -v" erhält man eine Versionsnummer.

Ich habe auch unter ubuntu "gcc-Version 4.3.4 (GCC)"

Unter welchem BS arbeitest Du, ich denke mit dem Hinweis "Programmer's 
Notepad" ist es ein Win*OS ?

.

von Besucher (Gast)


Lesenswert?

Hallo Uwe,

die Version ist avr-gcc 3.4.6

BS ist Win-XP

mfg

von Uwe S. (de0508)


Lesenswert?

Hallo,

dann müsstest Du auf die avr-gcc Version 4.3.x umstellen.

von Besucher (Gast)


Lesenswert?

Hallo Uwe,

die Umstellung werde ich morgen machen, jetzt geht es leider nicht mehr.

Schönen Dank und guten Abend an euch allen.

von Uwe S. (de0508)


Lesenswert?

Hallo Michael D.,

ich antworte lieber hier, wo es hingehört.

Ja ich habe nach einem Jahr das gesamte Programm umgestellt, teilweise 
neu geschrieben und die LCD-Routine gegen die von Peter Fleury 
getauscht.

 Title  :   HD44780U LCD library
 Author:    Peter Fleury <pfleury@gmx.ch>  http://jump.to/fleury
 File:      $Id: lcd.c,v 1.14.2.1 2006/01/29 12:16:41 peter Exp $

Danach hatte ich keinerlei Probleme mehr !

Nun bedient die Firmware 2 Eingangskanäle; über einen Taste, kann man 
den Kanal wechseln - kurz Drücken - und die Anzeige zwischen Hz, kHz, 
MHz und GHz umschalten - lang Drücken -.

Es gibt getrennte Prescaler E {1,64,128,256} für jeden Kanal, die über 
Jumper gesetzt werden.

Und über die serielle Schnittstelle mit 19200 Baud,8,N,1 werden alle 
Messwerte ausgegeben und lassen sich auch die Kanäle "1","2" und die 
Einheiten mit "h","k","m","g" umschalten.

In der zweiten Zeile wird noch die Spannung an ADC0 (0-5V), über 64 
Werte gemittelt, als Bargraph aus gegeben.

Das war's so weit.

.

von Matthias H. (mshopf)


Lesenswert?

Hallo Uwe,

danke dass Du Dich noch um das Projekt kuemmerst :-)
Ich habe den Job gewechselt, und jetzt leider erst mal noch weniger 
Zeit als bisher fuer solche Sachen :-/

Die LCD-Routinen von Peter Fleury werde ich mir auf jeden Fall mal 
ansehen - es wurmt mich immer noch, dass ich nicht verstehe, warum meine 
Funktionen bei Dir nicht funktioniert haben...

Danke nochmal!

von Uwe S. (de0508)


Lesenswert?

Hallo Matthias,

Du hast ja meine Quellen, damit sollte es einfach sein, einen Vergleich 
zwischen beiden Libs zu machen.

Gerade stelle ich einen - für mich - endgültigen Schaltplan zusammen und 
dann geht's an das Platine layouten.

von Peter D. (peda)


Lesenswert?

Matthias Hopf schrieb:
> Die libgcc-Routinen für
> 64 bit Zahlen sind leider viel zu groß für einen ATtiny, deshalb habe
> ich noch ein paar Spezialroutinen in Assembler geschrieben.

Da könnte das hier helfen:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=113673


Ich habs bei AVRFreaks reingestellt, weil man da noch editieren kann bei 
Fehlern oder Erweiterungen.


Peter

von Uwe S. (de0508)


Lesenswert?

Hallo Peter,

nochmals danke für den 64-Bit Code, denn nun weiter verbessert hast ?!

Ich habe die 64-Bit Lib in mein Projekt eingefügt und die alten Aufrufe 
durch zwei Macro ersetzt.
1
// #define USE_64BIT_HOPF
2
3
#ifdef USE_64BIT_HOPF
4
5
# define BIND static
6
# include "uint64_ops.h"
7
8
#else
9
10
# define uint64_mul32(arg1, arg2)  ((arg1) * (arg2))
11
# define uint64_div32(arg1, arg2)  ((arg1) / (arg2))
12
13
#endif

Damit bleibt das Programm das selbe und ein Vergleich ist einfach 
möglich.

Da ich im Augenblick den atMega48 mit 4k im Frequenzzähler benutze, war 
der Flash-Speicher mit aktivierter uart0 lib (PeDa) nicht mehr 
ausreichend.
Nach deaktivieren konnte ich den Code testen.

Hier noch der Speicherbedarf:
1
// 64-Bit/ 32-Bit M. Hopf original 
2
Size after:
3
   text     data      bss      dec      hex  filename
4
   3514        8       84     3606      e16  main.elf
5
6
// 64-Bit von PeDa
7
Size after:
8
   text     data      bss      dec      hex  filename
9
   3868        8       84     3960      f78  main.elf

Und das wird alles berechnet:
1
  // Berechne die neue Frequenz
2
  const uint64_t fact = (uint64_t)(F_CPU * pow (10, MIN_RES) + .5);
3
4
  /* This would overflow in 32bit
5
   * (actually fact may even be larger than UINT32_MAX) */
6
  uint64_t frq = uint64_mul32(fact, cnt);
7
8
  // Runden f = f/nom + 1/2;
9
  frq += (nom >> 1);    // Runden
10
  frq = uint64_div32(frq, nom);
11
12
  // Frequenzbereich festlegen auf E {Hz, kHz, MHz, GHz}
13
  uint32_t freq_range = FREQ_RANGE();
14
15
  if ((uint8_t)freq_range == 1) {
16
    frq = uint64_mul32(frq, (uint32_t)prescale); 
17
18
  } else {
19
20
    frq = uint64_mul32(frq, (uint32_t)prescale); 
21
    frq = uint64_div32(frq, freq_range);
22
  }
23
24
  /* Find precision (auto-range) */
25
  uint8_t s = MIN_RES;
26
27
  while (s && frq >= (uint64_t)pow(10, MAX_RES)) {
28
    frq += 5;        // Runden
29
    frq = uint64_div32(frq, 10);
30
    s--;
31
  }

von Peter D. (peda)


Lesenswert?

Warum verwendest Du pow() um eine Konstante auszurechnen?
Damit kommt doch die float-Lib mit rein.


Peter

von Uwe S. (de0508)


Lesenswert?

Hallo Peter,

das dachte ich auch am Anfang, aber das wird wirklich beides mal als 
Konstante berechnet und verwendet.

Mein Workaround habe ich deshalb wieder gelöscht..

Auszug aus main.lst
1
**** const uint64_t fact = (uint64_t)(F_CPU * pow (10, MIN_RES) + .5);
2
 244:main.c        ****   uint64_t frq = uint64_mul32(fact, cnt);
3
 1180                 .LM128:
4
 1181 011e 20E0          ldi r18,lo8(0)
5
 1182 0120 38EC          ldi r19,lo8(-56)
6
 1183 0122 47E1          ldi r20,lo8(23)
7
 1184 0124 58EA          ldi r21,lo8(-88)
8
 1185 0126 64E0          ldi r22,lo8(4)
9
 1186 0128 70E0          ldi r23,lo8(0)
10
 1187 012a 80E0          ldi r24,lo8(0)
11
 1188 012c 90E0          ldi r25,lo8(0)
12
 1189 012e 00D0          rcall uint64_mul32
13
 1190 0130 E32F          mov r30,r19
14
 1191 0132 F42F          mov r31,r20
15
 1192 0134 552E          mov r5,r21
16
 1193 0136 462E          mov r4,r22
17
 1194 0138 372E          mov r3,r23
18
 1195 013a 282E          mov r2,r24
19
 1196 013c 9A8B          std Y+18,r25
1
267:main.c        ****   while (s && frq >= (uint64_t)pow(10, MAX_RES)) {
2
 1415                 .LM139:
3
 1416 028a 01F0          breq .L68
4
 1417                 .L53:
5
 1418 028c FF20          tst r15
6
 1419 028e 01F4          brne .L69
7
 1420 0290 0023          tst r16
8
 1421 0292 01F4          brne .L69
9
 1422 0294 1123          tst r17
10
 1423 0296 01F4          brne .L69
11
 1424 0298 BB23          tst r27
12
 1425 029a 01F4          brne .L69
13
 1426 029c F630          cpi r31,lo8(6)
14
 1427 029e 00F0          brlo .+2
15
 1428 02a0 00C0          rjmp .L69
16
 1429 02a2 F530          cpi r31,lo8(5)
17
 1430 02a4 01F4          brne .L68
18
 1431 02a6 E63F          cpi r30,lo8(-10)
19
 1432 02a8 00F0          brlo .+2
20
 1433 02aa 00C0          rjmp .L69
21
 1434 02ac E53F          cpi r30,lo8(-11)
22
 1435 02ae 01F4          brne .L68
23
 1436 02b0 A13E          cpi r26,lo8(-31)
24
 1437 02b2 00F0          brlo .+2
25
 1438 02b4 00C0          rjmp .L69

von Peter D. (peda)


Lesenswert?

Uwe S. schrieb:
> Hier noch der Speicherbedarf:// 64-Bit/ 32-Bit M. Hopf original
> Size after:
>    text     data      bss      dec      hex  filename
>    3514        8       84     3606      e16  main.elf
>
> // 64-Bit von PeDa
> Size after:
>    text     data      bss      dec      hex  filename
>    3868        8       84     3960      f78  main.elf

D.h mit meiner Lib sind es 354 Byte mehr.
Meine Lib ist aber nur 208 Byte groß. Bist Du sicher, daß Du die andere 
auch rausgenommen hast?.

Die Vermutung mit float lag daran, daß >3kB ja schon recht viel ist.
Hast Du vielleicht das komplette Programm zum Compilieren verfügbar?


Peter

von Frank Zähler (Gast)


Lesenswert?

Hallo,

ich bin auf dieses Projekt gestossen, weil ich mir "eben schnell" mit 
einfachen Mitteln einen Frequenzzähler aufbauen wollte.
Jetzt bin ich dabei den Code mit dem Atmel Studio 6.2 zu bearbeiten.

Dabei bin ich in der main.c der Version 1.2 auf die folgende Dinge am 
Ende vom Assembler-Abschnitt getroffen, die ich nicht verstehe :
1
: : "M" ((uint16_t) (MIN_SAMPLE_TIME * F_CPU / 65536.0)),
2
    "I" (_SFR_IO_ADDR (EIMSK)), 
3
    "M" (1 << INT0),
4
    "I" (_SFR_IO_ADDR (EIFR)));

Könnte mir vielleicht mal jemand erklären was obiges bewirkt ? Ich 
könnte mir vorstellen das die Daten aus "M" in die Register "I" EIMSK 
und EIFR geschrieben werden sollen ??
Das problem ist, _SFR_IO_ADDR scheint das Atmel Studio nicht (mehr?) zu 
kennen. Vielleicht kann man dafür was anders angeben ?

Im voraus schon mal Danke

von Frank Zähler (Gast)


Lesenswert?

Habe es selber rausbekommen ^^

Sorry für das rauskramen des ollen Threads ;-)

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.