/******* DevPVy.c *************** REVISED 10-1-09 * * Q&L Development Board Performance Verification program. * * VDD = 5V for use with standard off-the-shelf 24x2 character LCD. * Use Fosc = 4 MHz for Fcpu = Fosc/4 = 1 MHz. * Sleep for 10 ms, using Timer1 for wakeup. * Toggle RD0 output every loop time for measuring with scope. * Blink LED on RE2 for 10 ms every two seconds. * Display "Qwik&Low Development Bd." on top line of LCD. * Display a single letter on second line of LCD, incrementing its * ASCII code every two seconds from within BlinkAlive * * Current draw = 40 uA (regulator + 4321) * Current draw with LCD = 1.118 mA * ******* Program hierarchy ***** * * main * Initial * InitLCD * DisplayC * BlinkAlive * LoopTime * * 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 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.1V, nominal #pragma config LPT1OSC = OFF // Deselect low-power Timer1 oscillator /******************************* * Global variables ******************************* */ unsigned int DELAY; // Counter for obtaining a delay unsigned char ALIVECNT; // Counter for blinking "Alive" LED char COUNT; // Counter available as local to subroutines unsigned char PORTACOPY; // Used by InitLCD and DisplayC char LPISRFLAG; // Flag, set when LP interrupt has been // handled Timer1 const rom char *romPtr; // Pointer into ROM strings char *ramPtr; // Pointer into RAM strings /******************************* * Constant strings ******************************* */ const rom char LCDstr[] = { 0x33, 0x32, 0x28, 0x01, 0x0c, 0x06, 0x00, 0x00 }; // LCD controller initialization string const rom char StrtStr[] = { "\x80Qwik&Low Development Bd.\x00" }; // Startup screen /******************************* * Variable strings ******************************* */ char VarStr[] = { 0xCC, 0x41, 0x00 }; // One-character variable /******************************* * Function prototypes ******************************* */ void Initial(void); void BlinkAlive(void); void LoopTime(void); void InitLCD(void); void DisplayC(void); void DisplayV(void); void LoPriISR(void); /******************************* * Macros ******************************* */ #define Delay(x) DELAY = x; while(--DELAY){ Nop(); Nop(); } /******************************* * Interrupt vector ******************************* */ // For low priority interrupts: #pragma code low_vector=0x18 void interrupt_at_low_vector(void) { _asm GOTO LoPriISR _endasm} #pragma code #pragma interruptlow LoPriISR /////// Main program ///////////////////////////////////////////////////// /******************************* * main ******************************* */ void main() { Initial(); // Initialize everything T1CONbits.TMR1ON = 1; // Start Timer1 counting while (1) { PORTDbits.RD0 ^= 1; // Toggle RD0 for measuring loop time BlinkAlive(); // Blink LED every two seconds LoopTime(); // Sleep for ten ms } } /******************************* * LoPriISR * * This low-priority interrupt service routine updates Timer1 to interrupt * every 10 ms. Ten ms = 10000 * 0.032768 = 328 to cut out all * but 328 counts. Reload TMR1 with 65536+1-328 = 65209 = 0xFEB9. ******************************* */ 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 } /******************************* * Initial * * This function performs all initializations of variables and registers. ******************************* */ void Initial() { OSCCON = 0b01100010; // Use Fosc = 4 MHz (Fcpu = 1 MHz) ADCON1 = 0b00001111; // RA0,1,2,... digital TRISA = 0b00000000; // Set I/O for PORTA TRISB = 0b01110100; // Set I/O for PORTB TRISC = 0b10000000; // Set I/O for PORTC TRISD = 0b00000000; // Set I/O for PORTD TRISE = 0b00000000; // Set I/O for PORTE PORTA = 0; // Set initial state for all outputs PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; Delay(50000); // Pause for half a second RCONbits.SBOREN = 0; // Now disable brown-out reset ALIVECNT = 197; // Blink immediately InitLCD(); // Initialize LCD romPtr = StrtStr; // Point to StrtStr DisplayC(); // Display startup message TMR1H = 0xFE; // Set Timer1 to be 10 ms away from TMR1L = 0xB9; // next roll over (65536 + 1 - 328 = 0xFEB9) IPR1bits.TMR1IP = 0; // Use Timer1 for low-priority interrupts PIR1bits.TMR1IF = 0; // Clear overflow flag PIE1bits.TMR1IE = 1; // Enable local interrupt source T1CON = 0b01001111; // Start Timer1 counting CPU clock cycles 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 } /******************************* * InitLCD() * * Initialize the LCD for a four-bit interface, a two-line display. Then clear * display, turn off cursor, turn on display and increment cursor automatically. ******************************* */ void InitLCD() { PORTEbits.RE0 = 0; // RS=0 for command Delay(5000); // Wait 50 ms for LCD controller to power up romPtr = LCDstr; // Load pointer to beginning of string while (*romPtr) // Send char if the byte is not zero { PORTACOPY = *romPtr; // Get byte PORTACOPY >>= 2; // Shift upper nibble into position PORTA = PORTACOPY & 0b00111100; // Force other four bits to zero PORTEbits.RE1 = 1; // Drive E pin high PORTEbits.RE1 = 0; // and low so LCD will accept nibble Delay(500); // Wait for 5 ms PORTACOPY = *romPtr; // Get byte again PORTACOPY <<= 2; // Shift lower nibble into position PORTA = PORTACOPY & 0b00111100; // Force other four bits to zero PORTEbits.RE1 = 1; // Drive E pin high PORTEbits.RE1 = 0; // and low so LCD will process byte Delay(500); // Wait 5 ms romPtr++; // Increment pointer to next character } } /******************************* * DisplayC() * * This subroutine is called with romPtr loaded with the name of a * constant display string stored in program memory. The format of the * constant string is: * Cursor-position code (CPC) * Displayable characters * A null character (0x00) to terminate the string * The cursor-position code identifies the location to be used by the * first displayable character. * For the top row of the 24x2 LCD, the CPC's are 0x80, 0x81,..., 0x97 * For the second row of the 24x2 LCD, the CPC's are 0xC0, 0xC1,..., 0D7 ******************************* */ void DisplayC() { PORTEbits.RE0 = 0; // RS=0 for command (CPC) while (*romPtr) // Send char if the byte is not zero { PORTACOPY = *romPtr; PORTACOPY >>= 2; // Shift upper nibble into position PORTA = PORTACOPY & 0b00111100; // Force other four bits to zero PORTEbits.RE1 = 1; // Drive E pin high PORTEbits.RE1 = 0; // and low so LCD will accept nibble PORTACOPY = *romPtr; PORTACOPY <<= 2; // Shift lower nibble into position PORTA = PORTACOPY & 0b00111100; // Force other four bits to zero PORTEbits.RE1 = 1; // Drive E pin high PORTEbits.RE1 = 0; // and low so LCD will process byte Delay(4); // Wait 40 usec PORTEbits.RE0 = 1; // Drive RS pin high for displayable chars romPtr++; // Increment pointer to next character } } /******************************* * DisplayV() * * This subroutine is called with ramPtr loaded with the name of a * variable display string stored in RAM. The format of the * variable string is: * Cursor-position code (CPC) * Displayable characters * A null character (0x00) to terminate the string * The cursor-position code identifies the location to be used by the * first displayable character. * For the top row of the 24x2 LCD, the CPC's are 0x80, 0x81,..., 0x97 * For the second row of the 24x2 LCD, the CPC's are 0xC0, 0xC1,..., 0D7 ******************************* */ void DisplayV() { PORTEbits.RE0 = 0; // RS=0 for command (CPC) while (*ramPtr) // Send char if the byte is not zero { PORTACOPY = *ramPtr; PORTACOPY >>= 2; // Shift upper nibble into position PORTA = PORTACOPY & 0b00111100; // Force other four bits to zero PORTEbits.RE1 = 1; // Drive E pin high PORTEbits.RE1 = 0; // and low so LCD will accept nibble PORTACOPY = *ramPtr; PORTACOPY <<= 2; // Shift lower nibble into position PORTA = PORTACOPY & 0b00111100; // Force other four bits to zero PORTEbits.RE1 = 1; // Drive E pin high PORTEbits.RE1 = 0; // and low so LCD will process byte Delay(4); // Wait 40 usec PORTEbits.RE0 = 1; // Drive RS pin high for displayable chars ramPtr++; // Increment pointer to next character } } /******************************* * BlinkAlive() * * This function briefly blinks the LED every two seconds. * With a looptime of 10 ms, count 200 looptimes * Display a new character every two seconds on the LCD. ******************************* */ void BlinkAlive() { PORTEbits.RE2 = 0; // Turn off LED if (++ALIVECNT == 200) // Increment counter and return if not 200 { ALIVECNT = 0; // Reset ALIVECNT PORTEbits.RE2 = 1; // Turn on LED for 10 ms every 2 secs ramPtr = VarStr; // Set pointer <<<<---- DisplayV(); VarStr[1] += 1; // Increment character } } /******************************* * LoopTime * * This function puts the chip to sleep upon entry. * For a Timer1 interrupt, it executes the LPISR and then exits. * For any other interrupt, it executes the ISR and then returns to sleep. ******************************* */ 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 }