Use MSSP on PIC16F877A to read/write DS1307

Show off your PIC based project.

Use MSSP on PIC16F877A to read/write DS1307

Postby ric » Sun Jan 05, 2020 10:50 pm

This is a follow up to this topic on the MCHIP forum: https://www.microchip.com/forums/FindPost/1123657
I couldn't (quickly) find some simple I2C code to demonstrate using the MSSP peripheral, so wrote some.
It's untested, so may still contain some bugs. I'd appreciate any comments to make it clearer, or confirmation that it works if anyone cares to try it! :)

Code: Select all
// PIC16F877A Configuration Bit Settings

// 'C' source line config statements

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define _XTAL_FREQ 20000000
#define DS1307_ADDR 0xD0    //I2C slave address of DS1307 (8 bit format)
#define SECONDS_REG 0   //offset of the "seconds" register in the DS1307

void i2c_init(void)
{
    TRISCbits.TRISC3 = 1;   // SCL as input
    TRISCbits.TRISC4 = 1;   // SDA as input
    SSPCONbits.SSPM = 0b1000;  // Master mode using SSPADD as baud control
    SSPADD = 49;    //100kHz clock @ 20MHz Fosc
    SSPCONbits.SSPEN = 1;  //enable SSP
}

// Send an I2C START
// Return 0 if all ok, 1 if bus collision
__bit i2c_start(void)
{
    BCLIF = 0;  //Clear 'Bus collision" flag
    SEN = 1;    //initiate a START cycle
    while (SEN);    //wait until it has been sent
    return BCLIF;   //return value of BCLIF flag
}

// Send an I2C STOP
void i2c_stop(void)
{
    PEN = 1;    //initiate a STOP cycle
    while (PEN);    //wait until it has been sent
}

// Send an I2C REPEATED START
void i2c_restart(void)
{
    RSEN = 1;    //initiate a REPEATED START cycle
    while (RSEN);    //wait until it has been sent
}

//Send one byte. Return 0 if ACK received, or 1 if NAK received
__bit i2c_sendbyte(unsigned char dat)
{
    SSPBUF = dat;
    while (R_W);    //wait until byte sent and ACK/NAK received
    return ACKSTAT;
}

//Receive one byte. ackflag=0 to send ACK, or 1 to send NAK in reply
unsigned char i2c_recvbyte(unsigned char ackflag)
{
    RCEN = 1;   // initiate a RECEIVE cycle
    ACKDT = ackflag;    //specify if we should send ACK or NAK after receiving
    while (RCEN);   //wait until RECEIVE has completed
    ACKEN = 1;  //initiate an ACK cycle
    while (ACKEN);  //wait until it has completed
    return SSPBUF;
}

//Send an array of data to an I2C device.
//Return 0 if all OK, 1 if bus error, 2 if slave address NAK, 3 if slave register NAK, 4 if slave data NAK
unsigned char i2c_writeblock(unsigned char slave_address, unsigned char start_reg, unsigned char buflen, const unsigned char * bufptr)
{
    if (i2c_start() )   //send a start, and check if it succeeded
        return 1;   //abort if bus collision
    //send the I2C slave address (force R/W bit low)
    if (i2c_sendbyte(slave_address & 0xFE))
    {
        i2c_stop(); //if address was NAKed, terminate the cycle
        return 2;   //and return error code
    }
    //send the device register index
    if (i2c_sendbyte(start_reg))
    {
        i2c_stop(); //if register was NAKed, terminate the cycle
        return 3;   //and return error code
    }
    //send the data. buflen might be zero!
    for (; buflen>0; --buflen)
    {
        if (i2c_sendbyte(*bufptr++))
        {
            i2c_stop(); //if register was NAKed, terminate the cycle
            return 4;   //and return error code
        }
       
    }
    i2c_stop();
    return 0;   //no error
}

//Receive an array of data from an I2C device.
//Return 0 if all OK, 1 if bus error, 2 if slave address NAK, 3 if slave register NAK
unsigned char i2c_readblock(unsigned char slave_address, unsigned char start_reg, unsigned char buflen, unsigned char * bufptr)
{
    //do a dummy zero length write cycle to set the register address
    unsigned char retval = i2c_writeblock(slave_address, start_reg,0,0);
    if (retval)
    {
        return retval;  //abort if there was an error
    }
    //now start the READ cycle
    if (i2c_start() )   //send a start, and check if it succeeded
        return 1;   //abort if bus collision
    //send the I2C slave address (force the R/W bit high)
    if (i2c_sendbyte(slave_address | 0x01))
    {
        i2c_stop(); //if address was NAKed, terminate the cycle
        return 2;   //and return error code
    }
    //receive the data.
    for (; buflen>0; --buflen)
    {
        unsigned char ackflag = (buflen == 1);   //1 if this is the last byte to receive => send NAK
        *bufptr++ = i2c_recvbyte(ackflag);
    }
    i2c_stop();
    return 0;   //no error
}

const unsigned char rtc_data[] =
{
    0x56,   //56 seconds
    0x34,   //34 minutes
    0x12,   //12 hours & 24 hour mode
    0x01,   //01 day=Sunday
    0x03,   //03 date
    0x12,   //12 month=dec
    0x01,   //01 year=2001
};

unsigned char rd_buf[7];

void main(void) {
    i2c_init();
   
    //write some dummy fixed data to the RTC chip
    if (i2c_writeblock(DS1307_ADDR, SECONDS_REG, sizeof(rtc_data), rtc_data) )
    {
        // here if failed
    } else
    {
        // here if succeeded
    }
   
    //read data back from the RTC chip
    if (i2c_readblock(DS1307_ADDR, SECONDS_REG, sizeof(rd_buf), rd_buf) )
    {
        // here if failed
    } else
    {
        // here if succeeded
    }
    while(1);   //endless loop to avoid exiting main() function)
}

(n.b. I would strongly recommend a newer PIC, and a newer RTC chip if anyone was doing a new project!)
Latest test project, an LED matrix display made from one reel of addressable LEDs. here
User avatar
ric
Verified identity
 
Posts: 659
Joined: Sat May 24, 2014 2:35 pm
Location: Melbourne, Australia
PIC experience: Professional 5+ years with MCHP products

Return to Project Show Case

Who is online

Users browsing this forum: No registered users and 5 guests

cron