手把手一起玩转AD5522:避坑指南与软硬件实战全解析
从芯片介绍、STM32下位机驱动到Python上位机,一站式搞定高性能参数测量单元(PMU)
从芯片介绍、STM32下位机驱动到Python上位机,一站式搞定高性能参数测量单元(PMU)
大家好!在之前的项目中,我需要构建一个高精度的自动化测试平台,核心是电压电流输出与采集。ADI的AD5522(参数测量单元PMU) 负责输出精确的可编程电压/电流并测量响应,而AD7606(同步采样ADC) 则负责高速、高精度地采集多路信号。这对“黄金搭档”在半导体测试、传感器标定和精密仪器中非常常见。
本次项目使用的硬件板卡并非由我本人设计(淘宝购买),因此硬件部分仅作简要介绍和关键点提示,重点会放在软件调试上。

第一次拿到它的时候,看着厚厚的Datasheet和复杂的寄存器,确实有点头皮发麻。网上成体系的、能跑通的中文资料又比较少。经过一番摸索,终于成功地驱动了起来。今天我就把自己调试和使用AD5522的全过程分享给大家,包括芯片简介、硬件连接注意事项、STM32下位机程序编写、以及Python上位机控制软件的实现,希望能帮各位小伙伴少走弯路!
一、 AD5522是什么?为什么是它?
ADI的AD5522是一个集成了源(Source)、测(Measure)、序(Sequence)三大功能的参数测量单元(PMU),常见于半导体测试(ATE)、芯片验证等高端场景。简单来说,AD5522是一个四通道的PMU芯片。每个通道都能独立工作,并且非常“全能”:
- 源(Source): 可以输出精确的电压或电流,force模式。
- 测(Measure): 可以测量通道上的电压或电流,sense模式。
- 序(Sequence): 其内部有一个序列发生器,可以预先编程一系列操作(如 Force Voltage -> Measure Current -> 下一步…),然后自动执行,极大减轻主控MCU的负担。

正是因为这些特性,它在给待测器件(DUT)施加激励并精确测量其响应方面表现得非常出色。
二、 硬件连接简析(虽然不是“亲生”的硬件)
硬件板卡是现成的,下面对其进行简要分析,如图所示:

整个板卡由电源、STM32、AD5522、AD7606组成。AD5522每个通道可独立输出(Force) 精确的电压/电流,并测量(Sense/Measure) 负载上的电压或电流。完美用于激励待测器件(DUT)。AD7606负责同步采集DUT的多路响应信号,或者同时监测系统关键节点的电压,8通道同步采样、16位ADC。STM32F103通过SPI配置和控制AD5522与AD7606,处理数据,执行逻辑,并通过串口与上位机通信,其强大的性能足以处理两个外设的中断和实时数据流。
整板需要多种不同规格的电源,这是最复杂也是最关键的部分!除了数字电源,如2.5V,3.3V,5V外还需要模拟正负电源,如±15V。我在调试过程中,12V转±15V电源芯片发烫极其严重,最终选择不再使用该电源芯片,直接用其他电源供电,这个问题最终没找到原因。不过商家其他板卡没有这个现象,晕。
AD5522、AD7606与STM32的通信方式都是SPI,AD5522有SPI和LVDS两种接口模式

一定要检查硬件,看该引脚是接的高电平还是低电平,我当时在调试时使用的是SPI,但是没发现硬件是选用的LVDS,导致通信极其不稳定,最后还是在同事帮助下找到该问题。
AD7606要注意硬件上这两个引脚是否正确连接

三、 下位机驱动(STM32篇)之双设备驱动
驱动的核心是让STM32正确地与两个设备“对话”。
完整的工程目录如下:

打开工程文件:

完整的工程打开如下:

AD5522核心程序AD5522_WriteRegister和AD5522_ReadRegister如下
void AD5522_WriteRegister( uint32_t Code)
{
uint8_t i;
delay_us(1);
AD5522_SYNC_0;
delay_us(1);
for (i=0;i<32;i++)
{
if((Code&0x80000000)==0x80000000)
AD5522_SDI_1;
else
AD5522_SDI_0;
delay_ns(5);
AD5522_SCLK_1;
delay_us(1);
AD5522_SCLK_0;
delay_us(1);
Code<<=1;
}
delay_us(1);
AD5522_SYNC_1;
delay_us(1);
}
uint8_t AD5522_ReadByte(void)
{
uint8_t i;
uint8_t result;
AD5522_SYNC_0;
for(i=0;i<8;i++)
{
AD5522_SCLK_1;
delay_us(1);
AD5522_SCLK_0;
result<<=1;
if(AD5522_SDO)
result |= 0x01;
delay_us(1);
}
AD5522_SYNC_1;
return result;
}
uint32_t AD5522_ReadRegister(uint32_t Code)
{
uint32_t temp,L,M,H;
AD5522_WriteRegister(Code);
delay_us(5);
AD5522_SYNC_0;
AD5522_SDI_1;
delay_us(1);
H=AD5522_ReadByte();
M=AD5522_ReadByte();
L=AD5522_ReadByte();
delay_us(1);
AD5522_SYNC_1;
temp=H<<16|M<<8|L;
return temp;
}

注意AD5522读寄存器时,不要将已经写的数据篡改。因此SDI一定要拉高,置1。
AD7606核心程序ad7606_ReadData如下
uint16_t SPI_ReceiveData(void)
{
uint8_t count=0;
uint16_t Num=0;
AD7606_SCLK_1;
for(count=0;count<16;count++)
{
Num<<=1;
AD7606_SCLK_0;
if(AD7606_SDO) Num |= 0x01;
AD7606_SCLK_1;
}
return Num;
}
uint16_t ad7606_ReadBytes(void)
{
uint16_t usData = 0;
usData = SPI_ReceiveData();
return usData;
}
void ad7606_ReadData(int16_t *ad1,int16_t *ad2,int16_t *ad3,int16_t *ad4,
int16_t *ad5,int16_t *ad6,int16_t *ad7,int16_t *ad8)
{
AD7606_CS_0;
*ad1=ad7606_ReadBytes();
*ad2=ad7606_ReadBytes();
*ad3=ad7606_ReadBytes();
*ad4=ad7606_ReadBytes();
*ad5=ad7606_ReadBytes();
*ad6=ad7606_ReadBytes();
*ad7=ad7606_ReadBytes();
*ad8=ad7606_ReadBytes();
AD7606_CS_1;
}
由于AD5522寄存器非常复杂,为了更方便地控制和调试,设计了简单的上位机代替普通的串口调试助手。
四、 上位机软件(Python篇)与联动测试
上位机软件可以做的更强大,比如加入智能校准,数据可视化等等,下面是个简单的版本,如图所示:

使用pyqt开发的Python上位机:

核心程序AD5522.py如下:
from enum import Enum
import re
# On power-up, the default code loaded to the offset DAC is 0xA492
# ±5 µA (200 kΩ)、±20 μA (50 kΩ)、±200 μA(5 kΩ)和±2 mA(500 Ω)
# FI = 4.5 × VREF × ((DAC_CODE − 32,768)/2^16)/(RSENSE × MI_放大器增益); MI_放大器增益:5或10, VREF = 5V
# FV = 4.5 × VREF × (DAC_CODE/2^16) − (3.5 × VREF × (OFFSET_DAC_CODE/2^16)) + DUTGND; VREF = 5V, OFFSET_DAC_CODE = 0xA492
# GAIN1 = 0, MEASOUT增益 = 1
# MV : ±VDUT
# MI : GAIN0 = 0, IDUT × RSENSE × 10
# MI : GAIN0 = 1, IDUT × RSENSE × 5
# GAIN1 = 1, MEASOUT增益 = 0.2
# MV : 0.2 × (VDUT - DUTGND + VMIN),VMIN = 3.5 × VREF × (OFFSET_DAC_CODE/2^16)
# MI : GAIN0 = 0, (IDUT × RSENSE × 10 × 0.2) + (0.45 × VREF)
# MI : GAIN0 = 1, (IDUT × RSENSE × 5 × 0.2) + (0.45 × VREF)
class CurrentRange(Enum):
Range5uA = 0 # ±5 μA (200 kΩ)
Range20uA = 1 # ±20 μA (50 kΩ)
Range200uA = 2 # ±200 μA (5 kΩ)
Range2mA = 3 # ±2 mA (500 Ω)
class MiGain(Enum):
Gain5 = 5
Gain10 = 10
class VrefValue(Enum):
V2_5 = 2 # 2.5V
V5 = 5 # 5V
class DUTGND(Enum):
GND_0V = 0
class OffsetDacCode(Enum):
OffsetCode_0 = 0
OffsetCode_32768 = 32768
OffsetCode_42130 = 42130
OffsetCode_60855 = 60855
OffsetCode_65535 = 65535
def get_rsense_for_range(current_range):
if current_range == CurrentRange.Range5uA:
return 200000 # 200 kΩ
elif current_range == CurrentRange.Range20uA:
return 50000 # 50 kΩ
elif current_range == CurrentRange.Range200uA:
return 5000 # 5 kΩ
elif current_range == CurrentRange.Range2mA:
return 500 # 500 Ω
else:
raise ValueError(f"Invalid CurrentRange: {current_range}")
def get_vref_value(vref):
if vref == VrefValue.V2_5:
return 2.5
elif vref == VrefValue.V5:
return 5.0
else:
raise ValueError(f"Invalid VrefValue: {vref}")
def binary_to_hex(binary_str):
# 确保长度是4的倍数
padding = (4 - len(binary_str) % 4) % 4
if padding != 0:
binary_str = '0' * padding + binary_str
hex_str = ""
for i in range(0, len(binary_str), 4):
nibble = binary_str[i:i+4]
hex_digit = hex(int(nibble, 2))[2:].upper()
hex_str += hex_digit
return hex_str
def is_binary_string(input_str):
return all(char in '01' for char in input_str)
def is_hex_string(input_str):
return all(char in '0123456789ABCDEFabcdef' for char in input_str)
def dac_register_address(force_mode):
mapping = {
"±5uA": "001000",
"±20uA": "001001",
"±200uA": "001010",
"±2mA": "001011",
"外部电流": "001100",
"电压": "001101"
}
if force_mode in mapping:
return mapping[force_mode]
else:
raise ValueError(f"无效的force_mode: {force_mode}")
def write_system_control_register(clamp_enable, comparator_output_enable, cpbias_enable, ini10k, measoutx_output_range):
bits = ['0'] * 29 # 29位寄存器
# 设置控制位
if clamp_enable:
bits[21] = '1' # CLAMP_EN[3]
bits[20] = '1' # CLAMP_EN[2]
bits[19] = '1' # CLAMP_EN[1]
bits[18] = '1' # CLAMP_EN[0]
if comparator_output_enable:
bits[17] = '1' # COMP_EN[3]
bits[16] = '1' # COMP_EN[2]
bits[15] = '1' # COMP_EN[1]
bits[14] = '1' # COMP_EN[0]
if cpbias_enable:
bits[13] = '1'
if ini10k:
bits[9] = '1' # INI10K
# MEASOUTx输出范围
meas_mapping = {
"GAIN00": (),
"GAIN01": (('6', '1'),),
"GAIN10": (('7', '1'),),
"GAIN11": (('7', '1'), ('6', '1'))
}
if measoutx_output_range.upper() in meas_mapping:
for pos, val in meas_mapping[measoutx_output_range.upper()]:
bits[int(pos)] = val
else:
raise ValueError(f"无效的MEASOUTx_output_range: {measoutx_output_range}")
bits[10] = '1'
bits[5] = '1'
# 反转并转换为16进制
bits.reverse()
binary_str = ''.join(bits)
return binary_to_hex(binary_str)
def write_pmu_register(ch_en, pmu_channels, force_mode, measure_current_range, measure_mode, sf_ss, cl_enable):
if not pmu_channels:
raise ValueError("必须至少选择一个PMU通道")
bits = ['0'] * 29
# CH_EN
if ch_en:
bits[21] = '1'
# PMU通道选择
for channel in pmu_channels:
ch_upper = channel.upper()
if ch_upper == "PMU0":
bits[24] = '1'
elif ch_upper == "PMU1":
bits[25] = '1'
elif ch_upper == "PMU2":
bits[26] = '1'
elif ch_upper == "PMU3":
bits[27] = '1'
else:
raise ValueError(f"无效的PMU通道: {channel}")
# Force模式
force_mapping = {
"FV": (),
"FI": (('19', '1'),),
"FOHX电压": (('20', '1'),),
"FOHX电流": (('19', '1'), ('20', '1'))
}
fm_upper = force_mode.upper()
if fm_upper in force_mapping:
for pos, val in force_mapping[fm_upper]:
bits[int(pos)] = val
else:
raise ValueError(f"无效的Force模式: {force_mode}")
# 电流范围
current_range_mapping = {
"±5uA": (),
"±20uA": (('15', '1'),),
"±200uA": (('16', '1'),),
"±2mA": (('15', '1'), ('16', '1')),
"±外部电流范围": (('17', '1'),)
}
if measure_current_range in current_range_mapping:
for pos, val in current_range_mapping[measure_current_range]:
bits[int(pos)] = val
else:
raise ValueError(f"无效的电流范围: {measure_current_range}")
# Measure模式
measure_mapping = {
"ISENSE": (),
"VSENSE": (('13', '1'),),
"TEMP": (('14', '1'),),
"HIGHZ": (('13', '1'), ('14', '1'))
}
mm_upper = measure_mode.upper()
if mm_upper in measure_mapping:
for pos, val in measure_mapping[mm_upper]:
bits[int(pos)] = val
else:
raise ValueError(f"无效的Measure模式: {measure_mode}")
# SF/SS
sf_ss_mapping = {
"00": (),
"01": (('10', '1'),),
"10": (('11', '1'),),
"11": (('10', '1'), ('11', '1'))
}
if sf_ss in sf_ss_mapping:
for pos, val in sf_ss_mapping[sf_ss]:
bits[int(pos)] = val
else:
raise ValueError(f"无效的SF/SS选择: {sf_ss}")
if cl_enable:
bits[9] = '1'
# 固定控制位
bits[12] = '1'
bits[8] = '1'
bits.reverse()
binary_str = ''.join(bits)
return binary_to_hex(binary_str)
def write_dac_register(pmu_channels, dac_register, dac_address, dac_value):
if len(dac_address) != 6 or not is_binary_string(dac_address):
raise ValueError("地址必须是6位二进制字符串")
if not pmu_channels:
raise ValueError("必须至少选择一个PMU通道")
if len(dac_value) != 4 or not is_hex_string(dac_value):
raise ValueError("必须提供4位十六进制字符串")
bits = ['0'] * 29
# PMU通道选择
for channel in pmu_channels:
ch_upper = channel.upper()
if ch_upper == "PMU0":
bits[24] = '1'
elif ch_upper == "PMU1":
bits[25] = '1'
elif ch_upper == "PMU2":
bits[26] = '1'
elif ch_upper == "PMU3":
bits[27] = '1'
else:
raise ValueError(f"无效的PMU通道: {channel}")
# DAC寄存器选择
dac_reg_mapping = {
"DACM": (('22', '1'),),
"DACC": (('23', '1'),),
"DACX1": (('22', '1'), ('23', '1'))
}
dr_upper = dac_register.upper()
if dr_upper in dac_reg_mapping:
for pos, val in dac_reg_mapping[dr_upper]:
bits[int(pos)] = val
else:
raise ValueError(f"无效的DAC寄存器: {dac_register}")
# DAC地址 (6位)
for i in range(6):
bits[16 + i] = dac_address[5 - i]
# DAC值 (16位)
dac_int = int(dac_value, 16)
dac_bin = bin(dac_int)[2:].zfill(16)
for i in range(16):
bits[i] = dac_bin[15 - i]
bits.reverse()
binary_str = ''.join(bits)
return binary_to_hex(binary_str)
def read_register(register_name, pmu_channel=None, dac_register=None, dac_address=None):
bits = ['0'] * 29
reg_upper = register_name.upper()
if reg_upper == "SYSTEMCONTROL":
bits[28] = '1'
elif reg_upper == "PMU":
if not pmu_channel:
raise ValueError("必须提供PMU通道")
bits[28] = '1'
ch_upper = pmu_channel.upper()
if ch_upper == "PMU0":
bits[24] = '1'
elif ch_upper == "PMU1":
bits[25] = '1'
elif ch_upper == "PMU2":
bits[26] = '1'
elif ch_upper == "PMU3":
bits[27] = '1'
else:
raise ValueError(f"无效的PMU通道: {pmu_channel}")
elif reg_upper == "DAC":
if not pmu_channel or not dac_register or not dac_address:
raise ValueError("读取DAC寄存器需要所有参数")
if len(dac_address) != 6 or not is_binary_string(dac_address):
raise ValueError("地址必须是6位二进制字符串")
bits[28] = '1'
ch_upper = pmu_channel.upper()
if ch_upper == "PMU0":
bits[24] = '1'
elif ch_upper == "PMU1":
bits[25] = '1'
elif ch_upper == "PMU2":
bits[26] = '1'
elif ch_upper == "PMU3":
bits[27] = '1'
else:
raise ValueError(f"无效的PMU通道: {pmu_channel}")
dr_upper = dac_register.upper()
if dr_upper == "DACM":
bits[22] = '1'
elif dr_upper == "DACC":
bits[23] = '1'
elif dr_upper == "DACX1":
bits[22] = '1'
bits[23] = '1'
else:
raise ValueError(f"无效的DAC寄存器: {dac_register}")
for i in range(6):
bits[16 + i] = dac_address[5 - i]
else:
raise ValueError(f"无效的寄存器名称: {register_name}")
bits.reverse()
binary_str = ''.join(bits)
return binary_to_hex(binary_str)
def voltage_to_dac_value_hex(voltage, vref, offset_dac_code, dut_gnd):
vref_val = get_vref_value(vref)
offset_val = offset_dac_code.value / 65536.0
numerator = voltage - dut_gnd.value + 3.5 * vref_val * offset_val
denominator = 4.5 * vref_val / 65536.0
dac_code = int(round(numerator / denominator))
dac_code = max(0, min(65535, dac_code))
return format(dac_code, '04X')
def dac_value_hex_to_voltage(dac_hex, vref, offset_dac_code, dut_gnd):
if len(dac_hex) != 4 or not is_hex_string(dac_hex):
raise ValueError("必须提供4位十六进制字符串")
dac_code = int(dac_hex, 16)
vref_val = get_vref_value(vref)
offset_val = offset_dac_code.value / 65536.0
voltage = (4.5 * vref_val * (dac_code / 65536.0) -
3.5 * vref_val * offset_val +
dut_gnd.value)
return voltage
def current_to_dac_value_hex(current, vref, current_range, mi_gain):
rsense = get_rsense_for_range(current_range)
vref_val = get_vref_value(vref)
gain_val = mi_gain.value
numerator = current * rsense * gain_val
denominator = 4.5 * vref_val / 65536.0
dac_value = numerator / denominator + 32768
dac_code = int(round(dac_value))
dac_code = max(0, min(65535, dac_code))
return format(dac_code, '04X')
def dac_value_hex_to_current(dac_hex, vref, current_range, mi_gain):
if len(dac_hex) != 4 or not is_hex_string(dac_hex):
raise ValueError("必须提供4位十六进制字符串")
dac_code = int(dac_hex, 16)
rsense = get_rsense_for_range(current_range)
vref_val = get_vref_value(vref)
gain_val = mi_gain.value
current = (4.5 * vref_val * ((dac_code - 32768) / 65536.0) /
(rsense * gain_val))
return current
def ad7606_value_hex_to_voltage(hex_str):
if len(hex_str) != 4 or not is_hex_string(hex_str):
raise ValueError("必须提供4位十六进制字符串")
adc_value = int(hex_str, 16)
if adc_value & 0x8000: # 负数
complement = (~adc_value + 1) & 0xFFFF
return -complement / 32768 * 10
else: # 正数
return adc_value / 32768 * 10
def ad7606_voltage_to_true_voltage(voltage, vref, offset_dac_code, dut_gnd, measoutx_output_range):
vref_val = get_vref_value(vref)
vmin = 3.5 * vref_val * (offset_dac_code.value / 65536.0)
range_upper = measoutx_output_range.upper()
if range_upper in ["GAIN00", "GAIN01"]:
return voltage
elif range_upper in ["GAIN10", "GAIN11"]:
return voltage / 0.2 + dut_gnd.value - vmin
else:
raise ValueError(f"无效的MEASOUTx_output_range: {measoutx_output_range}")
def ad7606_voltage_to_true_current(voltage, vref, current_range, measoutx_output_range):
rsense = get_rsense_for_range(current_range)
vref_val = get_vref_value(vref)
range_upper = measoutx_output_range.upper()
if range_upper == "GAIN00":
return voltage / (rsense * 10)
elif range_upper == "GAIN01":
return voltage / (rsense * 5)
elif range_upper == "GAIN10":
return (voltage - 0.45 * vref_val) / (rsense * 10 * 0.2)
elif range_upper == "GAIN11":
return (voltage - 0.45 * vref_val) / (rsense * 5 * 0.2)
else:
raise ValueError(f"无效的MEASOUTx_output_range: {measoutx_output_range}")
上位机软件使用步骤:1、选择设备,设置波特率等参数;2、设备连接;3、刷新设备;4、设置系统参数;5、选择PMU通道;6、设置PMU参数;7、Force或Measure功能测试

使用单帧命令控制AD5522系统,如下图中发送指定命令即可

| 具体操作 | 单帧指令 |
|---|---|
| 刷新设备:00 | |
| 写系统寄存器 | 01 00 3F E4 A0 |
| 写PMU寄存器并回读 | 02 0F 21 B3 00 11 00 00 00 |
| 写DAC寄存器并回读 | 03 0F CD FF 55 11 CD 00 00 |
| 四个PMU通道加压测压 | 05 BF FF |
| 四个PMU通道加压测流 | 06 9F FF |
| 四个PMU通道加流测流 | 07 8F FF |
| 四个PMU通道加流测压 | 08 9F FF |
| 读取CH1 ADC值 | 09 |
| 读取CH2 ADC值 | 0A |
| 读取CH3 ADC值 | 0B |
| 读取CH4 ADC值 | 0C |
六、 总结与展望
通过这次项目,我们成功驯服了AD5522这头“猛兽”。总结一下关键步骤:
- 硬件是基础: 确保供电和连接正确。
- SPI是桥梁: 写好底层的读写函数,并用逻辑分析仪验证。
- 寄存器是地图: 仔细阅读Datasheet,正确配置每一位。
- 上位机是遥控器: 做一个方便的工具来快速验证和调试。
完整的下位机程序、数据手册和上位机软件手把手一起玩转AD5522:避坑指南与软硬件实战全解析资源包

未来还可以探索AD5522更强大的功能,比如利用其内部的序列发生器(Sequencer)实现自动化的测试流程,或者实现更复杂的四线制测量以提升精度。
希望这篇“手把手一起玩转AD5522”的指南能对大家有所帮助!如果在调试中也遇到了有趣的问题或坑,欢迎在评论区一起交流讨论。
希望本文对大家有帮助,上文若有不妥之处,欢迎指正
分享决定高度,学习拉开差距
更多推荐



所有评论(0)