spi daisy chain communication of ad5764

There are four dac chips, two ad5762 and two ad5764 on an analog board. Finally, the hardware spi of mcu is used for daisy chain communication.



It can be seen that after the spi sdo of mcu enters the sdin of u29 ad5762, the sdo of u29 ad5762 enters the sdi of u57 ad5762, and so on. Finally, from the sdo of u69 ad5764 into the sdin of mcu, a ring is completed. All ldacs of ad5762 and ad5764 are connected in parallel and share a pin pin on the mcu.

For the daisy chain, you should read the chip manual more.


Here you can see that there is a t15 time, very short, but very important. And the t10 time of LDAC is also very important. The maximum and minimum values of time will be described in the chip manual.

First initialize the reset pin and ldac pin of the chip

static void dac_init_ldac_reset_pin(void)
{
    // dac reset pin
    GPIO_SetMode(PA, BIT14, GPIO_PMD_OUTPUT);
    SYS->GPA_MFP &= (~(1 << 14));
    // dac ldac pin
    GPIO_SetMode(PA, BIT15, GPIO_PMD_OUTPUT);
    SYS->ALT_MFP &= (~(1 << 9));
    SYS->GPA_MFP &= (~(1 << 15));
    LDAC = PIN_HIGH;
}

Then initialize the spi interface of mcu, and initialize the registers of ad5762 and ad5764. The relevant registers are range, offset and gain. The values of offset and gain directly affect the output accuracy of dac.

void hw_ad5764_init(void)
{
    dac_init_ldac_reset_pin();
    CLK_SetModuleClock(SPI1_MODULE, CLK_CLKSEL1_SPI1_S_HCLK, MODULE_NoMsk);
    CLK_EnableModuleClock(SPI1_MODULE);
    // PC8 -> CS
    SYS->GPC_MFP |= (1 << 8);
    SYS->ALT_MFP1 &= (~(1 << 23));
    // PC9 -> SCLK
    SYS->GPC_MFP |= (1 << 9);
    // PC10 -> MISO
    SYS->GPC_MFP |= (1 << 10);
    // PC11 -> MOSI
    SYS->GPC_MFP |= (1 << 11);
    RSTIN = 0;
    delay_100ms(1);
    RSTIN = 1;
    SYS_ResetModule(SPI1_RST);
    SPI_Open(SPI1, SPI_MASTER, SPI_MODE_1, 24, 4000000); // send 24bit once, spi frequence 4MHz
    SPI1->CNTRL &= (~(0x0f << 12));
    SPI_DisableFIFO(SPI1);
    SPI_DisableAutoSS(SPI1);    // nuc123 spi autoss error
    init_dac_range_reg(spi_ad5764.send_array, DAC_A);
    init_dac_range_reg(spi_ad5764.send_array, DAC_B);
    init_dac_range_reg(spi_ad5764.send_array, DAC_C);
    init_dac_range_reg(spi_ad5764.send_array, DAC_D);
    init_dac_offset_reg(spi_ad5764.send_array);
    init_dac_gain_reg(spi_ad5764.send_array);
    init_dac_data_reg(spi_ad5764.send_array, 0x8000);
    memset(spi_ad5764.send_array, 0, sizeof(spi_ad5764.send_array));
}

The mcu I use here is NUC123SD4AN0 of Xintang company. The spi interface does not use the interrupt mode. According to the chip manual, the registers of ad5762 and ad5764 are 24bit. When initializing the spi interface, it is necessary to set the bit length of one transmission to 24, but the variable sent by the spi interface must be 32bit, that is, uint32_t type, which does not prevent the spi interface from sending only 24bit at a time, which is in nuc123 H is described in the document.

I set the speed of spi interface to 4MHz, which is very fast. The maximum clock of ad5762 and ad5764 is 16MHz, which I can't use at present.
When using NUC123SD4AN0, I found that AutoSS (give the chip selection pin to the hardware management) can not communicate normally. When the software controls the chip selection pin, it is normal. I haven't solved this bug.
Finally, we need to implement the read-write function of spi. For ad5762 and ad5764, we only need to write the value inside to let the dac chip output. In fact, the return value can be omitted.

void hw_spi_read_write(uint32_t *send_data, uint32_t *receice_data, uint8_t size)
{
    SPI_SET_SS0_LOW(SPI1);
    for (uint8_t i = 0; i < size; i++)
    {
        SPI_WRITE_TX0(SPI1, send_data[i]);
        SPI_TRIGGER(SPI1);
        while (SPI_IS_BUSY(SPI1));
        receice_data[i] = SPI_READ_RX0(SPI1);
    }
    SPI_SET_SS0_HIGH(SPI1);
    LDAC = PIN_LOW;
    Delay(1);
    LDAC = PIN_HIGH;
}

Here, lower and raise the SS0 pin in the function (that is, the chip selection pin). When the LDAC pin is pulled down and raised, a delay should be placed in the middle, which has been explained in the previous daisy chain timing.
In the ladc pin initialization function, the LDAC is raised. There is a saying that LDAC = PIN_HIGH. Therefore, in the read-write function of spi, the LDAC must be pulled down for the first time to make the data sent by spi to dac chip take effect, then delay, and finally pull up the LDAC to keep the value of dac register until the next update. This can be seen clearly from the timing of the daisy chain. In the previous blog post, it was said that if the daisy chain mode is not used, that is, reading and writing a single chip, the LDAC does not need to be pulled down and raised frequently. After the mcu initializes the LDAC pin, it will be pulled down all the time, so that the value written to the ad5762 data register will take effect immediately.
Here, because there are four dac chips, two ad5762 and two ad5764 on the board, the data sent by spi at one time is 4*24bit, that is, send_ The length of the data array is 4.

typedef struct DAC_SEND_ARRAY
{
    uint32_t send_array[4];//AB
    uint32_t send_cd_array[4];//CD
    uint32_t receive_array[4];
}ad_5764_spi;

So far, the logic of daisy chain communication is completed. Here are some encapsulation functions.

uint32_t daisy_chain_writecoarsegainregister(uint32_t dac_ch, uint8_t range)
{
    return WRITE + REG_COARSE_GAIN + dac_ch + range;
}
uint32_t daisy_chain_writefinegainregister(enum lcc_ch_num ch, enum lcc_ch_abcd abcd, LCC6_EQUIP_T *t)
{
    uint32_t result = 0;
    switch (ch)
    {
    case ch1:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[0].gain_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[0].gain_B;
        }
        break;
    case ch2:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[1].gain_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[1].gain_B;
        }
        break;
    case ch3:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].gain_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].gain_B;
        }
        break;
    case ch4:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].gain_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].gain_B;
        }
        break;
    case ch5:
        if (abcd == c)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].gain_C;
        }
        else if (abcd == d)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].gain_D;
        }
        break;
    case ch6:
        if (abcd == c)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].gain_C;
        }
        else if (abcd == d)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].gain_D;
        }
        break;
    default:
        break;
    }
    return result;
}

uint32_t daisy_chain_writeoffsetregister(enum lcc_ch_num ch, enum lcc_ch_abcd abcd, LCC6_EQUIP_T *t)
{
    uint32_t result = 0;
    switch (ch)
    {
    case ch1:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[0].offset_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[0].offset_B;
        }
        break;
    case ch2:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[1].offset_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r1[1].offset_B;
        }
        break;
    case ch3:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].offset_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].offset_B;
        }
        break;
    case ch4:
        if (abcd == a)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].offset_A;
        }
        else if (abcd == b)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].offset_B;
        }
        break;
    case ch5:
        if (abcd == c)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].offset_C;
        }
        else if (abcd == d)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[0].offset_D;
        }
        break;
    case ch6:
        if (abcd == c)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].offset_C;
        }
        else if (abcd == d)
        {
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].offset_D;
        }
        break;
    default:
        break;
    }
    return result;
}

uint32_t daisy_chain_writedataregister(uint32_t dac_ch, uint16_t data)
{
    return WRITE + REG_DATA + dac_ch + data;
}
void init_dac_range_reg(uint32_t *array, uint32_t ch)
{
    array[0] = daisy_chain_writecoarsegainregister(ch, RANGE_10V);
    array[1] = array[0];
    if (ch == DAC_A || ch == DAC_B)
    {
        array[2] = array[0];
        array[3] = array[0];
    }
    else
    {
        array[2] = 0;
        array[3] = 0;
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
}

void init_dac_offset_reg(uint32_t *array)
{
    array[0] = WRITE + REG_OFFSET + DAC_A + 0;
    for (uint8_t i = 1; i < 4; i++)
    {
        array[i] = array[0];
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = WRITE + REG_OFFSET + DAC_B + 0;
    for (uint8_t i = 1; i < 4; i++)
    {
        array[i] = array[0];
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = WRITE + REG_OFFSET + DAC_C + 0;
    array[1] = array[0];
    array[2] = 0;
    array[3] = 0;
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = WRITE + REG_OFFSET + DAC_D + 0;
    array[1] = array[0];
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
}

void init_dac_gain_reg(uint32_t *array)
{
    array[0] = WRITE + REG_FINE_GAIN + DAC_A + 0;
    for (uint8_t i = 1; i < 4; i++)
    {
        array[i] = array[0];
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = WRITE + REG_FINE_GAIN + DAC_B + 0;
    for (uint8_t i = 1; i < 4; i++)
    {
        array[i] = array[0];
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = WRITE + REG_FINE_GAIN + DAC_C + 0;
    array[1] = array[0];
    array[2] = 0;
    array[3] = 0;
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = WRITE + REG_FINE_GAIN + DAC_D + 0;
    array[1] = array[0];
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
}

void init_dac_data_reg(uint32_t *array, uint16_t data)
{
    array[0] = daisy_chain_writedataregister(DAC_A, data);
    for (uint8_t i = 1; i < 4; i++)
    {
        array[i] = array[0];
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = daisy_chain_writedataregister(DAC_B, data);
    for (uint8_t i = 1; i < 4; i++)
    {
        array[i] = array[0];
    }
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = daisy_chain_writedataregister(DAC_C, data);
    array[1] = array[0];
    array[2] = 0;
    array[3] = 0;
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
    array[0] = daisy_chain_writedataregister(DAC_D, data);
    array[1] = array[0];
    hw_spi_read_write(array, spi_ad5764.receive_array, 4);
}

Tags: C++ Embedded system stm32 SPI

Posted by markl999 on Thu, 05 May 2022 10:08:13 +0300