Browse Source

finished basic oscilloscope functionalities (max sampling rate seems to be 38KHz)

pull/5/head
Youen Toupin 10 years ago
parent
commit
d0ef4ee9f9
  1. 25
      OneWireIO.ino
  2. 2
      SerialMonitor/SerialMonitor/App.xaml.cs
  3. 15
      SerialMonitor/SerialMonitor/MainWindow.xaml
  4. 31
      SerialMonitor/SerialMonitor/MainWindow.xaml.cs
  5. 152
      SerialMonitor/SerialMonitor/MainWindowContext.cs
  6. 21
      SerialMonitor/SerialMonitor/MathEx.cs
  7. 1
      SerialMonitor/SerialMonitor/SerialMonitor.csproj

25
OneWireIO.ino

@ -5,12 +5,14 @@
#define OWPin 2 #define OWPin 2
#define InterruptNumber 0 // Must correspond to the OWPin to correctly detect state changes. On Arduino Uno, interrupt 0 is for digital pin 2 #define InterruptNumber 0 // Must correspond to the OWPin to correctly detect state changes. On Arduino Uno, interrupt 0 is for digital pin 2
const int SkipSamples = 8; // how many samples we want to skip between two samples we keep (can be used to lower the sampling frequency) // how many samples we want to skip between two samples we keep (can be used to lower the sampling frequency)
const int BufferSize = 128; #define SkipSamples 0
const int BufferSize = 512;
byte buffer1[BufferSize]; byte buffer1[BufferSize];
byte buffer2[BufferSize]; byte buffer2[BufferSize];
byte* backBuffer = buffer1; byte* backBuffer = buffer1;
volatile byte backBufferPos = 0; volatile short backBufferPos = 0;
byte samplesSkipped = SkipSamples; byte samplesSkipped = SkipSamples;
unsigned long backBufferStartTime = micros(); unsigned long backBufferStartTime = micros();
@ -24,7 +26,7 @@ void setup()
digitalWrite(LEDPin, LOW); digitalWrite(LEDPin, LOW);
attachInterrupt(InterruptNumber,onewireInterrupt,CHANGE); //attachInterrupt(InterruptNumber,onewireInterrupt,CHANGE);
cli();//disable interrupts cli();//disable interrupts
@ -36,7 +38,7 @@ void setup()
ADMUX |= (1 << REFS0); //set reference voltage ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //set ADC clock with 128 prescaler- 16mHz/128=125kHz ; 13 cycles for a conversion which means 9600 samples per second ADCSRA |= (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500KHz ; 13 cycles for a conversion which means 38000 samples per second
ADCSRA |= (1 << ADATE); //enabble auto trigger ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC ADCSRA |= (1 << ADEN); //enable ADC
@ -44,7 +46,7 @@ void setup()
sei();//enable interrupts sei();//enable interrupts
Serial.begin(200000); Serial.begin(400000);
} }
void loop() void loop()
@ -52,27 +54,32 @@ void loop()
while(backBufferPos < BufferSize / 2) ; while(backBufferPos < BufferSize / 2) ;
cli();//disable interrupts cli();//disable interrupts
byte* currentBuffer = backBuffer; byte* currentBuffer = backBuffer;
unsigned long currentBufferStartTime = backBufferStartTime; short currentBufferSize = backBufferPos;
byte currentBufferSize = backBufferPos;
backBuffer = (backBuffer == buffer1 ? buffer2 : buffer1); backBuffer = (backBuffer == buffer1 ? buffer2 : buffer1);
backBufferPos = 0; backBufferPos = 0;
backBufferStartTime = micros();
sei();//enable interrupts sei();//enable interrupts
unsigned long currentBufferStartTime = backBufferStartTime;
backBufferStartTime = micros();
digitalWrite(LEDPin, LOW);
//Serial.write(currentBuffer, currentBufferSize);
oscilloscope.write(currentBuffer, currentBufferSize, currentBufferStartTime); oscilloscope.write(currentBuffer, currentBufferSize, currentBufferStartTime);
} }
ISR(ADC_vect) {//when new ADC value ready ISR(ADC_vect) {//when new ADC value ready
byte sample = ADCH; //store 8 bit value from analog pin 0 byte sample = ADCH; //store 8 bit value from analog pin 0
#if SkipSamples > 0
if(samplesSkipped++ < SkipSamples) if(samplesSkipped++ < SkipSamples)
return; return;
samplesSkipped = 0; samplesSkipped = 0;
#endif
backBuffer[backBufferPos++] = sample; backBuffer[backBufferPos++] = sample;
if(backBufferPos >= BufferSize) if(backBufferPos >= BufferSize)
{ {
// overflow of back buffer, we loose the current sample // overflow of back buffer, we loose the current sample
digitalWrite(LEDPin, HIGH);
backBufferPos = BufferSize - 1; backBufferPos = BufferSize - 1;
} }
} }

2
SerialMonitor/SerialMonitor/App.xaml.cs

@ -28,7 +28,7 @@ namespace SerialMonitor
Serial = new SerialPort(); Serial = new SerialPort();
Serial.PortName = "COM4"; Serial.PortName = "COM4";
Serial.BaudRate = 200000; Serial.BaudRate = 400000;
Serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(OnDataReceived); Serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(OnDataReceived);

15
SerialMonitor/SerialMonitor/MainWindow.xaml

@ -1,11 +1,18 @@
<Window x:Class="SerialMonitor.MainWindow" <Window x:Class="SerialMonitor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Serial Monitor" Height="396" Width="629"> Title="Serial Monitor" Height="490.75" Width="923">
<Grid> <Grid>
<TextBlock TextWrapping="Wrap" VerticalAlignment="Bottom" Height="150" Text="{Binding ConsoleText}" Style="{DynamicResource Console}" Grid.ColumnSpan="2"/> <Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Visible" Margin="0,0,0,155" Grid.ColumnSpan="2"> <RowDefinition Height="290" />
<Canvas Name="Oscilloscope" Style="{DynamicResource Oscilloscope}"/> <RowDefinition Height="5" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Canvas Name="Oscilloscope" Style="{DynamicResource Oscilloscope}" Margin="0,0,0,17" />
<ScrollBar Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch" Height="17" VerticalAlignment="Bottom" Minimum="{Binding MinTime}" Maximum="{Binding MaxTime}" ViewportSize="{Binding ViewportTimeWidth}" Value="{Binding ScrollValue}"/>
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" />
<ScrollViewer Grid.Row="2">
<TextBlock TextWrapping="Wrap" Style="{DynamicResource Console}" Text="{Binding ConsoleText}"/>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
</Window> </Window>

31
SerialMonitor/SerialMonitor/MainWindow.xaml.cs

@ -26,6 +26,37 @@ namespace SerialMonitor
var context = new MainWindowContext(this); var context = new MainWindowContext(this);
context.WriteLine("Connecting..."); context.WriteLine("Connecting...");
this.DataContext = context; this.DataContext = context;
this.MouseWheel += OnMouseWheel;
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
MainWindowContext context = (MainWindowContext)this.DataContext;
Point cursorPos = e.GetPosition(context.OscilloscopeCanvas);
if (cursorPos.X < 0 || cursorPos.X > context.OscilloscopeCanvas.ActualWidth || cursorPos.Y < 0 || cursorPos.Y > context.OscilloscopeCanvas.ActualHeight)
return;
double cursorPosRatio = cursorPos.X / context.OscilloscopeCanvas.ActualWidth;
double cursorTime = context.ViewportStartTime + cursorPosRatio * context.ViewportTimeWidth;
double newTimeWidth = context.ViewportTimeWidth;
if (e.Delta > 0)
newTimeWidth /= e.Delta * 0.01;
else if (e.Delta < 0)
newTimeWidth *= -e.Delta * 0.01;
double totalTimeWidth = Math.Max(0.1, context.MaxTime - context.MinTime);
if (newTimeWidth > totalTimeWidth)
newTimeWidth = totalTimeWidth;
double newStartTime = cursorTime - cursorPosRatio * newTimeWidth;
if (newStartTime < context.MinTime)
newStartTime = context.MinTime;
if (newStartTime + newTimeWidth > context.MaxTime)
newStartTime = context.MaxTime - newTimeWidth;
context.SetViewport(newStartTime, newTimeWidth);
} }
} }
} }

152
SerialMonitor/SerialMonitor/MainWindowContext.cs

@ -1,11 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shapes; using System.Windows.Shapes;
namespace SerialMonitor namespace SerialMonitor
@ -28,11 +30,14 @@ namespace SerialMonitor
public void WriteLine(string line) public void WriteLine(string line)
{ {
Lines.Add(line); Lines.Add(line);
if (Lines.Count > 9) if (Lines.Count > 100)
Lines.RemoveAt(0); Lines.RemoveAt(0);
OnPropertyChanged("ConsoleText"); OnPropertyChanged("ConsoleText");
} }
public double SampleDelay { get { return 0.000026; } }
public int MaxSequenceSize { get { return 2048; } }
private class Sequence private class Sequence
{ {
public ulong StartTime { get; set; } public ulong StartTime { get; set; }
@ -46,7 +51,11 @@ namespace SerialMonitor
} }
} }
} }
double ValueToHeight(byte value) { return (double)value + 10; } double ValueToHeight(double time, byte value)
{
//value = (byte)(Math.Sin(time * 100.0) * 127.0 + 128.0);
return 256.0 - (double)value + 10;
}
private SortedSet<Sequence> Sequences = new SortedSet<Sequence>(new Sequence.Comparer()); private SortedSet<Sequence> Sequences = new SortedSet<Sequence>(new Sequence.Comparer());
public IEnumerable<Line> Oscilloscope public IEnumerable<Line> Oscilloscope
{ {
@ -55,63 +64,146 @@ namespace SerialMonitor
if(!Sequences.Any()) if(!Sequences.Any())
yield break; yield break;
ulong startTime = Sequences.ElementAt(0).StartTime;
foreach (var sequence in Sequences) foreach (var sequence in Sequences)
{ {
foreach (var line in ConvertToLines(sequence, startTime)) double seqStartTime = (double)sequence.StartTime/1000000.0;
yield return line; if (seqStartTime + (double)sequence.Data.Length * SampleDelay > ViewportStartTime && seqStartTime < ViewportStartTime + ViewportTimeWidth)
{
foreach (var line in ConvertToLines(sequence))
yield return line;
}
} }
} }
} }
private IEnumerable<Line> ConvertToLines(Sequence sequence, ulong displayStartTime) private IEnumerable<Line> ConvertToLines(Sequence sequence)
{ {
double sampleDelay = 0.000936; // in seconds double viewportWidth = OscilloscopeCanvas.ActualWidth;
double scale = 10; // in pixels per second double scale = viewportWidth / ViewportTimeWidth; // in pixels per second
double pos = (sequence.StartTime - displayStartTime) / 1000000.0 * scale; ulong displayStartTime = (ulong)(viewportStartTime_ * 1000000.0);
double pos = ((double)sequence.StartTime - (double)displayStartTime) / 1000000.0 * scale;
if (pos > 1000) if (pos > 1000)
yield break; yield break;
double prevHeight = ValueToHeight(sequence.Data[0]); double prevPos = pos;
byte minValue = sequence.Data[0];
byte maxValue = minValue;
int prevIdx = 0;
double prevHeight = ValueToHeight(sequence.StartTime / 1000000.0, minValue);
for (int idx = 1; idx < sequence.Data.Length; ++idx) for (int idx = 1; idx < sequence.Data.Length; ++idx)
{ {
byte value = sequence.Data[idx]; byte value = sequence.Data[idx];
var line = new Line(); pos += SampleDelay * scale;
line.Stroke = System.Windows.Media.Brushes.LightSteelBlue; if (value > maxValue) maxValue = value;
line.X1 = pos; if (value < minValue) minValue = value;
pos += sampleDelay * scale;
line.X2 = pos; if (pos > 0 && pos < viewportWidth && pos - prevPos >= 5 || idx == sequence.Data.Length - 1)
line.Y1 = prevHeight; {
prevHeight = ValueToHeight(value); var line = new Line();
line.Y2 = prevHeight; line.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
line.HorizontalAlignment = HorizontalAlignment.Left; line.HorizontalAlignment = HorizontalAlignment.Left;
line.VerticalAlignment = VerticalAlignment.Center; line.VerticalAlignment = VerticalAlignment.Center;
line.StrokeThickness = 1; line.StrokeThickness = 1;
yield return line;
line.X1 = prevPos;
prevPos = pos;
line.X2 = pos;
double time = (double)sequence.StartTime / 1000000.0 + (double)idx * SampleDelay;
double lastHeight = ValueToHeight(time, value);
if (idx == prevIdx + 1)
{
line.Y1 = prevHeight;
line.Y2 = lastHeight;
}
else
{
if (value - minValue > maxValue - value)
{
line.Y1 = ValueToHeight(time, minValue);
line.Y2 = ValueToHeight(time, maxValue);
}
else
{
line.Y1 = ValueToHeight(time, maxValue);
line.Y2 = ValueToHeight(time, minValue);
}
}
prevHeight = lastHeight;
minValue = value;
maxValue = value;
prevIdx = idx;
yield return line;
}
} }
} }
public void AddSequence(ulong startTime, byte[] data) public void AddSequence(ulong startTime, byte[] data)
{ {
// TODO: merge sequences if total size is lower than MaxSequenceSize
var sequence = new Sequence { StartTime = startTime, Data = data }; var sequence = new Sequence { StartTime = startTime, Data = data };
Sequences.Add(sequence); Sequences.Add(sequence);
OnPropertyChanged("Oscilloscope"); OnPropertyChanged("Oscilloscope");
var canvas = (Canvas)Window.FindName("Oscilloscope"); OnPropertyChanged("MinTime");
/*canvas.Children.Clear(); OnPropertyChanged("MaxTime");
if (Sequences.Count == 1)
{
ViewportStartTime = MinTime;
}
var canvas = OscilloscopeCanvas;
foreach (var line in ConvertToLines(sequence))
canvas.Children.Add(line);
}
void RefreshOscilloscope()
{
var canvas = OscilloscopeCanvas;
canvas.Children.Clear();
foreach (var line in Oscilloscope) foreach (var line in Oscilloscope)
canvas.Children.Add(line);*/
foreach (var line in ConvertToLines(sequence, Sequences.ElementAt(0).StartTime))
canvas.Children.Add(line); canvas.Children.Add(line);
} }
private Canvas oscilloscopeCanvas_;
public Canvas OscilloscopeCanvas { get { if (oscilloscopeCanvas_ == null) oscilloscopeCanvas_ = (Canvas)Window.FindName("Oscilloscope"); return oscilloscopeCanvas_; } }
public double MinTime { get { return Sequences.Any() ? (double)Sequences.First().StartTime / 1000000.0 : 0.0; } }
public double MaxTime { get { return Sequences.Any() ? Math.Max(MinTime + 0.1, (double)Sequences.Last().StartTime / 1000000.0) : 0.1; } }
private double viewportTimeWidth_ = 0.1;
public double ViewportTimeWidth
{
get { return viewportTimeWidth_; }
set { viewportTimeWidth_ = value; OnPropertyChanged("ViewportTimeWidth"); RefreshOscilloscope(); }
}
private double viewportStartTime_ = 0;
public double ViewportStartTime
{
get { return viewportStartTime_; }
set { viewportStartTime_ = value; OnPropertyChanged("ViewportStartTime"); RefreshOscilloscope(); }
}
public double ScrollValue
{
get { return MathEx.Unproject(MathEx.Project(ViewportStartTime, MinTime, MaxTime - ViewportTimeWidth), MinTime, MaxTime); }
set { ViewportStartTime = MathEx.Unproject(MathEx.Project(value, MinTime, MaxTime), MinTime, MaxTime - ViewportTimeWidth); }
}
public void SetViewport(double startTime, double timeWidth)
{
viewportStartTime_ = startTime;
ViewportTimeWidth = timeWidth;
}
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) protected void OnPropertyChanged(string name)
{ {
PropertyChangedEventHandler handler = PropertyChanged; if (PropertyChanged != null)
if (handler != null)
{ {
handler(this, new PropertyChangedEventArgs(name)); PropertyChanged(this, new PropertyChangedEventArgs(name));
} }
} }
} }

21
SerialMonitor/SerialMonitor/MathEx.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SerialMonitor
{
public static class MathEx
{
public static double Project(double value, double rangeMin, double rangeMax)
{
return (value - rangeMin) / (rangeMax - rangeMin);
}
public static double Unproject(double ratio, double rangeMin, double rangeMax)
{
return rangeMin + ratio * (rangeMax - rangeMin);
}
}
}

1
SerialMonitor/SerialMonitor/SerialMonitor.csproj

@ -54,6 +54,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="MainWindowContext.cs" /> <Compile Include="MainWindowContext.cs" />
<Compile Include="MathEx.cs" />
<Compile Include="SerialMessage.cs" /> <Compile Include="SerialMessage.cs" />
<Compile Include="SerialPortExtensions.cs" /> <Compile Include="SerialPortExtensions.cs" />
<Page Include="MainWindow.xaml"> <Page Include="MainWindow.xaml">

Loading…
Cancel
Save