泳池_SMU
通过STM32 Nucleo板和UART通信控制4通道DAC和ADC的综合系统,专为光学测试和IV曲线测量而设计。
概述
本项目使用以下方式实现了源测量单元(SMU):
- STM32 Nucleo板 (STM32F446RE)作为微控制器
- MCP4728 用于电压/电流源的4通道12位DAC
- ADS1115 用于电压/电流测量的16位ADC
- Python控制脚本 用于仪器自动化
- 是德科技OPM 光功率测量的集成
该系统能够同时控制多个通道、IV曲线表征和与电扫描同步的光功率测量。
硬件组件
STM32 Nucleo(STM32F446RE)
- I2C1 (PB6/SCL、PB7/SDA):连接到MCP4728 DAC
- I2C2 (PB10/SCL、PB11/SDA):连接到ADS1115 ADC
- UART2 (PA2/TX、PA3/RX):与Python脚本的串行通信(115200波特)
MCP4728数模转换器
- 4通道12位数模转换器
- I2C地址:0x60(A0引脚到GND)
- 5V参考电压
- 分辨率:4096步(0-4095)
- 输出范围:0-5V
ADS1115模数转换器
- 16位模数转换器
- I2C地址:0x48(地址引脚到GND)
- 可编程增益放大器(PGA):±6.144V范围
- 4个单端输入通道(AIN0-AIN3 vs GND)
- 每秒128个样本(SPS)数据速率
- 用于通过分流电阻器进行电流测量
软件体系结构
STM32固件(uart_communication/nucleo/)
main.c / main.h
目的:STM32微控制器的主应用程序文件
主要特点:
- 系统初始化(I2C1、I2C2、UART2)
- 用于接收Python命令的UART命令解析器
- 命令处理程序:
- COMM_OK:通信测试 - channel,dac_value:设置单个DAC通道(0-3、0-4095) - set_all,dac_value:将所有DAC通道设置为相同的值 - read_adc,channel:从ADC通道(0-3)读取电压 - test_adc:测试与ADC的I2C通信
- ADC电压读取功能
ADS1115_ReadVoltage() - 带命令缓冲区的非阻塞UART接收
I2C配置:
- I2C1:100kHz,用于MCP4728 DAC
- I2C2:100kHz,用于ADS1115 ADC
mcp4728.c / mcp4728.h
目的:MCP4728 DAC驱动程序实现
关键功能:
MCP4728_Init():初始化DAC,发送唤醒命令MCP4728_WriteChannel():使用顺序写入模式将值写入单通道MCP4728_SetAllChannels():同时将值写入所有4个通道MCP4728_Write_GeneralCall():发送一般呼叫命令(唤醒、重置)
通信模式:
- 快速写入(0x40):立即更新输出
- 顺序写入(0x50):用VREF/GAIN设置更新所有通道
- 常规调用:唤醒、重置、软件更新命令
ADS1115.c / ADS1115.h
目的:ADS1115 ADC驱动程序实现
关键功能:
ADS1115_init():使用配置初始化ADCADS1115_oneShotMeasure():执行单镜头转换ADS1115_getData():从ADC读取转换结果ADS1115_updateConfig():更新ADC配置(信道、PGA、数据速率)ADS1115_setThresholds():设置比较器阈值ADS1115_startContinousMode()/ADS1115_stopContinousMode():连续转换控制
配置:
- 单发模式(节能)
- ±6.144V PGA范围(支持0-5V测量)
- 128 SPS数据速率
- 单端测量的MUX设置(AINx与GND)
密钥修复:ADC读数固定如下:
- 在开始转换之前直接更新句柄的配置
- 使用固定的15ms延迟而不是操作系统位轮询(更可靠)
- I2C通信中的正确错误处理
Python控制脚本(uart_communication/)
uart_com.py
目的:用于STM32通信和仪器控制的Python接口
类:
SerialController(基本类)
- 串口管理 - 通信验证(check_communication()) - 响应等待和解析 - 端口可用性检查和错误处理
DACController(继承自SerialController)
- set_dac(channel, dac_value):设置单通道 - set_all_channels(dac_value):将所有通道设置为相同的值 - sweep_channel(channel, start, end, steps, delay):扫描单通道 - sweep_all_channels(start_values, end_values, steps, delay):同时扫描所有频道 - sweep_channels_independent(channel_configs, delay):在多个频道上独立扫描
ADCController(继承自SerialController)
- read_voltage(channel):从ADC通道读取电压 - read_current(channel):计算分流电阻器的电流 - read_all_voltages():阅读全部4个频道 - read_all_currents():读取所有通道的电流 - test_adc_i2c():测试与ADC的I2C通信 - set_shunt_resistor(channel, value):配置分流电阻值
特性:
- 自动端口检测和连接
- 错误处理和重试逻辑
- 详细调试模式
- 上下文管理器支持(
with声明)
keysight_opm.py
目的:Keysight光功率计(OPM)控制接口
类: KeysightOPM
关键方法:
get_power(chan):以当前单位读取功率(dBm或瓦特)get_power_mw(chan):读取功率,单位为毫瓦(自动转换单元)get_power_all():同时读取所有频道set_unit(chan, unit):设置功率单位(dBm或瓦特)set_wavelength(chan, wavel):设置测量波长set_range(chan, pwr_range):设置功率范围或自动范围measure_power_vs_dac():在扫描DAC值的同时测量光功率
特性:
- 自动设备检测(2或4通道支持)
- 单位转换(dBm↔ mW)
- 用于快速多通道测量的二进制数据读取
- 与DAC控制器集成,实现同步扫描
utils.py
目的:用于数据分析、绘图和文件操作的实用程序类
类:
Electrical
- IV曲线计算 - calculate_resistance(), calculate_power(), calculate_current_from_shunt() - analyze_iv_curve():提取关键参数(最大功率、开路电压、短路电流)
Optical
- 结合DAC、ADC和OPM进行光学测量 - measure_iv_point():单次光功率测量 - sweep_iv_curve():全IV扫描,带光功率读数
DataHandler
- CSV文件操作 - save_to_csv(), load_from_csv() - create_timestamped_filename():生成唯一的文件名
Plotter
- Matplotlib绘图函数 - plot_iv_curve():绘制电压与电流的关系图 - plot_power_curve():用MPP标记绘制电压与功率的关系图 - plot_optical_power():绘制光功率与电功率的关系图 - plot_from_csv():从CSV文件中绘制数据
设计理念:模块化、可重用的类,可以组合在一起进行复杂的测量。
特性
DAC控制
- ✅ 单通道控制(0-3)
- ✅ 多渠道同步更新
- ✅ 不同频道的独立扫描
- ✅ 可配置的扫描范围和步数
- ✅ 实时反馈和错误处理
ADC测量
- ✅ 16位电压测量(0-5V范围)
- ✅ 通过分流电阻器计算电流
- ✅ 多通道同步读取
- ✅ I2C通信诊断
- ✅ 自动错误检测
光学集成
- ✅ 同步DAC扫描与OPM读数
- ✅ 功率与DAC代码测量
- ✅ 多通道OPM支持
- ✅ 自动单位转换
数据分析
- ✅ IV曲线特征
- ✅ 功率曲线分析(MPP检测)
- ✅ CSV数据导出/导入
- ✅ 自动绘图
- ✅ 时间戳文件管理
使用示例
基本DAC控制
from uart_communication.uart_com import DACController
dac = DACController(port="COM3", verbose=True)
dac.set_dac(0, 2048) # Set channel 0 to mid-scale
dac.sweep_channel(0, 0, 4095, 100, delay=0.05)
dac.close()IV曲线测量
from uart_communication.uart_com import DACController, ADCController
from uart_communication.utils import Electrical, Plotter, DataHandler
dac = DACController(port="COM3", verbose=False)
adc = ADCController(port="COM3", shunt_resistors=[200.0, 200.0, 200.0, 200.0])
# Sweep DAC and measure current
voltages = []
currents = []
for dac_val in range(0, 4096, 100):
dac.set_dac(0, dac_val)
time.sleep(0.1)
voltage = adc.read_voltage(0)
current = adc.read_current(0)
voltages.append(voltage)
currents.append(current)
# Analyze and plot
elec = Electrical()
analysis = elec.analyze_iv_curve(voltages, currents)
Plotter.plot_iv_curve(voltages, currents)
DataHandler.save_to_csv({'voltages': voltages, 'currents': currents}, 'iv_curve.csv')光功率与DAC扫描
from uart_communication.uart_com import DACController
from uart_communication.keysight_opm import KeysightOPM
from uart_communication.utils import Plotter
dac = DACController(port="COM3", verbose=False)
opm = KeysightOPM("TCPIP::192.168.1.100::INSTR")
# Measure power vs DAC code
results = opm.measure_power_vs_dac(
dac_controller=dac,
dac_channel=0,
start_dac=0,
end_dac=4095,
steps=100,
opm_channel=1,
delay=0.1,
verbose=True
)
# Plot results
Plotter.plot_optical_power(results['dac_codes'], results['power'])光电联合测量
from uart_communication.uart_com import DACController, ADCController
from uart_communication.keysight_opm import KeysightOPM
from uart_communication.utils import Optical, Electrical, Plotter
dac = DACController(port="COM3", verbose=False)
adc = ADCController(port="COM3", shunt_resistors=[200.0, 200.0, 200.0, 200.0])
opm = KeysightOPM("TCPIP::192.168.1.100::INSTR")
elec = Electrical(shunt_resistors=[200.0, 200.0, 200.0, 200.0])
# Create optical measurement system
optical = Optical(dac, adc, opm, elec)
# Sweep IV curve with optical power
data = optical.sweep_iv_curve(
dac_channel=0, adc_channel=0,
start_value=0, end_value=4095, steps=100,
opm_channel=1
)
# Plot results
Plotter.plot_iv_curve(data['voltages'], data['currents'])
Plotter.plot_optical_power(data['voltages'], data['powers_optical_mw'],
data['powers_electrical'])文件结构
POORMANs_SMU/
├── README.md # This file
└── uart_communication/
├── uart_com.py # Python serial communication and controllers
├── keysight_opm.py # Keysight OPM control interface
├── utils.py # Utility classes (Electrical, Optical, DataHandler, Plotter)
└── nucleo/
├── main.c / main.h # STM32 main application
├── mcp4728.c / mcp4728.h # MCP4728 DAC driver
└── ADS1115.c / ADS1115.h # ADS1115 ADC driver安装说明
STM32侧面
- 在STM32CubeIDE中打开项目
- 为MCP4728配置I2C1(PB6/SCL、PB7/SDA)
- 为ADS1115配置I2C2(PB10/SCL、PB11/SDA)
- 将UART2(PA2/TX、PA3/RX)配置为115200波特
- 构建并闪存到STM32 Nucleo板
Python端
- 安装所需的软件包:
pip install pyserial pyvisa numpy matplotlib- 通过USB连接STM32(创建虚拟COM端口)
- 更新Python脚本中的COM端口(默认值:Windows上的“COM3”,Linux上的“/dev/ttyUSB0”)
- 对于OPM:配置VISA资源字符串(例如,“TCPIP::192.168.1.100::INSTR”)
硬件连接
MCP4728数据采集卡(I2C1)
- VDD → 5V
- GND → GND
- SCL → PB6
- SDA → PB7
- A0 → GND(I2C地址:0x60)
ads1115adc(I2C2)
- VDD → 3.3V或5V
- GND → GND
- SCL → PB11
- SDA → PB10
- ADDR → GND(I2C地址:0x48)
- AIN0-AIN3→ 测量输入
- 上拉电阻:SDA/SCL上的4.7kΩ
电流测量
- 在ADC输入和GND之间连接分流电阻器(例如200Ω)
- 在DAC输出和ADC输入之间连接被测设备
- 电流=电压_交叉/分流电阻
故障排除
ADC读数为零
- 检查I2C2连接(SDA=PB10,SCL=PB11)
- 验证ADS1115的电源和接地
- 检查I2C地址(ADDR引脚到GND=0x48)
- 验证上拉电阻(4.7kΩ)
- 使用
test_adc_i2c()验证I2C通信
DAC未响应
- 检查I2C1连接(SDA=PB7,SCL=PB6)
- 验证MCP4728电源(5V)和接地
- 检查I2C地址(A0引脚到GND=0x60)
- 使用
check_communication()验证UART连接
串行端口问题
- 关闭STM32CubeIDE串行监视器
- 关闭使用COM端口的任何其他程序
- 拔下并重新插入USB电缆
- 检查设备管理器的COM端口号是否正确
许可证
本项目按原样提供,用于研究和教育目的。
作者
- STM32固件和驱动程序
- Python控制脚本和实用程序
- 与Keysight OPM集成
致谢
- Filip Rak从工作中改编的ADS1115驱动器
- MCP4728驱动程序实现
- 意法半导体STM32 HAL库
