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

1
2
3
4
5
6
7
8
9
10
11
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的方法在芯片手册写的很详细了,在此就不做说明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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));
}

这里我使用的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芯片输出就行了,其实返回值是可以不要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。

1
2
3
4
5
6
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;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
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);
}

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