/******* RPG.c ************* * * Fosc = 4 MHz for Fcpu = Fosc/4 = 1 MHz. * Timer1 is clocked by the Timer1 crystal oscillator. * LoopTime function puts chip to sleep. Timer1 awakens chip every 10 ms * within LoopTime function. CPU adjusts Timer1 content for it to timeout * after another 10 milliseconds. * INT2 edges from RPG produce high-priority interrupts and inc/dec DELRPG. * RPGcounter increments/decrements a two-digit number from DELRPG. * RC2 output is toggled every 10 milliseconds for measuring looptime. * LED on RD4 is blinked for 10 ms every four seconds. * * Current draw = 16 or 19 uA (depending on RPG position, * with LED and LCD switched off) * ******* Program hierarchy ***** * * main * Initial * BlinkAlive * RPGcounter * UpdateLCD * Display * LoopTime * * HiPriISR * * LoPriISR * ******************************* */ #include // Define PIC18LF4321 registers and bits /******************************* * Configuration selections ******************************* */ #pragma config OSC = INTIO1 // Use internal osc, RA6=Fosc/4, RA7=I/O #pragma config PWRT = ON // Enable power-up delay #pragma config LVP = OFF // Disable low-voltage programming #pragma config WDT = OFF // Disable watchdog timer initially #pragma config MCLRE = ON // Enable master clear pin #pragma config PBADEN = DIG // PORTB<4:0> = digital #pragma config CCP2MX = RB3 // Connect CCP2 internally to RB3 pin #pragma config BOR = SOFT // Brown-out reset controlled by software #pragma config BORV = 3 // Brown-out voltage set for 2.0V, nominal #pragma config LPT1OSC = OFF // Deselect low-power Timer1 oscillator /******************************* * Global variables ******************************* */ char LCDFLAG; // Flag, set to send string to display char LPISRFLAG; // Flag, set when LP interrupt has been handled unsigned char i; // Index into strings unsigned int DELAY; // Sixteen-bit counter for obtaining a delay unsigned int ALIVECNT; // Scale-of-400 counter for blinking "Alive" LED signed char DELRPG; // RPG output signed char RPGNUM; // For display of RPG position signed char TEMPCHAR; // Temporary signed character /******************************* * Variable strings ******************************* */ char LCDSTRING[] = " 00 "; // Ongoing display string (9 characters) /******************************* * Function prototypes ******************************* */ void Initial(void); void BlinkAlive(void); void RPGcounter(void); void UpdateLCD(void); void Display(void); void LoopTime(void); void HiPriISR(void); void LoPriISR(void); /******************************* * Macros ******************************* */ #define Delay(x) DELAY = x; while(--DELAY){ Nop(); Nop(); } /******************************* * Interrupt vectors ******************************* */ // For high priority interrupts: #pragma code high_vector=0x08 void interrupt_at_high_vector(void) { _asm GOTO HiPriISR _endasm } #pragma code #pragma interrupt HiPriISR // For low priority interrupts: #pragma code low_vector=0x18 void interrupt_at_low_vector(void) { _asm GOTO LoPriISR _endasm } #pragma code #pragma interruptlow LoPriISR /******************************* * HiPriISR * * Respond to rising and falling edges on INT2 input from RPG. ******************************* */ void HiPriISR() { PORTEbits.RE0 = 1; // Power pullup resistor to read direction INTCON3bits.INT2IF = 0; // Reset interrupt flag if (PORTEbits.RE1 == INTCON2bits.INTEDG2) // Direction? { ++DELRPG; } else { --DELRPG; } PORTEbits.RE0 = 0; // Power down pullup resistor INTCON2bits.INTEDG2 ^= 1; // Toggle edge sensitivity } /******************************* * LoPriISR * * Control 10 ms looptime ******************************* */ void LoPriISR() { T1CONbits.TMR1ON = 0; // Pause Timer1 counter TMR1L += 0xB9; // Cut out all but 328 counts of Timer1 T1CONbits.TMR1ON = 1; // Resume Timer1 counter TMR1H = 0xFE; // Upper byte of Timer1 will be 0xFE PIR1bits.TMR1IF = 0; // Clear interrupt flag LPISRFLAG = 1; // Set a flag for LoopTime } /////// Main program ////////////////////////////////////////////////////////// /******************************* * main ******************************* */ void main() { Initial(); // Initialize everything while (1) { PORTCbits.RC2 ^= 1; // Toggle pin, for measuring loop time BlinkAlive(); // Blink "Alive" LED RPGcounter(); UpdateLCD(); // Update LCD LoopTime(); // Use Timer1 to wakeup and loop again } } /******************************* * Initial * * This function performs all initializations of variables and registers. ******************************* */ void Initial() { OSCCON = 0b01100010; // Use Fosc = 4 MHz (Fcpu = 1 MHz) SSPSTAT = 0b00000000; // Set up SPI for output to LCD SSPCON1 = 0b00110000; ADCON1 = 0b00001011; // RA0,RA1,RA2,RA3 pins analog; others digital TRISA = 0b00001111; // Set I/O for PORTA TRISB = 0b01000100; // Set I/O for PORTB TRISC = 0b10000000; // Set I/O for PORTC TRISD = 0b10000000; // Set I/O for PORTD TRISE = 0b00000010; // Set I/O for PORTE PORTA = 0; // Set initial state for all outputs low PORTB = 0; PORTC = 0; PORTD = 0b00100000; // except RD5 that drives LCD interrupt PORTE = 0; LCDFLAG = 0; // Flag to signal LCD update is initially off ALIVECNT = 300; // Blink immediately SSPBUF = ' '; // Send a blank to initialize state of UART Delay(50000); // Pause for half a second RCONbits.SBOREN = 0; // Now disable brown-out reset T1CON = 0b01001111; // Timer1 - loop time via low-pri interrupts TMR1H = 0xFE; // Set Timer1 to be 10 ms away from TMR1L = 0xB9; // next roll over (65536 + 1 - 328 = 0xFEB9) PIE1bits.TMR1IE = 1; // Enable local interrupt source IPR1bits.TMR1IP = 0; // Use Timer1 for low-priority interrupts LPISRFLAG = 0; // Flag to signal that LPISR has been executed PORTEbits.RE2 = 1; // Power up the pullup for the RPG's interrupt Delay(10); // Pause for 100 us INTCON2bits.INTEDG2 = !PORTBbits.RB2; // Select initial interrupt edge INTCON3bits.INT2IE = 1; // Enable INT2 interrupt source INTCON3bits.INT2IP = 1; // Use INT2 for high-priority interrrupts INTCON3bits.INT2IF = 0; // Clear interrupt flag DELRPG = 0; // Indicate no initial edge RPGNUM = 0; // and initial RPG position of 00 LCDFLAG = 1; // Display initial 00 value RCONbits.IPEN = 1; // Enable high/low priority interrupt feature INTCONbits.GIEL = 1; // Global low-priority interrupt enable INTCONbits.GIEH = 1; // Enable both high and low interrupts } /******************************* * BlinkAlive * * This function briefly blinks the LED every four seconds. * With a looptime of about 10 ms, count 400 looptimes. ******************************* */ void BlinkAlive() { PORTDbits.RD4 = 0; // Turn off LED if (++ALIVECNT == 400) // Increment counter and return if not 400 { ALIVECNT = 0; // Reset ALIVECNT PORTDbits.RD4 = 1; // Turn on LED for one looptime } } /******************************* * RPGcounter * * This function uses DELRPG to update the signed char variable, RPGNUM. * DELRPG is returned to zero. * LCDSTRING[6] and LCDSTRING[7] display RPGNUM. ******************************* */ void RPGcounter() { while (DELRPG > 0) { --DELRPG; if (++RPGNUM > 99) // Increment RPGNUM, modulo 100 { RPGNUM -= 100; } TEMPCHAR = RPGNUM; LCDSTRING[6] = '0' + TEMPCHAR / 10; LCDSTRING[7] = '0' + TEMPCHAR % 10; LCDFLAG = 1; // Set flag to display } while (DELRPG < 0) { ++DELRPG; if (--RPGNUM < 0) // Decrement RPGNUM, modulo 100 { RPGNUM += 100; } TEMPCHAR = RPGNUM; LCDSTRING[6] = '0' + TEMPCHAR / 10; LCDSTRING[7] = '0' + TEMPCHAR % 10; LCDFLAG = 1; // Set flag to display } } /******************************* * UpdateLCD * * This function updates the 8-character LCD if the LCDFLAG is set. ******************************* */ void UpdateLCD() { if (LCDFLAG) { Display(); LCDFLAG = 0; } } /******************************* * Display * * This function sends LCDSTRING to the LCD. ******************************* */ void Display() { PORTDbits.RD5 = 0; // Wake up LCD display for (i = 0; i < 9; i++) { PIR1bits.SSPIF = 0; // Clear SPI flag SSPBUF = LCDSTRING[i]; // Send byte while (!PIR1bits.SSPIF); // Wait for transmission to complete } PORTDbits.RD5 = 1; // Return RB5 high, ready for next string } /******************************* * LoopTime * * This function puts the chip to sleep upon entry. * It wakes up and executes a HPISR and then returns to sleep. * It wakes up and executes a LPISR and then exits. ******************************* */ void LoopTime() { while (!LPISRFLAG) // Sleep upon entry and upon exit from HPISR { // Return only if LPISR has been executed, Sleep(); // which sets LPISRFLAG Nop(); } LPISRFLAG = 0; // Sleep upon next entry to LoopTime }