www.micro-examples.com : PicOscilloMeter

Views
From www.micro-examples.com
Jump to: navigation, search

How to display text on an oscilloscope ?

PicOscilloMeter screen capture

Contents

PicOscilloMeter: A direct oscilloscope reading frequency/volt meter

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 :

  • 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 function

Circuit Schematic

PicOscilloMeter-schematic.png

  • a simple 2 bit digital to analog converter R2R ladder generates a 4 levels signal for vertical deviation of the spot
  • GP2 ouput generates horizontal sync
  • frequency meter input : 0...5V signal, from 1 to 1.5 MHz

C Source code

/*
 *******************************************************************************
 * PICOSCILLOMETER : PIC Oscilloscope Meter
 *******************************************************************************
 *
 * This program shows 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
                }
        }

Don't expect to get a high-precision frequency meter, since the PIC is clocked from the internal oscillator !

Download project

Download PicOscilloMeter-project.ZIP file for mikroC : File:PicOscilloMeter-project.zip

Includes :

  • mikroC PRO project files for PIC12F683, should work also with most of PIC
  • PicOscilloMeter C source code
  • PicOscilloMeter ready to flash .HEX files

Discussion and comments

Current user rating: 89/100 (32 votes)

 You need to enable JavaScript to vote

You need JavaScript enabled for viewing comments

Navigation
Others
Donation
You can help :
with Paypal
Share
Personal tools
www.micro-examples.com Electronic circuits with micro-controllers, projects with source code examples