I need a LED clock so I used a PCB I made some years ago.
Soldering the TSSOP IC on the small pads isnt comfortable.
Added a watch crystal and 6pF capacitors.
There is no schematic but a table how the display is wired to the controller.
Most of the work here is done in software with two decoding tables so how the display is wired is secondary.
Such clocks have been done before- in assembler, and requiring to wire the display to 8bit port straight.
Of course in such a case, IO is easy and fast. But its bound to a particular kind display then.
Ive rewitten the firmware completely.
Now I use 4 copies of the 7segment decoding table, for each multiplex phase (the data is same but sink bits change),
and for two IO ports. Its about the limit what the 16F54 can contain!
Second topic that bothered me was the flickering its almost unbearable at 32 KHz when C language is used.
But the 7segment data is only updated once a minute! Table reads with offset take quite a long time.
While using a pointer dereference in C is much faster.
So Im buffering the data for each digit in the small RAM. It works out well.
Heres the complete source code, that is, a push button query should be added to set the time, or its just a timer, not a clock.
It has a blinking dot too!
The display used is a very common one. At first I did run into the trap to enter the bits backwards, had to enter them again.
Using a table like this you can use any IO bits, though, if you use 3 or 4 ports you need as many tables.
When you share the ports you need extra masking tables as well.

Soldering the TSSOP IC on the small pads isnt comfortable.
Added a watch crystal and 6pF capacitors.
There is no schematic but a table how the display is wired to the controller.
Most of the work here is done in software with two decoding tables so how the display is wired is secondary.
Such clocks have been done before- in assembler, and requiring to wire the display to 8bit port straight.
Of course in such a case, IO is easy and fast. But its bound to a particular kind display then.
Ive rewitten the firmware completely.
Now I use 4 copies of the 7segment decoding table, for each multiplex phase (the data is same but sink bits change),
and for two IO ports. Its about the limit what the 16F54 can contain!
Second topic that bothered me was the flickering its almost unbearable at 32 KHz when C language is used.
But the 7segment data is only updated once a minute! Table reads with offset take quite a long time.
While using a pointer dereference in C is much faster.
So Im buffering the data for each digit in the small RAM. It works out well.
Heres the complete source code, that is, a push button query should be added to set the time, or its just a timer, not a clock.
It has a blinking dot too!
C:
#include <xc.h> // 32 KHZ LEd 7 segment clock
#pragma config OSC = LP WDT = OFF CP = OFF // configuration 16F54
const unsigned char dig_PORTA[] @0x01a ={0b1111,0b0011,0b1011,0b1011,\
0b0111,0b1110,0b1110,0b1011,\
0b1111,0b1111,0b1111,0b0110,\
0b1110,0b0011,0b1010,0b1010,\
0b1101,0b0001,0b1001,0b1001,\
0b0101,0b1100,0b1100,0b1001,\
0b1101,0b1101,0b1101,0b0100,\
0b1100,0b0001,0b1000,0b1000,\
0b1111,0b0011,0b1011,0b1011,\
0b0111,0b1110,0b1110,0b1011,\
0b1111,0b1111,0b1111,0b0110,\
0b1110,0b0011,0b1010,0b1010,\
0b1111,0b0011,0b1011,0b1011,\
0b0111,0b1110,0b1110,0b1011,\
0b1111,0b1111,0b1111,0b0110,\
0b1110,0b0011,0b1010,0b1010}; // segment and sink driving table for PORTA
const unsigned char dig_PORTB[] @0x60 ={0b11010110,0b11010000,0b11100110,0b11110100,\
0b11110000,0b11110100,0b11110110,0b11110000,\
0b11110110,0b11110100,0b11110010,0b11100110,\
0b11000110,0b11010110,0b11110110,0b11100110,\
0b11010111,0b11010001,0b11100111,0b11110101,\
0b11110001,0b11110101,0b11110111,0b11110001,\
0b11110111,0b11110101,0b11110011,0b11100111,\
0b11000111,0b11010111,0b11110111,0b11100111,\
0b01010111,0b01010001,0b01100111,0b01110101,\
0b01110001,0b01110101,0b01110111,0b01110001,\
0b01110111,0b01110101,0b01110011,0b01100111,\
0b01000111,0b01010111,0b01110111,0b01100111,\
0b10010111,0b10010001,0b10100111,0b10110101,\
0b10110001,0b10110101,0b10110111,0b10110001,\
0b10110111,0b10110101,0b10110011,0b10100111,\
0b10000111,0b10010111,0b10110111,0b10100111}; // segment and sink driving table for PORTB
unsigned char* tptr;
unsigned char v_phase,v_digphase,hrh,hrl,minh,minl,secs;
unsigned char ddata[8];
void update() // precompute the display data, load from ROM table
{
*(tptr)=dig_PORTA[hrh];
*(tptr+1)=dig_PORTB[hrh];
*(tptr+2)=dig_PORTA[hrl+0x10];
*(tptr+3)=dig_PORTB[hrl+0x10];
*(tptr+4)=dig_PORTA[minh+0x20];
*(tptr+5)=dig_PORTB[minh+0x20];
*(tptr+6)=dig_PORTA[minl+0x30];
*(tptr+7)=dig_PORTB[minl+0x30];
}
void display() // update the multiplex display
{
TRISA=0xff;TRISB=0xff;
PORTA=*(tptr+v_digphase);
PORTB=*(tptr+1+v_digphase);
TRISA=0;TRISB=0;
v_phase+=0x10;v_phase&=0x30;
v_digphase+=2;v_digphase&=0x07;
}
void main(void)
{
v_phase=0;v_digphase=0; //initialization
secs=0;minl=0;minh=0;hrl=0;hrh=0;
TRISB=0;TRISA=0;OPTION=0x07;
tptr=&ddata[0];
update(); // display 00.00
while(1)
{
if(TMR0&0x20) // clock logic, quite simple
{
TMR0&=0x1f; secs++;
*(tptr+3)^=0x8; // blink rhe LED
}else if(secs==60)
{
secs=0;minl++;if(minl==10)
{
minl=0;minh++;if(minh==6)
{
minh=0;
hrl++;if(hrl==10){hrh++;hrl=0;}
if(hrl==4)if(hrh==2){hrh=0;hrl=0;}
}
}
update();
}
display();
}
}
Using a table like this you can use any IO bits, though, if you use 3 or 4 ports you need as many tables.
When you share the ports you need extra masking tables as well.



