I wanted to build something simpler, that everybody could test on its own oscillocope in 5 minutes, with just a little PIC and a few resistors.
Basic concept
For those are not into electronics, you must know that an oscilloscope has basicaly only one timebase to move the spot horizontaly from left to right with the same intensity. The vertical deviation is function to the input voltage. You understand immediately that you can't directly display 7 segment digits, because you can't move the spot from right to left.
By using X/Y mode, where the spot is controlled on two axes by two different voltages, it is possible to draw a picture (as in the examples mentioned above), but a fast digital to analog converter with two channels and at least 8 bits of resolution would be needed.
So we have to deal with a spot that always goes from left to right in the same period of time. If we want to have a 7segment-like display, we have to draw :
vertical segments : easy to do, just change voltage up and down quickly a few times.
horizontal segments : easy to do, just set a voltage level and keep it as long as you need.
By using 2 PIC outputs and a basic R2R digital to analog converter, we can have up to four different voltage levels : 3 for the vertical segment, and another one where to put the spot when it is not in use to draw a segment.
But the problem is that a 7 segment digit may have up to 3 horizontal lines at a time (like 2, 3, 8, 9..) but we can draw only one during one spot deviation. So we will have to cheat with retinal persistance and use multiple frames : since we can have only one vertical segment per period, three periods will be needed to draw a full 7 segment digit.
Supposing we want to display 12:34:56 on the screen :
During the first period, we will draw all vertical segments, and horizontal upper segments only :
As a game, I let you try to find out the spot trajectory. Don't forget the rules :
you can't go backward
you can't clear the spot
But you can move so fast verticaly that the eye can't see the spot moving.
The lowest line under the digits is not significant, it his the place where the spot is parked when not used to draw a segment.
first period
During the second period, we do exactly the same, with horizontal middle segments only :
second period
Then third period draws horizontal lower segments :
third period
This process is repeated again and again very quickly, and this is what the eyes can see, thanks to retinal persistance :
All periods superimposed
To be sure that the spot will trigger exactly when we want, a PIC output will give a synchronization pulse to the external trigger input of the oscilloscope.
CIRCUIT DIAGRAM
How simple ! It will work with any PIC with at least a PORTA and a PORTB. Here is how to do with a PIC16F84A :
Here is the mikroC source code. Since it is less than 900 words of program, it can be build with unlicenced mikroC (free, no licence key needed) :
/*
*******************************************************************************
* PICOCLOCK : PIC Oscilloscope CLOCK
*******************************************************************************
*
* This program shows how to display a digital clock on an oscilloscope
* with a PIC and only 4 resistors.
*
* Circuit schematics :
*
* ------------+
* RA0 +----------------> to oscilloscope X trigger input
* |
* RA1 +----------------> pull-up, button to ground : minutes adjustment
* RA2 +----------------> pull-up, button to ground : hours adjustment
* PIC | ____
* RB1 +----|____|-----+---------> to oscilloscope Y input
* | 680 |
* | +-+
* | | | 330
* | +-+
* | ____ |
* RB0 +----|____|-----+
* | 680 |
* ------------+ +-+
* | | 680
* +-+
* |
* -----
* --- GND
* -
*
* Oscilloscope setup :
* set timebase to 0.1 ms, V/div = 1 V
* select external trigger.
*
* source code for mikro C compiler V7.0.0.3
* feel free to use this code at your own risks
* and don't bother me if you get addicted watching this clock.
*
* target : PIC16 or PIC18, 16 Mhz crystal
* HS clock, no watchdog.
*
* tested with PIC16F84A and PIC16F877A
*
* Author : Bruno Gavand, October 2007
* see more details on http://www.micro-examples.com/
*
*******************************************************************************
*/
#define TRIGGER PORTA.F0 // this output is to be connected to oscilloscope trigger input
#define KEY PORTA & 0b110 // input keys mask
#define KEY_MIN_UP PORTA & 0b010 // minute adjust button
#define KEY_HH_UP PORTA & 0b100 // hour adjust button
/*
* 2 bits R2R DAC gives 4 output levels :
*/
#define HIGH PORTB = 0b11 // uper line
#define MID PORTB = 0b10 // middle line
#define LOW PORTB = 0b01 // lower line
#define ZERO PORTB = 0b00 // lowest line
#define MAX_SCALER 15625 // number of timer 0 overflow per second @ 16 Mhz = 16000000 / 4 / 256
#define MAX_DIGIT 6 // number of digits to be displayed
#define SLOTS (MAX_DIGIT * 3 + 4) // number of time slots : 2 for header, 3 per digits, 2 for trailer
/*
* 10 digits 7 segment encoding + blank
*/
constunsignedcharseptSeg[11]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
/*
* slot index for digit start
*/
constunsignedcharsIdx[]={1,4,8,11,15,18};
unsignedchardisplay[MAX_DIGIT];// digit to be displayed
/*
* time slot encoded line flags :
* bit 0 is upper line
* bit 1 is middle line
* bit 2 is lower line
* (if no line flag is set, spot is redirected to lowest line)
* bit 6 is lower vertical bar
* bit 7 is upper vertical bar
*/
unsignedcharline[SLOTS];
unsignedchardIdx=0;// time slot counter
unsignedcharfIdx=0;// frame counter
unsignedintscaler=0;// RTC scaler
unsignedcharss=0,mn=0,hh=0;// RTC
/*
* around 10 micro-second delay
*/
voiddelay10us()
{
Delay_us(10);
}
/*
* ISR
*/
voidinterrupt(void)
{
if(INTCON.T0IF)// if timer 0 overflow
{
scaler++;// increment scaler
if(scaler>MAX_SCALER)// one second has expired ?
{
scaler=0;// clear scaler
ss++;// next second
if(ss==60)// last second in minute ?
{
ss=0;// clear second
mn++;// next minute
if(mn==60)// last minute in hour ?
{
mn=0;// clear minute
hh++;// next hour
if(hh==24)// last hour in day ?
{
hh=0;// clear hour
}
}
}
}
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;
}
elseif(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;
}
elseif(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;
}
if(dIdx==7)// hour : minute separator
{
LOW,Delay10us();
MID,Delay10us();
}
elseif(dIdx==14)// minute : second separator
{
if(scaler<MAX_SCALER/2)// blink 0.5 Hz
{
LOW,Delay10us();
MID,Delay10us();
}
}
switch(fIdx)// depending on frame index
{
case0:// upper line
if(line[dIdx]&1)
{
HIGH;
}
else
{
ZERO;
}
break;
case1:// middle line
if(line[dIdx]&2)
{
MID;
}
else
{
ZERO;
}
break;
case2:// lower line
if(line[dIdx]&4)
{
LOW;
}
else
{
ZERO;
}
break;
}
dIdx++;// next slot
if(dIdx==SLOTS)// last slot ?
{
dIdx=0;// clear slot
TRIGGER=1;// triggers the scope
fIdx++;// next frame
if(fIdx==3)// last frame ?
{
fIdx=0;// clear frame
}
TRIGGER=0;// end trigger
}
INTCON.T0IF=0;// clear timer 0 overflow
}
}
/*
* main entry
*/
voidmain()
{
#ifdef P16F877A
/*
* set PORTA as digital I/O
*/
ADCON1=7;
CMCON=7;
#endif
TRISA=0b110;// PORTA direction register
PORTA=0;
TRISB=0;// PORTB is output
PORTB=0;
/*
* clear buffers
*/
memset(&line,0,sizeof(line));
memset(display,0,sizeof(display));
OPTION_REG=0b11011000;// timer 0 prescaler is 1:1
INTCON=0b10100000;// start interrupts
for(;;)// main loop
{
unsignedchari;
if(KEY)// is a button pressed ?
{
if(KEY_MIN_UP)// adjust minutes
{
ss=0;
mn++;
}
elseif(KEY_HH_UP)// adjust hours
{
ss=0;
hh++;
}
mn%=60;// prevent minute overflow
hh%=24;// prevent hours overflow
Delay_ms(100);// debounce
}
/*
* prepare display buffer
*/
display[5]=ss%10;// seconds
display[4]=ss/10;
display[3]=mn%10;// minutes
display[2]=mn/10;
display[1]=hh%10;// hours
display[0]=(hh>9)?hh/10:10;// blank first digit if zero
/*
* prepare time slot flags
*/
for(i=0;i<MAX_DIGIT;i++)// for each digit
{
unsignedchars;
unsignedchar*p;
s=septSeg[display[i]];// get 7 segment encoding
p=&line[sIdx[i]];// get pointer to time slot, left part of the digit
(*p).F0=s.F0;// a segment
(*p).F1=s.F6;// g segment
(*p).F2=s.F3;// d segment
(*p).F6=s.F4;// e segment
(*p).F7=s.F5;// f segment
p++;// next slot, center part of the digit
(*p).F0=s.F0;// a segment (continuation)
(*p).F1=s.F6;// g segment (continuation)
(*p).F2=s.F3;// d segment (continuation)
p++;// next slot, right part of the digit
(*p).F6=s.F2;// b segment
(*p).F7=s.F1;// c segment
}
}
}
Instruction for use
Connect X1 (see circuit diagram above) to the Y input (vertical deviation) of the oscilloscope
Connect X2 (see circuit diagram above) to the external trigger input of the oscilloscope
Set on your oscilloscope :
timebase to 0.1 ms
vertical deviation 1/V division
external trigger
Power the circuit : display starts with 0:00:00
Press hours button to change hours
Press minutes button to change minutes
Enjoy !
Please add comments, suggestions and report bugs to me in my forums
All trademarks and registered trademarks are the property of their respective owners