28 Eylül 2017 Perşembe

Write and Read to/from EEPROM

Proteus: We will use an LCD connected to B ports,


MCC: Just add MEMORY peripheral and left default settings. internal 1MHz osc, disable MCLR in pin manager. no interrupt.

XC8: we will write addresses starting from 0x01 . Read these addresses and show on LCD. That's so simple. 

   volatile unsigned char RAMArray[10];
    unsigned char i, s1[10];

    for(i=0; i<10; i++){
    DATAEE_WriteByte(0x01 + i, i*i);
    }
    
    OpenXLCD(FOUR_BIT & LINES_5X7);
    WriteCmdXLCD(DON&CURSOR_OFF&BLINK_OFF); 
    while (1)
    {
        // Add your application code
        
        for(i=0; i<10; i++){
        RAMArray[i] = DATAEE_ReadByte(0x01 + i);
        
        sprintf(s1,"%02d",RAMArray[i]);
        
        while(BusyXLCD());
        SetDDRamAddr(0x01); // clear LCD        
        putrsXLCD("Memory Example");
        SetDDRamAddr(0x40); // goto second line
        while(BusyXLCD());
        putrsXLCD("Data=");putrsXLCD(s1);
        
        __delay_ms(1000);
        }
    }
}

download the example

27 Eylül 2017 Çarşamba

SPI Example Using TC77

Proteus: SPI is another digital bus to communicate multiple devices. We will read temperature through TC77 using SPI bus. I connected oscilloscope and SPI debugger for diagnostic purposes. Connections  :
PIC RC3 SCL clock --> TC77 SCK clock
PIC RC4 SDI input data --> TC77 I/O
PIC RC0 output --> TC77 CS chip select

Equipment in SPI is selected by making chip select port to Low.



MCC: internal osc 1Mhz selected, MSSP with SPI master selected. input data sampled at End, clock polarity is idle to active, clock edge active to idle. SPI clock is selected at 15.625khz. We also selected  RC0 as output pin for chip select on SPI bus. Nothing is selected in interrupt tab.





XC8: dont touch interrupts, everything is disabled.  We will set Low RC0 to enable communication with TC77. Then, read 8bits, twice. disable TC77 by setting RC0 high. TC77 uses 13bits with 0.0625 resolution. Thus multiply 0.0625 with 13 bits. It may need different formula for negative temperatures.


    unsigned char s1[10];
    uint8_t  TempLSB, TempMSB           = 0;
    
    OpenXLCD(FOUR_BIT & LINES_5X7);
    WriteCmdXLCD(DON&CURSOR_OFF&BLINK_OFF); 

    while (1)
    {
        // Add your application code
        //SPI_Exchange8bit(0x00);
        
        IO_RC0_SetLow(); 
        TempMSB = SPI_Exchange8bit(0x00);
        TempLSB = SPI_Exchange8bit(0x00);
        IO_RC0_SetHigh(); 

        sprintf(s1, "%3.1f", ((TempMSB<<8|TempLSB)>>3)*0.0625);   
    

        while(BusyXLCD());
        SetDDRamAddr(0x01); // clear LCD        
        putrsXLCD("TC77 Example");
        SetDDRamAddr(0x40); // goto second line
        while(BusyXLCD());
        putrsXLCD("Temp=");putrsXLCD(s1);
        __delay_ms(1000);
    }
}


26 Eylül 2017 Salı

Setting DS1307

If we dont set DS1307 it will show computer clock where Proteus is running on. Instead we can set hour, minnutes,seconds etc any value on registers. Let's set everything to zero for example.




    unsigned char s1[10], s2[10];
    uint8_t readValue[7], writeValue[8]={0,0,0,0,0,0,0,0};
    I2C_MESSAGE_STATUS    I2C_status;
 
 
    I2C_MasterWrite( &writeValue, 7, 0b1101000, &I2C_status); //set pointer address zero and write 7 zero
    while (I2C_MESSAGE_PENDING  == I2C_status );

Time will start from 00:00:00 Similarly we can set only hours, minutes, seconds etc by adding some buttons to the circuit. If button pressed increase hours +1 mod 24 ...



 

Reading multiple TC74 on I2C

We can read/write multiple devices on I2C by addressing them. Let's read two TC74 with different addresses, 0x96 (A1 option) and 0x9A (A5 option).  we will unhide address to see on Proteus. What will be the 7-bit address on write/read commands
0x9A, 9=1001, A=1010, 3bit 101,  7bit= 0b1001101
0x96,  9=1001, 6=0110, 3bit 011, 7bit=0b1001011


we can modify the code for multiple TC74 as below:

    unsigned char s1[10], s2[10];
    uint8_t readValue[1], readValue2[1];
    I2C_MESSAGE_STATUS    I2C_status;
    
    OpenXLCD(FOUR_BIT & LINES_5X7);
    WriteCmdXLCD(DON&CURSOR_OFF&BLINK_OFF); 
    
    while (1)
    {
        // Add your application code
        
    I2C_MasterWrite( 0, 1, 0b1001101, &I2C_status);
    while (I2C_MESSAGE_PENDING  == I2C_status);    
    I2C_MasterRead( &readValue, 1, 0b1001101, &I2C_status);
    while (I2C_MESSAGE_PENDING  == I2C_status );

    sprintf(s1, "%02d", readValue[0]);   
    
    I2C_MasterWrite( 0, 1, 0b1001011, &I2C_status);
    while (I2C_MESSAGE_PENDING  == I2C_status);    
    I2C_MasterRead( &readValue2, 1, 0b1001011, &I2C_status);
    while (I2C_MESSAGE_PENDING  == I2C_status );
    
    sprintf(s2, "%02d", readValue2[0]);   
    

        while(BusyXLCD());
        SetDDRamAddr(0x01); // clear LCD        
        putrsXLCD("Temp1=");putrsXLCD(s1);
        SetDDRamAddr(0x40); // goto second line
        while(BusyXLCD());
        putrsXLCD("Temp2=");putrsXLCD(s2);
        __delay_ms(1000);

    }
}

I2C Debugger

I2C debugger of Proteus is a useful virtual instrument. You can follow the traffic on the BUS as sniffer or you send messages to devices on the bus. If we look at TC74 example, we see that there are two type of communication caused by write and read functions.

    I2C_MasterWrite( 0, 1, 0b1001101, &I2C_status) :                 S 9A A 00 A P
    I2C_MasterRead( &readValue, 1, 0b1001101, &I2C_status) : S 9B A 1A N P

Syntax of I2C is given at help file.

TC74 datasheet provides structure of write and read protocols. As you can see S 9A A 00 A P corresponds to a write sequence and S 9B A 1A N P corresponds to a receive byte. 1A is the temperature value.


We can monitor clock (blue) and data (yellow) also in oscilloscope 



Possible errors in I2C: 
  1. Pullup resistors are left at ANALOG, change them to DIGITAL
  2. Power supply must be VCC, dont put separate +5V to feed I2C bus
  3. Address may become wrong. In that case, equipment will not respond and you will receive 0x00 byte. for example, I changed the A5 address of TC74 to A1, look at I2C debugger will show only traffic coming from PIC like "S 9A N P" but there is no response from TC74.


RTC with DS1307 and I2C

Proteus: RC3 (clock) and RC4 (data) connected to DS1307. You can add scope and I2C debugger for diagnostics. Pullup registors will be 4.7k and DIGITAL. Power supply will be VCC, not a separate +5V, otherwise it will not work.


MCC: same settings as it was in TC74 example.

XC8: 
include driver files for LCD. ENABLE global and peripheral interrupts. DS1307 updates hour, minutes seconds etc at different registers. Select register zero for read using write function. Read 7 bytes starting from address zero and display on LCD accordingly. Set addresses using 0b1101000 binary format but not 0xD0 otherwise it will not work because write and read functions shifts the address and adds zero for writing or one for reading. This causes change of 0xD0 to a different address and will result with 00:00:00 



#include "mcc_generated_files/mcc.h"
#include "delays.h"
#include "myxlcd.h"
#include <stdio.h>

/*
                         Main application
 */
void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

    // Enable the Global Interrupts
     INTERRUPT_GlobalInterruptEnable();

    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();

    // Enable the Peripheral Interrupts
     INTERRUPT_PeripheralInterruptEnable();

    unsigned char s1[10], s2[10];
    uint8_t readValue[7];
    I2C_MESSAGE_STATUS    I2C_status;
    
    OpenXLCD(FOUR_BIT & LINES_5X7);
    WriteCmdXLCD(DON&CURSOR_OFF&BLINK_OFF); 

    while (1)
    {
        // Add your application code
        
    I2C_MasterWrite( 0, 1, 0b1101000, &I2C_status); // set pointer address to zero
    while (I2C_MESSAGE_PENDING  == I2C_status);
    I2C_MasterRead( &readValue, 7, 0b1101000, &I2C_status); // read 7 bytes starting from zero
    while (I2C_MESSAGE_PENDING  == I2C_status );
           
    sprintf(s1, "%02X/%02X/%02X", readValue[4],readValue[5],readValue[6]);             
    sprintf(s2, "%02X:%02X:%02X", readValue[2],readValue[1],readValue[0]);             

        while(BusyXLCD());
        SetDDRamAddr(0x01); // clear LCD        
        putrsXLCD("Date=");putrsXLCD(s1);
        SetDDRamAddr(0x40); // goto second line
        while(BusyXLCD());
        putrsXLCD("Time=");putrsXLCD(s2);
        __delay_ms(1000);

    }
}



Reading TC74 via I2C


Proteus: RC3 (clock) and RC4 (data) ports  are used two wire I2C bus connection. Pullup resistors  R1, R2 are 4.7kohm and are connected to VCC. Be careful that R1 and R2 will be set to DIGITAL in proteus (edit, properties) and power line will be VCC not separate +5V, otherwise it is not working and took my couple of days. It may work with debugger but may not work without debugger if you keep resistors in analog.

You can use oscilloscope and I2C debugger for better understanding of I2C bus operation or diagnostics.

MCC: Add MSSP peripheral for I2C bus. PIC will be the master on the bus and you can select bus clock as you want, such as 1kHz. 1MHz internal oscillator is used as in previous examples. default selections in interrupt and pin modules. RC3, RC4 will be selected in pin manager.

XC8: include delays.h and myxlcd.h for LCD operation. stdio.h is required for sprintf() function. ENABLE Global and Peripheral interrupts.
I2C_MasterWrite( 0, 1, 0b1001101, &I2C_status) will select register-0 at address 0x9A=0b1001101 for reading. 
I2C_MasterRead( &readValue, 1, 0b1001101, &I2C_status) will read 1 byte at the same address. dont worry about adding 0 for write and 1 for read addresses, these functions will handle it for you.

Convert readValue to decimal and print on LCD.


TC74 has single one byte register for temperature. 

common errors:
If address of TC74 is not the same as in the code you can read zero value.
write addresses in binary 0b1001101 not in hex 0x9A otherwise will not work.


#include "mcc_generated_files/mcc.h"
#include "delays.h"
#include "myxlcd.h"
#include <stdio.h>

/*
                         Main application
 */
void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

    // Enable the Global Interrupts
     INTERRUPT_GlobalInterruptEnable();

    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();

    // Enable the Peripheral Interrupts
     INTERRUPT_PeripheralInterruptEnable();

    unsigned char s1[10], s2[10];
    uint8_t readValue[1];
    I2C_MESSAGE_STATUS    I2C_status;
    
    OpenXLCD(FOUR_BIT & LINES_5X7);
    WriteCmdXLCD(DON&CURSOR_OFF&BLINK_OFF); 
    
    while (1)
    {
        // Add your application code
        
    I2C_MasterWrite( 0, 1, 0b1001101, &I2C_status);
    while (I2C_MESSAGE_PENDING  == I2C_status);    
    I2C_MasterRead( &readValue, 1, 0b1001101, &I2C_status);
    while (I2C_MESSAGE_PENDING  == I2C_status );

    sprintf(s1, "%02d", readValue[0]);             

        while(BusyXLCD());
        SetDDRamAddr(0x01); // clear LCD        
        putrsXLCD("Thermometer");
        SetDDRamAddr(0x40); // goto second line
        while(BusyXLCD());
        putrsXLCD("Temp=");putrsXLCD(s1);
        __delay_ms(1000);

    }
}

download the example

17 Eylül 2017 Pazar

Motor Speed Control

We can further enhance the simple motor control example by adding speed control using PWM.

Proteus: RA0 is used for ADC input, RC1 is for PWM output (it may differ to PIC model used). RC0 and RC2 will control the motor direction. We added a scope to observe PWM signal. PWM output is connected to ENABLE port of the motor to control the speed.


MCC: add CCP2, TMR2 and ADC peripherals. CCP2 uses TMR2. Select PWM mode.








XC8: first set motor rotation direction using RC0, RC2 ports. Now, you can adjust the speed within while loop; read ADC, get the value change the duty cycle.


    IO_RC0_SetHigh();
    IO_RC2_SetLow();

    while (1)
    {
        // Add your application code
        ADC_StartConversion();
        while(!ADC_IsConversionDone());
        c=ADC_GetConversionResult();
        PWM2_LoadDutyValue(c);

    }
}



16 Eylül 2017 Cumartesi

Control a DC Motor


Proteus: We will use RC0 and RC1 output to control the motor. L293D will drive the motor. 
RC0=1, RC1=0 will turn CW
RC0=0, RC1=1 will turn CCW
RC0=0, RC1=0 will stop



MCC:  just set RC0, RC1 as output. 1MHZ, INTOSC, disable MCLR

XC8: inside the main while(1) loop you can change RC0, RC1 pins as you want. turn CW, turn CCW and stop for example.

    while (1)
    {
        // Add your application code
        IO_RC0_SetHigh();
        __delay_ms(2000);
        IO_RC0_SetLow();
        IO_RC1_SetHigh();
        __delay_ms(2000);
        IO_RC0_SetLow();
        IO_RC1_SetLow();
        __delay_ms(2000);
       
    }

download the example

Comparator and Fixed Voltage Reference

Comparator function of PIC can compare a voltage at one comparator port to other comparator port. We can create external reference voltages using diodes, zener etc. Other option is to use internal Fixed Voltage Reference (FVR).

Proteus: we will use RA0 (C12IN0-) comparator port for input voltage using a resistor POT. PIC18f24K20 has internal 1.2V fixed voltage reference that we will compare to RA0 port voltage. If the input voltage is above 1.2V, LED will light.


MCC:  add CMP1 and FVR peripherals. start with FVR setting, just enable no other option. Enable comparator, select CVREF (internal 1.2V) as positive input and CIN0- negative input at comparator options. select RC1 as output pin to connect the LED. Select RA0 as CIN0- port for input voltage.     



XC8: we add an if statement inside the while(1) loop. If comparator status is "1",  means that RA0 voltage is above 1.2V, set the LED high, else low. That's so simple.

    while (1)
    {
        // Add your application code
        if (CMP1_GetOutputStatus()){
            IO_RC1_SetHigh();
        } 
        else {
            IO_RC1_SetLow();
        }
            
        
    }


14 Eylül 2017 Perşembe

PWM

Pulse Width Modulation output of PIC's can be used for dimming lights (LEDs) or motor control.

Proteus: We will use RC1 PWM output, it may differ for each PIC model check the datasheet. You will observe that duty cycle will change 0-50% and dim the LED.


MCC: add CCP2 and TMR2 peripherals. Select RC1 as output in pin manager and pin module. at CCP2 peripheral, select PWM mode and 50% duty cycle. at TMR2, enable timer, 1:1 prescaler, 1:16 postscaler and 16.384ms period.





XC8: include pwm2.h file. in the main while loop, change duty cycle within a for loop. I used __delay_ms() function instead of for loop to create delay.


#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/pwm2.h"

/*
                         Main application
 */
void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();



    while (1)
    {
        // Add your application code
        for (int duty = 1; duty < 512; duty = duty + 10)
        {
            PWM2_LoadDutyValue(duty);
            //for (int delay = 1; delay < 500; delay++)
            //{;}
            __delay_ms(100);
        }
    }
}

download the example

Interrupt TMR0

We can use TMR0 timer interrupt in a different way. We will add interrupt code inside tmr0.c file instead of main.c as it was in TMR1 example.

Proteus: the same circuit with a LED connected to RA2. We will toggle the LED at 250ms using TMR0 interrupt.


MCC: add TMR1 timer. enable interrupt, select FOSC/4, 16bit and 250ms


XC8: we will add the code to toggle RA2 inside tmr0.c we have to add "mcc.h" to include files otherwise IO_RA2_Toggle() will give error. So, this was 3rd method to blink a LED using timers interrupt and overflows.


#include <xc.h>
#include "tmr0.h"
#include "mcc.h"

void TMR0_ISR(void)
{

    // clear the TMR0 interrupt flag
    INTCONbits.TMR0IF = 0;

    // reload TMR0
    // Write to the Timer0 register
    TMR0H = timer0ReloadVal >> 8;
    TMR0L = (uint8_t) timer0ReloadVal;

    if(TMR0_InterruptHandler)
    {
        TMR0_InterruptHandler();
    }

    // add your TMR0 interrupt custom code
    IO_RA2_Toggle();
}




Timer0 overflow

Timer0 will be used with overflow


Proteus: the same circuit as in interrupt. RA2 connected to a LED and we will blink this LED at every 250ms which will be counted by timer0.


XCC: we will use timer0 but not for interrupt. maximum period value 262ms, we choose 250ms. 1:256 prescaler and FOSC/4 oscillator. internal 1MHZ osc selected and RA2 is selected for output.


XC8: We add a if statement inside the main while loop. If overflow occures (at 250ms) toggle RA2 LED and clear overflow. LED will blink at 250ms period.


   while (1)
    {
        // Add your application code
        if (TMR0_HasOverflowOccured()) //Has Timer0 Overflowed?
        {
            IO_RA2_Toggle(); //Switch state of RA2 LED when overflow occurs
            TMR0IF = 0;      //Clear the Timer0 overflow flag
        }
    }

download the example

13 Eylül 2017 Çarşamba

Interrupt TMR1

Timer interrupt is used to interrupt the code for a given time. I followed the example at http://microchipdeveloper.com/mcu1101:project-10 in PIC18F24K20. It works well.

Proteus: just connect a LED to RA2. We will blink this LED using TMR1 interrupt with a period of 500ms


MCC: select RA2 for output, 1MHz internal OSC and add TMR1 peripheral. clock FOSC/4, scaler 1:2, enable interrupt and synch and write 500ms period.


XC8: enable global and peripheral interrupts. On the ISR routine toggle RA2, this will happen at every 500ms interrupt. that's all.


include "mcc_generated_files/mcc.h"

void myTimerISR(void);


/*
                         Main application
 */
void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();
    
    TMR1_SetInterruptHandler (myTimerISR);     //Define interrupt Handler


    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();


    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();


    while (1)
    {
        // Add your application code
    }
}

void myTimerISR(void){
    IO_RA2_Toggle();
}



Interrupt On Change

I followed the example at microchip development site: http://microchipdeveloper.com/mcu1101:project-9 but LED was blinking instead of toggling the the state at SW change. Finally I found the answer at http://www.microchip.com/forums/m289430.aspx I had to read the port. "A mismatch condition will continue to set this bit. Reading PORTB will end the mismatch condition and allow the bit to be cleared. " DONT FORGET. 

So Proteus part is simple, state change of RB5 will toggle the LED connected to RA0. RB4,5,6,7 ports can be used for on change interrupt in PIC18F24K20. It may be different for different models.



I was thinking that MCC is not working and decided to continue without MCC but finally I fixed the problem.  in MCC: select RB5 as input, RA0 as output. Check IOC for "any" and select internal 1MHz osc. that's all you need. Interrupt module select RBI enabled.



Code is quiete easy with MCC. Just enable Global and peripheral interrupts at main.c by removing comments.

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();


    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();

nothing in while loop
    while (1)
    {
        // Add your application code
    }

in interrupt_manager.c you can add what you want once interrupt is caught. I toggle the LED, read the port and  reset RBIF Port Change Interrupt Flag Bit.

void interrupt INTERRUPT_InterruptManager (void)
{
    // interrupt handler
    if(INTCONbits.RBIE == 1 && INTCONbits.RBIF == 1)
    {
        PIN_MANAGER_IOC();
        IO_RA0_Toggle();
        IO_RB5_GetValue();
        INTCONbits.RBIF=0;
    }
    else
    {
        //Unhandled Interrupt
    }
}

download the example

12 Eylül 2017 Salı

RS232 Communication

We will create a simple circuit to read temperature remotely using RS232 line. RA0 analog port is connected to a LM35 thermometer and RC6, RC7 ports TX/RX connected to a virtual RS232 terminal.




MCC: select peripheral ADC and select RA0 analog port. 

Select EUSART peripheral with default values, 9600, 8bit etc


XC8: reading thermometer value and conversion is as we did in thermometer example. Write the value to EUSART port, char by char. Delay one second. delete the line with backspace to refresh. When you change the temperature on LM35 you will see new values on virtual terminal. 

source code is:

   unsigned char v[12], h;
    uint16_t cV = 0;
    float  t;
    int i;
    
    while (1)
    {
        ADC_StartConversion(channel_AN0);
        while(!ADC_IsConversionDone());
        cV = ADC_GetConversionResult(); 
        t=cV*500.0/65536;
        sprintf(v, "%.1f", t); 
       
        EUSART_Write('T');
        EUSART_Write('e');
        EUSART_Write('m');
        EUSART_Write('p');
        EUSART_Write('=');
        
        EUSART_Write(v[0]);
        EUSART_Write(v[1]);
        EUSART_Write(v[2]);
        EUSART_Write(v[3]);
        EUSART_Write('C');

        __delay_ms(1000);
        
        for (i=0; i<10; i++){
        EUSART_Write(0x08); }//backspace
    
    }



11 Eylül 2017 Pazartesi

Keypad

We can connect a keypad and show pressed key on LCD.

Proteus:
PORTB connected to LCD as in our previous examples. 


MCC: PORTB is set to output for LCD. PortC 0,1,2,3 pins are output and 4,5,6 pins are input. I noticed that if you don't select "Analog"for RB pins in MCC , LCD is not working.

XC8: logic of the program is to make PORTC, 0,1,2,3 high sequentially and check 4,5,6 pins. for example set RC0 high. if "1" is pressed RC4 will be high, "2" will make RC5 high and , "3" pressed will do RC6 high. This is how the keypad switch is working.   

in the code, we are making PORTC, 0,1,2,3 ping high in a for loop and setting the key pressed parameter "c" with if conditions. Then, writing the key pressed "c" to LCD.  


include "mcc_generated_files/mcc.h"
#include "myxlcd.h"
#include "delays.h"
/*
                         Main application
 */
void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();
  int i=0;
    unsigned char c;
    OpenXLCD(FOUR_BIT & LINES_5X7);
    WriteCmdXLCD(DON&CURSOR_OFF&BLINK_OFF); 
    while (1)
    {
        
        for (i=0; i<4; i++){
        if (i==0){
        IO_RC0_SetHigh();
        if (IO_RC4_PORT==1) c='1';
        if (IO_RC5_PORT==1) c='2';
        if (IO_RC6_PORT==1) c='3';
        IO_RC0_SetLow();
        } //if
        if (i==1){
        IO_RC1_SetHigh();
        if (IO_RC4_PORT==1) c='4';
        if (IO_RC5_PORT==1) c='5';
        if (IO_RC6_PORT==1) c='6';
        IO_RC1_SetLow();
        } //if
        if (i==2){
        IO_RC2_SetHigh();
        if (IO_RC4_PORT==1) c='7';
        if (IO_RC5_PORT==1) c='8';
        if (IO_RC6_PORT==1) c='9';
        IO_RC2_SetLow();
        } //if
        if (i==3){
        IO_RC3_SetHigh();
        if (IO_RC4_PORT==1) c='*';
        if (IO_RC5_PORT==1) c='0';
        if (IO_RC6_PORT==1) c='#';
        IO_RC3_SetLow();
        } //if
        
        while(BusyXLCD());
        WriteCmdXLCD(0x01); //clear LCD
        putrsXLCD("Keypad Example");
        SetDDRamAddr(0x40); // goto second line
        putrsXLCD("Key=");putcXLCD(c);
        __delay_ms(100);
        } //for
        
    }