Here is another clock... but what kind of clock !
Take a piece of wood, and drill 27 5mm holes.
Insert a LED into each hole.
Take a Dymo labeler, print a few numbers and stick them to the board.
Wire the LEDs, connect them to a PIC... That's all !
Insert a LED into each hole.
Take a Dymo labeler, print a few numbers and stick them to the board.
Wire the LEDs, connect them to a PIC... That's all !
This example will show you how to connect up to 30 LEDs to a PIC, using only six I/O pins.
I wanted to build a clock as simple as possible :
- built around a little 18 pins PIC
- no 7 segment display, only LEDs
- no decoder, no buffer, no driver for the LED display
- a cheap temperature sensor
The solution of direct LED driving comes from a Microchip Application Note AN234, and as I'm using 25 mA LEDs, I simply removed all current limiting resistors.
A silicon diode is used as temperature sensor, and the rest of the circuit is very classic around a PIC16F819 :
A silicon diode is used as temperature sensor, and the rest of the circuit is very classic around a PIC16F819 :
Click on the schematic to enlarge
Upper row : hours
Middle row : minutes or seconds or degrees C
Lower row : add it to middle row
For example, the clock is displaying 5 hours and 23 minutes
The wiring side of the LEDs is a little bit messy,
some colored wires are helpful !
See the source code for LED numbering.
THE SOURCE CODE
*************************************************************************
* file : dymoclock.c
* project : Simple LED clock with thermometer
* author : Bruno Gavand
* compiler : mikroC V6.0.0.0
* date : september 15, 2006
*
* description :
* This is a 12 hours clock with 27 LEDs display, with a 2°C resolution thermometer
*
* target device :
* PIC16F819 with 16 Mhz crystal
*
* configuration bits :
* HS clock
* no watchdog
* no power up timer
* RA5 as MCLR pin
* brown out detect
* LVP disabled
* data EE protect disabled
* ICD disabled
* CCP1 pin on RB2
*
*************************************************************************
*/
/*
* display modes
*/
#define MODE_HOURMN 0 // display hours:minutes
#define MODE_SS 1 // display seconds
#define MODE_TEMP 2 // display temperature
#define MAX_MODE 3 // number off display modes
/*
* buttons
*/
#define BUTTON ((PORTA.F1 == 0) || (PORTA.F2 == 0)) // at least one
#define BUTTON_MODE (PORTA.F1 == 0) // mode / advance
#define BUTTON_TOGGLE (PORTA.F2 == 0) // toggle / valid
#define BUTTON_SET ((PORTA.F1 == 0) && (PORTA.F2 == 0)) // both at the same time
#define TEMP_REF 115 // silicon junction offset : 600 mV
#define MAX_TEMP 20 // number of temperature samples
/*
* LED multiplexing tables
*
* LED index : 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
* LED number : 1 2 3 4 9 10 5 6 11 12 25 30 7 8 5 10 35 40 45 50 2 1 15 20 3 4 55
* LED hours : 1 2 3 4 5 6 7 8 9 10 11 12
* LED min/deg : 5 10 15 20 25 30 35 40 45 50 55
* LED 1234 : 1 2 3 4
*/
/*
* upper row : hours from 1 to 12
*/
const unsigned char hhTable[] = { 0, 1, 2, 3, 4, 7, 8, 13, 14, 5, 6, 9, 10 } ;
/*
* middle row : minutes/seconds/°C from 5 to 55 step 5
*/
const unsigned char mnTableH[] =
{
0, 0, 0, 0, 0,
15, 15, 15, 15, 15,
16, 16, 16, 16, 16,
23, 23, 23, 23, 23,
24, 24, 24, 24, 24,
11, 11, 11, 11, 11,
12, 12, 12, 12, 12,
17, 17, 17, 17, 17,
18, 18, 18, 18, 18,
19, 19, 19, 19, 19,
20, 20, 20, 20, 20,
27, 27, 27, 27, 27
} ;
/*
* lower row : increment of minutes/seconds/°C from 1 to 4
*/
const unsigned char mnTableL[] =
{
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26,
0, 22, 21, 25, 26
} ;
/*
* RAM variables
*/
unsigned char mode ; // display mode
unsigned char toggleMode ; // toggle mode flag
unsigned int scaler = 0 ; // timer overflow divider
unsigned char hh = 1 ; // hours
unsigned char mn = 1 ; // minutes
unsigned char ss = 0 ; // seconds
int temp ; // temperature in °C
unsigned char tdeg[MAX_TEMP] ; // array of temperatures samples
unsigned char tidx = 0 ; // index of the temperature sample
/*
* interrupt routine service
* called Fosc / 4 / 256 times per second
*/
void interrupt(void)
{
if(INTCON.TMR0IF) // timer overflow ?
{
scaler++ ; // increment scaler
if(scaler == 15625) // one second has expired ?
{
scaler = 0 ; // reset scaler
ss++ ; // next second
if(ss == 60) // one minute has expired ?
{
ss = 0 ; // reset seconds
mn++ ; // next minute
if(mn == 60) // one hour has expired ?
{
mn = 0 ; // reset minutes
hh++ ; // next hour
if(hh == 13) // 12 hours mode
{
hh = 1 ; // back to first hour
}
}
}
}
INTCON.TMR0IF = 0 ; // clear interrupt flag
}
}
/*
* light the LED number n during d milliseconds
*/
void dymoLight(const unsigned char n, unsigned char d)
{
switch(n)
{
case 0 : TRISB = 0b11111111 ; PORTB = 0b00000000 ; break ;
case 1 : TRISB = 0b11111100 ; PORTB = 0b00000001 ; break ;
case 2 : TRISB = 0b11111100 ; PORTB = 0b00000010 ; break ;
case 3 : TRISB = 0b11111010 ; PORTB = 0b00000001 ; break ;
case 4 : TRISB = 0b11111010 ; PORTB = 0b00000100 ; break ;
case 5 : TRISB = 0b11111001 ; PORTB = 0b00000010 ; break ;
case 6 : TRISB = 0b11111001 ; PORTB = 0b00000100 ; break ;
case 7 : TRISB = 0b11110110 ; PORTB = 0b00000001 ; break ;
case 8 : TRISB = 0b11110110 ; PORTB = 0b00001000 ; break ;
case 9 : TRISB = 0b11110101 ; PORTB = 0b00000010 ; break ;
case 10 : TRISB = 0b11110101 ; PORTB = 0b00001000 ; break ;
case 11 : TRISB = 0b11110011 ; PORTB = 0b00000100 ; break ;
case 12 : TRISB = 0b11110011 ; PORTB = 0b00001000 ; break ;
case 13 : TRISB = 0b11101110 ; PORTB = 0b00000001 ; break ;
case 14 : TRISB = 0b11101110 ; PORTB = 0b00010000 ; break ;
case 15 : TRISB = 0b11101101 ; PORTB = 0b00000010 ; break ;
case 16 : TRISB = 0b11101101 ; PORTB = 0b00010000 ; break ;
case 17 : TRISB = 0b11101011 ; PORTB = 0b00000100 ; break ;
case 18 : TRISB = 0b11101011 ; PORTB = 0b00010000 ; break ;
case 19 : TRISB = 0b11100111 ; PORTB = 0b00001000 ; break ;
case 20 : TRISB = 0b11100111 ; PORTB = 0b00010000 ; break ;
case 21 : TRISB = 0b11011110 ; PORTB = 0b00000001 ; break ;
case 22 : TRISB = 0b11011110 ; PORTB = 0b00100000 ; break ;
case 23 : TRISB = 0b11011101 ; PORTB = 0b00000010 ; break ;
case 24 : TRISB = 0b11011101 ; PORTB = 0b00100000 ; break ;
case 25 : TRISB = 0b11011011 ; PORTB = 0b00000100 ; break ;
case 26 : TRISB = 0b11011011 ; PORTB = 0b00100000 ; break ;
case 27 : TRISB = 0b11010111 ; PORTB = 0b00001000 ; break ;
case 28 : TRISB = 0b11010111 ; PORTB = 0b00100000 ; break ;
case 29 : TRISB = 0b11001111 ; PORTB = 0b00010000 ; break ;
case 30 : TRISB = 0b11001111 ; PORTB = 0b00100000 ; break ;
}
VDelay_ms(d) ;
}
/*
* set clock time
*/
void setTime()
{
INTCON.GIE = 0 ; // stop time counting
hh = 1 ; // set hours
for(;;)
{
dymoLight(hhTable[hh], 20) ; // light hour
if(BUTTON_MODE) // advance hour ?
{
VDelay_ms(15) ; // debounce button
while(BUTTON_MODE) ;
VDelay_ms(15) ;
hh++ ; // next hour
if(hh > 12) // overflow ?
{
hh = 1 ; // back to first hour
}
}
if(BUTTON_TOGGLE) // valid ?
{
VDelay_ms(15) ;
while(BUTTON_TOGGLE) ; // debounce button
VDelay_ms(15) ;
break ; // exit loop
}
}
mn = 1 ; // set minutes
for(;;)
{
dymoLight(mnTableH[mn], 10) ; // light minutes, upper row
dymoLight(mnTableL[mn], 10) ; // light minutes, lower row
if(BUTTON_MODE) // advance minute ?
{
VDelay_ms(15) ; // debounce button
while(BUTTON_MODE) ;
VDelay_ms(15) ;
mn++ ; // next minute
if(mn > 59) // overflow ?
{
mn = 0 ; // back to first minute
}
}
if(BUTTON_TOGGLE) // valid ?
{
VDelay_ms(15) ; // debounce button
while(BUTTON_TOGGLE) ;
VDelay_ms(15) ;
break ; // exit loop
}
}
ss = 0 ; // reset seconds
INTCON.GIE = 1 ; // start clock
toggleMode = 0 ; // no toggle
mode = MODE_HOURMN ; // display hours:minutes
}
/*
* main entry
*/
void main()
{
unsigned int loopCtr = 0 ; // counter for temperature sampling delay
unsigned int toggleCtr = 0 ; // counter for toggle delay
// OSCCON |= 0b01110000 ; // untag if you use internal RC clock, 8Mhz
ADCON1 = 0b00001110 ; // RA0 as analog input, other input as digital I/O
TRISA = 0b11111111 ; // all PORTA as inputs
PORTA = 0 ; // clear PORTA
TRISB = 0xff ; // switch off display
INTCON.TMR0IF = 0 ; // clear timer 0 overflow interrupt flag
INTCON.TMR0IE = 1 ; // allow timer 0 overflow interrupt
OPTION_REG.T0CS = 0 ; // timer 0 clock source is internal instruction cycle clock
OPTION_REG.PS2 = 0 ; // no prescaler
OPTION_REG.PS1 = 0 ;
OPTION_REG.PS0 = 0 ;
OPTION_REG.PSA = 1 ;
setTime() ; // set time
for(;;)
{
loopCtr++ ; // increment loop counters
toggleCtr++ ;
if(BUTTON) // if at least one button is pressed
{
unsigned char b = 0 ;
VDelay_ms(15) ; // debounce
while(BUTTON) // while one button is pressed
{
if(BUTTON_SET) // are they both pressed ?
{
dymoLight(hhTable[0], 1) ; // clear display
dymoLight(mnTableH[0], 1) ;
dymoLight(mnTableL[0], 1) ;
VDelay_ms(1000) ; // 1 second delay
setTime() ; // enter time setting
break ;
}
else if(BUTTON_MODE) // change mode ?
{
b = 1 ;
}
else if(BUTTON_TOGGLE) // toggle the toggle mode ?
{
b = 2 ;
}
VDelay_ms(15) ;
}
if(b == 1) // change mode
{
VDelay_ms(15) ; // debounce button
while(BUTTON_MODE) ;
VDelay_ms(15) ;
mode++ ; // next mode
toggleMode = 0 ; // clear toggle mode
if(mode == MAX_MODE) // last mode ?
{
mode = 0 ; // back to first one
}
}
else if(b == 2) // toggle mode
{
VDelay_ms(15) ; // debounce button
while(BUTTON_TOGGLE) ;
VDelay_ms(15) ;
toggleMode = !toggleMode ; // toggle display mode
}
}
if(mode == MODE_HOURMN) // display mode is hours:minutes
{
dymoLight(hhTable[hh], 5) ; // light hours
dymoLight(mnTableH[mn], 5) ; // light minutes, upper row
dymoLight(mnTableL[mn], 5) ; // light minutes, lower row
if(toggleMode && (toggleCtr > 400)) // if toggle mode and delay expired
{
toggleCtr = 0 ; // reset counter
mode = MODE_TEMP ; // switch to temperature
}
}
else if(mode == MODE_SS) // display mode is seconds
{
dymoLight(mnTableH[ss], 5) ; // light seconds, upper row
dymoLight(mnTableL[ss], 5) ; // light seconds, lower row
}
else if(mode == MODE_TEMP) // display mode is temperature
{
unsigned char i ;
if(loopCtr > 20) // if delay expired
{
loopCtr = 0 ; // reset counter
temp = TEMP_REF - Adc_Read(0) ; // get sample
temp *= 221 ; // temperature coefficient of the silicon junction
temp /= 102 ;
temp = 25 + temp ; // get the result in celcius
tdeg[tidx] = temp ; // store temperature sample into array
tidx++ ; // advance index
if(tidx == MAX_TEMP) // index overflow ?
{
tidx = 0 ; // back to first slot
}
temp = 0 ; // compute average temperature
for(i = 0 ; i < MAX_TEMP ; i++) // for all samples
{
temp += tdeg[i] ; // add them
}
temp /= MAX_TEMP ; // divide by sample number
}
if((temp > 0) && (temp < 60)) // if temperature wihtin 0...59
{
dymoLight(mnTableL[temp], 5) ; // display temperature, upper row
dymoLight(mnTableH[temp], 5) ; // display temperature, lower row
}
if(toggleMode && (toggleCtr > 200)) // if toggle mode and delay expired
{
toggleCtr = 0 ; // reset counter
mode = MODE_HOURMN ; // switch to hours:minutes
}
}
}
}
No comments:
Post a Comment