切换到宽版
  • 23748阅读
  • 11回复

[AVR]普通arduino打造1.5mHz采样率 “高速”业余示波器Girino(转)(上) [复制链接]

上一主题 下一主题
离线abigbell
 

发帖
11330
M币
1683
专家
457
粉丝
532
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2014-06-14
         看到一个人Caffeinomane,写了一篇关于自制arduino的示波器的文章,觉得有用,转过来。
所用材料就是一块普通的arduino板子,还有一块用于调整输入信号的前级放大电路。不像网上大多数
基于arduino自制的示波器那样,性能低下。这个Girino号称可以将采样频率提高到1.5mhz!网上卖的
标称4M的实际能力也就一两兆的样子,这个能到这么高的速度,值得一提!本人业余、业余水平,
肯定有不少转错的地方,望高手出面,指点一二!

Girino - Fast Arduino Oscilloscope  by Caffeinomane
Girino 高速Arduino示波器 ,原文作者Caffeinomane
(本人能力有限,附上原文,以供校对)

I am a Physicist and the nicest part of working in this field is that I get to build my own instruments. With this way
of thinking, I decided to build a homebrew Arduino Oscilloscope. This instructable was written with the purpose of
teaching a bit about microcontrollers and data acquisition. This is an extreme project because I wanted to
squeeze out from Arduino as much velocity as I could, I have not seen any other Arduino Oscilloscope as fast as this one.
作者Caffeinomane是一名物理学家,喜欢自制仪器。作者决定自制一个速度尽可能快的Arduino示波器,
将arduino的性能压榨至尽,也借此向大家介绍一些关于单片机、数据采集的知识。

Some time ago I was working on an Arduino project and I needed to see if the output signal was into compliance with the
specifics. Thus I spent some time on the internet looking for Arduino Oscilloscopes already implemented, but I did not like
what I found. The projects that I found were mostly composed of a Graphical User Interface for the computer written in
Processing and a very simple arduino sketch. The sketches were something like:
作者之前曾经从事过arduino开发工作,需要查看输出信号。于是笔者开始在网上有没有现成的arduino示波器,
当发现已有的产品都不太满意,大多是基于Processing平台(python/java平台软件)的简单产品,性能不能
令人满意,具体的代码,类似于下面所示的:

    void setup() {
        Serial.begin(9600);
    }

    void loop() {
        int val = analogRead(ANALOG_IN);
        Serial.println(val);
    }

This approach is not wrong and I do not want to insult anyone, but this is too slow for me. The serial port is
slow and sending every result of an analogRead() through it is a bottleneck.
这种方法并不是不对,笔者也不像侮辱、贬低任何人(有点臭本事,就开始张狂啊),但这样的方法
对于我来说,实在是太慢!串口太慢,需要将发送analogRead(),这正是瓶颈所在。

I have been studying Waveform Digitizers for some time and I know reasonably well how do they work, so I
got inspiration from them. These were the starting points of the oscilloscope that I wanted to create:
笔者曾经研究过波形数字软件一段时间,知道一个示波器应该如何工作,受此启发,我要编写的这个示波器
应该包括如下特征:

    the incoming signal should be decoupled from the arduino to preserve it;
输入信号应该可以被单独保存下来
    with an offset of the signal it is possible to see negative signals;
能观察负电压信号波形
    the data should be buffered;
缓存机制
    a hardware trigger is required to catch the signals;
硬件触发,以捕获信号
    a circular buffer can give the signal shape prior to the trigger (more to follow on this point);
回环(预先)缓存,以便信号到达硬件触发阀值观察信号波形(此功能有待完善)
    using lower lever functions that the standard ones makes the program run faster.
硬件底层控制能力,以便更快地运行软件、程序、代码

The sketch for the Arduino is attached to this step, along with the schematic of the circuit that I made.
附上软体

The name that I came up with, Girino, is a frivolous pun in Italian. Giro means rotation and adding the
suffix -ino you get a small rotation, but Girino also means tadpole. This way I got a name and a mascot.
关于Girino这个名字,蝌蚪的意思。

Step 1: Disclaimer
第一步,声明

THE AUTHOR OF THIS INSTRUCTABLE MAKES NO GUARANTEE OF VALIDITY AND NO WARRANTY
WHATSOEVER.

Electronics can be dangerous if you do not know what you are doing and the author cannot guarantee
the validity of the information found here. This is not a professional advice and anything written in this
instructable can be inaccurate, misleading, dangerous or wrong. Do not rely upon any information found
here without independent verification.

It is up to you to verify any information and to double check that you are not exposing yourself, or anyone,
to any harm or exposing anything to any damage; I take no responsibility. You have to follow by yourself
the proper safety precautions, if you want to reproduce this project.

Use this guide at your own risk!
仿制后果自负


Step 2: What you need
第二步 物料



What we really need for this project is an Arduino board and the datasheet of the ATMega328P.
The datasheet is what tells us how the microcontroller works and it is very important to keep it if we want
a lower lever of control.
Arduino板一个,ATM328P参数页 The datasheet can be found here: http://www.atmel.com/Images/doc8271.pdf


The hardware that I added to the Arduino is partly necessary, its purpose is just to form the signal for
the ADC and to provide a voltage level for the trigger. If you want, you could send the signal directly to
the Arduino and use some voltage

reference defined by a voltage divider, or even the 3.3 V given by the Arduino itself.
本文所使用的硬件并非必须,只是向单片机的数模转换单元ADC输出一个触发信号。如果电压低于3.3V,
在Arduino电压允许的范围内,可以直接接驳信号输入。

Step 3: Debug output
第三步 调试代码

I usually put a lot of debug output in my programs because I want to keep track of anything that happens;
the problem with Arduino is that we do not have a stdout to write to. I decided to use the Serial port as a stdout.
笔者通常会在代码中启用调试,但由于arduino没有标注输出,所以用一个串口当作标准输出。

Be aware, though, that this approach does not work all the times! Because writing to the Serial port requires
some time for the execution and it can dramatically change things during some time sensible routines.
这种方式有时候不能工作完好!因为向串口执行输出会耗费些时间,这段时间内程序运行会发生很大变动。

I usually define debugging outputs inside a preprocessor macro, so when the debug is disabled they simply
disappear from the program and do not slow down the execution:
笔者通常会在程序前部设置调试代码,所以停用调试也非常加单,并不会影响程序运行速度。类似代码如下:

    dprint(x); - Writes to the serial port something like: # x: 123
    dshow("Some string"); - Writes the string

This is the definition:

    #if DEBUG == 1
    #define dprint(expression) Serial.print("# "); Serial.print( #expression ); Serial.print( ": " ); Serial.println( expression )
    #define dshow(expression) Serial.println( expression )
    #else
    #define dprint(expression)
    #define dshow(expression)
    #endif



Step 4: Setting register bits
第四部

With the purpose of being fast, it is necessary to manipulate the microcontroller features with lower lever
functions than the standard ones provided by the Arduino IDE. The internal functions are managed through
some registers, that are collections of eight bits where each one governs something particular. Each register
contains eight bits because the ATMega328P has an 8-bit architecture.
为了能将程序运行速度最大化,有必要利用单片机的底层功能,而不是arduinoIDE提供的接口。内部底层功能
由单片机的寄存器控制,每个寄存器能实现一种特定的功能,每个寄存器包含8bit数据,因为单片机就是8位架构。

The registers have some names that are specified in the datasheet depending on their meanings, like ADCSRA
for the ADC Setting Register A. Also each meaningful bit of the registers has a name, like ADEN for the ADC
Enable Bit in the ADCSRA register.
寄存器通常会根据其功能命名,例如ADCSRA,就是数模转换单元控制的意思。并且,每个有意义的寄存器单位
都会有个名字,ADEN就是开启ADC功能的位置。

To set their bits we could use the usual C syntax for binary algebra, but I found on the internet a couple of
macros that are very nice and clean:
可以利用C语言控制这些底层功能,但在网上发现几句不错、简洁的语句:

    // Defines for setting and clearing register bits
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

Using them is very simple, if we want to set to 1 the Enable Bit of the ADC we can just write:
使用简单,开启ADC功能,只要在相应寄存器地址标注为1即可,用法如下:
    sbi(ADCSRA,ADEN);

While if we want to set it to 0 (id est clear it) we can just write:
关闭:

    cbi(ADCSRA,ADEN);

Step 5: What are the Interrupts
第五步 中断




As we will see in the next steps, the use of interrupts is required in this project. Interrupts are signals
that tell the microcontroller to stop the execution of the main loop and pass it to some special functions.
The images give an idea of the program flow.
中断就是告诉单片机停止执行主循环代码,开始执行特定功能代码。如图:

The functions that are executed are called Interrupt Service Routines (ISR) and are more or less simple
functions, but that do not take arguments.
执行的这个功能叫做中断服务程序,十分简单,不带参数运行

Let us see an example, something like counting some pulses. The ATMega328P has an Analog Comparator
that has an interrupt associated that is activated when a signal surpasses a reference voltage. First of all you
must define the function that will be exectuted:
例子,脉冲信号的例子。A328P的模拟比较器通常会在一个信号触发后,执行一个中断。但首先你要定义这个功能

:

    ISR(ANALOG_COMP_vect)
    {
        counter++;
    }

This is really simple, the instruction ISR() is a macro that tells the compiler that the following function is an Interrupt
Service Routine. While ANALOG_COMP_vect is called Interrupt Vector and it tells the compiler which interrupt is
associated to that routine. In this case it is the Analog Comparator Interrupt. So everytime that the comparator sees a
signal bigger than a reference it tells the microcontroller to execute that code, id est in this case to increment that
variable.
语句相当简单,ISR()指令会告诉编译器随后的指令是一个中断,ANALOG_COMP_vect被叫做中断向量,它告诉
编译器这个中断属于服务,具体地就是这里所说的模拟比较器中断。所以每当比较器发现一个超出阀值的信号,

他就会告诉控制器执行这段代码,这个例子中,就是增加变量数值。

The next step is to enable the interrupt associated. To enable it we must set the ACIE (Analog Comparator Interrupt
Enable) bit of the ACSR (Analog Comparator Setting Register) register:
下一步就是要开启相应地中断功能,通过设定ACSR (Analog Comparator Setting Register)寄存器中的ACIE (Analog

Comparator Interrupt Enable)值来实现,具体方法

    sbi(ACSR,ACIE);

In the following site we can see the list of all Interrupt Vectors:
从这里可以看到全部的中断向量
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html


Step 6: Continuously acquire with a circular buffer
第六步 持续读取回环缓存

The concept of using a Circular Buffer is pretty straight forward:
回环缓存的使用非常简单
Acquire continuously till a signal is found, then send the digitized signal to the computer.
不断地读取直到发现信号,然后将数字化的信号发送到计算单元。
This approach allows to have the incoming signal shape also before the trigger event.
这样的方法可以观测到低于阀值的信号。
I prepared some diagrams to make myself clear. The following points are referring to the images.
图解如下


    On the first image we can see what I mean with continuous acquisition. We define a buffer that will store
the data, in my case an array with 1280 slots, then we start to continuously read the ADC output register
(ADCH) ad filling the buffer with the data. When we get to the end of the buffer we restart from the beggining
without clearing it. If we immagine the array arranged in a circular way it is easy to see what I mean.
第一张图可以看到回环缓存读取。这里定义了1280个缓存地址,它就会不断读取ADC的输入信号,向缓存中
不断写入数据,周而复始,一直下去。叫它“回环”或者是“环路”只是因为这样会更好理解,更形象。

    When the signal surpasses the threshold, the Analog Comparator Interrupt is activated. Then we start
a waiting phase in which we continue to acquire the signal but keep a count of the ADC cycles that passed
from the Analog Comparator Interrupt.
当外部输入信号超过阀值,激活模拟比较器中断。开始以一个等待阶段,这时继续取输入信号,但也开始
记录比较器循环次数。

    When we waited for N cycles (with N < 1280), we freeze the situation and stop the ADC cycles. So we
end up with a buffer filled with the digitization of the signal temporal shape. The great part of this, is that
we have also the shape prior to the trigger event, because we were already acquiring before that.
当记录到N(N < 1280)个循环后,冻结这个缓存,停止ADC循环。这时我们就有了一个存满信号的缓存。
这样处理,还有一个好处,就是可以看到触发阀值前的信号,因为之前已经读取这部分信号。

    Now we can send the whole buffer to the serial port in a block of binary data, instead of sending the single
ADC reads. This reduced the overhead required to send the data and the bottleneck of the sketches that I
found on the internet.
现在可以将整缓存的数据以二进制数据块的形式输出到串口,而不是每读到一个信号就像串口写入一次。
这就解决了网上流行代码的瓶颈问题。

Step 7: Oscilloscope Triggering
示波器的触发

An oscilloscope shows on its display a signal, on that we all agree, but how can it show it steadily and
do not show it jumping around the screen? It has an internal trigger that is able to show the signal always
on the same position of the screen (or at least most of the times), creating the illusion of a stable plot.
触发就是将同一数值保持在相对固定显示位置的一种机制,它保证信号展示的稳定、可读性。


The trigger is associated with a threshold that activates a sweep when the signal passes it. A sweep is
the phase in which the oscilloscope records and displays the signal. After a sweep another phase occurs:
the holdoff, in which the oscilloscope rejects any incoming signal. The holdoff period can be composed of
a part of dead time, in which the oscilloscope is unable to accept any signal, and a part that can be user
selectable. The dead time can be caused by

various reasons like having to draw on the screen or having to store the data somewhere.
每当信号超过阀值,触发示波器将这个信号显示出来,但执行显示指令的时候,可能会发生功能暂停,
这段时间内示波器将不会接受任何信号输入,因为整忙着其他事呢。图例如下:

Looking at the image we get a sense of what happens.
上述,如图
1.    Signal 1 surpasses the threshold and activates the sweep;
信号1 超过阀值,示波器将这个信号输出到屏幕
2.    signal 2 is inside the sweep time and gets caught with the first;
信号2 同信号1一同被捕获,然后是暂停时期
3.    after the holdoff, signal 3 activates the sweep again;
暂停过后,信号3继续被捕获、输出,由1个暂停发生
4.    instead signal 4 is rejected because it falls inside the holdoff region.
信号4位于这个暂停期间,此时示波器不能对其作出反应

The raison d'être of the holdoff phase is to prevent some undesired signals to get in the sweep region.
It is a little bit long to explain this point and it eludes the purpose of this instructable.
暂停的存在,主要是避免不想要的信号输入干涉到信号的输出

The moral of this story is that we need:
1.    a threshold level to wich we can compare the incoming signal;
阀值让我们能比较输入信号
2.    a signal that tells the microcontroller to start the waiting phase (see preceding step).
信号高速处理器开始等待

We have several possible solutions for point 1. :
对于上边第一点,可能的解决办法

    using a trimmer we can manually set a voltage level;
使用修剪器手动设定电压值
    using the PWM of the Arduino we can set the level by software;
使用Arduino的脉宽调制功能,利用软件设定电压值
    using the 3.3 V provided by the Arduino itself;
直接使用arduino自己的3.3V电压
    using the internal bangap reference we can use a fixed level.
使用内部bangap(ban gap/bang ap)

For point 2. we have the right solution: we can use the interrupt of the internal Analog Comparator of
the microcontroller.
对于第二点,使用内部模拟比较器中断


Step 8: How the ADC works
第八步 ADC 数模转换

The Arduino microcontroller features a single 10-bit successive approximation ADC. Before the ADC
there is an analog multiplexer that lets us send, to the ADC, the signals from different pins and sources
(but only one at a time).
Arduino本身有10位逐次近似计算单元。在这之前,有一个模拟信号的多路复用器,以便我们从各针脚读取
信号,一次只能读取一个信号。






Successive Approximation ADC means that the ADC takes 13 clock cycles to complete the conversion
(and 25 clock cycles for the first conversion). There is a clock signal dedicated to the ADC that is "computed"
from the main clock of the Arduino; this is because the ADC is a little slow and can not keep up with the pace
of the other parts of the microcontroller. It requires an input clock frequency between 50 kHz and 200 kHz to
get maximum resolution. If a lower resolution than 10 bits is needed, the input clock frequency to the ADC can
be higher than 200 kHz to get a higher sample rate.
逐次近似计算数模转换需要花费13个指令周期完成转换(首个要25个周期),并且数模转换运行在自己较慢的
速度,通常在50Khz至200Khz之间。如果降低精度/分辨率,可以得到更高频率/运行速度。

But how much higher rates can we use? There are a couple of good guides about the ADC at the Open Music
Labs that I

suggest to read:
最快能达到多快,参考以下链接

    http://www.openmusiclabs.com/learning/digital/atmega-adc/
    http://www.openmusiclabs.com/learning/digital/atmega-adc/in-depth/(运行速度4Mhz,采样率114khz)

Since my purpose is to get a fast oscilloscope I decided to limit the precision to 8-bits. This has several bonuses:
因为这帖是要制作一个尽可能快(采样率)的示波器,所以这里8位精度,有以下这些好处:

    the data buffer can store more data; 数据缓存可以存储更多数据(?)
    you do not waste 6-bits of RAM per datum;每个数据节省6位内存
    the ADC can acquire faster. ADC运行速度更快

The prescaler lets us divide the frequency, by some factors, by setting the ADPS0-1-2 bits of the ADCSRA
register. Seeing the plot of the precision from the Open Music Labs Article, we can see that for 8-bits precision
the frequency could go up to 1.5 MHz, good! But since the ability of changing the prescaler factor lets us change
the acquisition rate, we can use it also to change the timescale of the oscilloscope.
分频器,可以通过ADCSRA寄存器中的ADPS0-1-2 bits项,进行分频设定。8bit时运行速度可以达到1.5mhz。
分频器参数可以调节信号读取速度,也可以用于改变示波器的单位间隔。

There is a good feature about the output registers: we can decide the adjusting of conversion bits, by setting
the ADLAR bit in the ADMUX register. If it is 0 they are right adjusted and viceversa (see the image). Since I
wanted 8-bits precision I set it to 1 so I could read just the ADCH register and ignore the ADCL.
通过ADMUX中的ADLAR项可以控制转换过程,如果该值为0,则转换正常进行,反之亦然(如图)。
由于只需要8位精度。设定该值为1,只读取ADCH,忽略ADCL。

I decided to have just one input channel to avoid having to switch channel back and forth at every conversion.
采用单通道,省了来回切换的麻烦,也优化了速度。

One last thing about the ADC, it has different running modes each one with a different trigger source:
关于ADC的运行模式

    Free Running mode
自由模式
    Analog Comparator
模拟量比较模式
    External Interrupt Request 0
外部中断问询 0
    Timer/Counter0 Compare Match A
计时器/计数器0 比较模式 A
    Timer/Counter0 Overflow
计时器/计数器 0 溢出
    Timer/Counter1 Compare Match B
计时器/计数器0 比较模式 B
    Timer/Counter1 Overflow
计时器/计数器 1 溢出
    Timer/Counter1 Capture Event
计时器/计数器 捕获事件

I was interested in the free running mode that is a mode in which the ADC continuously converts the input
and throws an Interrupt at the end of each conversion (associated vector: ADC_vect).
这里使用的是第一种 自由模式,这种模式中ADC持续转换输入信号,每次转换跟着一个中断

Step 9: Digital input buffers
第九步 数字输入缓存

The analog input pins of the Arduino can also be used as digital I/O pins, therefore they have an input
buffer for digital functions. If we want to use them as analog pins you should disable this feature.
模拟量输入针脚会被当做信号出入脚,需要关闭针脚的缓存功能


Sending an analog signal to a digital pin induces it to toggle between HIGH and LOW states, especially
if the signal is near the boundary between the two states; this toggling induces some noise to the near
circuits like the ADC itself (and induces a higher energy consumption).
向数字针脚输入模拟信号,活触发它的高低电平状态,特别是在信号强度接近触发值时,这样会对邻近产生
电路产生噪音,还消耗电力。

To disable the digital buffer we should set the ADCnD bits of the DIDR0 register:
处理方法,关闭缓存,使用以下语句

    sbi(DIDR0,ADC5D);
    sbi(DIDR0,ADC4D);
    sbi(DIDR0,ADC3D);
    sbi(DIDR0,ADC2D);
    sbi(DIDR0,ADC1D);
    sbi(DIDR0,ADC0D);


Step 10: Setting up the ADC
第十步 设置ADC

In the sketch, I wrote a initialization function that sets-up all the parameters of the ADC functioning.
As I tend to write clean and commented code, I will just past the function here. We can refer to the
preceding step and to the comments for the meaning of the registers.
具体ADC控制代码,启动代码,设置各种参数


    void initADC(void)
    {
            //---------------------------------------------------------------------
            // ADMUX settings
            //---------------------------------------------------------------------
            // These bits select the voltage reference for the ADC. If these bits
            // are changed during a conversion, the change will not go in effect
            // until this conversion is complete (ADIF in ADCSRA is set). The
            // internal voltage reference options may not be used if an external
            // reference voltage is being applied to the AREF pin.
            // REFS1 REFS0 Voltage reference
            // 0 0 AREF, Internal Vref turned off
            // 0 1 AVCC with external capacitor at AREF pin
            // 1 0 Reserved
            // 1 1 Internal 1.1V Voltage Reference with external
            //   capacitor at AREF pin
            cbi(ADMUX,REFS1);
            sbi(ADMUX,REFS0);
            // The ADLAR bit affects the presentation of the ADC conversion result
            // in the ADC Data Register. Write one to ADLAR to left adjust the
            // result. Otherwise, the result is right adjusted. Changing the ADLAR
            // bit will affect the ADC Data Register immediately, regardless of any ongoing conversions.
            sbi(ADMUX,ADLAR);
            // The value of these bits selects which analog inputs are connected to
            // the ADC. If these bits are changed during a conversion, the change
            // will not go in effect until this conversion is complete (ADIF in ADCSRA is set).
            ADMUX |= ( ADCPIN & 0x07 );

            //---------------------------------------------------------------------
            // ADCSRA settings
            //---------------------------------------------------------------------
            // Writing this bit to one enables the ADC. By writing it to zero, the
            // ADC is turned off. Turning the ADC off while a conversion is in
            // progress, will terminate this conversion.
            cbi(ADCSRA,ADEN);
            // In Single Conversion mode, write this bit to one to start each
            // conversion. In Free Running mode, write this bit to one to start the
            // first conversion. The first conversion after ADSC has been written
            // after the ADC has been enabled, or if ADSC is written at the same
            // time as the ADC is enabled, will take 25 ADC clock cycles instead of
            // the normal 13. This first conversion performs initialization of the
            // ADC. ADSC will read as one as long as a conversion is in progress.
            // When the conversion is complete, it returns to zero. Writing zero to
            // this bit has no effect.
            cbi(ADCSRA,ADSC);
            // When this bit is written to one, Auto Triggering of the ADC is
            // enabled. The ADC will start a conversion on a positive edge of the
            // selected trigger signal. The trigger source is selected by setting
            // the ADC Trigger Select bits, ADTS in ADCSRB.
            sbi(ADCSRA,ADATE);
            // When this bit is written to one and the I-bit in SREG is set, the
            // ADC Conversion Complete Interrupt is activated.
            sbi(ADCSRA,ADIE);
            // These bits determine the division factor between the system clock
            // frequency and the input clock to the ADC.
            // ADPS2 ADPS1 ADPS0 Division Factor
            // 0 0 0 2
            // 0 0 1 2
            // 0 1 0 4
            // 0 1 1 8
            // 1 0 0 16
            // 1 0 1 32
            // 1 1 0 64
            // 1 1 1 128
            sbi(ADCSRA,ADPS2);
            sbi(ADCSRA,ADPS1);
            sbi(ADCSRA,ADPS0);

            //---------------------------------------------------------------------
            // ADCSRB settings
            //---------------------------------------------------------------------
            // When this bit is written logic one and the ADC is switched off
            // (ADEN in ADCSRA is zero), the ADC multiplexer selects the negative
            // input to the Analog Comparator. When this bit is written logic zero,
            // AIN1 is applied to the negative input of the Analog Comparator.
            cbi(ADCSRB,ACME);
            // If ADATE in ADCSRA is written to one, the value of these bits
            // selects which source will trigger an ADC conversion. If ADATE is
            // cleared, the ADTS2:0 settings will have no effect. A conversion will
            // be triggered by the rising edge of the selected Interrupt Flag. Note
            // that switching from a trigger source that is cleared to a trigger
            // source that is set, will generate a positive edge on the trigger
            // signal. If ADEN in ADCSRA is set, this will start a conversion.
            // Switching to Free Running mode (ADTS[2:0]=0) will not cause a
            // trigger event, even if the ADC Interrupt Flag is set.
            // ADTS2 ADTS1 ADTS0 Trigger source
            // 0 0 0 Free Running mode
            // 0 0 1 Analog Comparator
            // 0 1 0 External Interrupt Request 0
            // 0 1 1 Timer/Counter0 Compare Match A
            // 1 0 0 Timer/Counter0 Overflow
            // 1 0 1 Timer/Counter1 Compare Match B
            // 1 1 0 Timer/Counter1 Overflow
            // 1 1 1 Timer/Counter1 Capture Event
            cbi(ADCSRB,ADTS2);
            cbi(ADCSRB,ADTS1);
            cbi(ADCSRB,ADTS0);

            //---------------------------------------------------------------------
            // DIDR0 settings
            //---------------------------------------------------------------------
            // When this bit is written logic one, the digital input buffer on the
            // corresponding ADC pin is disabled. The corresponding PIN Register
            // bit will always read as zero when this bit is set. When an analog
            // signal is applied to the ADC5..0 pin and the digital input from this
            // pin is not needed, this bit should be written logic one to reduce
            // power consumption in the digital input buffer.
            // Note that ADC pins ADC7 and ADC6 do not have digital input buffers,
            // and therefore do not require Digital Input Disable bits.
            sbi(DIDR0,ADC5D);
            sbi(DIDR0,ADC4D);
            sbi(DIDR0,ADC3D);
            sbi(DIDR0,ADC2D);
            sbi(DIDR0,ADC1D);
            sbi(DIDR0,ADC0D);
    }


Step 11: How the Analog Comparator works
第十一步 模拟比较器


The Analog Comparator is an internal module of the microcontroller and it compares the input values
on the positive pin (Digital Pin 6) and negative pin (Digital Pin 7). When the voltage on the positive pin
is higher than the voltage on the negative pin AIN1, the Analog Comparator outputs a 1 in the ACO bit
of the ACSR register.
模拟比较器是一个内置的模块,用于比较正电平针脚 6,负电平针脚7的数值。当针脚6上的电平高于
针脚7上的电平时,比较器在ACSR中的ACO值,输出为1。

Optionally, the comparator can trigger an interrupt, exclusive to the Analog Comparator. The associated
vector is ANALOG_COMP_vect.
比较器可以触发一个中断,专用于模拟比较器。具体参数ANALOG_COMP_vect。

We can also set the the interrupt to be launched on a rising edge, falling edge or on a toggle of the state.
还可以设定这个中断的发生时机,信号上升阶段、下降或者是状态转变阀值。

The Analog Comparator is just what we need for the triggering connecting out input signal to pin 6, now
what is left is a threshold level on pin 7.
这里只要通过控制针脚6,就可以设定信号输入针脚7的阀值。

Step 12: Setting up the Analog Comparator
第十二步 设置模拟比较器


In the sketch, I wrote another initialization function that sets-up all the parameters of the Analog Comparator
functioning. The same issue about ADC digital buffers applies to the Analog Comparator, as we can see on
the bottom of the routine.
这里是这次要用到的比较器设定代码,启始代码

    void initAnalogComparator(void)
    {
            //---------------------------------------------------------------------
            // ACSR settings
            //---------------------------------------------------------------------
            // When this bit is written logic one, the power to the Analog
            // Comparator is switched off. This bit can be set at any time to turn
            // off the Analog Comparator. This will reduce power consumption in
            // Active and Idle mode. When changing the ACD bit, the Analog
            // Comparator Interrupt must be disabled by clearing the ACIE bit in
            // ACSR. Otherwise an interrupt can occur when the bit is changed.
            cbi(ACSR,ACD);
            // When this bit is set, a fixed bandgap reference voltage replaces the
            // positive input to the Analog Comparator. When this bit is cleared,
            // AIN0 is applied to the positive input of the Analog Comparator. When
            // the bandgap referance is used as input to the Analog Comparator, it
            // will take a certain time for the voltage to stabilize. If not
            // stabilized, the first conversion may give a wrong value.
            cbi(ACSR,ACBG);
            // When the ACIE bit is written logic one and the I-bit in the Status
            // Register is set, the Analog Comparator interrupt is activated.
            // When written logic zero, the interrupt is disabled.
            cbi(ACSR,ACIE);
            // When written logic one, this bit enables the input capture function
            // in Timer/Counter1 to be triggered by the Analog Comparator. The
            // comparator output is in this case directly connected to the input
            // capture front-end logic, making the comparator utilize the noise
            // canceler and edge select features of the Timer/Counter1 Input
            // Capture interrupt. When written logic zero, no connection between
            // the Analog Comparator and the input capture function exists. To
            // make the comparator trigger the Timer/Counter1 Input Capture
            // interrupt, the ICIE1 bit in the Timer Interrupt Mask Register
            // (TIMSK1) must be set.
            cbi(ACSR,ACIC);
            // These bits determine which comparator events that trigger the Analog
            // Comparator interrupt.
            // ACIS1 ACIS0 Mode
            // 0 0 Toggle
            // 0 1 Reserved
            // 1 0 Falling edge
            // 1 1 Rising edge
            sbi(ACSR,ACIS1);
            sbi(ACSR,ACIS0);

            //---------------------------------------------------------------------
            // DIDR1 settings
            //---------------------------------------------------------------------
            // When this bit is written logic one, the digital input buffer on the
            // AIN1/0 pin is disabled. The corresponding PIN Register bit will
            // always read as zero when this bit is set. When an analog signal is
            // applied to the AIN1/0 pin and the digital input from this pin is not
            // needed, this bit should be written logic one to reduce power
            // consumption in the digital input buffer.
            sbi(DIDR1,AIN1D);
            sbi(DIDR1,AIN0D);
    }


Step 13: Threshold
第十三步 阀值


Recalling what we said about the trigger, we can implement these two solutions for the threshold:
前文所述的“触发”,这里可以使用两种方法设置触发阀值

    using a trimmer we can manually set a voltage level; 修剪器,手动定值
    using the PWM of the Arduino we can set the level by software. 软件脉宽定值

On the image we can see the hardware implementation of the threshold in both paths.
图示两种方法

For the manual selection, a multi-turn potentiometer put between +5 V and GND is sufficient.
手动定值,多圈电位器在+5V的地间使用

While for software selection we need a low-pass filter that filters a PWM signal coming from the Arduino.
PWM signals (more on this to follow) are square signals with a constant frequency but a variable pulse-width.
This variability brings a variable mean value of the signal that can be extracted with a low-pass filter. A good
cutoff frequency for the filter is about one hundredth of the PWM frequency and I chose about 560 Hz.
软件脉宽信号定值,1/100脉宽频率定值,这里定为560hz。

After the two threshold sources I inserted a couple of pins that allows to select, with a jumper, which source
I wanted. After the selection I also added an emitter follower to decouple the sources from the Arduino pin.
两种方法,采用跳线选择模式。
[ 此帖被abigbell在2014-06-14 15:10重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共2条打赏 ,M币+6
离线abigbell

发帖
11330
M币
1683
专家
457
粉丝
532
只看该作者 1楼 发表于: 2014-06-14
请登录后查看
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
离线铁皮屋

发帖
5966
M币
664
专家
6
粉丝
85
只看该作者 2楼 发表于: 2014-06-14
请登录后查看
离线温力口

发帖
60313
M币
51283
专家
569
粉丝
909
只看该作者 3楼 发表于: 2014-06-14
请登录后查看
离线laulaya

发帖
58
M币
1379
专家
0
粉丝
3
只看该作者 4楼 发表于: 2014-06-15
请登录后查看
离线phpamodernpe

发帖
368
M币
65
专家
2
粉丝
8
只看该作者 5楼 发表于: 2014-06-15
请登录后查看
离线a546011790a

发帖
1
M币
-494
专家
0
粉丝
0
只看该作者 6楼 发表于: 2015-05-16
请登录后查看
离线cuijie74

发帖
97
M币
4
专家
0
粉丝
7
只看该作者 7楼 发表于: 2015-11-04
请登录后查看
离线jpdd521

发帖
25694
M币
7956
专家
15
粉丝
350
只看该作者 8楼 发表于: 2015-11-06
回 cuijie74 的帖子
请登录后查看
离线zenrenlong
发帖
3
M币
-497
专家
0
粉丝
1
只看该作者 9楼 发表于: 2017-06-28
请登录后查看
快速回复
限80 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个