Berechnung eines RC-Gliedes für's Einstellen der Frequenz

Hemi

Aktives Mitglied
Premium Benutzer
30. Nov. 2008
1.103
19
38
Korntal-Münchingen, Germany
Sprachen
  1. ANSI C
  2. C++
  3. PHP
  4. Java
Servus zusammen,

irgendwie stehe ich auf dem Schlauch und brauche Eure Schwarmintelligenz. :)

Also, es existiert ein Rechtecksignal, das so aufgebaut ist:

signal.png

Dieses Signal wird von einem Multiplexer erzeugt. Der Multiplexer hat acht Eingänge (Switch 1, Switch 2, ....), die gelesen werden und dann die Flanken im Signal gesetzt werden (die Flächen mit einem X).

Dieser Multiplexer läuft mit einer Frequenz, die über ein RC-Glied eingestellt wird, nach der Formel:

formel.png
Also: fosc = 1/(Cosc * (0,79*Rosc + 2260))

Soweit so gut und auch verständlich.

Dann, gibt es im Datenblatt eine "Referenztabelle", die so aussieht:
werte.png

Und davon abgeleitete Timings:
timings.png

Auch, soweit so gut. One bit von 0,156ms entspricht 6,4kHz Frequenz, bzw. 6,41kHz.

Wenn ich aber die Werte für Cosc und Rosc in die Formel oben einfüge, bekomme ich ein ganz anderes Ergebnis:

fosc = 1/(0,000000001 * (0,79*200000 + 2260))
fosc = 1/(0,000000001 * 160260)
fosc = 1/0,00016026
fosc = 6239,86Hz

Es sollten aber 6410Hz rauskommen, das entspricht einer Abweichung von rund 170Hz, das ist schon heftig, wie ich finde.

Habe ich einen Denkfehler?

Hier ist das Datenblatt zum Multiplexer und Demultiplexer: klick mich

Die Formel ist direkt auf der Seite 1, die Timings und Referenzwerte finden sich auf der Seite 5.

Vielen Dank Euch!
 
Na ja, so ganz heftig finde ich 'ne Abweichung von 2,6 % jetzt nicht grade. Bei Bauteiltoleranzen von 10-20 % kann man das vernachlässigen.
Auf Seite 15 steht, daß die Oszillatorfrequenz für den Transmitter minimal 1kHz und maximal 20kHz betragen darf.
Und da der Receiver mit vierfacher Frequenz sampled, sollte so eine kleine Abweichung locker ausgeglichen werden können.
 
Hallo Hemi,

ich komme auf dasselbe Ergebniss und einen Denkfehler kann ich nicht erkennen.

Im Abschnitt 1.2 ist zwar auch von Toleranzen des internen Widerstands die Rede aber mit dem 100fach größeren externen Widerstand macht der gerade mal knapp 1% aus und die Abweichung liegt bei 2,7%.

Vielleicht ein Fehler im Datenblatt ? Vielleicht nochmal beim Hersteller danach suchen ... ?

Gruß
Pirx
 
Zuletzt bearbeitet:
Hallo Ihr Beide, danke schön erstmal :)

Hersteller kontaktieren ist so eine Sache, den Chip gibt seit 15 Jahren nicht mehr und Temic ist jetzt nicht der Hersteller, der gern weiterhilft...

So wie ich es rauslese, beträgt der MUX und DEMUX Verhältnis 1:4, sprich der Demultiplexer läuft mit 4x Multiplexerfrequenz.

Ich habe ein Signal, was eine Bitzeit von 0,17ms hat (statt 0,156ms von oben). Meint es wird trotzdem noch gehen? Oder soll ich RC doch etwas anpassen?
 
Im Datenblatt habe ich auf die Schnelle keine Angaben zu geforderten Bauteiletoleranzen gefunden, daher würde ich davon ausgehen, daß Bauteile mit üblichen Toleranzen ausreichen, d.h. 10 % für Widerstände und 20 % für Kondensatoren. Das kann einen Gesamtfehler von +-30 % ergeben. Da liegst Du mit 8 % doch recht gut.

Ich würde es so probieren. Wenn es nicht funktioniert, kannst Du die Frequenz ja immer noch anpassen.
 
Ich habe das Datenblatt durchgelesen und habe da auch nichts bezüglich der Toleranzen finden können.

Übrigens auch bei Berechnung der Frequenz für den Receiver mit den gegebenen RC Werten liegt man daneben... Es sollten 25,6kHz sein, sind aber mit den gegebenen Werten 28,363kHz...

EDIT: Wenn beim für Rocs 220k einsetzt, kommt man auf 25,817kHz, also liegt nicht mal so sehr daneben...

Dann latsche ich am Montag mal beim Conrad und hole die einpaar Kondensatoren und Widerstände.
 
Zuletzt bearbeitet:
War bei Conrad und Zeug geholt, leider hatten sie ein Widerstand nicht, muss erst noch einbisschen waren.

Hab ich in der Zwischenzeit etwas programmiert, man könnte es ja schön über ICP abtasten, hier der Code (angehängt ist das ganze Projekt, hier nur ISR):



CodeBox C
ISR(TIMER1_CAPT_vect) {  
    uint16_t clicks = ICR1;

    // interrupt gefeuert bei der fallenden Flanke
    if ( !(TCCR1B & (1 << ICES1)) ) {
        invert_icp();
      
        // start des frames, der Timerwert ist sehr groß
        if (is_between(clicks, DATA_INTERVAL_LOW, DATA_INTERVAL_HIGH)) {
            state = start_of_frame;  
            actual_bit = 0;
            total_edge_counter = 0;
            value = 0;
        }
      
        // kurzer bit = 1
        if (is_between(clicks, DATA_SHORT_LOW, DATA_SHORT_HIGH)) {
            value |= (1 << actual_bit);
            actual_bit += 1;
        }
      
        // langer bit = 0
        if (is_between(clicks, DATA_LONG_LOW, DATA_LONG_HIGH)) {
            value |= (0 << actual_bit);  
            actual_bit += 1;
        }
    }
  
    // interrupt gefeuert auf der steigenden flanke
    if ( TCCR1B & (1 << ICES1) ) {
        reset_icp();
        invert_icp();
    }
  
    total_edge_counter += 1;
  
    if ((total_edge_counter == 17) && (actual_bit == 7)) {
        value ^= (0xff);
        state = end_of_frame;
    }
}


Die Variable state ist nur eine StateMachine, diese wird in der main() abgefragt und ist ein Merker dafür, dass der Frame vollständig empfangen wurde.

Was meint Ihr?
 

Anhänge

  • MFL.zip
    33,1 KB · Aufrufe: 3
Habe nicht alles genau angesehen, aber diese Zeile:


CodeBox C
value |= (0 << actual_bit);

tut garnichts.

Ich hätte das eher so gemacht:


CodeBox C
// interrupt gefeuert bei der fallenden Flanke
    if ( !(TCCR1B & (1 << ICES1)) ) {
        invert_icp();
      
        // start des frames, der Timerwert ist sehr groß
        if (is_between(clicks, DATA_INTERVAL_LOW, DATA_INTERVAL_HIGH)) {
            state = start_of_frame;
            actual_bit = 0x01; // bit 0 setzen
            total_edge_counter = 0;
            value = 0;
        }
      
        // kurzer bit = 1
        if (is_between(clicks, DATA_SHORT_LOW, DATA_SHORT_HIGH)) {
            value |= actual_bit; // aktuelles bit speichern
            actual_bit <<= 1; // bit um eine Position nach links verschieben
        }
      
        // langer bit = 0
        if (is_between(clicks, DATA_LONG_LOW, DATA_LONG_HIGH)) {
                             // hier braucht nichts gespeichert zu werden
            actual_bit <<= 1; // aber das bit muß auch hier geschoben werden
        }
    }

und hier muß es dann natürlich


CodeBox C
if ((total_edge_counter == 17) && (actual_bit == 0x80))

heißen.
Da die AVRs keinen Barrelshifter haben, muß in Deiner Version die 1 bzw. 0 insgesamt 28 mal geschoben werden, um ein Byte zu empfangen. (Der Compiler würde das Schieben der 0 wahrscheinlich wegoptimieren)
In meiner Version braucht nur 7 mal geschoben zu werden.
 
Hallo Mikro,

danke Dir erstmal.

Die Variable "actual_bit" ist lediglich ein Zähler, der die Bits im Frame fählt (oben im Bild heißt es Switch 1 bis Switch 8).

Das Schiften einer 0 kann man auch weglassen, da hast Du natürlich Recht, war nur eine Gedankenstütze für mich.

Auch in meiner Version wird nur acht Mal geschoben, weil es nur 8 fallenden Flanken gibt und die vor den Startbits ist für's Schieben uninteressant (das wäre dann die 9te fallende Flanken, die Initialisiert das Ganze).
 
Auch in meiner Version wird nur acht Mal geschoben,
Nein, da actual_bit keine Konstante ist, muß die Anzahl der Schiebeoperationen zur Laufzeit berechnet werden, wie man im Disassemblerlisting sehen kann.


CodeBox Assembler
         value |= (1 << actual_bit);
0000009D  LDS R18,0x0061     Load direct from data space
0000009F  LDI R24,0x01       Load immediate
000000A0  LDI R25,0x00       Load immediate
000000A1  MOV R0,R18         Copy register
000000A2  RJMP PC+0x0003     Relative jump
000000A3  LSL R24            Logical Shift Left
000000A4  ROL R25            Rotate Left Through Carry
000000A5  DEC R0             Decrement
000000A6  BRPL PC-0x03       Branch if plus
000000A7  LDS R25,0x0060     Load direct from data space
000000A9  OR R24,R25         Logical OR
000000AA  STS 0x0060,R24     Store direct to data space
         actual_bit += 1;
000000AC  SUBI R18,0xFF      Subtract immediate
000000AD  STS 0x0061,R18     Store direct to data space
000000AF  RJMP PC-0x0035     Relative jump

Warum sogar eine 16bit-Variable geschoben wird ist mir jetzt auch noch nicht ganz klar.
Auf jeden Fall wird, wenn actual_bit = 1 ist einmal geschoben, bei 2 zweimal... und bei 7 siebenmal.

Edit:
Da die 1 in

CodeBox C
value |= (1 << actual_bit);
nicht weiter spezifiziert ist, nimmt der Compiler wahrscheinlich an, daß es sich um eine Integer-Konstante handelt und Integer hat auf AVRs 16 Bit.
 
Zuletzt bearbeitet:
Da die 1 in

CodeBox C und C++ C C++1value |= (1 << actual_bit);nicht weiter spezifiziert ist, nimmt der Compiler wahrscheinlich an, daß es sich um eine Integer-Konstante handelt und Integer hat auf AVRs 16 Bit.
Nicht auf AVRs, sondern Euer C-Compiler nimmt für Integer 16-Bit an.
Die AVRs selbst kennen eigentlich(*) nur ganzzahlige (->Integer) 8-Bit-Zahlen. Selbst vorzeichenbehaftet/-los ist für den AVR kein Unterschied. Der ergibt sich nur durch die, ... genau ... vom Compiler(!) verwendeten Instruktionen.

(*) Als Ausnahmen gibt es:
  • zwei Instruktionen, die eine 5-Bit-Konstante (also 0..63) auf ein Doppelregister addieren, oder von diesem subtrahieren können.
  • eine Instruktion, die ein Register-Paar in ein anderes Register-Paar kopieren kann.
 
Die AVRs selbst kennen eigentlich(*) nur ganzzahlige (->Integer) 8-Bit-Zahlen.
Ja, klar. Die Controller selbst kennen überhaupt keine Datentypen wie Integer o.ä.
Ich meinte natürlich, daß der GCC für AVRs die Wortbreite für Integer standardmäßig auf 16 Bit setzt, wenn nichts anderes angegeben ist. Wenn der GCC Programme für neuere PCs übersetzt, ist Integer ohne genauere Angabe 64 Bit breit.
 
Ist jetzt nicht wirklich sinnvoll, was der GCC da macht.
Der Compiler macht genau das, was Du ihm sagst. Wenn Du ihm sagst: nimm eine 16bit-Konstante (die 1) und schiebe sie um eine zur Compilezeit unbekannte Anzahl von Stellen (actual_bit) nach links, dann hat er das hier genau richtig gemacht. ;)
Da die aktuelle Bitposition nirgendwo explizit gebraucht wird, habe ich statt des Bitzählers die 8bit-Variable mit einer 1 an Bitposition 0 geladen, verodere sie bei Bedarf mit der Zielvariablen und schiebe sie anschließend um eine Bitposition nach links.
Man könnte das sicher auch so hinkriegen, das er nur eine 8bit-Konstante nimmt, da ich aber so gut wie nie Bitpositionen brauche, sondern immer nur Bitmasken, habe ich mir darüber noch keine Gedanken gemacht. (Die Anzahl der Schiebeoperationen (28 wenn alle Bits gesetzt sind) würde das aber auch nicht ändern.)

Bei solchen Ausdrücken dagegen:

CodeBox C
TIMSK |= (1 << TICIE1);
ist TICIE1 eine Konstante und der Ausdruck (1 << TICIE1) kann gleich vom Präprozessor berechnet und in eine Bitmaske verwandelt werden. Zur Laufzeit kein Schieben mehr nötig.

Ich habe mit Assembler (6800, 6502, 6805, 6809, 68000) angefangen zu programmieren und schaue deshalb gerne mal nach, was der Compiler so aus meinem Programm macht. Daher ist der C-Compiler für mich immer noch sowas wie ein besserer Makroassembler. ;)

Im Allgemeinen werden C-Programme mit dem GCC schneller und kleiner, als ich es selbst in Assembler schreiben könnte, allerdings es gibt immer mal wieder so kleine Haken, die man aber meistens durch eine andere Programmstruktur umgehen kann, wie man an meiner Version sehen kann.

CodeBox Assembler
        value |= actual_bit;
0000009E LDS R24,0x0061   Load direct from data space
000000A0 LDS R25,0x0060   Load direct from data space
000000A2 OR R25,R24       Logical OR
000000A3 STS 0x0060,R25   Store direct to data space
        actual_bit <<= 1;
000000A5 LSL R24          Logical Shift Left
000000A6 STS 0x0061,R24   Store direct to data space
000000A8 RJMP PC-0x002E   Relative jump
 
Hallo zusammen,

dieser blöde Demultiplexer hat mir keine Ruhe gelassen. Mittlerweile habe ich das Ganze mit einer MCU gelöst, was auch 1A funktioniert, aber ich will es trotzdem wissen.

Mein Signal sieht so aus (sieht man auch einen Zustandswechsel am Bit 8):

SDS00011.png

Und rangezoomt:

SDS00012.png

Also, eine Frequenz von 1488Hz.

Zum Thema abtasten steht im Datenblatt:

abweichung.png

Also wäre die Abtastfrequenz 5952Hz +-15%, sprich zwischen 5059,2 und 6844,8.

Nach der Formel fosc = 1/(Cosc * (0,79*Rosc + 2260))
Rosc: 180000 Ohm (180kOhm)
Cosc: 0,0000000012 F (1,2nF)
landet man bei ziemlich genau 5768,6Hz.

Alternativ:
Rosc: 200000 (200k)
Cosc: 0,000000001 (1nF)
ergibt 6239,86Hz, das liegt auch noch unter 6844,8Hz.

Einverstanden? Ich glaube, ich habe richtig gerechnet.
 
Zuletzt bearbeitet:
Mit den angegebenen Werten komme ich auf''s gleiche Ergebnis.
 
War heute bei Conrad und habe Kerko und Widerstand geholt und siehe da:

 
Zuletzt bearbeitet:
Irgendwie stehe ich wieder auf dem Schlauch....

Ich habe beim Aufbau im Video einen Fehler gemacht... und zwar im Datenblatt (Datenblatt im Post #1, Fig. 9 auf Seite 8) ist der Widerstand Rosc an den Pin 14 angeschlossen, also Vstab. Ich habe es aber verwechselt und habe ihn an Vbat angeschlossen, das hat auch wunderbar funktioniert, wie man im Video sehen kann. Aber, wenn ich den Widerstand, wie es richtig wäre, an den Pin 14 hänge, geht gar nichts.

Vstab hat eine Spannung von 5,2V....

Hier sieht man den Pin 4 (also OSC), wenn der Rocs am Vbat hängt:

SDS00008.png

Und hier, wenn Rocs am Vstab hängt:

SDS00009.png

Steht dann Blödsinn im Datenblatt oder wie soll man das verstehen?
 
Hallo Heinrich!

VBAT wird anscheinend direkt, oder über Diode oder über seriellen Widerstand mit VS verbunden.

VBAT und VS sind höher als VSTAB. Es ist dort ein "5V" Linearregler verbaut. VS wird die Eingangsspannung sein, VSTAB die Ausgangsspannung.

Wenn du ROSC mit VS verbindest, ist die Oszillatorfrequenz höher, da COSC schneller geladen wird.

Das wird auch so nicht richtig sein, da die Oszillatorfrequenz nun auch abhängig von der Versorgungsspannung (VS VBAT) ist, die kann sich ja in einem bestimmten Bereich bewegen.

Verbindest du ROSC mit VSTAB, dann hast du eine definierte Spannung. Die Frequenz ist hier kleiner, da ja VSTAB kleiner als VS (VBAT) ist und COSC nicht mehr so schnell geladen wird. Das sieht man auch bei deinen Oszillogrammen.

Im unteren Oszillogramm ist die Frequenz kleiner, was hier eigentlich auch zu erwarten ist.

Was funktioniert denn nicht genau?
 
Hallo Dirk,

alles richtig, was Du schreibst. Vstab ist wohl der Ausgang vom Spannungsregler, hier liegen konstant 5,2V an, VS kann ja bis zu 16V sein.

Ich habe die Werte für Cosc und Rosc gemäß der Formel berechnet. (im ersten Post fosc = 1/(Cosc * (0,79*Rosc + 2260)), Rosc ist 200k und Cosc 1nF). Mein Eingangssignal ist rund 1,5kHz, also muss ich vier Mal so schnell abtasten, +/- 15%. Meine (aus der Formel berechnete) Abtastfrequenz liegt bei ca. 6239Hz, also passt.

Und jetzt kommt es:
  • Rosc ist zwischen Pin 4 (OSC) und Pin 14 (Vstab, also 5V Ausgang): der Eingangssignal wird nicht erkannt und nicht dekodiert (das untere Bild in meinem letzten Post)
  • Rosc ist zwischen Pin 4 (OSC) und Pin 13 (Vs): der Eingangssignal wird erkannt und dekodiert (das obere Bild in meinem letzten Post)
Das Verhalten auf den Oszillogrammen ist absolut richtig, mehr Spannung, so wird die obere Threshold-Spannung schneller erreicht und dann wird der Cosc entladen, alles gut. Aber es past halt gar nicht zu den Formeln.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)