Skip to content

two device on same spi bus not working #461

@quantrpeter

Description

@quantrpeter

Hello
Two months ago, it works. i build with latest source and now it doesm't. I am driving ST7735 and AD9833 in the same SPI bus, so MOSI and CLK pins are connected to same pins on Waveshare Zero C6 board. Now, AD9833 is still working but the LCD only see random dots.

import lcd_bus
from micropython import const
import machine
from time import sleep
import st7735
import lvgl as lv
import utime as time
from fs_driver import fs_register
from machine import Pin
import AD9833


# AD9833 c6 zero
# AD9833_SDO = 1
# AD9833_CLK = 2
# AD9833_CS = 22

# AD9833 c6 zero
AD9833_SDO = 15
AD9833_CLK = 14
AD9833_CS = 22

# display settings
_WIDTH = 128
_HEIGHT = 128
_BL = 21
_RST = 18
_DC = 19

_MOSI = 15  # SDA
# _MISO = 20
_SCK = 14  # SCL
_HOST = 1  # SPI2
_LCD_CS = 20
_LCD_FREQ = 4000000
_OFFSET_X = 2
_OFFSET_Y = 3

# Buttons
BUTTON0 = 4
ROTATE_BUTTON_S1 = 5
ROTATE_BUTTON_S2 = 3

selected = 0
selectedFreqBtn = 0
currentFreq = 4000
currentPhase = 0  # Current phase in degrees


def format_frequency(freq):
    """Format frequency display with appropriate units"""
    if freq < 1000:
        return f"{freq} Hz"
    elif freq < 1000000:
        khz = freq / 1000.0
        if freq % 1000 == 0:
            return f"{khz} kHz"
        else:
            return f"{khz:.3f} kHz"
    else:
        mhz = freq / 1000000.0
        if freq % 1000000 == 0:
            return f"{mhz} MHz"
        else:
            return f"{mhz:.6f} MHz"


def drawMenu():
    # Only update button backgrounds and frequency label
    global menu_buttons, selected
    for i, btn in enumerate(menu_buttons):
        if i == selected:
            btn.set_style_bg_color(lv.color_hex(0xffffff), 0)
        else:
            btn.set_style_bg_color(lv.color_hex(0x8888dd), 0)


spi_bus = machine.SPI.Bus(
    host=_HOST,
    mosi=_MOSI,
    # miso=_MISO,
    sck=_SCK
)
display_bus = lcd_bus.SPIBus(
    spi_bus=spi_bus,
    freq=_LCD_FREQ,
    dc=_DC,
    cs=_LCD_CS,
)

display = st7735.ST7735(
    data_bus=display_bus,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    backlight_pin=_BL,
    reset_pin=_RST,
    reset_state=st7735.STATE_LOW,
    backlight_on_state=st7735.STATE_HIGH,
    color_space=lv.COLOR_FORMAT.RGB565,
    color_byte_order=st7735.BYTE_ORDER_BGR,
    rgb565_byte_swap=True,
    offset_x=_OFFSET_X,
    offset_y=_OFFSET_Y
)

# Initialize display
display.init(st7735.TYPE_R_RED)
display.set_rotation(lv.DISPLAY_ROTATION._180)
display.set_backlight(100)

# Create screen
scrn = lv.screen_active()
scrn.set_style_bg_color(lv.color_hex(0x000000), 0)
scrn.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)

fs_drv = lv.fs_drv_t()
fs_register(fs_drv, "S")

img = lv.image(scrn)
img.set_src("S:blue.png")
img.set_size(20, 20)
img.set_pos(0, 5)


label = lv.label(scrn)
label.set_text("Func Generator")
label.set_pos(24, 8)
label.set_style_text_color(lv.color_hex(0xffffff), 0)
label.set_style_text_font(lv.font_montserrat_12, 0)

button0 = Pin(BUTTON0, Pin.IN, Pin.PULL_UP)  # Button pin

# Rotary encoder pins
rotary_s1 = Pin(ROTATE_BUTTON_S1, Pin.IN, Pin.PULL_UP)
rotary_s2 = Pin(ROTATE_BUTTON_S2, Pin.IN, Pin.PULL_UP)

# Initialize rotary encoder state
rotary_s1_prev = rotary_s1.value()
rotary_s2_prev = rotary_s2.value()

# --- Create menu buttons and labels once ---

freq = ["SQU", "SIN", "TRI"]
currentFreqLabelIndex = 0

menu_buttons = []
btn = lv.button(scrn)
btn.set_pos(0, 30)
btn.set_size(75, 20)  # Reduced width for waveform button
freqLbl = lv.label(btn)
freqLbl.set_text(freq[0])
freqLbl.set_style_text_color(lv.color_hex(0x000000), 0)
freqLbl.set_style_text_font(lv.font_montserrat_12, 0)
freqLbl.center()
menu_buttons.append(btn)

# PHASE button next to waveform button
phase_btn = lv.button(scrn)
phase_btn.set_pos(80, 30)  # Position next to waveform button
phase_btn.set_size(45, 20)
phase_btn_lbl = lv.label(phase_btn)
phase_btn_lbl.set_text("PHASE")
phase_btn_lbl.set_style_text_color(lv.color_hex(0x000000), 0)
phase_btn_lbl.set_style_text_font(lv.font_montserrat_12, 0)
phase_btn_lbl.center()
menu_buttons.append(phase_btn)  # Insert PHASE button as second button

for i, label in enumerate(["1Hz", "10Hz", "100Hz", "1kHz", "10kHz", "1MHz"]):
    btn = lv.button(scrn)
    if i < 3:
        btn.set_pos(i*40+i*3, 55)
    else:
        btn.set_pos((i-3)*40+(i-3)*3, 80)
    btn.set_size(40, 20)
    lbl = lv.label(btn)
    lbl.set_text(label)
    lbl.set_style_text_color(lv.color_hex(0x000000), 0)
    lbl.set_style_text_font(lv.font_montserrat_12, 0)
    lbl.center()
    menu_buttons.append(btn)


# --- Create frequency label once ---
freq_label = lv.label(scrn)
freq_label.set_pos(5, 100)
freq_label.set_style_text_color(lv.color_hex(0xffffff), 0)
freq_label.set_style_text_font(lv.font_montserrat_16, 0)
freq_label.set_text(format_frequency(currentFreq))

# --- Create phase label ---
phase_label = lv.label(scrn)
phase_label.set_pos(5, 115)
phase_label.set_style_text_color(lv.color_hex(0xffffff), 0)
phase_label.set_style_text_font(lv.font_montserrat_12, 0)
phase_label.set_text(f"Phase: {currentPhase}°")

drawMenu()

# temp.value(1)
# display_bus.deinit()

ad9833 = AD9833.AD9833(sdo=AD9833_SDO, clk=AD9833_CLK, cs=AD9833_CS,  fmclk=25)
ad9833.set_frequency(currentFreq, 0)
ad9833.set_phase(currentPhase, 0, rads=False)
ad9833.set_phase((currentPhase + 180) % 360, 1, rads=False)
ad9833.select_freq_phase(0, 0)
ad9833.set_mode('SQUARE')
time.sleep(2)

drawMenu()
lv.refr_now(lv.screen_active().get_display())
lv.task_handler()
while True:
    # time.sleep_ms(20)
    # sleep(0.2)
    b = False

    # Button handling
    if not button0.value():  # Button pressed
        selected = (selected + 1) % len(menu_buttons)
        print(selected)
        drawMenu()
        lv.refr_now(lv.screen_active().get_display())
        lv.task_handler()
        b = True
        while not button0.value():
            pass  # Wait for release (debounce)

    # Rotary encoder handling
    s1 = rotary_s1.value()
    s2 = rotary_s2.value()

    if s1 != rotary_s1_prev or s2 != rotary_s2_prev:
        # Detect rotation direction
        if selected == 0 and rotary_s1_prev == 1 and s1 == 0:  # S1 falling edge
            if s2 == 0:  # Clockwise
                currentFreqLabelIndex = (currentFreqLabelIndex + 1) % len(freq)
                print(f"Rotary CW: selected {selected}")
                freqLbl.set_text(freq[currentFreqLabelIndex])
                lv.refr_now(lv.screen_active().get_display())
                lv.task_handler()
                b = True
        elif selected == 0 and rotary_s2_prev == 1 and s2 == 0:  # S2 falling edge
            if s1 == 0:  # Counter-clockwise
                currentFreqLabelIndex = (currentFreqLabelIndex - 1) % len(freq)
                print(f"Rotary CCW: selected {selected}")
                freqLbl.set_text(freq[currentFreqLabelIndex])
                lv.refr_now(lv.screen_active().get_display())
                lv.task_handler()
                b = True
        elif selected > 1 and selected < 8 and rotary_s1_prev == 1 and s1 == 0:  # S1 falling edge
            if s2 == 0:  # Clockwise
                print(f"Rotary CW: selected {selected}")
                if selected == 2:
                    currentFreq += 1
                elif selected == 3:
                    currentFreq += 10
                elif selected == 4:
                    currentFreq += 100
                elif selected == 5:
                    currentFreq += 1000
                elif selected == 6:
                    currentFreq += 10000
                elif selected == 7:
                    currentFreq += 1000000
                if currentFreq > 12600000:
                    currentFreq = 12600000
                freq_label.set_text(format_frequency(currentFreq))
                lv.refr_now(lv.screen_active().get_display())
                lv.task_handler()
                b = True
        elif selected > 1 and selected < 8 and rotary_s2_prev == 1 and s2 == 0:  # S2 falling edge
            if s1 == 0:  # Counter-clockwise
                if selected == 2:
                    currentFreq -= 1
                elif selected == 3:
                    currentFreq -= 10
                elif selected == 4:
                    currentFreq -= 100
                elif selected == 5:
                    currentFreq -= 1000
                elif selected == 6:
                    currentFreq -= 10000
                elif selected == 7:
                    currentFreq -= 1000000
                if currentFreq < 0:
                    currentFreq = 0
                freq_label.set_text(format_frequency(currentFreq))
                lv.refr_now(lv.screen_active().get_display())
                lv.task_handler()
                b = True
        elif selected == 1 and rotary_s1_prev == 1 and s1 == 0:  # Phase adjustment - S1 falling edge
            if s2 == 0:  # Clockwise - increase phase
                currentPhase = (currentPhase + 10) % 360
                phase_label.set_text(f"Phase: {currentPhase}°")
                lv.refr_now(lv.screen_active().get_display())
                lv.task_handler()
                b = True
        elif selected == 1 and rotary_s2_prev == 1 and s2 == 0:  # Phase adjustment - S2 falling edge
            if s1 == 0:  # Counter-clockwise - decrease phase
                currentPhase = (currentPhase - 10) % 360
                phase_label.set_text(f"Phase: {currentPhase}°")
                lv.refr_now(lv.screen_active().get_display())
                lv.task_handler()
                b = True

        rotary_s1_prev = s1
        rotary_s2_prev = s2
        time.sleep_ms(10)  # Debounce delay

    if b:
        print(currentFreqLabelIndex, currentFreq, currentPhase)
        if currentFreqLabelIndex == 0:
            ad9833.set_frequency(currentFreq, 0)
            # ad9833.select_freq_phase(0, 0)
            ad9833.set_mode('SQUARE')
        elif currentFreqLabelIndex == 1:
            ad9833.set_frequency(currentFreq, 0)
            ad9833.set_phase((currentPhase + 180) % 360, 0, rads=False)
            # ad9833.set_phase((currentPhase + 180) % 360, 1, rads=False)
            ad9833.select_freq_phase(0, 0)
            ad9833.set_mode('SIN')
        elif currentFreqLabelIndex == 2:
            ad9833.select_freq_phase(0, 0)
            ad9833.set_frequency(currentFreq, 0)
            ad9833.set_mode('TRIANGLE')
"""AD9833, micropython module to use the AD983X
programable waveform generators

created June 2, 2023
modified June 2, 2023
"""

"""
Copyright 2023 Owain Martin

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from math import pi, radians
import machine
import utime as time

class AD9833:

    def __init__(self, sdo, clk, cs, fmclk=25):
        """__init__, set up AD9833 object"""

        self.fmclk = fmclk*10**6

        # set up SPI connection details
        # had to use phase = 1 even though the datasheet
        # specifies phase = 0, on Pi SBC used phase = 0.
        self.sdo = machine.Pin(sdo)
        self.clk = machine.Pin(clk)
        self.cs = machine.Pin(cs, machine.Pin.OUT)
        self.cs.value(1)
        
        # Use software SPI to avoid conflicts with LCD hardware SPI
        # self.spi = machine.SoftSPI(
        #     baudrate=1000000,  # Lower speed for reliability
        #     polarity=1,
        #     phase=1,
        #     sck=machine.Pin(clk),
        #     mosi=machine.Pin(sdo),
        #     miso=machine.Pin(0)  # AD9833 doesn't need MISO
        # )
        
        _HOST=1  # Use separate SPI host to avoid conflicts with LCD
        self.spi = machine.SPI.Bus(
            host=_HOST,
            sck=machine.Pin(clk),
            mosi=machine.Pin(sdo),
            # miso=machine.Pin(0)  # AD9833 doesn't need MISO
        )
        # self.spi = machine.SPI(_HOST, baudrate = 4000000, polarity = 1, phase = 1, sck= self.clk, mosi = self.sdo)      


        self.set_control_reg(B28=1, RESET=1)

        self.mode = "RESET"
        self.writeMode = "BOTH"
        self.freq0 = 0
        self.freq1 = 0
        self.phase0 = 0
        self.phase1 = 0
        return

    def write_data(self, data):
        """write_data, function to write data to
        the AD983x chip"""

        # print(data)
        data = bytearray(data)  # creates buffer object

        self.cs.value(0)
        self.spi.write(data)
        self.cs.value(1)
        
        return

    def set_control_reg(self, B28=1, HLB=0, FS=0, PS=0, RESET=0, SLP1=0, SLP12=0, OP=0, DIV2=0, MODE=0):
        """set_control_reg, function to set any/all of the bits
        of the AD9833 control register"""

        self.B28 = B28
        self.HLB = HLB
        self.FS = FS
        self.PS = PS
        self.RESET = RESET
        self.SLP1 = SLP1
        self.SLP12 = SLP12
        self.OP = OP
        self.DIV2 = DIV2
        self.MODE = MODE

        controlReg = (B28 << 13) + (HLB << 12) + (FS << 11) + (PS << 10) + (RESET <<
                                                                            8) + (SLP1 << 7) + (SLP12 << 6) + (OP << 5) + (DIV2 << 3) + (MODE << 1)

        # print(hex(controlReg))

        controlRegList = [(controlReg & 0xFF00) >> 8, controlReg & 0x00FF]

        self.write_data(controlRegList)

        return

    def set_frequency(self, fout, freqSelect):
        """set_frequency, function to set the frequency registers"""

        # calculate frequncy register value from fout
        freqR = int((fout*pow(2, 28))/self.fmclk)

        # split frequency register value into 2
        # 14 bit segments
        fMSB = (freqR & 0xFFFC000) >> 14
        fLSB = freqR & 0x3FFF

        # add register address to each 14 bit segment
        if freqSelect == 0:
            addr = 0b01
            self.freq0 = fout
        else:
            addr = 0b10
            self.freq1 = fout

        fMSB = fMSB + (addr << 14)
        fLSB = fLSB + (addr << 14)

        # print(hex(fLSB), hex(fMSB))

        # split fMSB & fLSB into 8 bits segements
        # for writing to AD9833 freq registers

        fLSBList = [(fLSB & 0xFF00) >> 8, fLSB & 0x00FF]
        fMSBList = [(fMSB & 0xFF00) >> 8, fMSB & 0x00FF]
        fBoth = fLSBList + fMSBList

        if self.writeMode == 'MSB':
            self.write_data(fMSBList)
        elif self.writeMode == 'LSB':
            self.write_data(fLSBList)
        else:
            self.write_data(fBoth)

        return

    def set_phase(self, pout, phaseSelect, rads=True):
        """set_phase, function to set the phase registers"""

        # calculate the phase register value
        if rads == False:
            # convert degrees to radians
            pout = radians(pout)

        phaseR = int(pout*4096/(2*pi))

        # add phase address
        # 12 bit regValue
        # 1 bit - don't care
        # 3 bit address
        phaseR = phaseR + (0b11 << 14) + (phaseSelect << 13)

        # split phaseR into 8 bits segements
        # for writing to AD9833 phase registers
        phaseRList = [(phaseR & 0xFF00) >> 8, phaseR & 0x00FF]

        self.write_data(phaseRList)

        return

    def set_mode(self, mode='SIN'):
        """set_mode, function to set the mode/output type of the
        AD9833 as well as the active frequency and phase registers.
        Valid modes include: 'RESET', 'OFF', 'SIN','TRIANGLE',
        'SQUARE', 'SQUARE/2'"""
        
        print(mode)

        self.mode = mode

        if mode == 'SIN':
            self.set_control_reg(B28=self.B28, HLB=self.HLB,
                                 FS=self.FS, PS=self.PS, RESET=0, MODE=0)
        elif mode == 'TRIANGLE':
            self.set_control_reg(B28=self.B28, HLB=self.HLB,
                                 FS=self.FS, PS=self.PS, RESET=0, MODE=1)
        elif mode == 'SQUARE':
            self.set_control_reg(B28=self.B28, HLB=self.HLB, FS=self.FS, PS=self.PS, RESET=0, SLP12=1,
                                 OP=1, DIV2=1, MODE=0)
        elif mode == 'SQUARE/2':
            self.set_control_reg(B28=self.B28, HLB=self.HLB, FS=self.FS, PS=self.PS, RESET=0, SLP12=1,
                                 OP=1, DIV2=0, MODE=0)
        elif mode == 'RESET':
            self.set_control_reg(B28=self.B28, HLB=self.HLB,
                                 FS=self.FS, PS=self.PS, RESET=1)
        elif mode == 'OFF':
            self.set_control_reg(B28=self.B28, HLB=self.HLB,
                                 FS=self.FS, PS=self.PS, RESET=1, SLP1=1, SLP12=1)

        return

    def set_write_mode(self, writeMode='BOTH'):
        """set_write_mode, function to set the B28 and HLB bits in the
        control register which control how data is written to the
        frequency registers. Valid value for writeMode are 'BOTH',
        'MSB' and 'LSB'"""

        B28 = 1
        HLB = 0
        self.writeMode = 'BOTH'

        if writeMode == 'MSB':
            B28 = 0
            HLB = 1
            self.writeMode = 'MSB'
        elif writeMode == 'LSB':
            B28 = 0
            HLB = 0
            self.writeMode = 'LSB'

        self.set_control_reg(B28=B28, HLB=HLB, FS=self.FS, PS=self.PS, RESET=self.RESET, SLP1=self.SLP1,
                             SLP12=self.SLP12, OP=self.OP, DIV2=self.DIV2, MODE=self.MODE)

        return

    def select_freq_phase(self, FS, PS):
        """select_freq_phase, function to select which frequency and phase register
        the AD9833 uses and changes to them"""

        self.set_control_reg(B28=self.B28, HLB=self.HLB, FS=FS, PS=PS, RESET=self.RESET, SLP1=self.SLP1,
                             SLP12=self.SLP12, OP=self.OP, DIV2=self.DIV2, MODE=self.MODE)

        return


if __name__ == "__main__":

    # ad9833 = AD9833(sdo = 19, clk = 18, cs = 17,  fmclk = 25)
    # ad9833 = AD9833(sdo=3, clk=2, cs=1,  fmclk=25)
    ad9833 = AD9833(sdo=8, clk=7, cs=3,  fmclk=25)

    delay = 10

    ad9833.set_frequency(1100, 0)
    ad9833.set_frequency(2200, 1)
    ad9833.set_phase(0, 0, rads=False)
    ad9833.set_phase(180, 1, rads=False)
    ad9833.select_freq_phase(0, 0)
    ad9833.set_mode('SIN')
    print('Sin')
    time.sleep(delay)

    ad9833.set_write_mode('LSB')
    ad9833.set_frequency(1200, 0)
    print('LSB')
    time.sleep(delay)

    ad9833.select_freq_phase(1, 0)
    time.sleep(delay)

    ad9833.set_mode('TRIANGLE')
    print('TRIANGLE')
    time.sleep(delay)

    # freq 0 Triangle wave output
    ad9833.select_freq_phase(0, 0)
    ad9833.set_mode('TRIANGLE')
    print('TRIANGLE')
    time.sleep(delay)

    # freq 0 Square wave output
    ad9833.set_mode('SQUARE')
    print('SQUARE')
    time.sleep(delay)

    # freq 0 divide by 2 Square wave output
    ad9833.set_mode('SQUARE/2')
    print('SQUARE/2')
    time.sleep(delay)

    # change freq 0 to 1700 Hz, Sin wave output
    ad9833.set_write_mode('BOTH')
    ad9833.set_frequency(1700, 0)
    ad9833.set_mode('SIN')
    print('BOTH')
    time.sleep(delay)

    ad9833.set_mode('OFF')

thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions