ad5764的spi菊花链通信

有一个模拟板子上有4个dac芯片,2颗ad5762和2颗ad5764,最终使用了mcu的硬件spi做菊花链通信。
dac原理图
dac原理图
mcu原理图
可以看到mcu的spi sdo进入到u29 ad5762的sdin后,u29 ad5762的sdo进入到u57 ad5762的sdi,依次类推,最后从u69 ad5764的sdo进入到mcu的sdin中,完成一个环。而ad5762与ad5764的所有LDAC都是并联的,公用mcu上的一个pin脚。
spi菊花链原理图
菊花链的部分还是要多看芯片手册。
LDAC的作用
从图中可以看到spi第一个发送的bit是db23,也就是MSB在前,同时SDO线上标明了INPUT WORD FOR DAC N,是什么意思呢?板子上有4颗dac芯片,就是说spi第1次发送的数据其实是给第4颗芯片的,spi第4次发送的数据才是给第1颗芯片的。第几颗芯片跟原理图的连线有关系,需要自己根据实际情况判断。
菊花链时序
这里可以看到有一个t15时间,极短,但是很重要。还有LDAC的t10时间,也非常重要。在芯片手册中会有对时间的最大最小值说明。
关键时间
首先初始化芯片的reset pin和ldac pin

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;
}

紧接着对mcu的spi接口进行初始化,并对ad5762、ad5764的寄存器进行初始化,相关寄存器就是量程、offset与gain。这其中offset与gain的值直接影响了dac的输出精度。在我之前的文章中有提到如何校准没有offset和gain寄存器的adc芯片。如何设置offset与gain的方法在芯片手册写的很详细了,在此就不做说明了。

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(&t1, &spi_ad5764);
    init_dac_gain_reg(&t1, &spi_ad5764);
    init_dac_data_reg(spi_ad5764.send_array, 0x8000);
    memset(spi_ad5764.send_array, 0, sizeof(spi_ad5764.send_array));
}

这里我使用的mcu是新唐公司的NUC123SD4AN0,spi接口没有使用中断模式,根据芯片手册可以知道,ad5762、ad5764的寄存器是24bit的,那么在初始化spi接口时,需要将一次发送的bit长度设置为24,但是spi接口发送的变量一定是32bit的,也就是uint32_t类型,这并不妨碍spi接口一次只发送24bit,这一点在NUC123.h文件中有说明。
TX寄存器说明
spi接口的速度我设置的是4MHz,已经很快了,而ad5762和ad5764的最大时钟是16MHz,目前我还用不到。
在使用NUC123SD4AN0的时候,我发现AutoSS(将片选pin交给硬件管理)并不能正常通信,当软件控制片选pin的时候则是正常的,这个bug我并未解决。
最后需要实现spi的读写函数,对ad5762和ad5764来说,我们只需要往里写值让dac芯片输出就行了,其实返回值是可以不要的。

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;
}

这里,在函数中拉低和拉高SS0 pin(也就是片选pin)。LDAC pin的拉低拉高中间要放一个延时,这一点在前面说菊花链时序的时候已经做了说明。
在ladc pin初始化函数中,把LDAC拉高了,有一句话是LDAC = PIN_HIGH,因此在spi的读写函数中,LDAC第一次一定是拉低,让spi发送到dac芯片的数据生效,然后延时,最后把LDAC拉高,让dac寄存器的值保持到下次更新之前。这一点可以从菊花链的时序中看的很清楚。在之前的博文中有说到不使用菊花链模式,也就是读写单颗芯片,那么LDAC就不需要经常拉低拉高了,在mcu初始化LDAC pin之后,一直拉低就行了,这样每次写入到ad5762数据寄存器的值就立即生效了。
这里因为板子上有4颗dac芯片,2颗ad5762和2颗ad5764,所以spi一次发送的数据是4*24bit,也就是说send_data数组的长度是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;

至此,菊花链通信的逻辑就完成了。下面是一些封装函数。

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_FINE_GAIN + DAC_A + t->r1[0].gain_A;
        }
        else if (abcd == b)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_B + t->r1[0].gain_B;
        }
        break;
    case ch2:
        if (abcd == a)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_A + t->r1[1].gain_A;
        }
        else if (abcd == b)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_B + t->r1[1].gain_B;
        }
        break;
    case ch3:
        if (abcd == a)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_A + t->r2[0].gain_A;
        }
        else if (abcd == b)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_B + t->r2[0].gain_B;
        }
        break;
    case ch4:
        if (abcd == c)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_C + t->r2[0].gain_C;
        }
        else if (abcd == d)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_D + t->r2[0].gain_D;
        }
        break;
    case ch5:
        if (abcd == a)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_A + t->r2[1].gain_A;
        }
        else if (abcd == b)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_B + t->r2[1].gain_B;
        }
        break;
    case ch6:
        if (abcd == c)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_C + t->r2[1].gain_C;
        }
        else if (abcd == d)
        {
    
    
            result = WRITE + REG_FINE_GAIN + DAC_D + 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_B + 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_B + 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_B + t->r2[0].offset_B;
        }
        break;
    case ch4:
        if (abcd == c)
        {
    
    
            result = WRITE + REG_OFFSET + DAC_C + t->r2[0].offset_C;
        }
        else if (abcd == d)
        {
    
    
            result = WRITE + REG_OFFSET + DAC_D + t->r2[0].offset_D;
        }
        break;
    case ch5:
        if (abcd == a)
        {
    
    
            result = WRITE + REG_OFFSET + DAC_A + t->r2[1].offset_A;
        }
        else if (abcd == b)
        {
    
    
            result = WRITE + REG_OFFSET + DAC_B + t->r2[1].offset_B;
        }
        break;
    case ch6:
        if (abcd == c)
        {
    
    
            result = WRITE + REG_OFFSET + DAC_C + t->r2[1].offset_C;
        }
        else if (abcd == d)
        {
    
    
            result = WRITE + REG_OFFSET + DAC_D + 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;
}

static 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);
}

static void init_dac_offset_reg(LCC6_EQUIP_T *t, ad_5764_spi *array)
{
    
    
    array->send_array[3] = daisy_chain_writeoffsetregister(ch1, a, t);
    array->send_array[2] = daisy_chain_writeoffsetregister(ch2, a, t);
    array->send_array[1] = daisy_chain_writeoffsetregister(ch3, a, t);
    array->send_array[0] = daisy_chain_writeoffsetregister(ch5, a, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
    array->send_array[3] = daisy_chain_writeoffsetregister(ch1, b, t);
    array->send_array[2] = daisy_chain_writeoffsetregister(ch2, b, t);
    array->send_array[1] = daisy_chain_writeoffsetregister(ch3, b, t);
    array->send_array[0] = daisy_chain_writeoffsetregister(ch5, b, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
    array->send_array[3] = 0;
    array->send_array[2] = 0;
    array->send_array[1] = daisy_chain_writeoffsetregister(ch4, c, t);
    array->send_array[0] = daisy_chain_writeoffsetregister(ch6, c, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
    array->send_array[3] = 0;
    array->send_array[2] = 0;
    array->send_array[1] = daisy_chain_writeoffsetregister(ch3, d, t);
    array->send_array[0] = daisy_chain_writeoffsetregister(ch5, d, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
}

static void init_dac_gain_reg(LCC6_EQUIP_T *t, ad_5764_spi *array)
{
    
    
    array->send_array[3] = daisy_chain_writefinegainregister(ch1, a, t);
    array->send_array[2] = daisy_chain_writefinegainregister(ch2, a, t);
    array->send_array[1] = daisy_chain_writefinegainregister(ch3, a, t);
    array->send_array[0] = daisy_chain_writefinegainregister(ch5, a, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
    array->send_array[3] = daisy_chain_writefinegainregister(ch1, b, t);
    array->send_array[2] = daisy_chain_writefinegainregister(ch2, b, t);
    array->send_array[1] = daisy_chain_writefinegainregister(ch3, b, t);
    array->send_array[0] = daisy_chain_writefinegainregister(ch5, b, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
    array->send_array[3] = 0;
    array->send_array[2] = 0;
    array->send_array[1] = daisy_chain_writefinegainregister(ch4, c, t);
    array->send_array[0] = daisy_chain_writefinegainregister(ch6, c, t);
    hw_spi_read_write(array->send_array, array->receive_array, 4);
    array->send_array[3] = 0;
    array->send_array[2] = 0;
    array->send_array[1] = daisy_chain_writefinegainregister(ch3, d, t);
    array->send_array[0] = daisy_chain_writefinegainregister(ch5, d, t);
    hw_spi_read_write(array->send_array, array->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);
}

如果还是不明白,请下载我上传的源码吧。链接如下:
https://download.csdn.net/download/cp_srd/13199314

猜你喜欢

转载自blog.csdn.net/cp_srd/article/details/110260385