中高速模拟信号采集系统
米娜桑好久不见!
废话就不多说啦。来点正经的技术文~
这次来试试做一个中高速精密模拟信号采集系统,是一套模拟向数字转换的完整信号链~
资料汇总
- ADS8860驱动代码(即将作废,预计将合并到下面的repo中): https://github.com/Floyd-Fish/ADS8860-STM32F4
- 本教程所有设计资源: https://github.com/Floyd-Fish/ADS8860_SignalChain
- ADS8860 Datasheet
- OPA2189 Datasheet
- REF03G Datasheet
电路性能参数
- 使用ADS8860完成16-bit 1Msps(1M sample per second)的模拟信号采集
- 输入信号为跨在0V上的(无直流偏置的正负信号),幅值为16V(Vpp=32V)的模拟信号
- 模拟信号频率范围: DC~100KHz
- 通过数字串行接口(SPI)将采集到的数据传回MCU
- 整个模拟电路系统的供电为3.3V单电源
ADC前端设计
参考《新概念模拟电路》-Section-125:单电源标准运放ADC驱动电路
参考电路性能设计需求:
- 采用3.3V为运放和ADC供电(运放为轨到轨运放)
- 16bit采样精度,采样率1Msps,VRef=2.5V(REF03G)
- 采集输入信号跨在0V上,幅度为16V(Vpp = 32V),频率范围 DC~100KHz
那么ADC的单端输入最大范围是0-2.5V,考虑裕量,输入安全范围为0V-2V。
确定ADC驱动电路参数要求如下:
- 电路输出静默电压 Uoz = 1V
- 增益 G = 2/32 = 1/16 = 0.0625
- Vdd = 3.3V
分析过程就简略了~如果需要了解的话请自行翻书哦
R_2 = {{G*V_{dd}} \over {U_{oz}}} * R_1
R_5 = {{G*V_{dd}} \over {V_{dd} - G * V_{dd} - U_{oz}}} * R_1
R_4 = R1 || R2 || R5
设R1 = 100k,根据上述参数代入公式计算R2,R4,R5
抗混叠滤波器设计
什么&为什么?
当输入被测信号频率为Fi,按照奈奎斯特采样定律,要想采集完整的信号,采样率Fs必须大于2倍被测信号频率Fi。当采样率小于2Fi时,一定会出现混叠现象
,即采集的波形中出现很低的混叠频率。下图演示了混叠频率出现的原因——
输入信号为黑色的高频信号,当Fs小于2Fi时,我们获得的采样点形成了红色的波形,其频率不是信号频率,而是混叠频率,很低。混叠现象欺骗了我们的双眼,因此我们不希望出现这种现象。一旦采样数据中出现混叠频率,即使后期增加软件滤波,也是难以滤除的。
唯一的方法就是让大于Fs/2的频率的信号不要出现在ADC的输入端,或者这种频率分量的信号在ADC输入端只有很小的幅度。因此增加一个抗混叠滤波器是非常有必要的。
最简单、最常见的方法就是在ADC输入端前增加一级截止频率Fh的无源低通滤波电路,以实现抗混叠滤波。
抗混叠滤波器的截止频率选择有如下要求:
f_{signal} << f_H << {{f_s} \over {2}}
比如信号频率100KHz,采样频率1MHz,那么fH就在100KHz和500KHz之间取值。但是不能太过于靠近这2个极限值,太过接近100k,会损伤有用信号。太过接近500k,滤波效果会打折扣。
杨老师的比喻是:
这样看起来,fH像个夹板丈夫,左边是媳妇,右边是母亲......
所以还是需要斟酌一下.jpg
设计
ADS8860最高采样率1MHz,输入信号频率不超过100KHz,根据前面的公式可得:
1kHz < f_H < {f_s \over 2} = 500kHz
在没有其他已知条件下,建议将Fh选为两者的乘法平均数:
f_H = {1 \over 2*pi*R_{ISO}*C_L} = {\sqrt{100k*500k}} = 223.607kHz
即
R_{ISO}*C_L = 0.7118us
同时兼顾采样误差对电阻的要求,查询 ADS8860的Datasheet
可知转换时间最少为500ns,也就是
T_{SAM} = 0.5us
再看ADS8860内部等效采样电路
而ADS8860内部采样电容约为4pF,考虑到最为苛刻的要求:
R_s*(4pF+C_L) <= {T_{SAM} \over {0.69314*(16+1)}} = 0.042433us
设CL = 100pF,解得:
R_s <= 408Ω
即前级驱动电路的输出电阻加内部开关电阻不得超过408欧姆。OPA2189的开环输出电阻在f=100kHz时约为200Ω(见下图)。所以可以选择电阻为100欧姆。
至此,抗混叠滤波器设计完毕。
仿真一下
使用LTSpice对ADC驱动电路进行仿真,选择了一个能够单电源供电且轨到轨输出的运算放大器作为OPA2189的替代:
仿真结果表明,在输入信号频率为100kHz,幅度为32V时,输出能够满足设计要求。
放大看一下,顶部和底部存在少量非线性失真,具体效果如何待我实际测试的时候再康康。
总之,输入幅度范围是满足了设计要求且十分安全的。
电路图与BOM
最后,用开源人喜闻乐见的KiCad设计了schematic。
并且阻容取值如下:
元件 | 计算值 | 实际取值 |
---|---|---|
R1 | 100kΩ | 100kΩ |
R2 | 20.625kΩ | 20kΩ+620Ω |
R4 | 6.2kΩ | 6.2kΩ |
R5 | 9850Ω | 10kΩ |
Riso | 100Ω | 100Ω |
CL | 100pF | 100pF |
IC清单
本次实验使用的器件如下:
Model | Quantity | Description | Usage | 厂牌 |
---|---|---|---|---|
ADS8860 | 1 | 16-bit 1MHz采样率 高精度单端ADC | 用途?当然是作主ADC呀 | Texas Instruments |
OPA2189 | 1 | 极低偏置 高精度运算放大器,同时具有高GBW | 一个单元作为基准电压BUFFER,一个单元用作ADC驱动电路 | Texas Instruments |
REF03G | 1 | 2.5V 精密基准电压源 | 用作ADC的基准电压 | Analog Devices |
TLV70033 | 1 | 3.3V LDO | 整个模拟电路系统的电源 | Texas Instruments |
PCB Layout
同样的,用KiCad进行PCB Layout,最后交由某5块钱PCB代工厂(懂得都懂)进行加工。
测试——
测试单片机平台 STM32F303CCT6
代码我之前写过一份最简单的3-wire模式操作,放在文章开头的资料链接里了~
啊,板子还没到,暂时先鸽着.jpg
到了到了,但是基准电压IC-REF03G我还没买(好贵的说),于是先用LDO的3.3V输出当reference用用~
正面
反面
连上杜邦线的样子:
ADC驱动电路测试
使用的测试仪器有:
- 500MHz带宽,4Gsa采样率示波器
- 200MHz信号源
首先,用信号源产生一个 16Vpp,无直流偏置(±8V),频率100kHz的正弦信号 ,输入模拟前端,在ADC输入侧,即模拟前端输出的测试点,用示波器测试如下:
**100kHz正弦信号**
依据我们ADC驱动电路的设计传输函数:
y = G * x + b
G = 0.0625, b = 1
y为输出,x为输入
计算得出的结果符合实际测试结果~ 真不戳
再来测试一些别的信号:
**1kHz,±8V正弦信号**
**10Hz,±8V正弦信号**
**100kHz,±8V三角波信号**
**100kHz,±1V三角波信号**
**100kHz,奇怪的信号增加了~**
单片机收集数据
我用了STM32F303CCT6来控制ADS8860采集信号并接受其发送的转换数据,配置ADS8860工作模式为
**3-wire CS mode without BUSY Indicator**
我使用TIM6定时产生的事件来控制ADS8860的采样率,在定时器中断中完成采样和数据传输。
最核心的代码是这一小段,非常简单。
uint16_t ads8860_readADCRawValue(void)
{
uint8_t data[2];
// Set CONVST to high to start a conversion
GPIOB -> BSRR = ADS8860_CVST_Pin;
// At least 700ns delay
for(uint16_t i = 0; i < 42; i++)
__nop();
GPIOB -> BRR = ADS8860_CVST_Pin;
HAL_SPI_Receive(&ADS8860_SPI_Port, data, 2, 0xFF);
// Return converted data
return ((data[0] << 8) | data[1]);
}
定时器回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef * htim)
{
if (htim -> Instance == TIM6)
{
if(adc_conv_flag == 1)
{
if (adc_printf_flag == 1)
{
adc_conv_flag = 0;
adc_printf_flag = 0;
}
else
__nop();
}
else
{
adcRawData[adc_counter] = ads8860_readADCRawValue();
adc_counter++;
if (adc_counter == ADC_SAMPLE_SIZE)
{
adc_counter = 0;
adc_conv_flag = 1;
}
}
}
}
驱动代码和其他资料一起打包放在开头的github仓库里了~
采集一个1kHz 峰峰值16V,无直流偏置的正弦信号,并将波形数据通过串口打印到上位机绘图,得到如下结果:
真不戳。
在调试的时候遇到了一些问题,比如采样率和定时器计算值不符,后来发现可能是F3的主频太低,spi速率不太够...
现在测试出来最高采样率差不多只能跑到20kHz (啊喂,这也差太多了吧!) 如果以后有条件我会尝试使用更高速的单片机进行测试的~
这期就是这样,感谢你耐心地看完~