打印

【分享】+ 高速数据采样系统设计参考:基于ARM+FPGA+AD实现

[复制链接]
11089|60
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
emouse|  楼主 | 2013-12-29 12:23 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 emouse 于 2013-12-29 19:09 编辑

设计思路+电路设计+程序设计+测试与仿真结果
更新中……
2#程序代码
3#个人设计的一些心得体会
其他内容根据回复适当更新。


希望各位支持,也希望指点交流。
如果您觉得此帖内容对你有所帮助,欢迎点击右下角评分。

内容简介
    ADS8364是TI应用非常广泛的一款具备6个通道采样的高速AD,多应用与电机控制、电力系统检测等应用。在很多高速采样系统中,为了能够完成数据的实时采集与处理,可以使用FPGA控制完成数据采集与处理,由于FPGA的并行处理优势,相对于传统的通过MCU或者DSP的控制采集方式,这种方式有着更好的实时性。

    这个帖子就以ADS8364为例,给大家谈谈在应用FPGA控制并行AD的采集与处理方面的一些设计心得。这样的设计相对于其他芯片一样是通用的。这个只是我设计的一些方法,如果有更好的方法,欢迎各位指教。


ADS8364简介

    这个片子用的非常广泛,因此我先来简单介绍下。

    ADS8364是美国TI公司的一款单5V供电的16位高速、低功耗、6通道同步采样模数转换器,片上带有2.5V电压基准信号,可用作ADS8364的参考电压。ADS8364输出总线为16位,无误码采样精度为14bit,可以较好地满足有源电力滤波器测量的精度需要。ADS8364实际上由3个最高转换速率为250KSPS(最高输入时钟为5MHz)的逐次逼近式ADC单元构成,每个ADC单元有两个模拟输入通道(称为一组),因此可以很灵活地同时采1~3组模拟信号。6个输入通道都有带宽为300MHz的采样保持器,由于6个通道可以同时采样保持后被采样,ADS8364很适合用于需要同时采集多种信号的场合。


硬件设计
    ADS8364内部有一个+2.5V的参考电压源,可以直接作为AD的参考电压,也可以使用外部的参考电压源,使用外部参考电压源的电压范围为1.5V-2.6V。在本设计中使用内部的参考电压源,由于内部参考电压源的驱动电流只有10μA,因此设计了两级电压跟随电路,保证内部参考电压源有足够的驱动能力。

    ADS8364的模拟部分电源和数字部分可以分开供电,这样可以很灵活的适应不同系统的需求,在本课题中由于CPLD使用的是3.3V电平信号,而模拟信号的输入范围是0-5V,因此本课题中使用两个不同的电源对ADS8364供电,AVDD、DVDD使用5V的电源,而和总线缓冲器相关的BVDD使用3.3V电源,同时不同电源之间加入去耦和滤波电路保证ADS8364能够稳定工作。

    ADS8364具有六个差分输入通道,但是在本课题中采样信号为单端输出,因此需要将ADS8364接成单端的方式,在本设计中每个差分通道的负输入端接到参考电压上,这样0-5V的输入相对于参考电压实际上就变成了-2.5V-2.5V的参考输入,不仅充分利用了AD的输入范围,也方便软件部分的数据处理。电路图如图所示。



程序设计
ADS8364的数据通信格式        ADS8364采用的是并行总线的通信方式,由于内部有3ADC转换单元,因此在硬件上对应的有3个启动转换信号HOLDA-HOLDC,对应的启动转换信号给出低电平就会启动对应ADC转换单元的转换。在转换完成后ADS8364会自动输出半个时钟周期的EOC信号,CPU可以根据此信号开始数据的读取。ADS8364的读取时序图如所示。


     ADS8364 的时钟信号由外部提供,模数转换时间为20个时钟周期,最高频率为5 MHz,此时ADS8364的转换时间为3.2 μs,相应的数据采集时间为0.8 μs,每个通道的总的转换时间仅为4 μs,在A/D转换完成后产生转换结束信号/EOC(低电平信号)实际测量到的信号如图所示。


基于状态机的ADS8364读写程序
根据ADS8364的信号时序在FPGA中设计读写时序,实际发现在FPGA中的程序要比在DSP、ARM等芯片中略有麻烦,主要有以下两个原因:

(1)  和单片机类的控制器相比FPGA中没有硬件的外部中断信号,虽然可以使用电平触发来实现但是由于EOC信号只有半个时钟周期的宽度,在一ADC时钟为时钟的进程中无法捕捉到这半个周期的信号。通过其他方式可以实现,但是逻辑实现上要稍微麻烦一点。
(2)   FPGA并行执行的特点和进程的限制可以保证程序高速的执行,但是在程序设计时就相对不是那么方便。

基于以上两点,这里采用状态机的方法实现ADS8364部分的程序设计。仔细阅读ADS8364的手册发现,虽然在FPGA中无法方便的获取EOC转换结束信号,但是ADS8364的转换一定会在16.5个时钟周期内完成,因此在程序设计上转换信号发出后固定等待17个ADC时钟周期,然后进行数据的读取。读取流程如图所示。


按照ADS8364的读取时序,在程序中使用了st0-st2223个状态,每个状态对应一个12位的数据,定义如下:
constant st0 :std_logic_vector(11 downto0):="000000100000";

在这12位的状态标志中有高至低依次为转换启动信号位HOLDA-C(11-9),地址位ADC A-C(8-6),读信号位RD(5),状态机状态位(4-0)。
ADS8364的读取进程中,根据状态机状态位对23个状态依次切换,从而完成了一个完成读取时序,仿真波形如所示。


数据处理
由于ADS8364为差分输入AD,其数据输出为补码形式,按照2.5V的参考电压源输出的数据范围为-32768~+32768,如果AIN- 连接到VREF(2.5V),那么当AIN+ 输入为0时输出的数据为0x8000,如果AIN+ 输入为2.5V则输出数据为0x0000,AIN+ 输入为5V时输出数据位0x7F,具体表示如图所示。

在本设计中,采用的是单端的方式连接,即AIN- 连接到VREF(2.5V),因此为了方便数据的读取需要对数据做一个格式转换,转换后0-5V对应0x0000~0xFFFF。VHDL实现程序如下:
adc_data_buf(14downto 0) <= adc_d(14 downto 0);
adc_data_buf(15)<= not adc_d(15);

    这样补码输出的数据格式就转换成了常用的0x0000-0xFFFF格式,便于计算和处理。由于采集到的数据波动比较大,为了是测量更加准确,需要在不影响采样精度和速度的基础之上进行相应的数据处理,由于ADS8364在高速采样的情况下数据量比较大,为了保证采样数据的充分应用首先对数据进行求平均处理。然而由于CPLD没有硬件乘法器,如果直接进行累加求平均会消耗大量的片内资源,甚至无法完成,因此在程序设计中定义一个23位的变量对16位的采样数据进行累加,累加了128次后,舍弃低七位,得到的数据便是128次的平均值,这种方式有效节省了片内资源。






相关帖子

沙发
emouse|  楼主 | 2013-12-29 12:40 | 只看该作者
上程序,程序是基于上面所说的软件设计思路的基础之上的,程序注释比较清楚,这里给出AD相关的部分程序,基于VHDL的,还是比较好理解的。
--ads8364信号
signal adcdata1h_reg,adcdata1l_reg,adcdata2h_reg,adcdata2l_reg        :        std_logic_vector(7 downto 0);
signal adcdata3h_reg,adcdata3l_reg,adcdata4h_reg,adcdata4l_reg        :        std_logic_vector(7 downto 0);
signal adcdata5h_reg,adcdata5l_reg,adcdata6h_reg,adcdata6l_reg        :        std_logic_vector(7 downto 0);
signal adc_data_buf :        std_logic_vector(15 downto 0);

--定义读取过程的各个状态
--13位控制分别为 hold adc_a rd 状态机状态5位     hhhabcr
---------------------------------------------------98365
constant st0        :std_logic_vector(11 downto 0):="000000100000";--启动转换
constant st1        :std_logic_vector(11 downto 0):="111000100001";--进入17个周期等待转换结束,不检测EOC
constant st2        :std_logic_vector(11 downto 0):="111000100010";
constant st3        :std_logic_vector(11 downto 0):="111000100011";
constant st4        :std_logic_vector(11 downto 0):="111000100100";
constant st5        :std_logic_vector(11 downto 0):="111000100101";
constant st6        :std_logic_vector(11 downto 0):="111000100110";
constant st7        :std_logic_vector(11 downto 0):="111000100111";
constant st8        :std_logic_vector(11 downto 0):="111000101000";
constant st9        :std_logic_vector(11 downto 0):="111000101001";
constant st10        :std_logic_vector(11 downto 0):="111000101010";
constant st11        :std_logic_vector(11 downto 0):="111000101011";
constant st12        :std_logic_vector(11 downto 0):="111000101100";
constant st13        :std_logic_vector(11 downto 0):="111000101101";
constant st14        :std_logic_vector(11 downto 0):="111000101110";
constant st15        :std_logic_vector(11 downto 0):="111000101111";
constant st16        :std_logic_vector(11 downto 0):="111000110000";
constant st17        :std_logic_vector(11 downto 0):="111000110001";
constant st18        :std_logic_vector(11 downto 0):="111000110010";
constant st19        :std_logic_vector(11 downto 0):="111000010011";--读ch1数据
constant st20        :std_logic_vector(11 downto 0):="111001110100";
constant st21        :std_logic_vector(11 downto 0):="111001010101";--读ch2数据
constant st22        :std_logic_vector(11 downto 0):="111010110110";
constant st23        :std_logic_vector(11 downto 0):="111010010111";--读ch3数据
constant st24        :std_logic_vector(11 downto 0):="111011111000";
constant st25        :std_logic_vector(11 downto 0):="111011011001";--读ch4数据
constant st26        :std_logic_vector(11 downto 0):="111100111010";
constant st27        :std_logic_vector(11 downto 0):="111100011011";--读ch5数据
constant st28        :std_logic_vector(11 downto 0):="111101111100";
constant st29        :std_logic_vector(11 downto 0):="111101011101";--读ch6数据
constant st30        :std_logic_vector(11 downto 0):="111000111110";

signal adc1_datasum        :std_logic_vector(22 downto 0);--对采样数据进行累加存储
signal adc2_datasum        :std_logic_vector(22 downto 0);
signal adc3_datasum        :std_logic_vector(22 downto 0);
signal adc4_datasum        :std_logic_vector(22 downto 0);
signal adc5_datasum        :std_logic_vector(22 downto 0);
signal adc6_datasum        :std_logic_vector(22 downto 0);

signal adcadd_cnt        :std_logic_vector(7 downto 0);--累加次数统计
signal state                :std_logic_vector(11 downto 0);--用于状态跳转
signal readst                :std_logic_vector(3 downto 0);--adc_a,在另一个进程中根据此信号 选择输出的数据
-------------------------------------ads8364------------------------------------
--ads8364控制,默认byte=0,add=0
adc_cs <= '0'; --片选一直选中
adc_wr <= '1';
--ads状态转移
process(adc_clk,rst,state)
begin
        if(rst='0') then
                state<=st0;
                adc_reset<='0';
                adc_holda<='1';
                adc_holdb<='1';
                adc_holdc<='1';
        elsif(adc_clk'event and adc_clk='1') then
        adc_reset<='1';
        case state is
                when st0=> state<=st1;
                when st1=> state<=st2;
                when st2=> state<=st3;
                when st3=> state<=st4;
                when st4=> state<=st5;
                when st5=> state<=st6;
                when st6=> state<=st7;
                when st7=> state<=st8;
                when st8=> state<=st9;
                when st9=> state<=st10;
                when st10=> state<=st11;
                when st11=> state<=st12;
                when st12=> state<=st13;
                when st13=> state<=st14;
                when st14=> state<=st15;
                when st15=> state<=st16;
                when st16=> state<=st17;
                when st17=> state<=st18;
                when st18=> state<=st19;
                when st19=> state<=st20;
                when st20=> state<=st21;
                when st21=> state<=st22;
                when st22=> state<=st23;
                when st23=> state<=st24;
                when st24=> state<=st25;
                when st25=> state<=st26;
                when st26=> state<=st27;
                when st27=> state<=st28;
                when st28=> state<=st29;
                when st29=> state<=st30;
                when st30=> state<=st0;
                when others=> state<=st0;
        end case;
        end if;
        readst<=state(8 downto 5);
        adc_holdc<=state(11);
        adc_holdb<=state(10);
        adc_holda<=state(9);
        adc_a2<=state(8);
        adc_a1<=state(7);
        adc_a0<=state(6);
        adc_rd<=state(5);
end process;
adc_data_buf(14 downto 0) <= adc_d(14 downto 0);--ADC输出为补码形式,转换为单端输入的原码
adc_data_buf(15) <= not adc_d(15);
--------------------------------------求平均------------------------------------
process(rst,adc_clk,pwm_clk,adc_d,pwm_cnt)
begin
        if(rst='0')then
                adc1_datasum<="00000000000000000000000";
                adc2_datasum<="00000000000000000000000";
                adc3_datasum<="00000000000000000000000";
                adc4_datasum<="00000000000000000000000";
                adc5_datasum<="00000000000000000000000";
                adc6_datasum<="00000000000000000000000";
                adcadd_cnt<="00000000";
        elsif(adc_clk'event and adc_clk='0')then
                if readst="0000" then
                        adc1_datasum <= adc1_datasum + adc_data_buf;
                        adcadd_cnt <= adcadd_cnt + '1';--一次读取状态只需要计数一次
                elsif readst="0010" then
                        adc2_datasum <= adc2_datasum + adc_data_buf;
                elsif readst="0100" then
                        adc3_datasum <= adc3_datasum + adc_data_buf;
                elsif readst="0110" then
                        adc4_datasum <= adc4_datasum + adc_data_buf;
                elsif readst="1000" then
                        adc5_datasum <= adc5_datasum + adc_data_buf;
                elsif readst="1010" then
                        adc6_datasum <= adc6_datasum + adc_data_buf;
                end if;       
                if(adcadd_cnt >= "01111111") then

                        adcdata1h_reg <= adc1_datasum(22 downto 15);
                        adcdata1l_reg <= adc1_datasum(14 downto 7);
                        adcdata2h_reg <= adc2_datasum(22 downto 15);
                        adcdata2l_reg <= adc2_datasum(14 downto 7);
                       
                        adcdata3h_reg <= adc3_datasum(22 downto 15);
                        adcdata3l_reg <= adc3_datasum(14 downto 7);
                        adcdata4h_reg <= adc4_datasum(22 downto 15);
                        adcdata4l_reg <= adc4_datasum(14 downto 7);

                        adcdata5h_reg <= adc5_datasum(22 downto 15);
                        adcdata5l_reg <= adc5_datasum(14 downto 7);
                        adcdata6h_reg <= adc6_datasum(22 downto 15);
                        adcdata6l_reg <= adc6_datasum(14 downto 7);                        
                       
                        adc1_datasum<="00000000000000000000000";
                        adc2_datasum<="00000000000000000000000";
                        adc3_datasum<="00000000000000000000000";
                        adc4_datasum<="00000000000000000000000";
                        adc5_datasum<="00000000000000000000000";
                        adc6_datasum<="00000000000000000000000";
                        adcadd_cnt <= "00000000";
                end if;
        end if;
end process;


使用特权

评论回复
板凳
emouse|  楼主 | 2013-12-29 12:56 | 只看该作者
简单谈下自己的一些心得体会。
1、芯片选型
由于我之前做的很多项目都是样机,所以不涉及量产以及过多的成本问题,注重的都是功能实现,因此这部分也主要从我的功能实现角度来考虑。
芯片选型方面我觉得几个半导体巨头的官方网站是重要参考,常用的运放、AD、DA等器件,我觉得参考TI和ADI两家就已经足够。官方网站上不光查找的是芯片手册,还有一些参考设计和文档,而且很多有设计套件与评估板,这个就可以作为设计的主要参考。
参考了官方网站,参考的主要是方法和原理,由于官方网站上用的方案一些芯片之类的比较新或者性价比不高,因此,在弄懂了原理之后需要做一定的代换,找出芯片性价比高,好买,效果还又同样优秀的方案。

2、驱动程序设计
学生一个,所以项目经验相对一般,很多人在初学阶段对芯片的使用上很大的一个局限就是因为驱动方面,做了一阵子下来,发现实际多数芯片都是大同小异,无论什么MCU,无论什么芯片,接口无非就是哪几种,SPI IIC 并行 单总线等等,这几种只要弄清楚了,用其他芯片在驱动上就是稍微关注下通信格式问题就可以了,这部分不应占用设计流程太多时间。

使用特权

评论回复
地板
dirtwillfly| | 2013-12-29 14:14 | 只看该作者
谢谢分享

使用特权

评论回复
5
emouse|  楼主 | 2013-12-29 14:21 | 只看该作者
dirtwillfly 发表于 2013-12-29 14:14
谢谢分享

谢谢版主:lol

使用特权

评论回复
6
gygp| | 2013-12-29 15:22 | 只看该作者
高端

使用特权

评论回复
7
specialfrin| | 2013-12-29 18:08 | 只看该作者
设计思路+电路设计+程序设计+测试与仿真结果

使用特权

评论回复
8
someontime| | 2013-12-29 18:49 | 只看该作者
感觉像是个毕业设计 呵呵

使用特权

评论回复
9
emouse|  楼主 | 2013-12-29 18:52 | 只看该作者
someontime 发表于 2013-12-29 18:49
感觉像是个毕业设计 呵呵

之前做的一个项目,也是我的本科毕业设计,这里把主要的拿出来说。

使用特权

评论回复
10
emouse|  楼主 | 2013-12-29 18:52 | 只看该作者
gygp 发表于 2013-12-29 15:22
高端

谢谢支持哦。;P

使用特权

评论回复
11
haidixibahe| | 2013-12-29 19:04 | 只看该作者
想问楼主怎样复制代码能生成你的这个样子

使用特权

评论回复
12
emouse|  楼主 | 2013-12-29 19:06 | 只看该作者
haidixibahe 发表于 2013-12-29 19:04
想问楼主怎样复制代码能生成你的这个样子


发帖时点击这个,在弹出的窗口中粘贴代码就可以了。

使用特权

评论回复
13
chao11yue| | 2013-12-29 19:25 | 只看该作者
不错的分享 多谢楼主

使用特权

评论回复
14
engtafanzhuan| | 2013-12-29 20:00 | 只看该作者
好完备的分享,为什么没加精呢?

使用特权

评论回复
15
xichengmadia| | 2013-12-29 20:41 | 只看该作者
希望这篇帖子能够得到推荐一下 支持

使用特权

评论回复
16
emouse|  楼主 | 2013-12-29 22:08 | 只看该作者
engtafanzhuan 发表于 2013-12-29 20:00
好完备的分享,为什么没加精呢?

谢谢支持哈

使用特权

评论回复
17
emouse|  楼主 | 2013-12-29 22:08 | 只看该作者
xichengmadia 发表于 2013-12-29 20:41
希望这篇帖子能够得到推荐一下 支持

谢谢啦,欢迎交流

使用特权

评论回复
18
G21372| | 2013-12-29 22:29 | 只看该作者
惊现大神!求抱大腿!

使用特权

评论回复
19
firstblood| | 2013-12-29 23:51 | 只看该作者
该方案比较高端的 ,,我来学习的啦

使用特权

评论回复
20
emouse|  楼主 | 2013-12-30 18:30 | 只看该作者
firstblood 发表于 2013-12-29 23:51
该方案比较高端的 ,,我来学习的啦

高端算不上,这里分享下思路,欢迎指点哈。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:http://emouse.cnblogs.com我的博客欢迎各位关注,真诚与志同道合的朋友交流。

5

主题

88

帖子

4

粉丝