POSITRON PROGRAM FOR AD9833 PROGRAMMABLE WAVEFORM GENERATOR

Started by GDeSantis, Nov 26, 2021, 08:21 PM

Previous topic - Next topic

GDeSantis

If anyone has written a POSITRON program that works with the AD9833 Programmable Waveform Generator, may I ask that you share your work with forum members?

I ask because my efforts to date have fallen short due to what appears to be tight timing requirements on the AD9833's SPI interface.  For example, per Table 2 of the device data sheet, FSYNC must transition from LOW to HI in 20 nSec (i.e. T8= 20nSec Max).  As of this writing, the best I could achieve was T8max = 1 uSec.
 
Note: Per the attached program, a PORTA.2 logic level change initiates one cycle of subroutine DDS_SIN_400

charliecoutas


charliecoutas

Try again: here is the routine I use to load the generator on my Digital Theremin:

Proc New_freq_pitch()   
      Dim f2_ls_bits As Word
      Dim f2_ms_bits As Word
       
      f2_ms_bits = $4000 + (frequencyP >> 14)
      f2_ls_bits = $4000 + (frequencyP & $3FFF)                     
           
      osc_P_CE = 0                                                                             ;chip enable for generator
      SHOut osc_data, osc_clock, MsbFirst_H, [$2100\16]                 ;mode word, reset
      SHOut osc_data, osc_clock, MsbFirst_H, [f2_ls_bits\16]
      SHOut osc_data, osc_clock, MsbFirst_H, [f2_ms_bits\16]
      SHOut osc_data, osc_clock, MsbFirst_H, [$C000\16]                 ;phase word, not used as such
      SHOut osc_data, osc_clock, MsbFirst_H, [$2000\16]                 ;mode word, run
      osc_P_CE = 1
EndProc

See_Mos

This is some old unfinished code that I am working on at the moment, bringing it up to date

'****************************************************************
'*  Name    : UNTITLED.BAS                                      *
'*  Author  : [select VIEW...EDITOR OPTIONS]                    *
'*  Notice  : Copyright (c) 2015 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 23.02.2016                                        *
'*  Version : 3.0                                               *
'*  Notes   : AD9833 DDS with amplifier                         *
'*  Notes   : Modified from old AD9833 V3_0 for sig generator   *
'*          : modified to enable bit banging insteat of Shout   *
'****************************************************************
' V2 to test 400Hz modulator

' Bournes Encoder :-                 Port
' Ground
' CH A                               B.0
' switch    connected to ground
' switch                             B.2  Select mode, frequency, sweep, gain
' Power
' CH B                               B.1

' AD9833 module :-                   Port
' Supply
' ground
' FSY                                C.4
' CLK                                C.5
' DAT                                C.6
' CS                                 C.7

' Buttons :-
' 1 Mode                             AM, FM, Audio, modulated or sweep
' 2 Frequency
' 3 spare
' 4 spare


' from timer calculator 2000Hz / 50 steps = 400Hz
'//Timer0
'//Prescaler 1:32; TMR0 Preload = 6; Actual Interrupt Time : 500 us

'//Place/Copy this part in declaration section
'void InitTimer0(){
'  T0CON     = 0xC4;
'  TMR0L     = 0x06;
'  GIE_bit     = 1;
'  TMR0IE_bit     = 1;
'}

'void Interrupt(){
'  if (TMR0IF_bit){
'    TMR0IF_bit = 0;
'    TMR0L     = 0x06;
'    //Enter your code here
'  }
'}
$define _LCD_PORT_ 0                      ' LCD PortA = 0, PortB = 1, PortC = 2
Include "18F25K22 IntOsc 16.inc"          ' setup LCD etc.
' ?? 64MHz is too fast for writing to AD9833 so use 16MHzDevice = 18F25K22 ??

Symbol Enc_A      = PORTB.0                 ' use interrupt INT0 for Encoder
Symbol Enc_B      = PORTB.1
Symbol Pot_Switch = PORTB.2                 ' Push switch

Symbol Pot_CS   = PORTC.7                   ' chips select for digital pot
Symbol DPin     = PORTC.6                   ' data for pot and DDS
Symbol CPin     = PORTC.5                   ' clock for pot and DDS
Symbol DDS_FSY  = PORTC.4                   ' frame sync for DDS data

Symbol F_Control = 0
Symbol G_Control = 1

'Symbol AM = 1
'Symbol FM = 2
'Symbol Audio = 3
Dim DDS_OP As Float 'DDS Output
Dim FTW As Dword 'Frequency Tunning word
Dim FTWLSB As FTW.Word0 'First word LSB of the Frequency tuning word
Dim FTWMSB As FTW.Word1 'Second word MSB of the Frequency tuning word
Dim FTWLSB1 As Word '
Dim Waveform As Byte 'Select Waveforms Type Sine = 1, Triangle = 2, Square = 3 (TTL)
Dim Waveform_Old     As Byte
Dim DDS_Control_Register As Word
Dim Data_Word As Word   'used by My_SPI
Dim Freq_Reg As Bit 'Selects Frequency Register REG0 or REG1

                            ' variables used with buttons and encoder
Dim FR[8]       As Word     ' array 0 to 6 frequencey and phase. [1] = Hz
'Dim Buttons     As Byte
Dim Temp        As Byte
Dim Index       As Byte
Dim x           As Byte
Dim Mode        As Byte                 ' setting to adjust selected by buttons
Dim Gain        As Byte
Dim Pot_Data    As Word
Dim Pot_Control As Pot_Data.HighByte
Dim Pot_Gain    As Pot_Data.LowByte
Dim Gain_Old    As Byte
Dim Bit_Count   As Byte                 ' count the bits in My_SPI
Dim Pointer     As Byte

'Dim AM_FM_Audio As Byte
Dim Frequency   As Dword                ' Desired Frequency
Dim Freq_Old    As Dword                ' last freuqncy sent to DDS
Dim Sweep       As Word                 ' sweep bandwidth
Dim Sweep_On    As Bit = 1

Dim Audio As 1
Dim AM As 2
Dim AM_Modulated As 3
Dim AM_Sweep As 4
Dim FM As 5
Dim FM_Sweep As 6

Declare PORTB_Pullups On

    On_Hardware_Interrupt GoTo My_Int
    GoTo Start                                   ' jump over interrupt handler

My_Int:
    Context Save

    If TMR0IF = True Then                  ' timer 0 interrupt
    TMR0IF = 0
    TMR0L     = 0x06
'    //Enter your code here
    EndIf

    If INT0IF = True Then                      ' Encoder interrupt
        INT0IE = False                         ' disable the interrupt
        If Enc_B = 1 Then GoTo Reverse         ' check the direction
        Select Pointer
            Case 1                             ' function
                If Mode < 6 Then Inc Mode      ' AM, sweep, audio
            Case 2                             ' Frequency
                If Mode = 1 Then
                    If Frequency < 5000 Then
                        Frequency = Frequency + 100
                    EndIf
                Else                 
                    If Frequency < 10000 Then
                        Frequency = Frequency + 100
                    Else
                        Frequency = Frequency + 1000
                    EndIf
                EndIf
            Case 3                             ' Gain
                If Gain <255 Then Inc Gain
            Case 4                             ' waveform or sweep band
                If Mode = 1 Then
                   If Waveform < 3 Then Inc Waveform
                Else
                    If Mode = 4 Or Mode = 6 Then
                       If Sweep <10000 Then Sweep = Sweep + 1000
                    EndIf
                EndIf
        End Select
        EndIf
        GoTo Enc_End
Reverse:
        Select Pointer
            Case 1
                 If Mode > 1 Then Dec Mode
            Case 2
                If Mode = 1 Then
                    If Frequency > 100 Then Frequency = Frequency - 100
                Else
                    If Frequency < 10000  And Frequency > 200 Then
                        Frequency = Frequency - 100
                    Else
                        Frequency = Frequency - 1000
                    EndIf
                EndIf
            Case 3
                If Gain >0 Then Dec Gain
            Case 4
                 If Mode = 1 Then
                 If Waveform >= 2 Then Dec Waveform
                 Else

                     If Mode = 4 Or Mode = 6 Then
                         If Sweep > 1000 Then Sweep = Sweep - 1000
                     EndIf
                EndIf
        End Select
Enc_End:
        INT0IF = False                            ' clear interrupt flag
        INT0IE = True                          ' set interrupt eenable


INT_End:
    Context Restore            ' Restore the registers and exit the interrupt

Start:
    DelayMS 50
    LATA = 0 : TRISA = 0            ' PortA = LCD and buttons
    LATB = 0
    TRISC = 0
    Mode = 1                        ' Audio frequency setting
    Pointer = 2                       ' start with frequency setting
    Frequency = 1000                ' start at 1KHz
    Gain = 120                      ' and 50% output
    Sweep = 0                       ' no sweep in audio mode
'    High Pot_CS                     ' turn off digital potentiometer
    Pot_Control = %00010001         ' Select write to pot' 0
    GoSub Gain_Adjust               ' set gain to 50%
    Cls
    GoSub LCD
                                        ' setup interrupt on B.0
    GIE = 0                             ' Disable interrupts
    IPR2 = 0
    IPEN = 0
    INT0IF = 0
                 ' Encoder interrupt
    INT0IE = 1
'    GIE = 1                             ' and enable the interrupts

' timer 0 setup
  T0CON     = 0xC4
  TMR0L     = 0x06
  GIE = 1
  TMR0IE = 1

'    High Pot_CS             ' low only when writing to MCP41010 potentiometer
'    DelayMS 50
                                        'Initialise DDS
    DDS_FSY = 0                         'Activate DDS (Set Chip Enable Low)
'    SHOut DPin,CPin,5, [$2100\16]      'Apply Reset
    Data_Word = $2100 : GoSub My_SPI
'    SHOut DPin,CPin,5, [$2000\16]      'Remove reset
    Data_Word = $2000 : GoSub My_SPI
    DDS_FSY = 1                         'Disable DDS (Set Chip Enable High)
    DDS_Control_Register = $2100

    Waveform = 1                            ' start with sine wave
    Freq_Reg = 1
    GoSub FTW_CALC

While 1 = 1 ' Loop
    GoSub Button_Press:                     ' first check the buttons
    GoSub LCD                               ' then update the display
    If Frequency <> Freq_Old Then GoSub FTW_CALC
'    If Frequency < 50000 Then Mode = 1      ' < 50KHZ is audio
    If Gain <> Gain_Old Then GoSub Gain_Adjust

'    If PORTB.2 = 0 Then Inc Mode         ' encoder switch
'    If Mode = 5 Then Mode = 1
'    While PORTB.2 = 0
'        Print At 2,14,Dec Mode
'        DelayMS 100
'    Wend

    If Mode = AM Then Sweep = 0
   
' need to add pointer check here   
    If Pointer = 1 And Mode = 4 Then Sweep = 1000
    If Pointer = 1 And Mode = 6 Then Sweep = 10000
    If Waveform <> Waveform_Old Then GoSub Wave_Form
Wend

LCD:                                        ' update display
'    Print at 1,3
    Select Mode
        Case 1
            Print At 1,3,"Audio (sweep Off) "
            Sweep = 0
        Case 2
            Print At 1,3,"AM (no modulation)"
            Sweep = 0
        Case 3
            Print At 1,3,"AM (modulated)    "
            Sweep = 0
        Case 4
            Print At 1,3,"AM (sweep on)     "
'            Sweep = 5000
        Case 5
            Print At 1,3,"FM (sweep off)    "
            Sweep = 0
        Case 6
            Print At 1,3,"FM (sweep on)     "
'            Sweep = 25000
        Case 6
    End Select

'    If Sweep_On = True Then
'        Print At 3,3,"Sweep ", Dec Sweep
'    Else
'        Print At 3,3,"Sweep Off    "
'    EndIf

    Print At 2,3,"Hz ", Dec Frequency ,"      "
    Print At 3,3,"Gain = ", Dec Gain ,"  "

    If Mode = Audio Then
        Select Case Waveform
            Case 1
                Print At 4,3,"Sine         "
            Case 2
                Print At 4,3,"Triangle     "
            Case 3
                Print At 4,3,"Square       "
        EndSelect
    Else
        If Mode = 4 Or Mode = 6 Then
            Print At 4,3,"Sweep ",Dec Sweep,"     "
        Else
            Print At 4,3,"Sweep off        "
        EndIf
    EndIf

    For x = 1 To 4                          ' clear old pointer
        Print At x,1,"  "
    Next
    Print At Pointer,1,126                     ' set new pointer

Return

Button_Press:
    TRISA = %11110000                       ' Enable buttons
    PORTA.1 = 1
    DelayMS 1                               ' without a delay there are errors

    Temp = PORTA & %11110000                ' read port
    If Temp > $F Then                       ' button pressed?
                                            ' Yes, so change selected button
        Select Case Temp
            Case 16
                Pointer = 1
                Sweep = 0
'                Inc Mode
'                If Mode = 7 Then Mode = 1
            Case 32
                Pointer = 2
            Case 64
                Pointer = 3
'                If Mode <> AM Then Toggle Sweep_On
            Case 128
                Pointer = 4
'                Inc Waveform                '
'                If Waveform = 4 Then Waveform = 1
            Case Else
        End Select
        GoSub LCD
        DelayMS 500
    EndIf


    Return

Gain_Adjust:
    DDS_FSY = 1                     ' disable DDS IC
    Pot_Gain = Gain
    Pot_CS = 0                      ' Low to enable digital pot'
    SHOut DPin,CPin,1, [Pot_Data\16]
    Pot_CS = 1                      ' High to disable digital pot'
    Gain_Old = Gain
    Return
' ############################ Subs #############################

FTW_CALC:               ' Calculate Frequency tuning words =
                        ' F_Clock / 2^28 * F_out, = F_Clock / 268435456 * F_out
    'DDS_OP = Frequency/0.03725290298    ' for 10MHz clock
    DDS_OP = Frequency/0.093132257      ' FOR 25MHz

    'Extract LSB Tuning Word
    FTW = DDS_OP 'DDS_OP (Float) = Freq tuning word (Dword) << bit shift doesn't work with float
    FTWLSB1 = FTWLSB 'Extract LSB and put it in LSB1

    'Extract MSB tuning word
    FTW = FTW << 2 'Shift Bits Left twice

ProgrammeDDS:           ' Calculate control register
    Select Freq_Reg
    Case 0
        FTWLSB1.15 = 0 'Set LSB bit 15 & 14 to 01 (Code to write to DDS Freq Reg 0)
        FTWLSB1.14 = 1
        FTWMSB.15 = 0 'Set MSB bit 15 & 14 to 01 (Code to write to DDS Freq Reg 0)
        FTWMSB.14 = 1
        DDS_Control_Register.11 = 0 ' Tells DDS to use Frequency Register 0
    Case 1
        FTWLSB1.15 = 1 'Set LSB bit 15 & 14 to 10 (Code to write to DDS Freq Reg 1)
        FTWLSB1.14 = 0
        FTWMSB.15 = 1 'Set MSB bit 15 & 14 to 10 (Code to write to DDS Freq Reg 1)
        FTWMSB.14 = 0
        DDS_Control_Register.11 = 1 ' Tells DDS to use Frequency Register 1
    EndSelect
Wave_Form:              ' add waveform information to control register
    Select Waveform 'Sets the control register code to produce coressponding waveforms
        Case 1 'Sine
            DDS_Control_Register.5 = 0 : DDS_Control_Register.1 = 0
        Case 2 'Triangle
            DDS_Control_Register.5 = 0 : DDS_Control_Register.1 = 1
        Case 3 'Square
            DDS_Control_Register.5 = 1 : DDS_Control_Register.1 = 0 : DDS_Control_Register.3 = 1
    EndSelect
    Waveform_Old = Waveform
Program_DDS:            ' Send data to DDS
    DDS_FSY = 0 'Activate DDS (Set Chip Enable Low)
    Data_Word = FTWLSB1 : GoSub My_SPI
    Data_Word = FTWMSB : GoSub My_SPI
    DDS_Control_Register.8 = 0 'Removes reset from DDS chip
    Data_Word = DDS_Control_Register : GoSub My_SPI
    DDS_FSY = 1 'Disable DDS (Set Chip Enable High)
    Freq_Old = Frequency
    Return

     '###################################################
Update_DDS:

My_SPI:      ' bit banging instead of SHout
    For Bit_Count = 0 To 15                 ' bit counter
        If Data_Word & $8000 = $8000 Then   ' isolate the high bit
            DPin = 1                        ' put the data bit on the pin
        Else
            DPin = 0
        EndIf
        Nop
        CPin = 0                            ' data clocks on falling edge
        Nop
        CPin = 1
        Data_Word = Data_Word <<1           ' shift the data left by one bit
    Next
    DPin = 0                                ' data line idles low
    Nop
    Return
End

GDeSantis

Before writing my first Positron AD9833 program, I considered two options.  Option 1 involved using the micro's MSSP peripheral to control the DDS chip while option 2 involved using Positron commands SHOut, SHIn, etc.
 
Although both options were feasible, based on constructive comments from Charlie and See Mos, I went with option 2 and an example program is attached.

charliecoutas


GDeSantis

Yes, it works very well and thanks for your words of wisdom.

GDeSantis

ALL
I used third party software to create the program PDF file.  Unfortunately, it has an anomaly whereby it does not show underscores properly.  The GoSub commands should be as follows: 

    GoSub DDS_SIN_400
 
    GoSub DDS_SIN_1000
   
    GoSub DDS_TRIANGLE_400

    GoSub DDS_SQUARE_200