有一个模拟板子上有4个dac芯片,2颗ad5762和2颗ad5764,最终使用了mcu的硬件spi做菊花链通信。
可以看到mcu的spi sdo进入到u29 ad5762的sdin后,u29 ad5762的sdo进入到u57 ad5762的sdi,依次类推,最后从u69 ad5764的sdo进入到mcu的sdin中,完成一个环。而ad5762与ad5764的所有LDAC都是并联的,公用mcu上的一个pin脚。
菊花链的部分还是要多看芯片手册。
从图中可以看到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文件中有说明。
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