Pulsecodemodulation in C?!



  • Wir müssen in de Schule einen PCM machen.
    Dieser soll über ein in C geschriebenes Programm den Com-Port ansprechen,an dem ein Tiefpass aus Kondensator (47µF-Elko) und Lautsprecher hängt.
    Entsprechend der Frequenz soll ein Ton ausgegeben werden. Das Programm und die Schaltung haben wir schon,aber irgendwie funktioniert es nicht.
    Vielleicht habt ihr eine Idee...

    #include <math.h>
    #include <windows.h>
    #include <stdio.h>
    int main ()
    {
    /* Definitionen*/
    
        DWORD wb;
        HANDLE hCom;
    DCB dcb;
    FILE *handle;
    int Data[115200], n, p, nr, frequenz, perioden;
    double sinwert;
    
    /* Einstellung f. File & COM */
    
    handle = fopen("sin.txt","w");
        hCom = CreateFile("COM1",
                                 GENERIC_READ|GENERIC_WRITE,
                                 0, NULL, OPEN_EXISTING, 0, NULL);
    
    dcb.BaudRate = CBR_115200; // set the baud rate
    dcb.ByteSize = 8; // data size, xmit, and rcv
    dcb.Parity = NOPARITY; // no parity bit
    dcb.StopBits = ONESTOPBIT; // one stop bit
    
    perioden = pow(10,6);
        frequenz = 100000;
    nr = 115200/frequenz;
    
    /* Berechnung der Sinuswerte */
    
        for (n=0; n < nr; n=n+1)
        {
            sinwert = sin (2*M_PI * n/(double)nr);
            Data[n] = 128 + floor(100 * sinwert + 0.5);
    fprintf(handle,"%d\t%g\t%d\n",n,sinwert,Data[n]);
        }
    
    /* Ausgabe in File & COM */
    
        for(p=0; p<perioden; p+=1)
        {
            unsigned char wert;
            for (n=0; n < nr; n=n+1)
            {
                wert = Data[n];
                WriteFile(hCom, &wert , sizeof(wert), &wb, 0);
            }
        }
    printf("Ende!");
        getchar();
        return 0;
    }
    

    Eigentlich müsste es gehen,denn unser Lehrer hat uns ein Buch gezeigt da steht ein Visual Basic Code drinnen der funzen müsste...

    Private Sub Command1_Click()
    Dim Dat(10000)
    pi = 3.1415
    freq = Val(Text1.Text)
    Nr = Int(11520 / freq)
    For n = 1 To 10 * Nr
    Dat(n) = 128 + Int(100 * Sin(2 * pi * n / Nr))
    Next n
    out = 128
    For n = 1 To 10 * Nr
    out = out + 5
    Data = Dat(n)
    Bit = 1
    send = 0
    For i = 1 To 8
    If Data < out Then
    send = send + Bit
    out = out - 5
    Else
    out = out + 5
    End If
    Bit = Bit * 2
    Next i
    out = out - 5
    Dat(n) = send
    Next n
    REALTIME
    For n = 1 To 100
    For i = 1 To 10 * Nr
    SENDBYTE 2, Dat(i)
    Next i
    Next n
    NORMALTIME
    
    End Sub
    
    Private Sub Form_Load()
    OPENCOM 2, 115200, 0, 8, 1, 100, 100
    DTR 2, 1
    RTS 2, 1
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
    CLOSECOM 2
    End Sub
    

    Also:
    es funzt nicht:

    Es kommt ein ziemlich konstanter "Ton" (eher ein Rauschen) aus dem Lautsprecher, egal ob der Wert frequenz verändert wird.

    Der code ausm buch is net von mir,der andere schon. Zuerst hab ich mit den Leuten aus meiner Gruppe den Visual Basic Code in C versucht umzuschreiben... und jetzt haben wir das Proggi so verändert,wie es sich logisch anhört,aussieht,wie auch immer 🙂

    Es soll einen Ton entsprechend der Frequenz ausgeben.

    Unser Lehrer is ein bissi inkompetetnt und kann uns net helfen 😕 (ich glaub er hats net mal ausprobiert)

    Muss man da maybe irgenwie was mit bitweisem verschieben machen? wenn ja,wie geht das?



  • ich seh nirgends, dass du dcb nochmal verwendest... könnts das sein?



  • ...könnte vielleicht einen D/A-Wandler ansteuern. Mit dem COM Port wird das so nichts.



  • Hmm,also ganz ohne D/A Wandler wird das nix?

    Wie kann ich denn Zahlen zB 127 ausgeben (müsste ja bis auf Stopp-Bit nur High sein) ? Gehört da noch irgendeine Schleife oder so dazu?



  • nur mit nullen und einsen (die ein digitaler port wie der comport nunmal liefert) wird das wohl nie einen sinus geben.
    da ist euer lehrer aber wirklich nicht der kompetenteste 🙂 wer hat die Aufgabe eigtl. gestellt?



  • Dieser Lehrer... so ein Dödl -.-



  • Keinen Sinus, aber wenn man immer das gleiche Zeichen ausgibt, oder abwechselnd zwei Zeichen usw. gibts auch eine schöne Kurve, gerade wenn noch ein Tiefpass dranne hängt, der das Digitale weg macht, z.B. 115,2 kHz als Grundschwingung mit einer aufmoduliertenm kleineren Frequenz (Start und Stoppbits sind natürlich auch dabei). So unwahrscheinlich ist euer Experiment nicht, hängt doch einfach mal ein Oszilloskop dran statt Lautsprecher.



  • wie soll denn das was ordentliches geben? da kannst du ja die höhe der ausgangsspannung nur über den ladezustand des kondensators machen, natürlich kann man mit einem tiefpass wertdiskrete signale glätten, aber etwas mehr als zwei stufen dürften es schon sein, wenn man einen sauberen sinus machen will (ja, das abtasttheorem erlaubt für einen sinus auch so wenig werte, aber dennoch).
    außerdem würd ich dann eine steuerleitung nehmen, und nicht den datenkanal, weil man da besser kontrolle hat wann high und low ist.



  • ich wiederhole mich gerne.

    du benutzt dcb nicht
    ist das absicht, egal oder ein fehler?



  • ich wusste nicht was ich damit anfangen soll.. hmm.. ^^



  • der knackpunkt ist, daß COM ein serieller port ist! da kommen nur einsen und nullen hinten raus deswegen muß es ja auch pcm sein.

    du versuchst aber, sinuswerte auszugeben. noch dazu als double! kein wunder, daß es nur rauscht. das problem ist, daß du nicht verstanden hast, wie die datenübertragung vom computerüber über com an die hardware funktioniert. nein, dein lehrer ist kein dödel.

    com ist ein serieller port, der die bytes bit für bit im gänsemarsch über die leitung ausgibt. wenn du also zb das byte %01010111 sendest, dann liegt an der com-leitung keine spannung-spannung-keine spannung-spannung-keine spannung-spannung-spannung-spannung. oder auch andersrum. ich weiß nicht, in welcher reihenfolge der com-port die bits ausgibt. der einzelne spannungspuls dauert so lange wie die grundfrequenz, also 1/115.2 ms.

    da der com-port nur einsen und nullen sendet, kannst du auch kein sinus- sondern nur ein rechtecksignal ausgeben. das macht aber nichts, denn es ist dann sowieso der elko, der daraus daumen mal pi ein sinus macht.

    du mußt also eine folge von bits über com1 senden, die ein rechteck-signal der gewünschten frequenz formen. das problem ist, daß du an den com-port keine einzelnen bits sondern nur bytes senden kannst. du mußt also jeweils 8 bits zu einem byte zusammenfassen und dann senden.

    das programm muß also folgendermaßen arbeiten:

    1. berechne eine folge von bits, die ein rechtecksingal der gewünschen frequenz formen.

    2. fasse jeweils 8 bits zu einem byte zusammen und sende das byte an com1

    PS: du benutzt dcb nicht!



  • ein problem dabei ist nur, dass man nicht beliebige folgen ausgeben kann, weil ja noch start und stopbits dazukommen, und evtl. noch pausen.
    weiß nicht wie toll die realtime-fähigkeiten von dem verwendeten betriebssystem sind, aber evtl. eignet sich wie gesagt eine der steuerleitungen besser wegen beliebigem timing.



  • Mit "Puls-Code-Modulation" (PCM) hat das aber nichts zu tun, eher was mit "Puls-Width-Modulation" (PWM). Nur kann man damit aber auch keine verschiedenen Frequenzen erzeugen, sondern nur verschieden hohe Gleichspannungspegel (nach der Integration über einen Tiefpaß).
    Mit der seriellen Schnittstelle kann man durchaus analoge Ausgangsspannungen erzeugen, nur nicht von 0% (entspricht +10V) bis 100% (entspricht -10V). Sondern maximal von 10% bis 90%. Weil bei 8N1 (das sind 10 bit) das Startbit immer 0 ist (eben 90%) und das Stopbit immer 1 (deswegen 10%). Die 8 Datenbits dazwischen können dann die übrigen 8 (nur acht!, nicht 256) Stufen darstellen.
    Wird der Sendepuffer immer gefüllt, so ist der Ausgangs"-Takt" immer konstant (entsprechend der eingestellten Baudrate) und ohne Lücken zwischen Stopbit und folgendem Startbit, egal was der PC gerade macht. Nur schnell mal eben die Spannung ändern geht eben nicht, weil man ja nie weiß, wann das eigene Programm wieder mal drankommt, den Puffer zu füllen.

    Wenn also eine Schwingung erzeugt werden soll, so müssen also die 8 Datenbits entsprechend gesetzt sein: bei einer Dreieckschwingung z.B. so:
    0x00 0x01 0x03 0x07 0x0F 0x1F 0x3F 0x7F 0xFF 0x7F 0x3F 0x1F 0x0F 0x07 0x03 0x01

    Das ist eine volle Dreiecks-Schwingung, die natürlich immer wieder hintereinander ausgegeben werden muß, damit man auch einen Ton und nicht nur ein Knacken hört.
    Bei einer Baudrate von 115200,8N1 ergibt das 86,8056 Mikrosekunden Dauer für ein komplettes Byte und damit 0,1736112 ms für eine Schwingung (hier 2 Byte). Das sind dann 5,76 kHz (Dreieck, nicht Sinus).

    Den Rest der Rechnerei, um auf andere Frequenzen und Kurvenformen zu kommen, überlasse ich Dir. Siehe auch das Basic-Programm.

    Blackbird



  • Ich muss da Blackbird ein bischen korrigieren, man kann mit PWM sehr wohl Sinuswellen verschiedener Frequenzen erzeugen, ist ja quasi die Hauptanwendung von PWM, denk mal an all die Synchronmotoren.

    PS: Ein Sinus ist auch nur ein sich schnell genug veränderndes DC Potential, wenn du so willst.

    Greets



  • Ein Sinus ist auch nur ein sich schnell genug veränderndes DC Potential, wenn du so willst.

    Hatte ich das nicht geschrieben?

    ... mit PWM sehr wohl Sinuswellen verschiedener Frequenzen erzeugen, ist ja quasi die Hauptanwendung von PWM ...

    Nein, ist sie nicht. Weil die PWM-Wiederholrate ein Vielfaches der Sinusfrequenz sein muß, da sie erst durch einen Tiefpaß gefiltert wird bevor eine "Gleichspannung" entsteht. Sonst hat man den PWM-Takt im Sinus drin. Ein parallel DA-Wandler wird häufig benutzt, um Sinus und andere Kurvenformen zu erzeugen.

    PWM für Synchronmotoren? Wie geht denn das? Meinst Du einen 3-Phasen-Takt (oder -Sinus)?

    Blackbird



  • 🙂 Ja, ich denke wir sind uns schon einig, drückens einfach anders aus.
    Ich weiss nicht wie dieser motor auf deutsch heisst, ich studiere auf französisch, also "moteur synchrone à aimant permanent" z.b. auf englisch heisst der brushless DC oder auch Brushless AC, je nachdem wie du ihn ansteuerst.

    Aber zurück zum Thema, wenn er das richtige über seinen Port ausgiebt, hat er nach seinem Tiefpass einen Sinus, erzeugt durch PWM.
    🙂



  • muss man nicht um damit einen sinus erzeugen zu können negative und positive werte ausgeben können? Umpolen braucht man doch schon, oder geht das wirklich mit ein aus?



  • als steuersignal schon. mir kämen da spontan transistoren in den sinn. damit würde man dann 1/-1 draus machen und dann wie schon beschrieben.



  • Hi,

    den Aufwand zu treiben ist für dieses Experiment allerdings müßig. Hauptsache Periodisch, und entfernt sinusähnlich(so entfernt, daß ein rechteck auch reicht) ob da jetzt ein Offset drauf ist, ist ohne Belang.

    PS: ich glaub ein aus PWM erzeugtes Rechtecksignal langt zum testen völlig, und ist am einfachsten zu programmieren.

    grüße, Con@n



  • Diabolito schrieb:

    🙂 Ja, ich denke wir sind uns schon einig, drückens einfach anders aus.
    Ich weiss nicht wie dieser motor auf deutsch heisst, ich studiere auf französisch, also "moteur synchrone à aimant permanent" z.b. auf englisch heisst der brushless DC oder auch Brushless AC, je nachdem wie du ihn ansteuerst.

    Er heißt ganz einfach Synchronmotor. Viel verbreiteter sind allerdings Asynchronmotoren und auch die brauchen Frequenzvariablen Drehstrom mittels Frequenzumrichter. Aber bis dahin seid ihr scheinbar noch nicht vorgedrungen 😉

    btw. wird das heutzutage auch anders gemacht und nicht mehr per PWM, das war nur bei alten Geräten so.


Anmelden zum Antworten