Shop OBEX P1 Docs P2 Docs Learn Events
propeller FM modulator/demodulator — Parallax Forums

propeller FM modulator/demodulator

laser-vectorlaser-vector Posts: 118
edited 2011-03-27 16:06 in Propeller 1
Hi I have an experiment where Id like to use a propeller to take an audio input and convert it to an FM output. Then use another prop (or another cog) to demodulate that signal and conver it back to audio.

Any ideas or refrences to similar projects would be very helpful!

Thanks,
LV.

Comments

  • LeonLeon Posts: 7,620
    edited 2011-03-25 13:07
    What frequency is the FM carrier?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-25 13:08
    Is your objective to broadcast said FM over the radio? What frequency?

    -Phil
  • laser-vectorlaser-vector Posts: 118
    edited 2011-03-25 20:53
    Carrier frequency would be 30 - 60 Khz

    Signal would be transmitted via optical medium and consist of LED (TX) / Photodiode (RX)

    I need to send FM via optical to test a piece of data comm. equiptement we're designing.
    The reason we want FM is because its easy to quickly find signals when listening for music.

    If the prop could do this then it might save me some time to test

    LV
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-27 16:06
    Here's a brain-dead FM modulator/demodulator. I call it "brain-dead" because the demodulator uses the very unsophisticated technique of measuring the square-wave period of each positive half-cycle to get the instantaneous modulation value. This works fine in a noise-free channel. More advanced demodulation techniques would be needed in a noisy one.

    Here's a schematic of the circuit I used to test and monitor the signals:

    attachment.php?attachmentid=79610&d=1301265037

    The FM signal is created by modulating frqa of an NCO-mode counter. This produces a square wave whose instantaneous frequency is the carrier frequency, plus the value of a computed sine wave. The carrier frequency is clkfreq / 2048 == 39.0625 KHz. The modulation frequency is 250 Hz. The maximum FM deviation is ± 6.25% of the carrier frequency (± 2.44 KHz).

    I cheated a little on the demodulation, since I used the period, rather than the inverse period (i.e. frequency) to reconstitute the modulation envelope. This introduces some non-linearity; but if the FM deviation percentage is small, the non-linearity will also be small. Here's the formula:
    Δperiod = 1/(f + Δf) - 1/f = -Δf/(f2 + f Δf) ≅ -Δf/f2
    So long as Δf is very small with respect to f, the denominator will be dominated by the f2 term, and the result will be approximately linear in Δf, but with the opposite sign.

    The circuit shown above monitors both the modulating signal and the demodulated signal for comparison on a scope. Both use a counter's DUTY mode, along with an RC low-pass filter, to produce the digital-to-analog conversion. Here are the scope displays:

    attachment.php?attachmentid=79608&d=1301265036

    attachment.php?attachmentid=79609&d=1301265036

    Here's the program that generated them:
    CON
    
       _clkmode       = xtal1 + pll16x
       _xinfreq       = 5_000_000
    
       FMOUT          = 0
       FMINP          = 1
       MODOUT         = 2
       DEMODOUT       = 3
    
       SINE           = $e000
    
    OBJ
    
      sio:          "FullDuplexSerial"
    
    VAR
    
      long waveform[128]
    
    PUB  Start | i, t, f, amplitude
    
      sio.start(31, 30, 0, 9600)
    
      cognew(@demod, DEMODOUT << 8 | FMINP << 2)
    
      repeat i from 0 to 63
        waveform[i] := sin(i << 7) << 1
        sio.dec(waveform[i])
        sio.tx(13)
      dira[FMOUT]~~
      dira[MODOUT]~~
      ctra := %00100 << 26 | FMOUT            'NCO mode for FM out.
      ctrb := %00110 << 26 | MODOUT           'DUTY mode to monitor modulation waveform
      f := clkfreq / 16000
      t := cnt
      repeat
        waitcnt(t += f)
        amplitude := waveform[i := (i + 1) & 63]
        frqb := $8000_0000 + amplitude << 13
        frqa := $20_0000 + amplitude
    
    PRI sin(x) : value
    
      '' Sine of the angle x: 0 to 360 degrees = $0000 to $2000
    
      if (x & $fff == $800)
        value := $1_0000
      elseif (x & $800)
        value := word[SINE][-x & $7ff]
      else
        value := word[SINE][x & $7ff]
      if (x & $1000)
        value := -value
    
    DAT
    
    demod         mov       acc,par                 'Get the par register.
                  shr       acc,#2                  'Right shift input register into position.
                  mov       inpmask,#1              'Create an input mask.
                  shl       inpmask,acc             'Shift the bit into position.
                  shr       acc,#6                  'Right shift analog output register into position.
                  or        ctra0,acc               'Or it into counter setup variable.
                  mov       ctra,ctra0              'Set up counter.
                  mov       acc,#1                  'Create output mask.
                  shl       acc,ctra0               'Shift bit input into position.
                  mov       dira,acc                'Write to dira.
    
    mainlp        waitpeq   inpmask,inpmask         'Wait for high on input.
                  neg       acc,cnt                 'Negate into accumulator.
                  waitpne   inpmask,inpmask         'Wait for low on input.
                  add       acc,cnt                 'Add into accumulator to high period.
                  sub       acc,_1024               'Center half-period is 1024, so subtract to make zero-centered.
                  shl       acc,#24                 'Scale it upwards.
                  add       acc,_0x8000_0000        'Add offset.
                  neg       frqa,acc                'Negate into DUTY register.
                  jmp       #mainlp                 'Back for more.
    
    _1024         long      1024                    'Center half-period, in clocks. 
    _0x8000_0000  long      $8000_0000              'DUTY-mode offset for zero value.
    ctra0         long      %00110 << 26            'DUTY mode setup value.
    
    acc           res       1
    inpmask       res       1
    
    Hopefully, this will be useful to your project.

    -Phil
    640 x 480 - 27K
    640 x 206 - 9K
    367 x 240 - 2K
Sign In or Register to comment.