//*********************************************************** //* * //* AVR Osciloscope * //* * //* with ATmega16 and matrix display 128 x 64 * //* * //* by xzeman33 & xmilek00 * //*********************************************************** // // Vyuziva minimum externiho hardwaru. // S vnitrnim ADC ATmega16 a krystalem 16MHz lze dosahnout vzorkovaciho kmitoctu necelych 10kS/s, // Osciloskop je tedy realne pouzitelny do cca 1-2 kHz kmitoctu mereneho signalu. // Z vnitrniho 10bitoveho ADC prevodniku je odebirano pouze 8 nejvyznamnejsich bitu. // Vzhledem k vertikalni rozliseni displeje (64 bodu), neni treba vyssi presnosti // Datailni popis fungovani osciloskopu lze naleznout cetne fotografii v prilozene zprave. #include #include #include #define F_OSC 16000000 // frekvence krystalu #include "delay.c" // knihovna umoznujujici jednoduse definovat zpozdeni #include "ks0108.c" // knihovna pro LCD s radicem KS0108 - pro jiny radic LCD je nutno tuto knihovnu nahradit. // !!! POZOR !!! nejedna se o identickou kopii knihovny pouzivane ve cvicenich MMIA. // Jsou zde predefinovany porty pro komunikaci s displejem. // Displej musi byt vlozen do konektoru vyvojove desky obracene, // aby bylo mozne pouzit PORTA jako vstup ADC. // PORTB slouzi pro rizeni komunikace, PORTC je datovy. short int viewport_top = 0; short int viewport_left = 0; short int viewport_right = 123; short int viewport_bottom = 63; #include "avrlcd_ctrls.h" // knihovna pro praci s ovladacimi prvky #include "font.h" // datova struktura void normalizace (void); void vykresli_prubeh (void); void vykresli_meritka (void); short int data[100]; short int counter, max, min, delitel, Ypozice, Ypozice_pred, clr_counter; //------------------------------------------------------------ //| Obsluhy preruseni | //------------------------------------------------------------ ISR(ADC_vect) // obsluha preruseni od interniho ADC { data[counter] = ADCH; // hodnota z ADC je ulozena na aktualni pozici do matice vzorku // |MINIMUM, MAXIMUM| if (max < data[counter]) // hledani nejvyssi hodnoty vst.signalu v ramci 100 vzorku (pro funkci autoscale a dalsi mozna rozsireni) max = data[counter]; // pokud je aktualni hodnota vetsi nez dosavadni maximum, maximum se aktualizuje if (min > data[counter]) // hledani nejnizsi hodnoty vst.signalu v ramci 100 vzorku (pro dalsi mozna rozsireni) min = data[counter]; // pokud je aktualni hodnota mensi nez dosavadni minimum, minimum se aktualizuje // |JEDNODUCHY TRIGGER| // hledajici minimum (neni to optimalni, ale jednoduche a funkcni) if (counter > 0) // zjisti se, zdali se nejedna o prvni navzorkovanou hodnotu (ze 100 vzorku), { if ((data[counter-1] > data[counter]) && clr_counter == 0) // nejedna-li se o prvni vzorek, zjisti se, jestli prubeh vstupniho signalu klesa a zaroven jestli jiz nebylo pocitadlo vzorku vyresetovano { clr_counter = 1; // pri splneni predchozi podminky, dojde k povoleni vyreset pocitadlo } if ((data[counter-1] < data[counter]) && clr_counter == 1) // jakmile prubeh vstupniho signalu zacne stoupat a zaroven je povoleno vyresetovani pocitadla, je nalezeno lokalni minimum - okamzik synchronizace { clr_counter = 2; // zamezi se dalsimu vyresetovani pocitadla (pro nasledujicich 100 vzorku) counter = 0; // pocitadlo se vyresetuje data[counter]=ADCH; // vzorek se ulozi na prvni pozici } } counter++; // pocitadlo vzorku se inkrementuje (priprava pro prijeti dalsiho vzorku) } //------------------------------------------------------------ //| Hlavni programova smycka | //------------------------------------------------------------ void main (void) { ks0108ClearScreen(); ks0108SetReset(1); // Reset -> nastavenim na HIGH, tak musi i zustat delay_ms(1000); // Cekani na nabehnuti LCD ks0108Init(NON_INVERTED); // Inicializace LCD current_font = font13; // prirazeni fontu DDRA = 0x00; // PORTA -> vstupni ADMUX |= (1< ADC0; ADLAR - zarovnani vystupu ADC vlevo ADCSRA |= (1<128) SFIOR = 0x00; // ADTS2:0: ADC Auto Trigger Source -> Free running mode (nejvyssi rychlost vzorkovani - 1 vz. za 13 taktu 125 kHz oscilatoru ~ fvz = 9,615 kS/s) sei(); // povoleni globalniho preruseni while (1) // nekonecna smycka { if (counter == 100) // cekani az se ziska 100 vzorku (protoze vykreslujeme vsechny vzorky naraz) { ADCSRA &= (0<= viewport_bottom) // pokud je maximum (vst.sign.) vetsi nez vertikalni rozliseni displeje, delitel = delitel << 1; // dojde ke zdvojnasobovani hodnoty delitele (meritka vertikalni osy), dokud se prubeh na displej nevejde // delitel se meni v krocich 1-2-4-8-16... (bitovy posuv vlevo) } // |VYKRESLOVANI KRIVKY A RASTRU| void vykresli_prubeh (void) // funkce vykreslujici 100 namerenych vzorku { for (counter = 1 ; counter < 100; counter++) // cyklus pro vykresleni prubehu (vzorky 1 - 100) { line (counter, 1, counter, 62, 0); // vymazani predesleho prubehu pomoci vertikalni cary bile barvy (rychlejsi nez mazat cely displej) if ((counter % 10) == 0) // vykresleni rastru po 10 pixelech horizontalne { char i; // vykresleni rastru po 10 pixelech vertikalne (kresli se od pozice 63, protoze tato je brana jako nulova hladina vstupu) for (i = 63; i > 0; i -= 10) pixel (counter, i, 1); } Ypozice = 63 - data[counter]/delitel; // vypocet Y pozice aktualniho bodu (odecita se od 63, aby byla nulova hladina vespod displeje) Ypozice_pred = 63 - data[counter-1]/delitel; // vypocet Y pozice predesleho bodu - krivka je vykreslovana pomoci usecek mezi dvema vzorky, proto potrebujem znat predchozi pozici line (counter-1, Ypozice_pred, counter, Ypozice, CL_BLACK); // vykresleni spojnice predchozi hodnoty s aktualni - proto aby byl prubeh souvisly i pri rychle se menicich signalech } } // |VZKRESLOVANI POPISKU| void vykresli_meritka (void) // funkce pro vykresleni V a H meritek { if (delitel == 1) // na zaklade delitele se rozhodne kolik mV pripada na dilek (10 px) ctrl_edit(105, 1, 17, 12, "100", CTRL_NORMAL); // pro delitel 1 je to 100 mV / div (256 ~ Vref = 2,56V; 10 ~ Vref/256*10 =0,1V) else if (delitel == 2) ctrl_edit(105, 1, 17, 12, "200", CTRL_NORMAL); // pro delitel 2 je to cca 200 mV else if (delitel == 4) ctrl_edit(105, 1, 17, 12, "400", CTRL_NORMAL); // pro delitel 4 je to cca 400 mV else ctrl_edit(105, 1, 17, 12, "Err", CTRL_NORMAL); // pro delitel jiny vypise "Err" - jiny stav neni ocekavan (max hodnota delitele je 4 (256/4=64 - toto displej zvaldne)) draw_text(103,14, 17,12, "mV/d", DT_NORMAL); // vypise jednotku meritka ctrl_edit(105, 25, 17, 12, "1.00", CTRL_NORMAL); // casova zakladna je pevna, proto je zobrazena pouze hodnota 1.00 ms/div draw_text(103,38, 17, 12, "ms/d", DT_NORMAL); }