/******* Calibrate.c *********** * * Use Fosc = 4 MHz for Fcpu = Fosc/4 = 1 MHz. * Calibrate Fosc each time that the pushbutton is pressed, starting with * the nominal value and incrementing OSCTUNE with each subsequent press. * Display CALIB on PC. CALIB = 7812 if Fosc/4 = 1 MHz exactly. * Use Timer1 to set loop time to 10 ms. * Blink LED on RD4 for 10 ms every 2.5 seconds. * ******* Program hierarchy ***** * * main * Initial * InitTX * CalSup1 * CalSup2 * BlinkAlive * PBcalibrate * LoopTime * ******************************* */ #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 WDTPS = 4 // 16 millisecond WDT timeout period, nominal #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 ******************************* */ unsigned char ALIVECNT; // Scale-of-400 counter for blinking "Alive" LED signed char i; // Index into strings unsigned int DELAY; // Sixteen-bit counter used by Delay macro unsigned int TIMEL; // Int version of TMR0L unsigned int TIMEH; // Int version of TMR0H unsigned int CALIB; // Calibration constant unsigned char CAL; // Calibration state variable char OLDPB; // Old pushbutton state char NEWPB; // New pushbutton state char PCSTRING[] = "xxxx"; // String to represent 0 - 9999 value /******************************* * Function prototypes ******************************* */ void Initial(void); void InitTX(void); void BlinkAlive(void); void CalSup1(void); void CalSup2(void); void PBcalibrate(void); void LoopTime(void); /******************************* * Macros ******************************* */ #define Delay(x) DELAY = x; while(--DELAY){ Nop(); Nop(); } #define TXascii(in) TXREG = in; while(!TXSTAbits.TRMT) /////// Main program ////////////////////////////////////////////////////////// /******************************* * main ******************************* */ void main() { Initial(); // Initialize everything InitTX(); // Initialize UART's TX output while (1) { CalSup1(); // First part of calibration supervisor PORTCbits.RC2 ^= 1; // Toggle for looptime BlinkAlive(); // Blink "Alive" LED PBcalibrate(); // Calibrate each time Pushbutton is pressed Delay(600); // Pause CalSup2(); LoopTime(); // Sleep, letting watchdog timer wake up chip } } /******************************* * Initial * * This function performs all initializations of variables and registers. ******************************* */ void Initial() { OSCCON = 0b01100010; // Use Fosc = 4 MHz (Fcpu = 1 MHz) 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; Delay(50000); // Pause for half a second RCONbits.SBOREN = 0; // Now disable brown-out reset ALIVECNT = 247; // Blink immediately OLDPB = 0; // Initialize pushbutton flags NEWPB = 0; CAL =0; // Calibration state variable - do nothing yet OSCTUNE = 0b00011111; // CALIB = default value after first PB push PIE1bits.TMR1IE = 1; // Enable local interrupt source TMR1H = 0xFF; // Initial value TMR1L = 0x00; // T1CON = 0b01001111; // Timer1 runs from 32768 Hz oscillator PIR1bits.TMR1IF = 0; // Clear Timer1 flag INTCONbits.GIEL = 1; // Enable wake-up from sleep } /******************************* * InitTX * * This function initializes the UART for its TX output function. It assumes * Fosc = 4 MHz. For a different oscillator frequency, use Figure 6-3c to * change BRGH and SPBRG appropriately. ******************************* */ void InitTX() { RCSTA = 0b10010000; // Enable UART TXSTA = 0b00100000; // Enable TX SPBRG = 12; // Set baud rate BAUDCON = 0b00111000; // Invert TX output } /******************************* * BlinkAlive * * This function briefly blinks the LED every four seconds. * With a looptime of about 10 ms, count 250 looptimes. ******************************* */ void BlinkAlive() { PORTDbits.RD4 = 0; // Turn off LED if (++ALIVECNT == 250) // Increment counter and return if not 250 { ALIVECNT = 0; // Reset ALIVECNT PORTDbits.RD4 = 1; // Turn on LED for one looptime } } /******************************* * PBcalibrate * * Calibrate each time pushbutton is pressed with an increase calibration value ******************************* */ void PBcalibrate() { PORTEbits.RE0 = 1; // Power up the pushbutton Nop(); // Delay one microsecond before checking it NEWPB = !PORTDbits.RD7; // Set flag if pushbutton is pressed PORTEbits.RE0 = 0; // Power down the pushbutton if (!OLDPB && NEWPB) // Look for last time = 0, now = 1 { CAL = 1; // Initiate a calibration sequence ++OSCTUNE; // Increment calibration value } OLDPB = NEWPB; // Save present pushbutton state } /******************************* * CalSup1 * * First part of calibration supervisor. * ******************************* */ void CalSup1() { if (CAL == 1) { T0CON = 0b00001000; // Timer0 stopped TMR0H = 0; // Timer0 = 0 TMR0L = 0; T1CON = 0b01001110; // Stop Timer1 TMR1H = 0xFF; // Wait for exact rollover TMR1L = 0xFE; PIR1bits.TMR1IF = 0; // Clear flag T1CONbits.TMR1ON = 1; // Start Timer1 again while (!PIR1bits.TMR1IF); // and wait for it to set again T0CONbits.TMR0ON = 1; // Start Timer0 TMR1H = 0xFF; // Next overflow in 256 counts of Timer1 PIR1bits.TMR1IF = 0; // Clear flag (if not already cleared) CAL = 2; } } /******************************* * CalSup2 * * Second part of calibration supervisor. * ******************************* */ void CalSup2() { if (CAL == 2) // Collect measurement { while (!PIR1bits.TMR1IF); // Wait for Timer1 to roll over and set flag T0CONbits.TMR0ON = 0; // Stop Timer0 T1CONbits.TMR1ON = 0; // Pause Timer1 counter TMR1L += 0xBB; // Cut out remaining counts of Timer1 T1CONbits.TMR1ON = 1; // Resume Timer1 counter TMR1H = 0xFF; // Upper byte of Timer1 will be 0xFF PIR1bits.TMR1IF = 0; // Clear interrupt flag TIMEL = TMR0L; // Read Timer0 in correct sequence TIMEH = TMR0H; CALIB = (TIMEH << 8) +TIMEL; // Form calibration factor CAL = 3; // Done with calibration sequence } else if (CAL == 3) // Display result { for (i = 3; i >= 0; --i) // Form digits and display on PC { PCSTRING[i] = (CALIB % 10) + 0x30; CALIB = CALIB / 10; } for (i = 0; i <= 3; ++i) { TXascii(PCSTRING[i]); } TXascii(0x0D); // Carriage return TXascii(0x0A); // Line feed CAL = 0; } } /******************************* * LoopTime * * This function puts the chip to sleep, to be awakened by Timer1 rollover. ******************************* */ void LoopTime() { Sleep(); Nop(); 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 }