Search code examples
pythoninterfaceraspberry-pispidev

How i understanding AD9833 SPI communication using Python with my raspberry?


Hi i had an issue to discuss, and i dont realy understand to send data with SPI with Python

I want to send data with my Raspberry Pi 4 ver.b using Python to send data to my module named AD9833 DDS. So i found code in internet, writed in Python (sor. https://ez.analog.com/dds/f/q-a/28431/ad9833-programming-in-raspberry-pi-using-python). This is the code :

# The code write by SamMaster, Oct 21 2016

# importing library
import time
import spidev

# activate spidev module and settings SPI
spi = spidev.SpiDev()
spi.open(0,1)
spi.max_speed_hz = 976000

# initialize frequency and another value
freq_out = 400
Two28 = 268435456
phase = 0

after the programmer call all library, function and set the value, his try to define a function to send a data.

def send_data(input):
    tx_msb = input >> 8
    tx_lsb = input & 0xFF
    spi.xfer([tx_msb,txlsb])
    print(input)

so that this frequencies value is able to read by AD9833, this frequency must convert to freq word, so programmer write the code,

freq_word = int(round(float(freq_out*Two28)/25000000))

and then the programmer define all of MSB and LSB

MSB = (freq_word & 0xFFC000)>>14
LSB = (freq_word & 0x3FFF)

LSB |= 0x4000
MSB |= 0x4000

phase|= 0xC000

and then, function that the programmer built implement in this blocks of codes

send_data(LSB)
send_data(MSB)
send_data(phase)
send_data(0x2000)

its worked on my Raspberry Pi 4, this is the result on my device,

  1. Result for 400Hz
  2. Result for 500Hz

when i change the frequency so freq_out = 500 there is no changes, just the value is aproximately 400 Hz on my scope. So i try this simple solution, i put the code send_data(0x2000), 0x2000 it mean Reset AD9833 according to datasheet, above the send_data(LSB) code. So the code became,

send_data(0x2000)
send_data(LSB)
send_data(MSB)
send_data(phase)

and this the result,

  1. freq_out = 400 freq_out = 400
  2. freq_out = 500 freq_out = 500
  3. freq_out = 600 freq_out = 600
  4. freq_out = 1000freq_out = 1000

i don't know why when i writing freq_out = 600 the value output frequency not correct with what i'm inputing. So can anyone want to comment / state argument to my issue ?


Solution

  • This problem can be split into a number of sub tasks.

    1. Values to send
    2. Sequence values sent in
    3. How values sent over SPI

    As SamMaster pointed out there is an application note from Analog Devices that shows the sequence of values to send to set the frequency to 400 Hz

    https://www.analog.com/media/en/technical-documentation/application-notes/AN-1070.pdf

    They summarise the five values to send and in what order in the following table: enter image description here

    If I look at the code that SamMaster has written it is writing the correct values in the correct order (I don't have the hardware but I can print the values).

    sending: [0x21, 0x00]
    sending: [0x50, 0xc7]
    sending: [0x40, 0x00]
    sending: [0xc0, 0x00]
    sending: [0x20, 0x00]
    

    That just leaves bullet 3 that is causing the problems.

    The fact that you get changes happening on the hardware suggests that some kind of communication is happening, just not the correct values.

    Looking at the limited documentation at https://pypi.org/project/spidev/ there are two likely commands that could be used: xfer or xfer2.

    The difference between the two are the value of the chip select pin between blocks.

    Figure 4 in the data sheet I think is saying that chip select should not be released between the two bytes.

    https://www.analog.com/media/en/technical-documentation/data-sheets/ad9833.pdf

    enter image description here

    That would suggest that xfer2 should be to used to send the blocks and not xfer as SamMaster has done. Although SamMaster seems to suggest he got it working with xfer and you were able to set the value to 400 Hz successfully. You would need your scope/logic analyser to see if the GPIO is doing the right thing on the hardware.

    At some point in your development you seem to have changed the sequence of values to send. It should be:

    send_data(0x2100)  # Start
    send_data(LSB)  # Frequency 14 bits (LSB)
    send_data(MSB)  # Frequency 14 bits (MSB)
    send_data(phase)  # Phase value
    send_data(0x2000)  # End
    

    This could be another source of your error.

    I looked at what the values should be that get sent for the different frequency you tested. I calculated the values follows:

    For Frequency: 400
    Frequency setting: 4295 = 0x10c7 = 0001000011000111
            send_data(0x2100)
            send_data(0x50c7)
            send_data(0x4000)
            send_data(0xc000)
            send_data(0x2000)
    For Frequency: 500
    Frequency setting: 5369 = 0x14f9 = 0001010011111001
            send_data(0x2100)
            send_data(0x54f9)
            send_data(0x4000)
            send_data(0xc000)
            send_data(0x2000)
    For Frequency: 600
    Frequency setting: 6442 = 0x192a = 0001100100101010
            send_data(0x2100)
            send_data(0x592a)
            send_data(0x4000)
            send_data(0xc000)
            send_data(0x2000)
    For Frequency: 1000
    Frequency setting: 10737 = 0x29f1 = 0010100111110001
            send_data(0x2100)
            send_data(0x69f1)
            send_data(0x4000)
            send_data(0xc000)
            send_data(0x2000)
    

    And finally, I refactored the code to make it easier for me to test the different parts of the code. I'm sharing it here for your information. I had to comment out any of the spi communication parts because I don't have the hardware.

    import time
    import spidev
    
    # activate spidev module and settings SPI
    bus = 0
    device = 1
    spi = spidev.SpiDev()
    spi.open(bus, device)
    spi.max_speed_hz = 976000
    
    
    def output_freq(hz_value):
        return int(round((hz_value * 2**28) / 25e6))
    
    
    def freq_change_start():
        ctrl_reg = 0
        ctrl_reg += 2**13  # set DB13 (28 bit freq reg)
        ctrl_reg += 2**8  # set DB8 (Reset)
        return ctrl_reg
    
    
    def freq_reg_lsb(freq_reg):
        fourteen_bit_mask = 0b0011111111111111
        write_value = 0
        write_value += 2**14  # set DB14
        lsb = freq_reg & fourteen_bit_mask
        write_value += lsb
        return write_value
    
    
    def freq_reg_msb(freq_reg):
        fourteen_bit_mask = 0b0011111111111111
        write_value = 0
        write_value += 2**14  # set DB14
        msb = freq_reg >> 14 & fourteen_bit_mask
        write_value += msb
        return write_value
    
    
    def phase_register():
        # Currently always the same value
        write_value = 0
        # Set phase register address
        write_value += 2 ** 15  # set DB15
        write_value += 2 ** 14  # set DB14
        return write_value
    
    
    def freq_change_end():
        ctrl_reg = 0
        ctrl_reg += 2**13  # set DB13 (28 bit freq reg)
        return ctrl_reg
    
    
    def word_split(word16):
        tx_msb = word16 >> 8
        tx_lsb = word16 & 0xFF
        return tx_msb, tx_lsb
    
    
    def send_spi_sequence(sequence):
        for word16 in sequence:
            two_bytes = word_split(word16)
            print(f"\tsending:[{two_bytes[0]:#02x}, {two_bytes[1]:#02x}]")
            print(f"\tsend_data({word16:#06x})")
            spi.xfer(two_bytes)
            # spi.xfer2(two_bytes)
    
    
    def change_freq(freq_hz):
        # Calc values to send
        print("For Frequency:", freq_hz)
        freq_reg = output_freq(freq_hz)
        print(f"Frequency setting: {freq_reg} = {freq_reg:#04x} = {freq_reg:016b}")
        ctrl_start = freq_change_start()
        print(f"Control register write: {ctrl_start:#04x}")
        lsb_value = freq_reg_lsb(freq_reg)
        print(f"lsb value: {lsb_value:#04x}")
        msb_value = freq_reg_msb(freq_reg)
        print(f"lsb value: {msb_value:#04x}")
        phase_reg = phase_register()
        print(f"Phase register write: {phase_reg:#04x}")
        ctrl_end = freq_change_end()
        print(f"Control register write: {ctrl_end:#04x}")
    
        # Write values to spi
        send_spi_sequence([ctrl_start, lsb_value, msb_value, phase_reg, ctrl_end])
    
    
    def main():
        show_freq_for = 20
        change_freq(400)
        time.sleep(show_freq_for)
        change_freq(500)
        time.sleep(show_freq_for)
        change_freq(600)
        time.sleep(show_freq_for)
        change_freq(1000)
        time.sleep(show_freq_for)
    
    
    if __name__ == '__main__':
        main()