(→Download project) |
(→PicOscilloMeter: A direct oscilloscope reading frequency/volt meter) |
||
Line 4: | Line 4: | ||
A short video clip is sometimes better than a long explanation : | A short video clip is sometimes better than a long explanation : | ||
{{#ev:youtube|36m5Q_BKYrU&list}} | {{#ev:youtube|36m5Q_BKYrU&list}} | ||
+ | |||
+ | This project shares the basic of the [[PicoClock]] project, to use a simple 8 bit 8 pin microcontroller to print a message on an oscilloscope screen. | ||
+ | |||
+ | The enhancement are : | ||
+ | * circuit can display alphanumeric from 0-9 and A-Z characters | ||
+ | * circuit can work as frequency meter from 1 Hz to 1.5 MHz | ||
+ | * circuit can work as voltmeter from 0 to 5V | ||
+ | * a push button toggles frequency/volt meter | ||
==Circuit Schematic== | ==Circuit Schematic== |
Contents[hide] |
A short video clip is sometimes better than a long explanation :
This project shares the basic of the PicoClock project, to use a simple 8 bit 8 pin microcontroller to print a message on an oscilloscope screen.
The enhancement are :
/* ******************************************************************************* * PICOSCILLOMETER : PIC Oscilloscope Meter ******************************************************************************* * * This program show how to do a direct reading on an oscillocope * to build a frequency meter and a voltmeter * with a PIC and only 4 resistors. * * Circuit schematic : * * ------------+ * GP2 +----------------> to oscilloscope X trigger input * | * PIC | ____ * GP1 +----|____|-----+---------> to oscilloscope Y input * | 680 | * | +-+ * | | | 680 * | +-+ * | ____ | * GP0 +----|____|-----+ * | 680 | * ------------+ +-+ * | | 680 * +-+ * | * ----- * --- GND * - * * Oscilloscope setup : * set timebase to 0.5 ms, V/div = 2 V * select external trigger. * * source code for mikro C PRO compiler V5.30 * feel free to use this code at your own risks * * target : PIC12F683 * internal 8 MHz clock, no watchdog. * * Author : Bruno Gavand, february 2012 * see more details on http://www.micro-examples.com/ * ******************************************************************************* */ #include "built_in.h" /* * 2 bits R2R DAC gives 4 output levels : */ #define HIGH GPIO = 0b11 // uper line #define MID GPIO = 0b10 // middle line #define LOW GPIO = 0b01 // lower line #define ZERO GPIO = 0b00 // lowest line #define MAX_DIGIT 15 // number of digits to be displayed #define SLOTS (MAX_DIGIT * 3) // number of time slots, 3 per digit /* * to display text, we need to invent 12 segment digits * SEGMENT NAME : A H ----- ----- | | | F| |B |I | G | L | ----- ----- | | | E| |C |J | | | ----- ----- D K SEGMENT ENCODING : */ #define sA 0x001 #define sB 0x002 #define sC 0x004 #define sD 0x008 #define sE 0x010 #define sF 0x020 #define sG 0x040 #define sH 0x080 #define sI 0x100 #define sJ 0x200 #define sK 0x400 #define sL 0x800 /* * DIGIT ENCODING : */ const unsigned int twelveSeg[] = { sH + sI + sJ + sK + sC + sB, // 0 sI + sJ, // 1 sH + sI + sL + sC + sK, // 2 sH + sI + sL + sJ + sK, // 3 sB + sL + sI + sJ, // 4 sH + sB + sL + sJ + sK, // 5 sH + sB + sC + sK + sJ + sL, // 6 sH + sI + sJ, // 7 sH + sI + sJ + sK + sC + sB + sL, // 8 sL + sB + sH + sI + sJ + sK, // 9 0, // blank 10 sE + sF + sA + sH + sI + sJ + sG + sL, // a 11 sF + sA + sB + sG + sL + sJ + sK + sD + sE, // b sH + sA + sF + sE + sD + sK, // c sI + sL + sG + sE + sD + sK + sJ, // d sH + sA + sF + sG + sE + sD + sK, // e sA + sH + sF + sG + sE, // f sA + sF + sE + sD + sK + sJ + sL, // g sF + sE + sG + sL + sI + sJ, // h sB + sC, // i sI + sJ + sK + sD, // j sE + sF + sG + sL + sI + sC, // k sF + sE + sD + sK, // l sE + sF + sA + sB + sC + sH + sI + sJ, // m sC + sB + sH + sI + sJ, // n sA + sH + sI + sJ + sK + sD + sE + sF, // o sE + sF + sA + sH + sI + sL + sG, // p sJ + sI + sH + sA + sF + sG + sL, // q sE + sF + sA + sH + sI + sL + sG + sC, // r sA + sF + sG + sL + sJ + sK + sD + sH, // s sA + sH + sB + sC, // t sB + sC + sK + sJ + sI, // u sF + sE + sD + sC + sB + sH, // v sF + sE + sD + sC + sB + sK + sJ + sI, // w sA + sH + + sB + sC + sD + sK, // x sF + sG + sL + sI + sJ + sK + sD, // y sA + sB + sC + sK, // z } ; unsigned char display[MAX_DIGIT] ; // text to be displayed /* * time slot flags : * bit 0 is upper horizontal line (segments A or H) * bit 1 is middle horizontal line (segments G or L) * bit 2 is lower horizontal line (segments D or K) * (if no line flag is set, spot is redirected to lowest line) * bit 6 is lower vertical bar (segments E, C or J) * bit 7 is upper vertical bar (segments F, B or I) */ unsigned char line[SLOTS] ; unsigned char dIdx = 0 ; // time slot counter unsigned char fIdx = 0 ; // frame counter #define TICKS_PER_SEC ((Clock_KHz() * 1000) / 4 / 256) // number of ticks per second unsigned int scaler ; // ticks per second counter unsigned char t1roll ; // timer 1 rollover counter unsigned long t1ctr = 0 ; // timer 1 ticks per second counter unsigned char freqVolt = 0 ; // frequency or volt meter function // welcome message const char welcomeMsg[] = " welcome " ; const char freqMsg[] = " freq meter " ; const char voltMsg[] = " volt meter " ; /* * ISR */ void interrupt(void) { if(INTCON.T0IF) // if timer 0 overflow { if(line[dIdx].F6 && line[dIdx].F7) // if full vertical bar { LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; LOW, HIGH, LOW, HIGH ; } else if(line[dIdx].F6) // if lower vertical bar { MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; MID, LOW, MID, LOW ; } else if(line[dIdx].F7) // if upper vertical bar { MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; MID, HIGH, MID, HIGH ; } switch(fIdx) // depending on frame index { case 0: // upper horizontal line if(line[dIdx] & 1) { HIGH ; } else { ZERO ; } break ; case 1: // middle horizontal line if(line[dIdx] & 2) { MID ; } else { ZERO ; } break ; case 2: // lower horizontal line if(line[dIdx] & 4) { LOW ; } else { ZERO ; } break ; } dIdx++ ; // next slot if(dIdx == SLOTS) // last slot ? { GPIO.F2 = 1 ; // new frame, triggers the X scope entry dIdx = 0 ; // clear slot fIdx++ ; // next frame if(fIdx == 3) // last frame ? { fIdx = 0 ; // clear frame } } scaler++ ; if(scaler == TICKS_PER_SEC) // check for one second { // get TIMER 1 ticks count T1CON.TMR1ON = 0 ; // stop timer Lo(t1ctr) = TMR1L ; // read timer Hi(t1ctr) = TMR1H ; Higher(t1ctr) = t1roll ; // read timer rollovers TMR1L = 0 ; // reset timer TMR1H = 0 ; t1roll = 0 ; // reset rollover T1CON.TMR1ON = 1 ; // start timer scaler = 0 ; // reset counter } INTCON.T0IF = 0 ; // clear timer 0 overflow } if(PIR1.TMR1IF) // if timer 1 overflow { t1roll++ ; // inc rollover counter PIR1.TMR1IF = 0 ; // clear timer 1 overflow } GPIO.F2 = 0 ; // end trigger } /* * returns 12 segment encoding for character cc */ unsigned char mkdigit(const char cc) { unsigned char c ; c = tolower(cc) ; if((c >= '0') && (c <= '9')) { return(c - '0') ; } if((c >= 'a') && (c <= 'z')) { return(c - 'a' + 11) ; } return(10) ; // space if not printable with our encoding table } /* * build time slots from display string */ mkTimeSlots() { unsigned int i ; unsigned char *p ; /* * prepare time slot flags */ p = line ; for(i = 0 ; i < MAX_DIGIT ; i++) // for each digit { unsigned int s ; unsigned char sl, sh ; s = twelveSeg[display[i]] ; // get 7 segment encoding sl = Lo(s) ; sh = Hi(s) ; (*p).F7 = sl.F5 ; // f segment (*p).F6 = sl.F4 ; // e segment (*p).F0 = sl.F0 ; // a segment (*p).F1 = sl.F6 ; // g segment (*p).F2 = sl.F3 ; // d segment p++ ; // next slot, center part of the digit (*p).F6 = sl.F2 ; // b segment (*p).F7 = sl.F1 ; // c segment (*p).F0 = sl.F7 ; // h segment (*p).F1 = sh.F3 ; // l segment (*p).F2 = sh.F2 ; // k segment p++ ; // next slot, right part of the digit *p = 0 ; (*p).F6 = sh.F1 ; // i segment (*p).F7 = sh.F0 ; // j segment p++ ; } } /* * message scrolling */ void displayMsg(const unsigned char *msg) { unsigned char i ; i = 0 ; while(*msg) { display[i++] = mkdigit(*msg++) ; } mkTimeSlots() ; Delay_ms(1000) ; } /* * main entry */ void main() { unsigned char i ; unsigned long val ; OSCCON |= 0b01110000 ; // select 8 Mhz internal oscillator OSCTUNE = 0 ; // default oscillator calibration CMCON0 = 7 ; // no comparator TRISIO = 0b111000 ; // pin direction ANSEL = 0b010000 ; // configure GPIO 4 for DAC /* * clear buffers */ for(i = 0 ; i < sizeof(line) ; i++) { line[i] = 0 ; } for(i = 0 ; i < sizeof(display) ; i++) { display[i] = 0 ; } // TIMER 1 : T1GINV(1) TMR1GE(2) T1CKPS1 T1CKPS0 T1OSCEN T1SYNC TMR1CS TMR1ON T1CON = 0b00000011 ; // external clock // OPTION_REG : GPPU INTEDG T0CS T0SE PSA PS2 PS1 PS0 OPTION_REG = 0b11011000 ; // enables timer 1 rollover interrupt PIE1.TMR1IE = 1 ; PIR1.TMR1IF = 0 ; // INTCON : GIE PEIE T0IE INTE GPIE T0IF INTF GPIF INTCON = 0b11100000 ; // start interrupts displayMsg(welcomeMsg) ; for(;;) // main loop { /* * toggle frequency/volt meter */ if(GPIO.F3 == 0) { freqVolt ^= 1 ; if(freqVolt) { displayMsg(freqMsg) ; } else { displayMsg(voltMsg) ; } while(GPIO.F3 == 0) ; Delay_ms(200) ; } memset(display, 10, MAX_DIGIT) ; // clear display buffer if(freqVolt) { // frequency meter val = t1ctr ; if(t1ctr >= 1000000L) // automatic range { val /= 1000L ; // display in KHz if frequency > 1 MHz display[10] = mkdigit('k') ; display[11] = mkdigit('h') ; display[12] = mkdigit('z') ; } else { display[10] = mkdigit('h') ; // display in Hz display[11] = mkdigit('z') ; } display[2] = (val / 100000) % 10 ; // convert decimal to digits display[3] = (val / 10000) % 10 ; display[4] = (val / 1000) % 10 ; display[5] = 10 ; // thousands separator display[6] = (val / 100) % 10 ; display[7] = (val / 10) % 10 ; display[8] = (val / 1) % 10 ; /* * clear leading 0s */ if(display[2] == 0) { display[2] = 10 ; if(display[3] == 0) { display[3] = 10 ; if(display[4] == 0) { display[4] = 10 ; if(display[6] == 0) { display[6] = 10 ; if(display[7] == 0) { display[7] = 10 ; } } } } } } else { /* * volt meter */ val = Adc_Read(3) ; // read ADC channel 3 val *= 5000 ; // convert 12 bits reading to voltage val /= 1024 ; if(val < 1000) // automatic range { /* * display mV */ display[6] = (val / 100) % 10 ; display[7] = (val / 10) % 10 ; display[8] = (val / 1) % 10 ; /* * clear leading 0s */ if(display[6] == 0) { display[6] = 10 ; if(display[7] == 0) { display[7] = 10 ; } } display[9] = mkdigit(' ') ; display[10] = mkdigit('m') ; display[11] = mkdigit('v') ; } else { /* * display V */ display[4] = (val / 1000) % 10 ; display[5] = mkdigit('v') ; display[6] = (val / 100) % 10 ; display[7] = (val / 10) % 10 ; display[8] = (val / 1) % 10 ; } } mkTimeSlots() ; // prepare time slot Delay_ms(100) ; // short delay for display to be comfortable } }
Download PicOscilloMeter-project.ZIP file for mikroC : File:PicOscilloMeter-project.zip
Includes :
Current user rating: 89 (32 votes)
You didn't vote on this yet.
More | powered by commenterra | Recent comments Show all comments |
precompiled HEX-File: Config Word = 0x0014 (Code Protection set..)
recompiled Source Fie: Config Word = 0x0FF2
Which one is correct?
When I connect the Outputs to an Oscilloscope with external Trigger from X and Chan1 from Y, I can see a steady wave containing 4 dedicated Voltage levels, but no readable Text. What is wrong?
configuration word value 0x0014 is correct for PIC12F683, as it is in the binary file
please note that the oscilloscope must not be set in X/Y configuration but in external trigger mode&|60;: GP2 output pin is connected to the trigger input of your oscillo to get correct horizontal synchronization
timebase should be 0.5 ms/div
hope this will help
with my old analogue Oscilloscope I now get readable outputs, much better than using a digital Oszilloscope.
I also changed R3 from 680 Ohm to 330 Ohm to achieve the right R2R Voltage Levels.