// PIC18F1550/4550
// 4 - 74HC595 shift registers, 9 digit LCD, 3 voltage level backplanes
// uses timer0 interrupt, PORTA pins 3,4,5
#include <xc.h>
#define _XTAL_FREQ 48e6
char dupdate = 0;
char bpclock = 0;
char phase = 0;
char cycles = 0;
char dec = 0; // digit number for decimal point
char LCDdisplay[9] = {9,8,7,6,5,4,3,2,1}; // 9 digits to be displayed (in reverse order)
// s(10) = Space, c(11) = backplane Control levels
// 0,1,2,3,4,5,6,7,8,9,s,c numeral segment bit definitions
const char segdefs[][12] = { {0,6,4,4,2,1,3,4,0,0,7,3}, // phase 0
{2,6,1,4,4,4,0,6,0,4,7,6}, // phase 1
{5,7,5,5,7,5,5,7,5,7,7,5} }; // phase 2
void setup();
void loop();
void phase_cycle();
void main()
{
setup();
while(1)
loop();
}
void interrupt ISR()
{
if(TMR0IE && TMR0IF) //Check for TMR0 Overflow interrupt
{
TMR0L = 0; // writing TMR0L re-loads TMR0H to TMR0
dupdate = 1; // tell loop() to clock out 30 bits to shift registers
TMR0IF=0; // Clear timer0 interrupt Flag
}
}
void setup()
{
int tmrd;
//Setup Timer0
T0PS0=1; //Prescaler is divide by 256 (111)
T0PS1=1;
T0PS2=1;
PSA=0; //Timer Clock Source is from Pre-scaler
T0CS=0; //Pre-scaler gets clock from FCPU (12MHz)
T08BIT=0; // timer 0 set to 16 bit mode = 0
// set timer0 to @10 ms (1/fosc/4/256/512)
TMR0H = 0xFE; // load high and low bytes of TMR0 register
TMR0L = 0x00; // (needs to be done in this sequence)
TMR0IE=1; // Enable TIMER0 Interrupt
PEIE=1; // Enable Peripheral Interrupt
GIE=1; // Enable INTs globally
TMR0ON=1; // start timer0
TRISB4 = 1; // bootloader sense (set as input)
TRISA3 = 0; // SR latch clock (set as output)
TRISA4 = 0; // SR data (set as output)
TRISA5 = 0; // SR clock (set as output)
}
void phase_cycle()
{
char digit, group, segs, shift;
for(group = 10, digit = 0; group > 0; group--, digit++ ) // shift out 10 groups of three bits = 1 line of 30 bits
{
if(group == 1) // group 1 = pins 1,2,3, (the three backplanes)
segs = segdefs[phase][11]; // read 3 backplane bits from clkno
else
segs = segdefs[phase][LCDdisplay[digit]]; // read 3 number segment bits from clkno
if(phase == 2 && dec == digit) // check dec for decimal point position
segs = (segs & 0xFE); // if match, clear decimal point bit = decimal point on
if(bpclock > 2)
segs = ~segs; // invert segment and backplane control levels for clocks 3,4,5
for(shift = 3; shift > 0; shift--, segs = segs>>1) // shift out 3 bits ( 1 digit )
{ // and shift segs to next bit
if((segs & 0x01) == 0)
LATA4 = 0; // PORTA 4, data = 0
else
LATA4 = 1; // PORTA 4, data = 1
LATA5 = 1; // PORTA 5, shift clock HIGH
LATA5 = 0; // PORTA 5, shift clock LOW
}
}
LATA5 = 1; // PORTA 5, shift clock HIGH, (shift one more time)
LATA5 = 0; // PORTA 5, shift clock LOW
LATA3 = 0; // PORTA 3, latch clock LOW, (latch data to SR outputs)
LATA3 = 1; // PORTA 3, latch clock HIGH
if(++bpclock > 5) // go to next line at next interrupt
bpclock = 0; // go back to clock 0 on next interrupt
if(++phase > 2) // keep phase clock 0, 1,or 2
phase = 0;
dupdate = 0; // clear flag (to be set by next interrupt)
cycles++; // count interrupts for "counting display" timing
}
void loop()
{ // loop cycles through numbers and decimal points
char j;
if(dupdate == 1) // flag for a phase change
phase_cycle(); // go to next phase clock cycle
if(cycles == 65) // 65 interrupts between display[] counting
{
cycles = 0; // reset interrupt cycles count to 0
for(j = 0; j < 9; j++) // count each digit up
if(LCDdisplay[j]++ > 9)
LCDdisplay[j] = 0;
if(dec++ > 7) // cycle decimal points through display
dec = 0;
}
}