Author Topic: == Example projects in C for PIC18F452 ==  (Read 9812 times)

modtro2

  • Administrator
  • Hero Member
  • *****
  • Posts: 503
    • View Profile
== Example projects in C for PIC18F452 ==
« on: July 02, 2009, 02:52:07 AM »
We have added many example projects for using the LCD2S serial display board in I2C mode, using the PIC18F452 chip. Download it here!. This code can however very easily be ported for most PIC16 and PIC18 chip. The I2C peripheral on most modern PIC chips is the same, just have to ensure it supports master mode.

To use the examples without making any changes, you will need a SBC44B board, with a PIC18F452-I/L chip and 10MHz crystal - get it here!. Additionally you will have to download and install the free MPLAB IDE and MPLAB C18 C compiler from www.microchip.com.

After installing both these programs, unzip the downloaded source code to your local hard drive, and open the *.mcw "MPLAB Workspace" located in the "../demos" folder. Many different project will be opened in the project view of MPLAB. All LCD2S project using the I2C bus have names starting with "lcd2s_i2c_...". Simply select one in the project view (use context menu), click on "built all" button, and program your SBC board. For details on programming a Modtronix SBC board, click here.

For example, the "lcd2s_i2c_kpad_io" project contains code for writing text to the LCD display, checking if keys are pressed on the keypad, and using OUT1 and OUT2 outputs. The full source code is:

Code: [Select]
#include <p18cxxx.h>
#include "stdio.h"

// Configuration bits for PIC18F452 chip with a 10MHz crystal. It sets the following configuration:
// Debug OFF, Oscillator in HS mode with PLL enabled (40Mhz clock), Watchdog timer off, Extended CPU mode off
#pragma config OSC = HSPLL  //HS with 4 x PLL enabled - clock is speed of crystal x 4
#pragma config WDT = OFF    //Watchdog timer can be enabled in software with SWDTEN bit
#pragma config LVP = OFF    //Low voltage programming disabled

//External functions, and function prototypes
unsigned char lcdPutRomString(unsigned char lcdAdr, static rom char* s);
unsigned char lcdPutString(unsigned char lcdAdr, char* s);
unsigned char lcdPutRomCmd(unsigned char lcdAdr, unsigned char len, rom char* s);
unsigned char lcdPutCmd(unsigned char lcdAdr, unsigned char cmd, unsigned char putStop);
unsigned char lcdGetByte(unsigned char lcdAdr, unsigned char cmd, unsigned char* val);

//Defines
#define LCD2S_I2C_ADR 80    /* Default LCD I2C address when both switches on back of LCD2S board are set to 0 */

//Commands
#define CMD_WRITE_STRING    0x80ul      /* Write parsed String command */
#define CMD_CONF_DEV        0x96ul      /* Configure device command */
#define CMD_GET_STATUS      0xD0ul
#define CMD_GET_KEY         0xD1ul
#define CMD_SET_OUT1        0x38ul
#define CMD_CLEAR_OUT1      0x30ul
#define CMD_SET_OUT2        0x39ul
#define CMD_CLEAR_OUT2      0x31ul

/////////////////////////////////////////////////
//Configure LCD using "Configure Device" command (0x95). This command is only available in new V1.4 LCD Displays
//
//Byte 1 (Display) of command = 0x39
//xx1x xxxx - Interrupt pin is Open Collector output type
//xxx1 1xxx - Backlight On, Display On
//xxxx x001 - Cursor off, block cursor off, Cursor moves forward
//
//Byte 2 (Contrast and Brightness) of command = 0x73
//01xx xxxx - Set Backlight to 100 (range is 0 to 255)
//xx11 0011 - Set contrast to 204 (0x33 x 4)
//
//Byte 3 (keypad and IO) of command = 0x40
//0x40 Configures device for 4x3 keypad (if present), OUT1 enabled, GPIO1 to 3 disabled
//
//Byte 4 (Keypad and Buzzer) of command = 0x6A
//01xx xxxx - Keypad Buzzer off
//xx10 xxxx - Keypad repeat rate = 320ms
//xxxx 10xx - Keypad Repeat Delay = 1 second
//xxxx xx10 - Keypad Debounce Time = 64ms
rom char CONF_CMD[] = {0x95, 0x39, 0x73, 0x40, 0x6A};

void delay (unsigned long dly)
{
  unsigned long i;

  for (i = 0; i < dly; i++);
}

void main (void)
{
    unsigned char i;
    unsigned char c;
    unsigned char freeBuf;
    char buf[50];
    unsigned char out1;
    unsigned char out2;

    out1 = out2 = 0;    //Initialize out1 and out2

    /////////////////////////////////////////////////
    //Configure SSP Peripheral for I2C Master mode
    //Set port B6 as an output
    TRISBbits.TRISB6 = 0;

    //RC3 (I2C clock) and RC4 (I2C data) are configured as inputs at this stage
    TRISC |= 0b00011000;

    //Delay at while before writing to LCD display
    delay(56000l);    //Delay about 200ms at 40MHz

    //0xxx xxxx = Slew rate off, for high speed mode (400 kHz)
    //x0xx xxxx = Disable SMBus specific inputs
    SSPSTAT &= 0x3F;        // power on state, and clear bits 6 and 7
    //SSPSTAT |= 0x80;        //1xxx xxxx = Slew rate on

    //xx0x xxxx = SSPEN, Disable SSP
    //xxxx 1000 = I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
    SSPCON1 = 0x08;

    SSPCON2 = 0x00;         // power on state
    SSPCON1bits.SSPEN = 1;  //Enable SSP module, and configure port pins as serial port pins
    SSPADD = 24;            //Set Baud rate to 400,000. SSPADD = ( ((40,000,000/400,000) / 4) - 1 ) = 24


    /////////////////////////////////////////////////
    //Configure LCD using "Configure Device" command (0x95). This command is only available in new V1.4 LCD Displays
    if (lcdPutRomCmd(LCD2S_I2C_ADR, sizeof(CONF_CMD), CONF_CMD) == 1) {
        //If returned 1 = error = endless loop. This will cause LED on LCD not to flash.
        //An error will be returned if there is no LCD display on I2C bus for example
        while(1);
    }
   
    /////////////////////////////////////////////////
    //Send "Hello World" string. Sring is contained in ROM memory
    if (lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\fPress Key") == 1) {
        //If returned 1 = error = endless loop. This will cause LED on LCD not to flash.
        //An error will be returned if there is no LCD display on I2C bus for example
        while(1);
    }

    while (1)
    {
        // Delay between reading keys from LCD display
        //delay(280000l);   //280,000 is about 1 second at 40MHz
        //delay(28000l);    //28,000 is about 100ms at 40MHz
        delay(14000l);      //14,000 is about 50ms at 40MHz
        //delay(2800l);     //2,800 is about 10ms at 40MHz
        //delay(280l);      //280 is about 1ms at 40MHz

        PORTBbits.RB6 = !PORTBbits.RB6;

        //Empty all keypad buffers
        //Get status byte from LCD
        if (lcdGetByte(LCD2S_I2C_ADR, CMD_GET_STATUS, &c) == 0) {
            //Save free buffer space
            freeBuf = c & 0x7f;

            //If less than 4 bytes available in buffer, stop program!
            if (freeBuf < 4) {
                lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\fNo more space!");
                while(1);
            }

            //Has the LCD got keys to be read
            if (c & 0x80) {
                //Get key from LCD
                lcdGetByte(LCD2S_I2C_ADR, CMD_GET_KEY, &c);
           
                PORTBbits.RB6 = !PORTBbits.RB6;

                //If 'a' key pressed, toggle OUT1
                if (c == 'a') {
                    if (out1 == 0) {
                        out1 = 1;
                        lcdPutCmd(LCD2S_I2C_ADR, CMD_CLEAR_OUT1, 1);
                        lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\r\nCleared OUT1    ");
                    }
                    else {
                        out1 = 0;
                        lcdPutCmd(LCD2S_I2C_ADR, CMD_SET_OUT1, 1);
                        lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\r\nSet OUT1        ");
                    }
                }
                //If 'e' key pressed, toggle OUT2
                else if (c == 'e') {
                    if (out2 == 0) {
                        out2 = 1;
                        lcdPutCmd(LCD2S_I2C_ADR, CMD_CLEAR_OUT2, 1);
                        lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\r\nCleared OUT2    ");
                    }
                    else {
                        out2 = 0;
                        lcdPutCmd(LCD2S_I2C_ADR, CMD_SET_OUT2, 1);
                        lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\r\nSet OUT2        ");
                    }
                }
                else {
                    //lcdPutRomString(LCD2S_I2C_ADR, (rom char*)"\fKey Pressed!");
                    sprintf(buf, (rom char*)"\r\nKey Code = %c    ", c);
                    lcdPutString(LCD2S_I2C_ADR, buf);
                }
            }
        }
    }
}

/** Waits until the I2C bus is IDLE, and available to be used. */
#define i2cWaitForIdle()    while ((SSPCON2 & 0x1F) | (SSPSTATbits.R_W))

/** Generate bus start condition, and waits until it is finished */
#define i2cPutStartAndWait()  {SSPCON2bits.SEN=1; while ( SSPCON2bits.SEN );}

/** Generate bus restart condition, and waits until it is finished */
#define i2cRestartAndWait()    {SSPCON2bits.RSEN=1; while (SSPCON2bits.RSEN);}

/** Generate bus stop condition, and waits until it is finished */
#define i2cPutStopAndWait()  {SSPCON2bits.PEN=1; while(SSPCON2bits.PEN);}

/** Tests if an ACK was received from the slave. Returns true if it was, else false. */
#define i2cWasAckReceived() (!SSPCON2bits.ACKSTAT)

/**
 * This routine writes a single byte to the I2C bus,
 * and waits until the bus is idle before returning.
 *
 * @param data_out  Single data byte for I2C bus
 *
 * @return      Status byte for WCOL detection.<br>
 *              Returns 0 if OK, else 1 if error
 */
unsigned char i2cPutByteAndWait(unsigned char data_out) {
    SSPBUF = data_out;          // write single byte to SSPBUF
    if ( SSPCON1bits.WCOL ) {   // test if write collision occurred
        return ( 1 );           // if WCOL bit is set return negative #
    }

    while( SSPSTATbits.BF );    // wait until write cycle is complete

    i2cWaitForIdle();           // wait until bus goes idle

    return ( 0 );               // if WCOL bit is not set return non-negative #
}

/**
 * Read single byte from I2C bus
 *
 * @param ack Indicates if a ACK is sent after reading the byte. During sequencial read
 * the master will usually send an ACK after each byte read except the last byte. Not
 * sending the ack will indicate to the slave that this is the end of sequential read
 *
 * @return Contents of SSPBUF register
 */
unsigned char i2cGetByte(unsigned char ack)
{
    SSPCON2bits.RCEN = 1;          // enable master for 1 byte reception

    //At this stage, the slave can use clock streching to give it more time to collect the requested data.
    while ( !SSPSTATbits.BF );              // wait until byte received
while ( SSPCON2bits.RCEN );             // check that receive sequence is over

    //Send ACK or NACK
    SSPCON2bits.ACKDT = (ack==0) ? 1 : 0;   //Send ACK when 0 and NACK when 1
    SSPCON2bits.ACKEN = 1;
    while(SSPCON2bits.ACKEN);               // Wait till finished
   
    return ( SSPBUF );                  // return with read byte
}


/**
 * Get a single byte from the LCD display.
 *
 * @param lcddNum The number of the lcd to read status byte from. Is a number from 0-n.
 *        If, for example, the system is configured for 4 keypads, it is a number from 0-3.
 *
 * @param cmd LCD command for byte to get, for example 0xD0 is status byte, 0xD1 is key, 0xD3 is GPIO bytes.....
 *
 * @param val Read byte is returned in this parameter
 *
 * @return Returns 0 if OK, 1 if no ACK received
 */
unsigned char lcdGetByte(unsigned char lcdAdr, unsigned char cmd, unsigned char* val) {
    //Read LCD status byte
    i2cWaitForIdle();

    i2cPutStartAndWait();       //Write I2C Start Condition, and wait for it to complete

    while(1) {
        //Write module address. Read/Write bit (LSB) is 0 = write mode
        i2cPutByteAndWait(lcdAdr);

        //Only continue if slave sent ACK. If no slave present, no ACK is sent. If no slave with address sent is present, no ACK is sent.
        if(i2cWasAckReceived() == 0) break;

        //Write command byte
        i2cPutByteAndWait(cmd);

        //Only continue if slave sent ACK. If no slave present, no ACK is sent. If no slave with address sent is present, no ACK is sent.
        if(i2cWasAckReceived() == 0) break;

        //Send repeated I2C start condition
        i2cRestartAndWait();

        //Send module address. Read/Write bit (LSB bit) set - this is a read message.
        i2cPutByteAndWait(lcdAdr + 1);

        //Only continue if slave sent ACK. If no slave present, no ACK is sent. If no slave with address sent is present, no ACK is sent.
        if(i2cWasAckReceived() == 0) break;

        //Shift in byte from I2C port
        *val = i2cGetByte(0);    //ACK each byte except for last one. Only one byte, so no ACK

        i2cPutStopAndWait();    //Write I2C Stop condition, and wait for it to complete

        return 0;   //Return OK
    }

    //Error, reset I2C bus.
    i2cPutStopAndWait();    //Reset I2C bus
    return 1;               //Return error
}



/**
 * Sends a NULL terminated string (stored in ROM)to the LCD Display. It is appended
 * to the current LCD Display string. The trailing NULL (end of string) is NOT sent!
 *
 * @param lcdAdr The I2C address of the LCD, is 80 by default.
 *
 * @param s     Null terminated string to write out to the LCD Display
 *
 * @return 0 if OK, else error
 */
unsigned char lcdPutRomString(unsigned char lcdAdr, static rom char* s) {
    unsigned char i = 0;
    char c;

    //Write I2C Start Condition, followed by "Write String" command. No I2C Stop Condition is written
    if ( lcdPutCmd(lcdAdr, CMD_WRITE_STRING, 0) != 0) {
        return 1;               //Return Error
    }
       
    while((c = *s++)) {
        //Write next character to LCD display
        i2cPutByteAndWait(c);
    }

    i2cPutStopAndWait();    //Write I2C Stop condition, and wait for it to complete
   
    return 0;   //OK
}


/**
 * Sends a NULL terminated string to the LCD Display. It is appended to the current LCD
 * Display string. The trailing NULL (end of string) is NOT sent!
 *
 * @param lcdAdr The I2C address of the LCD, is 80 by default.
 *
 * @param s     Null terminated string to write out to the LCD Display
 *
 * @return 0 if OK, else error
 */
unsigned char lcdPutString(unsigned char lcdAdr, char* s) {
    char c;

    //Write I2C Start Condition, followed by "Write String" command. No I2C Stop Condition is written
    if ( lcdPutCmd(lcdAdr, CMD_WRITE_STRING, 0) != 0) {
        return 1;               //Return Error
    }

    while((c = *s++)) {
        //Write next character to LCD display
        i2cPutByteAndWait(c);
    }

    i2cPutStopAndWait();    //Write I2C Stop condition, and wait for it to complete
   
    return 0;   //OK
}


/**
 * Sends a single byte command to the LCD Display. This function:
 * - Waits for I2C but to become idle
 * - Writes I2C Start condition to bus
 * - Check if slave sent acknowledge, and only continues if true
 * - Writes given single byte command
 * - if putStop parameter!=0, writes a I2C Stop condition to bus
 *
 * @param lcdAdr    The I2C address of the LCD, is 80 by default.
 * @param cmd       Command to write to I2C bus
 * @param putStop   If 0, no I2C Stop condition is written to bus. Else, I2C Stop is written
 *                  to bus after writing given command.
 *
 * @return 0 if OK, else error
 */
unsigned char lcdPutCmd(unsigned char lcdAdr, unsigned char cmd, unsigned char putStop) {
    i2cWaitForIdle();

    i2cPutStartAndWait();       //Write I2C Start Condition, and wait for it to complete

    //Write module address
    i2cPutByteAndWait(lcdAdr);

    //Only continue if slave sent ACK. If no slave present, no ACK is sent. If no slave with address sent is present, no ACK is sent.
    if(i2cWasAckReceived()==0) {
        i2cPutStopAndWait();    //Reset I2C bus
        return 1;               //Error
    }

    //Write command byte
    i2cPutByteAndWait(cmd);

    if (putStop != 0) {
        i2cPutStopAndWait();    //Write I2C Stop condition, and wait for it to complete
    }
   
    return 0;   //OK
}


/**
 * Sends a command to the LCD Display. The command is contained in ROM byte array. The first byte
 * of the array is the command. All following bytes are the command data.
 *
 * @param lcdAdr    The I2C address of the LCD, is 80 by default.
 * @param len       The number of byres in the byte array. This should include the command byte.
 * @param s         Byte array containing command (first byte), and command data
 *
 * @return 0 if OK, else error
 */
unsigned char lcdPutRomCmd(unsigned char lcdAdr, unsigned char len, rom char* s) {
    unsigned char i = 0;
    unsigned char c;

    //Only continue if len > 0
    if (len < 1) return 0;

    //Write I2C Start Condition, followed by "Write String" command. No I2C Stop Condition is written
    if ( lcdPutCmd(lcdAdr, *s++, 0) != 0) {
        return 1;               //Return Error
    }

    while(--len > 0) {
        c = *s++;   //Get next byte
        //Write next character to LCD display
        i2cPutByteAndWait(c);
    }

    i2cPutStopAndWait();    //Write I2C Stop condition, and wait for it to complete
   
    return 0;   //OK
}
« Last Edit: July 02, 2009, 03:07:39 AM by modtro2 »