S7SM
Member
Right now, I got my LCD working. Just that I have this code but not sure where to modify it at so that it'll display a word instead of just the light itself.
Anyone can help me out with this code ?
http://microdesignsinc.com/picbook/downloads/c/P3_MCC18.c
/******* P3_MCC18.c ************************************************************
*
* Use 10 MHz crystal frequency.
* Use Timer0 for ten millisecond looptime.
* Blink "Alive" LED every two and a half seconds.
* Use pushbutton to exercise Screens utility.
*
******* Program hierarchy *****************************************************
*
* Mainline
* Initial
* InitLCD
* LoopTime
* DisplayC
* T40
* BlinkAlive
* Pbutton
* Screens
* DisplayC
* T40
* Frequency
* Period
* PWmax
* RateRPG
* ByteDisplay
* DisplayC
* T40
* DisplayV
* T40
* LoopTime
*
*******************************************************************************
*/
#include <p18cxxx.h>
/*******************************
* Assembler directives
*******************************
*/
/*******************************
* Definitions and equates
*******************************
*/
#define PBthres 30 // Pushbutton threshold for a long press
/*******************************
* Global variables
*******************************
*/
char TMR0LCOPY; // Copy of sixteen-bit Timer0 used by LoopTime
char TMR0HCOPY;
char INTCONCOPY; // Copy of INTCON for LoopTime subroutine
char COUNT; // Counter available as local to subroutines
char TEMP; // Temporary local variable
char ALIVECNT; // Counter for blinking "Alive" LED
char BYTE; // Eight-bit byte to be displayed
char OLDPORTD; // Holds previous value of inputs
char DELRPG; // Generated by RPG
char RPGCNT; // Used to display RPG changes
char PBCOUNT; // Counter for measuring duration of press
char SCREEN; // State of LCD subroutine
char LOOP10; // Scale of ten loop counter
char THR; // Threshold value used by Pbutton
struct { // Control/status bits for pushbutton
unsigned ISC:1; // Initiate screen change for slow press
unsigned ISA:1; // Initiate secondary action for fast press
unsigned PDONE:1; // Pushbutton action has been taken
unsigned OLDPB:1; // Old state of pushbutton
unsigned NEWPB:1; // New state of pushbutton
} PBSTATEbits;
/*******************************
* Constant strings
*******************************
*/
// For stability reasons, create an EVEN number of elements in any given array
const char LCDstr[] = {0x33,0x32,0x28,0x01,0x0c,0x06,0x00,0x00};// LCD Initialization string
const char StrtStr[] = {0x80,'P','u','s','h',' ','P','B',' ',0};// Startup screen
const char BYTE_1[] = {0x80,'B','Y','T','E','=',' ',' ',' ',0};// Write "BYTE=" 1st line of LCD
const char Clear1[] = {0x80,' ',' ',' ',' ',' ',' ',' ',' ',0};// Clear line 1
const char Clear2[] = {0xC0,' ',' ',' ',' ',' ',' ',' ',' ',0};// Clear line 2
const char FreqStr[] = {0x80,'F','r','e','q',' ','k','H','z',0};// Frequency instrument
const char PerStr[] = {0x80,'P','e','r',' ',' ',' ','u','s',0};// Period instrument
const char PWmaxStr[] = {0x80,'P','W','m','a','x',' ','u','s',0};// Max pulse width instrument
/*******************************
* Variable strings
*******************************
*/
char BYTESTR[] = {0,0,0,0,0,0,0,0,0,0}; // Display string for binary version of BYTE
/*******************************
* Function prototypes
*******************************
*/
void Initial(void);
void InitLCD(void);
void Looptime(void);
void T40(void);
void DisplayC(const char *);
void DisplayV(char *);
void BlinkAlive(void);
void LoopTime(void);
void ByteDisplay(void);
void RateRPG(void);
void Pbutton(void);
void Screens(void);
void Frequency(void);
void Period(void);
void PWmax(void);
/////// Mainline program ////////////////////////////////////////
/*******************************
* main()
*******************************
*/
void main()
{
Initial(); // Initialize everything
while(1)
{
PORTBbits.RB0 = !PORTBbits.RB0; // Toggle pin, to support measuring loop time
BlinkAlive(); // Blink "Alive" LED
Pbutton(); // Check pushbutton
Screens(); // Deal with SCREEN state
LoopTime(); // Make looptime be ten milliseconds
}
}
/*******************************
* Initial()
*
* This subroutine performs all initializations of variables and registers.
*******************************
*/
void Initial()
{
ADCON1 = 0b10001110; // Enable PORTA & PORTE digital I/O pins
TRISA = 0b11100001; // Set I/O for PORTA
TRISB = 0b11011100; // Set I/O for PORTB
TRISC = 0b11010000; // Set I/0 for PORTC
TRISD = 0b00001111; // Set I/O for PORTD
TRISE = 0b00000000; // Set I/O for PORTE
T0CON = 0b10001000; // Set up Timer0 for a looptime of 10 ms
PORTA = 0b00010000; // Turn off all four LEDs driven from PORTA
OLDPORTD = PORTD; // Initialize "old" value
RPGCNT = 0; // Clear counter to be displayed
PBCOUNT = 0; // and pushbutton count
SCREEN = 0; // Initialize LCD's SCREEN variable
THR = 0; // Initialize Pbutton's THR variable
PBSTATEbits.ISC = 0; // Initialize pushbutton state
PBSTATEbits.ISA = 0;
PBSTATEbits.PDONE = 0;
PBSTATEbits.OLDPB = 1;
PBSTATEbits.NEWPB = 0;
InitLCD(); // Initialize LCD
DisplayC(StrtStr); // Display startup message
}
/*******************************
* InitLCD()
*
* Initialize the Optrex 8x2 character LCD.
* First wait for 0.1 second, to get past display's power-on reset time.
*******************************
*/
void InitLCD()
{
char currentChar;
char *tempPtr;
COUNT = 10; // Wait 0.1 second
while (COUNT)
{
LoopTime(); // Call LoopTime() 10 times
COUNT--;
}
PORTEbits.RE0 = 0; // RS=0 for command
tempPtr = LCDstr;
while (*tempPtr) // if the byte is not zero
{
currentChar = *tempPtr;
PORTEbits.RE1 = 1; // Drive E pin high
PORTD = currentChar; // Send upper nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will accept nibble
LoopTime();
currentChar <<= 4; // Shift lower nibble to upper nibble
PORTEbits.RE1 = 1; // Drive E pin high again
PORTD = currentChar; // Write lower nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will process byte
LoopTime(); // Wait 40 usec
tempPtr++; // Increment pointerto next character
}
}
/*******************************
* T40()
*
* Pause for 40 microseconds or 40/0.4 = 100 clock cycles.
* Assumes 10/4 = 2.5 MHz internal clock rate.
*******************************
*/
void T40()
{
// Measured with oscilloscope to be about 42.80 us
// including the time to call this routine. Decrementing each
// "cCOUNT" takes approximately 4 us.
unsigned char cCOUNT = 7;
while (cCOUNT)
cCOUNT--;
}
/*******************************
* DisplayC(const char *)
*
* This subroutine is called with the passing in of an array of a constant
* display string. It sends the bytes of the string to the LCD. The first
* byte sets the cursor position. The remaining bytes are displayed, beginning
* at that position.
* This subroutine expects a normal one-byte cursor-positioning code, 0xhh, or
* an occasionally used two-byte cursor-positioning code of the form 0x00hh.
*******************************
*/
void DisplayC(const char * tempPtr)
{
char currentChar;
PORTEbits.RE0 = 0; // Drive RS pin low for cursor-positioning code
while (*tempPtr) // if the byte is not zero
{
currentChar = *tempPtr;
PORTEbits.RE1 = 1; // Drive E pin high
PORTD = currentChar; // Send upper nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will accept nibble
currentChar <<= 4; // Shift lower nibble to upper nibble
PORTEbits.RE1 = 1; // Drive E pin high again
PORTD = currentChar; // Write lower nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will process byte
T40(); // Wait 40 usec
PORTEbits.RE0 = 1; // Drive RS pin high for displayable characters
tempPtr++; // Increment pointerto next character
}
}
/*******************************
* DisplayV(char *)
*
* This subroutine is called with the passing in of an array of a variable
* display string. It sends the bytes of the string to the LCD. The first
* byte sets the cursor position. The remaining bytes are displayed, beginning
* at that position.
*******************************
*/
void DisplayV(char * tempPtr)
{
char currentChar;
PORTEbits.RE0 = 0; // Drive RS pin low for cursor-positioning code
while (*tempPtr) // if the byte is not zero
{
currentChar = *tempPtr;
PORTEbits.RE1 = 1; // Drive E pin high
PORTD = currentChar; // Send upper nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will accept nibble
currentChar <<= 4; // Shift lower nibble to upper nibble
PORTEbits.RE1 = 1; // Drive E pin high again
PORTD = currentChar; // Write lower nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will process byte
T40(); // Wait 40 usec
PORTEbits.RE0 = 1; // Drive RS pin high for displayable characters
tempPtr++; // Increment pointerto next character
}
}
/*******************************
* BlinkAlive()
*
* This subroutine briefly blinks the LED next to the PIC every two-and-a-half
* seconds.
*******************************
*/
void BlinkAlive()
{
PORTAbits.RA4 = 1; // Turn off LED
if (!(--ALIVECNT)) // Decrement loop counter and return if not zero
{
ALIVECNT = 250; // Reinitialize BLNKCNT
PORTAbits.RA4 = 0; // Turn on LED for ten milliseconds every 2.5 sec
}
}
/*******************************
* LoopTime()
*
* This subroutine waits for Timer0 to complete its ten millisecond count
* sequence. It does so by waiting for sixteen-bit Timer0 to roll over. To obtain
* a period of precisely 10000/0.4 = 25000 clock periods, it needs to remove
* 65536-25000 or 40536 counts from the sixteen-bit count sequence. The
* algorithm below first copies Timer0 to RAM, adds "Bignum" to the copy ,and
* then writes the result back to Timer0. It actually needs to add somewhat more
* counts to Timer0 than 40536. The extra number of 12+2 counts added into
* "Bignum" makes the precise correction.
*******************************
*/
void LoopTime()
{
#define Bignum 65536-25000+12+2
while (!INTCONbits.T0IF); // Wait until ten milliseconds are up
INTCONCOPY = INTCON; // Save INTCON bits
INTCONbits.GIEH = 0; // Disable all interrupts from CPU
INTCONbits.GIEL = 0;
TMR0LCOPY = TMR0L; // Read 16-bit counter at this moment
TMR0HCOPY = TMR0H;
TMR0LCOPY += Bignum & 0x00FF; // add LSB
if (STATUSbits.C) // If Carry, increment high byte
TMR0HCOPY++;
TMR0HCOPY += (Bignum>>8) & 0x00FF; // add MSB
TMR0H = TMR0HCOPY;
TMR0L = TMR0LCOPY; // Write 16-bit counter at this moment
WREG = INTCONCOPY & 0b11000000; // Reenable interrupts to CPU if enabled prior
INTCON = INTCON | WREG; // to LoopTime
INTCONbits.T0IF = 0; // Clear Timer0 flag
}
/*******************************
* ByteDisplay()
*
* Display whatever is in BYTE as a binary number.
*******************************
*/
void ByteDisplay()
{
DisplayC(BYTE_1); // Display "BYTE="
COUNT = 8; // 8 bits in BYTE
while (COUNT)
{
TEMP = (BYTE & 0b00000001); // Move bit 0 of BYTE into TEMP
TEMP |= 0x30; // Convert to ASCII
BYTESTR[COUNT] = TEMP; // and move to string
BYTE = BYTE>>1; // Right shift bits in BYTE by 1
COUNT--; // Decrement COUNT;
}
BYTESTR[0] = 0xC0; // Add cursor-positioning code
BYTESTR[9] = 0; // and end-of-string terminator
DisplayV(BYTESTR); // Display the string
}
/*******************************
* RateRPG()
*
* This subroutine decyphers RPG changes into values of DELRPG.
* DELRPG = +2 for fast CW change, +1 for slow CW change, 0 for no change,
* -1 for slow CCW change and -2 for fast CCW change.
*******************************
*/
void RateRPG()
{
#define Threshold 3 // Value to distinguish between slow and fast
char W_temp;
PORTAbits.RA2 = 0; // Turn LED off
DELRPG = 0; // Clear for "no change" return value
W_temp = PORTD; // Copy PORTD into W_temp
TEMP = W_temp; // and TEMP
W_temp = W_temp ^ OLDPORTD; // Any change?
W_temp = W_temp & 0b00000011; // If not, W_temp = 0
if (W_temp != 0) // If the two bits have changed then...
{
W_temp = OLDPORTD>>1; // Form what a CCW change would produce
if (OLDPORTD & 0x01) // Make new bit 1 = complement of old bit 0
W_temp &= 0b11111101;
else
W_temp |= 0b00000010;
W_temp = (W_temp ^ TEMP); // Did the RPG actually change to this output?
W_temp &= 0b00000011; // Mask off upper 6 bits
if (W_temp == 0) // If so, then change DELRPG to -1 for CCW
{
DELRPG--;
if (THR != 0)
{
DELRPG--; // If fast turning, decrement again
PORTAbits.RA2 = 1; // Turn LED on
}
}
else
{
DELRPG++; // Otherwise, change DELRPG to +1 for CW
if (THR != 0)
{
DELRPG++; // If fast turning, increment again
PORTAbits.RA2 = 1; // Turn LED on
}
}
THR = Threshold; // Reinitialize THR
}
if (THR != 0) // Does THR equal zero
THR--; // If not, then decrement it
OLDPORTD = TEMP; // Save PORTD as OLDPORTD for ten ms from now
}
/*******************************
* Pbutton()
*
* This subroutine sorts out long and short pushbutton presses into two outputs:
* ISC=1: Initiate screen change for slow press
* ISA=1: Initiate secondary action for fast press
* PDONE=1 One of the above actions has occurred for this press
*******************************
*/
void Pbutton()
{
PBSTATEbits.ISC = 0; // Clear Initiate Screen Change bit (if set)
PBSTATEbits.ISA = 0; // Clear Initiate Secondary Action bit (if set)
if (PORTDbits.RD3 == 1) // Copy pushbutton state to NEWPB
PBSTATEbits.NEWPB = 1;
else
PBSTATEbits.NEWPB = 0;
if (PBSTATEbits.OLDPB) // Look for leading edge (OLDPB=1, NEWPB=0)
{
if (!PBSTATEbits.NEWPB)
PBCOUNT = PBthres; // Start counter
}
if (!PBSTATEbits.NEWPB) // Pushbutton is still pressed
{
if (!PBCOUNT) // and counter has passed threshold
if (!PBSTATEbits.PDONE) // and no action has yet been taken
{
PBSTATEbits.ISC = 1; // Initiate screen change
PBSTATEbits.PDONE = 1; // Done with pulse
}
}
else // Pushbutton has been released
PBSTATEbits.PDONE = 0; // so clear PDONE
if (!PBSTATEbits.OLDPB) // Look for trailing edge (OLDPB=0, NEWPB=1)
if (PBSTATEbits.NEWPB)
{
if (PBCOUNT) // Fast pulse
PBSTATEbits.ISA = 1; // Initiate secondary action
PBSTATEbits.PDONE = 0; // Done with pulse
PBCOUNT = 0; // Finish counting
}
if (PBCOUNT) // Has counter reached zero?
PBCOUNT--; // If not, then decrement it
if (PBSTATEbits.NEWPB) // Copy NEWPB to OLDPB
PBSTATEbits.OLDPB = 1;
else
PBSTATEbits.OLDPB = 0;
}
/*******************************
* Screens()
*
* This subroutine uses the ISC bit from the Pbutton subroutine to cycle the
* state of SCREEN and to take action based upon its value.
* Initially SCREEN=0, so that whatever screen is displayed by the Initial
* subroutine is not changed until a PB switch press. Then the screen
* corresponding to SCREEN=1 is displayed. Subsequent PB switch
* presses cycle through SCREEN=2, 3, etc., recycling back to SCREEN=1.
*******************************
*/
void Screens()
{
#define NumberOfScreens 4 // Change this value if new screens are added
if (PBSTATEbits.ISC)
{
SCREEN++;
if (SCREEN == (NumberOfScreens+1)) // Check if past last screen
SCREEN = 1; // Cycle back to SCREEN = 1
DisplayC(Clear1); // Clear the display when switching screens
DisplayC(Clear2);
}
if (SCREEN == 1)
{
if (PBSTATEbits.ISC)
DisplayC(FreqStr);
Frequency();
}
if (SCREEN == 2)
{
if (PBSTATEbits.ISC)
DisplayC(PerStr);
Period();
}
if (SCREEN == 3)
{
if (PBSTATEbits.ISC)
DisplayC(PWmaxStr);
PWmax();
if (PBSTATEbits.ISA) // Fast pulse, toggle PORTAbits.RA1
PORTAbits.RA1 = !PORTAbits.RA1;
}
if (SCREEN == 4)
{
RateRPG(); // Decypher RPG inputs into DELRPG
RPGCNT += DELRPG; // Increment or decrement RPGCNT from RPG
BYTE = RPGCNT; // Point BYTE to RPGCNT
ByteDisplay();
if (PBSTATEbits.ISA) // Fast pulse, reset RPGCNT
RPGCNT = 0;
}
}
/////// Stubs for measurement subroutines /////////////////////////////////////
void Frequency() { }
void Period() { }
void PWmax() { }
Anyone can help me out with this code ?
http://microdesignsinc.com/picbook/downloads/c/P3_MCC18.c
/******* P3_MCC18.c ************************************************************
*
* Use 10 MHz crystal frequency.
* Use Timer0 for ten millisecond looptime.
* Blink "Alive" LED every two and a half seconds.
* Use pushbutton to exercise Screens utility.
*
******* Program hierarchy *****************************************************
*
* Mainline
* Initial
* InitLCD
* LoopTime
* DisplayC
* T40
* BlinkAlive
* Pbutton
* Screens
* DisplayC
* T40
* Frequency
* Period
* PWmax
* RateRPG
* ByteDisplay
* DisplayC
* T40
* DisplayV
* T40
* LoopTime
*
*******************************************************************************
*/
#include <p18cxxx.h>
/*******************************
* Assembler directives
*******************************
*/
/*******************************
* Definitions and equates
*******************************
*/
#define PBthres 30 // Pushbutton threshold for a long press
/*******************************
* Global variables
*******************************
*/
char TMR0LCOPY; // Copy of sixteen-bit Timer0 used by LoopTime
char TMR0HCOPY;
char INTCONCOPY; // Copy of INTCON for LoopTime subroutine
char COUNT; // Counter available as local to subroutines
char TEMP; // Temporary local variable
char ALIVECNT; // Counter for blinking "Alive" LED
char BYTE; // Eight-bit byte to be displayed
char OLDPORTD; // Holds previous value of inputs
char DELRPG; // Generated by RPG
char RPGCNT; // Used to display RPG changes
char PBCOUNT; // Counter for measuring duration of press
char SCREEN; // State of LCD subroutine
char LOOP10; // Scale of ten loop counter
char THR; // Threshold value used by Pbutton
struct { // Control/status bits for pushbutton
unsigned ISC:1; // Initiate screen change for slow press
unsigned ISA:1; // Initiate secondary action for fast press
unsigned PDONE:1; // Pushbutton action has been taken
unsigned OLDPB:1; // Old state of pushbutton
unsigned NEWPB:1; // New state of pushbutton
} PBSTATEbits;
/*******************************
* Constant strings
*******************************
*/
// For stability reasons, create an EVEN number of elements in any given array
const char LCDstr[] = {0x33,0x32,0x28,0x01,0x0c,0x06,0x00,0x00};// LCD Initialization string
const char StrtStr[] = {0x80,'P','u','s','h',' ','P','B',' ',0};// Startup screen
const char BYTE_1[] = {0x80,'B','Y','T','E','=',' ',' ',' ',0};// Write "BYTE=" 1st line of LCD
const char Clear1[] = {0x80,' ',' ',' ',' ',' ',' ',' ',' ',0};// Clear line 1
const char Clear2[] = {0xC0,' ',' ',' ',' ',' ',' ',' ',' ',0};// Clear line 2
const char FreqStr[] = {0x80,'F','r','e','q',' ','k','H','z',0};// Frequency instrument
const char PerStr[] = {0x80,'P','e','r',' ',' ',' ','u','s',0};// Period instrument
const char PWmaxStr[] = {0x80,'P','W','m','a','x',' ','u','s',0};// Max pulse width instrument
/*******************************
* Variable strings
*******************************
*/
char BYTESTR[] = {0,0,0,0,0,0,0,0,0,0}; // Display string for binary version of BYTE
/*******************************
* Function prototypes
*******************************
*/
void Initial(void);
void InitLCD(void);
void Looptime(void);
void T40(void);
void DisplayC(const char *);
void DisplayV(char *);
void BlinkAlive(void);
void LoopTime(void);
void ByteDisplay(void);
void RateRPG(void);
void Pbutton(void);
void Screens(void);
void Frequency(void);
void Period(void);
void PWmax(void);
/////// Mainline program ////////////////////////////////////////
/*******************************
* main()
*******************************
*/
void main()
{
Initial(); // Initialize everything
while(1)
{
PORTBbits.RB0 = !PORTBbits.RB0; // Toggle pin, to support measuring loop time
BlinkAlive(); // Blink "Alive" LED
Pbutton(); // Check pushbutton
Screens(); // Deal with SCREEN state
LoopTime(); // Make looptime be ten milliseconds
}
}
/*******************************
* Initial()
*
* This subroutine performs all initializations of variables and registers.
*******************************
*/
void Initial()
{
ADCON1 = 0b10001110; // Enable PORTA & PORTE digital I/O pins
TRISA = 0b11100001; // Set I/O for PORTA
TRISB = 0b11011100; // Set I/O for PORTB
TRISC = 0b11010000; // Set I/0 for PORTC
TRISD = 0b00001111; // Set I/O for PORTD
TRISE = 0b00000000; // Set I/O for PORTE
T0CON = 0b10001000; // Set up Timer0 for a looptime of 10 ms
PORTA = 0b00010000; // Turn off all four LEDs driven from PORTA
OLDPORTD = PORTD; // Initialize "old" value
RPGCNT = 0; // Clear counter to be displayed
PBCOUNT = 0; // and pushbutton count
SCREEN = 0; // Initialize LCD's SCREEN variable
THR = 0; // Initialize Pbutton's THR variable
PBSTATEbits.ISC = 0; // Initialize pushbutton state
PBSTATEbits.ISA = 0;
PBSTATEbits.PDONE = 0;
PBSTATEbits.OLDPB = 1;
PBSTATEbits.NEWPB = 0;
InitLCD(); // Initialize LCD
DisplayC(StrtStr); // Display startup message
}
/*******************************
* InitLCD()
*
* Initialize the Optrex 8x2 character LCD.
* First wait for 0.1 second, to get past display's power-on reset time.
*******************************
*/
void InitLCD()
{
char currentChar;
char *tempPtr;
COUNT = 10; // Wait 0.1 second
while (COUNT)
{
LoopTime(); // Call LoopTime() 10 times
COUNT--;
}
PORTEbits.RE0 = 0; // RS=0 for command
tempPtr = LCDstr;
while (*tempPtr) // if the byte is not zero
{
currentChar = *tempPtr;
PORTEbits.RE1 = 1; // Drive E pin high
PORTD = currentChar; // Send upper nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will accept nibble
LoopTime();
currentChar <<= 4; // Shift lower nibble to upper nibble
PORTEbits.RE1 = 1; // Drive E pin high again
PORTD = currentChar; // Write lower nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will process byte
LoopTime(); // Wait 40 usec
tempPtr++; // Increment pointerto next character
}
}
/*******************************
* T40()
*
* Pause for 40 microseconds or 40/0.4 = 100 clock cycles.
* Assumes 10/4 = 2.5 MHz internal clock rate.
*******************************
*/
void T40()
{
// Measured with oscilloscope to be about 42.80 us
// including the time to call this routine. Decrementing each
// "cCOUNT" takes approximately 4 us.
unsigned char cCOUNT = 7;
while (cCOUNT)
cCOUNT--;
}
/*******************************
* DisplayC(const char *)
*
* This subroutine is called with the passing in of an array of a constant
* display string. It sends the bytes of the string to the LCD. The first
* byte sets the cursor position. The remaining bytes are displayed, beginning
* at that position.
* This subroutine expects a normal one-byte cursor-positioning code, 0xhh, or
* an occasionally used two-byte cursor-positioning code of the form 0x00hh.
*******************************
*/
void DisplayC(const char * tempPtr)
{
char currentChar;
PORTEbits.RE0 = 0; // Drive RS pin low for cursor-positioning code
while (*tempPtr) // if the byte is not zero
{
currentChar = *tempPtr;
PORTEbits.RE1 = 1; // Drive E pin high
PORTD = currentChar; // Send upper nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will accept nibble
currentChar <<= 4; // Shift lower nibble to upper nibble
PORTEbits.RE1 = 1; // Drive E pin high again
PORTD = currentChar; // Write lower nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will process byte
T40(); // Wait 40 usec
PORTEbits.RE0 = 1; // Drive RS pin high for displayable characters
tempPtr++; // Increment pointerto next character
}
}
/*******************************
* DisplayV(char *)
*
* This subroutine is called with the passing in of an array of a variable
* display string. It sends the bytes of the string to the LCD. The first
* byte sets the cursor position. The remaining bytes are displayed, beginning
* at that position.
*******************************
*/
void DisplayV(char * tempPtr)
{
char currentChar;
PORTEbits.RE0 = 0; // Drive RS pin low for cursor-positioning code
while (*tempPtr) // if the byte is not zero
{
currentChar = *tempPtr;
PORTEbits.RE1 = 1; // Drive E pin high
PORTD = currentChar; // Send upper nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will accept nibble
currentChar <<= 4; // Shift lower nibble to upper nibble
PORTEbits.RE1 = 1; // Drive E pin high again
PORTD = currentChar; // Write lower nibble
PORTEbits.RE1 = 0; // Drive E pin low so LCD will process byte
T40(); // Wait 40 usec
PORTEbits.RE0 = 1; // Drive RS pin high for displayable characters
tempPtr++; // Increment pointerto next character
}
}
/*******************************
* BlinkAlive()
*
* This subroutine briefly blinks the LED next to the PIC every two-and-a-half
* seconds.
*******************************
*/
void BlinkAlive()
{
PORTAbits.RA4 = 1; // Turn off LED
if (!(--ALIVECNT)) // Decrement loop counter and return if not zero
{
ALIVECNT = 250; // Reinitialize BLNKCNT
PORTAbits.RA4 = 0; // Turn on LED for ten milliseconds every 2.5 sec
}
}
/*******************************
* LoopTime()
*
* This subroutine waits for Timer0 to complete its ten millisecond count
* sequence. It does so by waiting for sixteen-bit Timer0 to roll over. To obtain
* a period of precisely 10000/0.4 = 25000 clock periods, it needs to remove
* 65536-25000 or 40536 counts from the sixteen-bit count sequence. The
* algorithm below first copies Timer0 to RAM, adds "Bignum" to the copy ,and
* then writes the result back to Timer0. It actually needs to add somewhat more
* counts to Timer0 than 40536. The extra number of 12+2 counts added into
* "Bignum" makes the precise correction.
*******************************
*/
void LoopTime()
{
#define Bignum 65536-25000+12+2
while (!INTCONbits.T0IF); // Wait until ten milliseconds are up
INTCONCOPY = INTCON; // Save INTCON bits
INTCONbits.GIEH = 0; // Disable all interrupts from CPU
INTCONbits.GIEL = 0;
TMR0LCOPY = TMR0L; // Read 16-bit counter at this moment
TMR0HCOPY = TMR0H;
TMR0LCOPY += Bignum & 0x00FF; // add LSB
if (STATUSbits.C) // If Carry, increment high byte
TMR0HCOPY++;
TMR0HCOPY += (Bignum>>8) & 0x00FF; // add MSB
TMR0H = TMR0HCOPY;
TMR0L = TMR0LCOPY; // Write 16-bit counter at this moment
WREG = INTCONCOPY & 0b11000000; // Reenable interrupts to CPU if enabled prior
INTCON = INTCON | WREG; // to LoopTime
INTCONbits.T0IF = 0; // Clear Timer0 flag
}
/*******************************
* ByteDisplay()
*
* Display whatever is in BYTE as a binary number.
*******************************
*/
void ByteDisplay()
{
DisplayC(BYTE_1); // Display "BYTE="
COUNT = 8; // 8 bits in BYTE
while (COUNT)
{
TEMP = (BYTE & 0b00000001); // Move bit 0 of BYTE into TEMP
TEMP |= 0x30; // Convert to ASCII
BYTESTR[COUNT] = TEMP; // and move to string
BYTE = BYTE>>1; // Right shift bits in BYTE by 1
COUNT--; // Decrement COUNT;
}
BYTESTR[0] = 0xC0; // Add cursor-positioning code
BYTESTR[9] = 0; // and end-of-string terminator
DisplayV(BYTESTR); // Display the string
}
/*******************************
* RateRPG()
*
* This subroutine decyphers RPG changes into values of DELRPG.
* DELRPG = +2 for fast CW change, +1 for slow CW change, 0 for no change,
* -1 for slow CCW change and -2 for fast CCW change.
*******************************
*/
void RateRPG()
{
#define Threshold 3 // Value to distinguish between slow and fast
char W_temp;
PORTAbits.RA2 = 0; // Turn LED off
DELRPG = 0; // Clear for "no change" return value
W_temp = PORTD; // Copy PORTD into W_temp
TEMP = W_temp; // and TEMP
W_temp = W_temp ^ OLDPORTD; // Any change?
W_temp = W_temp & 0b00000011; // If not, W_temp = 0
if (W_temp != 0) // If the two bits have changed then...
{
W_temp = OLDPORTD>>1; // Form what a CCW change would produce
if (OLDPORTD & 0x01) // Make new bit 1 = complement of old bit 0
W_temp &= 0b11111101;
else
W_temp |= 0b00000010;
W_temp = (W_temp ^ TEMP); // Did the RPG actually change to this output?
W_temp &= 0b00000011; // Mask off upper 6 bits
if (W_temp == 0) // If so, then change DELRPG to -1 for CCW
{
DELRPG--;
if (THR != 0)
{
DELRPG--; // If fast turning, decrement again
PORTAbits.RA2 = 1; // Turn LED on
}
}
else
{
DELRPG++; // Otherwise, change DELRPG to +1 for CW
if (THR != 0)
{
DELRPG++; // If fast turning, increment again
PORTAbits.RA2 = 1; // Turn LED on
}
}
THR = Threshold; // Reinitialize THR
}
if (THR != 0) // Does THR equal zero
THR--; // If not, then decrement it
OLDPORTD = TEMP; // Save PORTD as OLDPORTD for ten ms from now
}
/*******************************
* Pbutton()
*
* This subroutine sorts out long and short pushbutton presses into two outputs:
* ISC=1: Initiate screen change for slow press
* ISA=1: Initiate secondary action for fast press
* PDONE=1 One of the above actions has occurred for this press
*******************************
*/
void Pbutton()
{
PBSTATEbits.ISC = 0; // Clear Initiate Screen Change bit (if set)
PBSTATEbits.ISA = 0; // Clear Initiate Secondary Action bit (if set)
if (PORTDbits.RD3 == 1) // Copy pushbutton state to NEWPB
PBSTATEbits.NEWPB = 1;
else
PBSTATEbits.NEWPB = 0;
if (PBSTATEbits.OLDPB) // Look for leading edge (OLDPB=1, NEWPB=0)
{
if (!PBSTATEbits.NEWPB)
PBCOUNT = PBthres; // Start counter
}
if (!PBSTATEbits.NEWPB) // Pushbutton is still pressed
{
if (!PBCOUNT) // and counter has passed threshold
if (!PBSTATEbits.PDONE) // and no action has yet been taken
{
PBSTATEbits.ISC = 1; // Initiate screen change
PBSTATEbits.PDONE = 1; // Done with pulse
}
}
else // Pushbutton has been released
PBSTATEbits.PDONE = 0; // so clear PDONE
if (!PBSTATEbits.OLDPB) // Look for trailing edge (OLDPB=0, NEWPB=1)
if (PBSTATEbits.NEWPB)
{
if (PBCOUNT) // Fast pulse
PBSTATEbits.ISA = 1; // Initiate secondary action
PBSTATEbits.PDONE = 0; // Done with pulse
PBCOUNT = 0; // Finish counting
}
if (PBCOUNT) // Has counter reached zero?
PBCOUNT--; // If not, then decrement it
if (PBSTATEbits.NEWPB) // Copy NEWPB to OLDPB
PBSTATEbits.OLDPB = 1;
else
PBSTATEbits.OLDPB = 0;
}
/*******************************
* Screens()
*
* This subroutine uses the ISC bit from the Pbutton subroutine to cycle the
* state of SCREEN and to take action based upon its value.
* Initially SCREEN=0, so that whatever screen is displayed by the Initial
* subroutine is not changed until a PB switch press. Then the screen
* corresponding to SCREEN=1 is displayed. Subsequent PB switch
* presses cycle through SCREEN=2, 3, etc., recycling back to SCREEN=1.
*******************************
*/
void Screens()
{
#define NumberOfScreens 4 // Change this value if new screens are added
if (PBSTATEbits.ISC)
{
SCREEN++;
if (SCREEN == (NumberOfScreens+1)) // Check if past last screen
SCREEN = 1; // Cycle back to SCREEN = 1
DisplayC(Clear1); // Clear the display when switching screens
DisplayC(Clear2);
}
if (SCREEN == 1)
{
if (PBSTATEbits.ISC)
DisplayC(FreqStr);
Frequency();
}
if (SCREEN == 2)
{
if (PBSTATEbits.ISC)
DisplayC(PerStr);
Period();
}
if (SCREEN == 3)
{
if (PBSTATEbits.ISC)
DisplayC(PWmaxStr);
PWmax();
if (PBSTATEbits.ISA) // Fast pulse, toggle PORTAbits.RA1
PORTAbits.RA1 = !PORTAbits.RA1;
}
if (SCREEN == 4)
{
RateRPG(); // Decypher RPG inputs into DELRPG
RPGCNT += DELRPG; // Increment or decrement RPGCNT from RPG
BYTE = RPGCNT; // Point BYTE to RPGCNT
ByteDisplay();
if (PBSTATEbits.ISA) // Fast pulse, reset RPGCNT
RPGCNT = 0;
}
}
/////// Stubs for measurement subroutines /////////////////////////////////////
void Frequency() { }
void Period() { }
void PWmax() { }

