Cześć,
prezentuje pierwszy projekt który jest oscyloskopem. Oczywiście nie przypomina takiego jak każdy sobie wyobraża lecz ma podstawową jego funkcję więc tak nazwałem ten projekt (jak zrobicie i pochwalicie się rodzinie, znajomym to też inaczej brzmi ;)). Projekt w żadnym stopniu nie jest skomplikowany więc zachęcam do budowy swojego.
Parametry:
- odczyt jednego kanału
- maksymalna częstotliwość (moich obliczeń nie warto się trzymać :D) to 250kHz (częstotliwość mikroprocesora/prescaler ADC) 8Mhz/4. Ale jestem pewny że częstotliwość w sieci spokojnie mógłbym odczytać oczywiście nie zalecam robienia czegoś takiego ;)
- zakres napięć 0-5V (VCC)
- 8 bitowa dokładność
Jak to działa?
Mikrokontroler mierzy napięcie za pomocą przetwornika ADC które jest w postaci 8bitowej (wartość od 0-255). Następnie wysyła odczyt za pomocą interfejsu USART i czeka 20ms (można zejść do 1ms). Komputer odbiera te dane a następnie rysowany jest wykres. Wartość na osi Y to wartość odczytana z ADC. Wartością X można regulować ile nam się zmieści na wykresie. Duża wartość X pozwala na obserwację okresu czasu rzędu kilku do kilkudziesięciu milisekund (to też zależy od przerw między wysłanymi danymi). Mała wartość X pozwala na obserwowanie długich “przebiegów” takich jak rozładowywanie kondensatora. Wartość X regulowana jest suwakiem w programie. Siatka która jest rysowana ułatwia odczytywanie wykresu.
Schemat
robiony “w biegu” dla osób które od razu chcą ruszyć do budowy projektu (zaraz zmienię na ładniejszy)
Z czego to jest zrobione?
- ATmega328P ATmega328 w BOTLANDZIE
- przejściówka USB<>RS232 konwerter w BOTLANDZIE
- płytka stykowa płytka w BOTLANDZIE
- kable do płytki stykowej przewody w BOTLANDZIE przewody w BOTLANDZIE (te sa przydatne do podpięcia konwertera)
- kondensatory do filtrowania zasilania kondensatory 1 w BOTLANDZIE kondensatory 2 w BOTLANDZIE
- elementy do badania wspaniały układ NE555 w BOTLANDZIE – dzięki niemu możemy zobaczyć przebieg prostokątny
Oczywiście można również w elementy zaopatrzyć się w sklepie nettigo.pl
Kod
Przedstawiony kod dla mikrokontrolera (plik USART.h w paczce do pobrania ;))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "USART.h" static inline void initFreerunningADC(void) { ADMUX |= (1 << REFS0); /* napięcie referencyjne to AVCC */ ADCSRA |= (0 << ADPS2) | (1 << ADPS1) | (0 << ADPS0); /* preskaler zegara konwertera /4 */ ADMUX |= (1 << ADMUX); ADMUX |= (1 << ADLAR); /* wyrównaj wynik do lewej, zwracaj tylko 8 bitów */ ADCSRA |= (1 << ADEN); /* włącz konwerter AC */ ADCSRA |= (1 << ADATE); /* włącz tryb ciągły */ ADCSRA |= (1 << ADSC); /* uruchom pierwszą konwersję */ } int main(void) { initUSART(); initFreerunningADC(); while (1) { transmitByte(ADCH); _delay_ms(20); } return (0); } |
Kod dla aplikacji na komputer (całą paczke projektu do Visual Studio znajdziecie do pobrania):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.IO.Ports; using System.Threading; namespace Scope { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private SerialPort port; private bool draw; private int dataWidth; private int valuesCount; private int dataSpeed; public MainWindow() { InitializeComponent(); ComboBoxPorts.Items.Clear(); foreach(string com in SerialPort.GetPortNames()) { ComboBoxPorts.Items.Add(com); } valuesCount = 0; dataWidth = 30; draw = false; } private void Button_Click(object sender, RoutedEventArgs e) //sprawdza aktualne porty { ComboBoxPorts.Items.Clear(); foreach(string com in SerialPort.GetPortNames()) { ComboBoxPorts.Items.Add(com); } } private void Button_Click_1(object sender, RoutedEventArgs e) //tworzy nowy port (klase SerialPort) { if (ComboBoxPorts.Text != "" && ComboBoxBaund.Text != "") { if (port != null) port.Close(); port = new SerialPort(ComboBoxPorts.Text, Convert.ToInt16(ComboBoxBaund.Text), Parity.None, 8, StopBits.One); port.Encoding = System.Text.Encoding.GetEncoding(1252); port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); port.Open(); } else MessageBox.Show("Nie wybrano portu i/lub prędkości.", "Błąd", MessageBoxButton.OK, MessageBoxImage.Information); } private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) { if(draw == true) //rysujemy tylko wtedy kiedy uzytkownik chce { var thread = new Thread(() => DrawThread(sender, e)); thread.SetApartmentState(ApartmentState.STA); thread.Start(); //nowy wątek; rysujemy bardzo szybko i wiele obiektow wiec to jest potrzebne :) thread.Join(); } } private void DrawThread(object sender, SerialDataReceivedEventArgs e) { this.Dispatcher.Invoke(() => { SerialPort sp = (SerialPort)sender; UInt16 value = Clamp(Convert.ToUInt16(sp.ReadChar()), 0, 255); if (valuesCount * dataWidth > chart.Width) //juz doszlismy do koncowki pola po ktorym rysujemy. zaczynamy od nowa. { valuesCount = 0; chart.Children.Clear(); drawGird(); } var point = new Ellipse(); point.Fill = System.Windows.Media.Brushes.Cyan; point.HorizontalAlignment = HorizontalAlignment.Center; point.VerticalAlignment = VerticalAlignment.Center; point.Width = 5; point.Height = 5; point.Margin = new Thickness(chart.Width - valuesCount * dataWidth, value, 0, 0); chart.Children.Add(point); valuesCount++; }); } public void drawGird() { for (int a = 0; a < 6; a++) //wyswietla poziome linie ktore oznaczaja wartosc (0V, 1V, 2V itd) { var line = new Line(); line.Stroke = System.Windows.Media.Brushes.White; line.X1 = 0; line.X2 = chart.Width; line.Y1 = a * 51; //255/5V = 51; 51px means 1VoLT line.Y2 = a * 51; line.HorizontalAlignment = HorizontalAlignment.Left; line.VerticalAlignment = VerticalAlignment.Bottom; line.StrokeThickness = 1; chart.Children.Add(line); } for (int a = 0; a < chart.Width / dataWidth; a++) //poziome kreski ktore wyznajczaja "szerokosc rysowanych punktow" { var line = new Line(); line.Stroke = System.Windows.Media.Brushes.White; line.X1 = dataWidth * a; line.X2 = dataWidth * a; line.Y1 = 0; line.Y2 = 255; line.HorizontalAlignment = HorizontalAlignment.Left; line.VerticalAlignment = VerticalAlignment.Bottom; line.StrokeThickness = 0.5; chart.Children.Add(line); } } /* * jeżeli odczyt przez jakiś szum/błąd skoczy powyżej 255 (np. 300) to ta wartość zostanie zamieniona na 255 * jeżeli odczyt będzie poniżej 0 (np. -3) to ta wartość zostanie nadpisana na 0 * w tym kodzie rzadko się to zdarza */ public static UInt16 Clamp(UInt16 value, UInt16 min, UInt16 max) { return (value < min) ? min : (value > max) ? max : value; } private void Button_Click_2(object sender, RoutedEventArgs e) { if (draw == true) draw = false; //rysujemy; prosta flaga else if (draw == false) draw = true; } private void SliderWidthDelay_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) //zmiana odstepow miedzy punktami { dataWidth = Convert.ToUInt16(SliderWidthDelay.Value); chart.Children.Clear(); drawGird(); } private void Button_Click_3(object sender, RoutedEventArgs e) //wyczysc { chart.Children.Clear(); drawGird(); } } } |
Naprawdę jeżeli ktoś programował trochę to na pewno zrozumie o co w tym kodzie chodzi. Dodałem tam komentarze które mogą być pomocne.
Wskazówki do budowy
Sam nie będę budował dalej tego projektu (powstał w celach edukacyjnych) ale jeżeli ktoś będzie chciał to proponuję zastosować się do wskazówek.
- warto zaopatrzyć się w jakaś obudowę aby zniwelować wpływ fal z zewnątrz
- do sondy warto zastosować kabel i wtyk który zniweluje zakłócenia
- regulację prędkości wysyłania danych
- przejrzeć kod po swojemu i zmodyfikować go jeżeli uznacie że zwiększy to wydajność/zmniejszy zakłócenia
Kilka zdjęć projektu
Oczywiście złożony na płytce stykowej.
Tutaj kiedy kabelek od ADC jest w górze (działa jak antena :D) – znajdował się on w odległości 30 cm od wzmacniacza audio.
Mierzenie napięcia układu.
Przebieg prostokątny generowany przez układ NE555.
(ciekawostka) Ciekawy efekt kiedy żaden kabel do ADC nie jest podłączony.
Tutaj potencjometr z maksymalną odległością punktów. Jak widać w tym przypadku mieszczą się tylko dwa.
Projekt powstawał w celach naukowych z pomocą książki “Programowanie układów AVR dla praktyków” którą polecam (dostępna w BOTLANDZIE).
Zapraszam do oceny oraz komentowania ;)
Pozdrawiam!
Bardzo ciekawy projekt.
Planowałem kupić oscyloskop, lecz nie pozwalały mi na to fundusze…
…ale dzięki Twojemu projektowi postanowiłem w przyszłości sam zbudować podobny :)
Wielkie dzięki :)
Panie Hubercie…
Ogólnie projekt fajny, podoba mi się.
Dobrym pomysłem było by dodanie możliwość niezależnej regulacji tłumienia sondy (1x, 10x, 100x) dla każdego z kanałów co zmienia odpowiednio wartości napięcia na działkę, oraz zwiększenie zakresu mierzonych częstotliwości przebiegów w czasie ekwiwalentnym.
Ale i tak daję 5 :)
Dziękuje. Ten projekt został stworzony w celach edukacyjnych aby rozeznać się w tym temacie ;) Jeżeli będę tworzył w przyszłości drugi taki projekt zastosuję się do cennych uwag.
Witam
To samo na arduino pójdzie ?
Poza tym luks malina muszę sobie właśnie takie coś zrobić i to jak najszybciej 5 z plusem
Dziękuje ;) Niestety na Arduino tego nie testowałem ale nie widzę żadnych przeciwwskazań dla których miałoby to nie działać.
Tx i Rx to do rs232 ? Fajnie bo mam laptopa z rs to się w końcu przyda
Tak.
Nie przyda się. W “prawdziwym” RS 232 panują napięcia -/+ 12 V. Podłączysz pod to ATMEGĘ i odeślesz ją do krainy wiecznych łowów.
Wg mnie problemu nie ma dam stabilizator albo osobne zasilanie takie problemy to nie problemy
Napięcie -/+ 12 V to stany logiczne na kanałach RS232, Arduino obsługuje trochę odmienny standart – TTL którego stany logiczne wynoszą 0/5V dlatego do podłączenia do gniazda RS232 potrzebny jest konwerter stanów logicznych np MAX232
Witam,
Projekt idealny dla mnie, właśnie szukałem jakiejś tańszej wersji oscyloskopu. Mam pytanie co do przejściówki, na stronie jest napisane że:
Uwaga!
To urządzenie nie współpracuje z systemami Windows 8, 8.1 oraz 10.
Czy są jakieś alternatywy?
Niestety należy poszukać przejściówki która będzie zgodna z tym systemem lub zaimplementować inną komunikację (można podłączyć wyświetlacz LCD bezpośrednio ;))
Zainstalować system win7 XD osobno na jednym dysku Tak aby podczas włączania systemu przełączyć na 7
Bardzo ciekawy projekt.
Gdy już będziesz rozbudowywał swój projekt pomyśl o zewnętrznym taktowaniu a częstotliwość dobierz z tabeli z noty katalogowej. Chodzi o błąd przy przesyłaniu danych przez USART. Przy standardowych częstotliwościach 1,4,8, 16 MHz jest on stosunkowo bardzo duży do np. 18.4320MHz gdzie nawet przy dużym beatratcie można osiągnąć zerowy błąd.
Jak wgrać to do atmegi? – jestem trochę zielony
Przez środowisko Eclipse najlepiej.
A i jeszcze jedno mam problem z odpaleniem tego okienka do symulowania oscyloskopu. Jeśli jesteś w stanie opisać mi to szczegółowo i jakie potrzebne są dodatki w visual studio to będe wdzięczny .Przepraszam za kłopoty ale w programowaniu typowo AVR jestem zielony ;)
projekt VisualStudio\Scope\bin\Debug\Scope.exe
powinno się uruchomić bez problemów ;)
A ok bo ja całe środowisko visualstudio pobrałem i coś mi nie działało a to 1 pliczek trzeba było odpalić. Ok dzięki i sory za wszystkie kłopoty. Następnym razem będe umiał ;) sobie poradzić.
Miło było pomóc ;) Mam nadzieję że oscyloskop się przyda :)
Projekt prosty a jednak bardzo ciekawy jakby go rozbudować to można by mierzyć więcej wielkości
Temat nieco stary, ale zastanawia mnie jedna rzecz. Wysyłasz za pomocą uartu 1bajt co odpowiada 8 bitom rozdzielczości adc. Z jaką prędkością komunikuje się po rs żeby uzyskać 250khz? Dodam że jednostka bps to nie bajty a body na sekundę. Zalatuje mi to na niemały cud, nie biorąc już pod uwagę wait’ów których używasz w swoim kodzie.