Arduino 的 ADC 是很好用的東西,可以用來測量電壓轉成數位資料,而示波器就是把隨著時間變化的電壓畫成圖形表示出來,利用 arduino 的 ADC 加上電腦的繪圖能力,作成了一個簡易示波器。
Arduino oscilloscope - flashlight waveform |
窮人的示波器 - 使用 arduino 透過序列埠傳到到電腦,然後用 Processing 程式作繪圖。
Arduino – Poor Man’s Oscilloscope
http://randomnerdtutorials.com/arduino-poor-mans-oscilloscope/
arduino 接上電腦後用 Processing IDE 執行他的程式,可以看到由右往左一直捲動的波形,我有一台示波器了 :)。用10k電阻加光敏電阻做了一個測試電路來觀察波形,用手電筒的光照射產生電壓變化,得到了以上的波形,手電筒有五檔變化:強光、中光、弱光、爆閃、SOS,可以看到中光和弱光是用PWM來調整亮度的。
Arduino oscilloscope photo resistor test circuit |
可惜的是原來的程式算了一下每秒大約更新 400 個點,對一個示波器來說最重要的參數就是頻寬,除了看到變化,更希望能看到細節。看了一下程式作了一些改進,首先是 Serial 的速度從 9600 提高到了115200 bps,然後每個點原來是送3個byte,改成只送1個byte,犧牲了解析度換取速度,這樣從每秒 400 個點提高到了每秒約 8300 個點,原以為這樣就是極限了,但還有更好的方式,待續 ...
11760
試試調高Baud rate ,結果我的機器只能到230400,再高 Serial 就開始錯亂了,再看現在的程式,最花時間的是ADC ,Google 找到了這一篇講 ADC clock 是可以調整的,
Faster analogRead()
http://www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/
最後調整 ADC clock 到 500 kHz, 每做一次約 36 us,配上 230400 的 baud,結果就是現在 arduino 每秒可送出 22240 個點。
Arduino oscilloscope fluorescent light |
Arduino oscilloscope Auto Run Trig'd 22240 |
接下來想要的功能是訊號的同步(Trigger)觸發功能,當訊號是周期性的波時,如果沒能掌握
波開始顯示的時機,週期性的波並不一定能重疊在一起,畫面上會看到一堆不同起始點的波跑來跑去,
unsynced waveform |
上升邊緣觸發
Arduino oscilloscope auto stop trigger rising edge |
Arduino oscilloscope single stop trigger falling edge |
手電筒的弱光模式波形
Arduino oscilloscope flashlight pwm |
拿來看 uart 9600 的波形
Arduino oscilloscope Serial 9600 bps singal |
按鍵說明:
左鍵 設定觸發電壓(Set trigger voltage)
右鍵 切換 上升邊緣(正緣)觸發、下降邊緣(負緣)觸發 (Rising edge/Falling edge trigger)
z,x 縮小、放大 (Zoom out, Zoom in)
-,+ 忽略資料點 (Ignore data, adjust bandwidth)
r,Space 開始、停止 (Run, Stop)
a,s 切換 自動、單次觸發 (Auto, Single trigger)
Enter 所有設定回到預設值 (Default Setup)
程式:從 google drive下載
// arduino 上跑的程式
/*
* Arduino oscilloscope
* Read ADC value from pin A3 and send byte data to serial port.
*/
#define ANALOG_IN 3
// Define ADC prescaler
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
void setup() {
Serial.begin(230400); // as quick as possible
pinMode(ANALOG_IN, INPUT);
// setup the ADC
ADCSRA &= ~PS_128;
ADCSRA |= PS_32;
}
void loop() {
Serial.write( (analogRead(ANALOG_IN) + 2) >> 2 );
}
/*********************************************************************/
// processing 上跑的程式,請下載 processing IDE 來執行 https://processing.org/download/
// 因為 Serial port 的位置可能不一樣,可以參考一開始會印的 COM port 位址,
// 不一樣的時候請改這一行 port = new Serial(this, Serial.list()[1], 230400);
//
/* \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/ \O/
* Arduino Oscilloscope
* Vsualize signal of analog pin output from arduino.
*
* (c) 2016 Kinmenalex Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*/
import processing.serial.*;
final String title = "Arduino Oscilloscope 0.80";
Serial port;
int[] values;
float zoom;
int count=0;
final int trigSensitivity=1;
int trigSlope=0;
int trigLevel;
int trigVoltage; // 2500 mV
boolean trigd;
boolean bSingle;
boolean bRun;
final int bufSize=1<<13 8k="" div="" nbsp="">13>
final int bufMask=bufSize-1;
int bufIndex = 0;
int t0=0;
final int skipLevel[] = {1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000};
int skipIndex;
int skipCount;
void setup()
{
size(1280, 512);
// List all the available serial ports
for (int i=0; i
println("["+ i +"] "+ Serial.list()[i]);
}
// Open the port that the arduino is connected to and use the same speed (230400 bps)
port = new Serial(this, Serial.list()[1], 230400);
values = new int[bufSize];
defaultSet();
surface.setTitle(title);
frameRate(50);
}
void trigVoltageSet(int voltage) {
trigLevel = voltage*256/5/1000;
trigVoltage = voltage;
}
void trigLevelSet(int level) {
int voltage = level *5 *1000 / height;
trigVoltageSet(voltage);
}
void defaultSet() {
zoom = 1.0f;
trigVoltageSet(2500);
trigSlope=0;
skipIndex=0;
bRun=true;
bSingle=false;
}
int getY(int val) {
return (int)(height - val * height / 256 );
}
int getValues() {
while ( port.available() > 0 ) {
if (skipCount>0) {
port.read();
skipCount--;
continue;
}
skipCount = skipLevel[skipIndex]-1;
values[bufIndex] = (port.read());
bufIndex = (bufIndex+1) & bufMask;
count++;
}
return bufIndex;
}
void drawLines() {
int displayWidth = (int) (width / zoom);
int k;
if ( trigd ) {
k = t0 - (displayWidth/2);
} else {
k = bufIndex-1-displayWidth;
}
if (k<0 div="" nbsp="">0>
k+=bufSize;
}
stroke(255);
int x0 = 0;
int y0 = getY(values[k]);
for (int i=1; i
k=(k+1)&bufMask;
int x1 = (int) (i * (width-1) / (displayWidth-1));
int y1 = getY(values[k]);
line(x0, y0, x1, y1);
x0 = x1;
y0 = y1;
}
}
int lastShow=0;
int lastCount;
int rate;
void drawGrid() {
int y;
int now = millis();
int dt = now - lastShow;
if ( dt >= 1000) {
rate = (count-lastCount)*1000 /dt;
lastCount = count;
lastShow = now;
}
// Voltage line
stroke(0, 128, 0);
textSize(14);
fill(255, 255, 0, 255);
for (int i=1; i<5 div="" i="">5>
y=height/5*i+1;
line(0, y, width, y);
text(nf(5-i)+"V", 1, y);
}
// Trigger voltage
stroke(96, 0, 0);
textSize(14);
fill(255, 0, 0, 255);
y=getY(trigLevel);
line(0, y, width, y);
text(nf(trigVoltage/1000)+"."+
nf((trigVoltage%1000)/100)+nf((trigVoltage%100)/10)+"V " +
(trigSlope == 0 ? "↗":"↘"), 22, y);
// text y
y = height-5;
fill(255, 128, 0, 255);
if ( bRun ) {
if ( rate > 0 && rate <= 50000 ) {
text(nf(rate) + " pps", 11, y);
}
if ( skipIndex > 0 ) {
text(" Skip "+nf(skipLevel[skipIndex]-1), 186, y);
}
}
String str;
fill(0, 255, 0, 255);
if ( bSingle ) {
str = "Single";
} else {
str = " Auto ";
}
text(str, 90, y);
float w = textWidth(str);
float h = textAscent() + textDescent();
if ( !bRun ) {
str = " Stop ";
fill(255, 0, 0, 255);
rect( 97+w-1, height -h-1, textWidth(str), h-1);
fill(0);
text(str, 96+w, y);
} else if ( trigd ) {
str = " Trig'd ";
fill(0, 255, 0, 255);
//stroke(0);
rect( 96+w-1, height -h-1, textWidth(str), h-1);
fill(0);
text(str, 96+w, y);
}
}
int b=0;
void draw()
{
// fade background
stroke(0, 100);
if ( trigd ) {
fill(0, 100);
} else {
fill(0, 200);
}
rect(0, 0, width, height);
// set trigger level
if (mousePressed && mouseButton == LEFT) {
trigLevelSet(height-mouseY);
}
drawGrid();
int startCount=count;
if ( bRun ) {
getValues();
}
trigd = findTrig(int(width / zoom));
if (bSingle && trigd) {
bRun = false;
}
if ( (b++ & 0x1ff) == 0 ) {
print(count-startCount + " ");
}
drawLines();
}
void mousePressed() {
if (mouseButton == LEFT) {
trigLevelSet(height-mouseY);
} else if (mouseButton == RIGHT) {
trigSlope = trigSlope==0 ? 1 : 0;
}
}
void keyReleased() {
println(key);
switch (key) {
case 'x':
zoom *= 2.0f;
println(zoom);
if ( (int) (width / zoom) <= 1 )
zoom /= 2.0f;
break;
case 'z':
zoom /= 2.0f;
println(zoom);
if ( bufSize * zoom < width)
zoom *= 2.0f;
break;
case 'r':
case ' ':
bRun = !bRun;
break;
case 'a':
case 's':
bSingle = !bSingle;
break;
case '+':
if ( skipIndex > 0) {
skipIndex--;
}
break;
case '-':
if ( skipIndex < (skipLevel.length-1)) {
skipIndex++;
}
break;
case '\n': //Enter
defaultSet();
break;
default :
break;
}
}
boolean findTrig(int displayWidth)
{
int th1, th2, k;
int len = bufSize-displayWidth;
boolean trig=false;
int sync=0;
k = bufIndex-1-(displayWidth/2); // get last half screen position in scan buffer
if (k<0 div="" nbsp="">0>
k+=bufSize;
}
// calculate trigger thresholds
th1 = trigLevel + trigSensitivity;
th2 = trigLevel - trigSensitivity;
// search for trigger
if (trigSlope == 0) { //trigger slope is rising edge
while (len>0) {
len--;
if ((sync == 0) && (values[k] > th1)) {
sync = 1; // above trigger threshold
} else if ((sync == 1) && (values[k] < th2)) {
trig=true; // below trigger threshold
break;
}
k = k - 1;
if ( k < 0 ) {
k+=bufSize;
}
}
} else { // trigger slope is falling edge
while (len>0) {
len--;
if ((sync == 0) && (values[k] <= th2)) {
sync = 1; // below trigger threshold
} else if ((sync == 1) && (values[k] >= th1)) {
trig=true; // above trigger threshold
break;
}
k = k - 1;
if ( k < 0 ) {
k+=bufSize;
}
}
}
t0=k;
return trig;
}
No comments:
Post a Comment