• AVR Freaks

Helpful ReplyHot!need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA

Author
soleil_sword
Starting Member
  • Total Posts : 35
  • Reward points : 0
  • Joined: 2009/03/12 14:03:59
  • Location: 0
  • Status: offline
2018/09/19 15:20:41 (permalink)
0

need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA

Hi there,
 
Goal: use PIC16F1823 to readout the pressure sensor MS5803-14BA. The target language is assembly.
The pressure sensor breakout I'm using is I2C connected, https://www.sparkfun.com/products/12909
 
Hardware Setup: I'm using the curiosity development, and connect PIC16F1823 to the pressure sensor breakout board via four jumper cables, the pressure sensor breakout board includes the pull-up resistors. I believe the hardware is ok.
 
Software setup: I have googled around for I2C example code, and modified them in order to read my sensor. The code is shown below. Right now the code is in C, after getting this to work, I will translate it to assembly. The reason I don't write in assembly from the beginning is because I need to pick up assembly again after almost 10 years...
 
Current status: I don't have experience on I2C protocol with PIC16F1823 and PIC family in general. The last time I used any PIC processor was about 9 years ago. and I'm now having a hard time already for a few weeks to get it working. When I tried to use scope to check the SCL bus, it's constantly high. I don't see any I2C-like signal on the scope.
 
I appreciate if anybody could point out a direction for me to go, and give some suggestion in terms of debugging. Otherwise than checking the scope, I don't have any clue now how to proceed. If there would be a more straightforward and simpler way, that would be great.
 
Thank you very much in advance!
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = ON // 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>
#include <stdint.h>

void I2C_init (void)
{
    SSP1ADD = 0x03;
    SSP1CON1 = 0x28;
    SSP1CON3 = 0x00;
    SSP1STAT = 0x80;
    
    /**
    LATx registers
    */
    LATA = 0x00;
    LATC = 0x00;

    /**
    TRISx registers
    */
    TRISA = 0x3F;
    TRISC = 0x3F;

    /**
    ANSELx registers
    */
    ANSELC = 0x0C;
    ANSELA = 0x17;
}

I2C_wait (void)
{
    while ((SSP1STAT & 0x08) || (SSP1CON2 & 0x1F)); // wait for start bit to clear in SSPSTAT and bits 0 to 4 in SSPCON2
}

void I2C_start (void)
{
    I2C_wait ();
    SSP1CON2 |= 0x01; // SEN=1 -> initiate the START condition on SDA and SCL pins
}


void I2C_repeated_start (void)
{
    I2C_wait();
    SSP1CON2 |= 0x02; // RSEN=1 -> initiate REPEATED START condition on SDA and SCL pins
}

void I2C_stop (void)
{
    I2C_wait ();
    SSP1CON2 |= 0x04; // PEN=1 -> initiate the STOP condition on SDA and SCL pins
}

void I2C_write (uint8_t data)
{
    I2C_wait ();
    SSP1BUF = data; // load data into SSPBUF register
}

uint8_t I2C_read (uint8_t ack)
{
    uint8_t temp;
    I2C_wait();
    RCEN = 1; // enable receive mode for I2c
    I2C_wait();
    temp = SSP1BUF; // load data from Buffer to the temp
    I2C_wait();
    ACKDT = (ack); // 0-- not ACK , 1-- ACK
    ACKEN = 1; // Send Acknowledgement
    return temp;
}

void main(void) {
    
    I2C_init (); // initialise i2c @ 100 KHz
    uint8_t data;
    
    while (1)
    {
        __delay_ms (1000); // 1 sec delay between write and read
        
        // read data
        I2C_start (); // start I2C
        I2C_write (0x76); // using 0x76 slave address
        I2C_repeated_start(); // repeated start
        I2C_write (0x1E); // reset
        I2C_stop ();
        
        __delay_ms (2000); // 2sec delay

        //data = I2C_read (1); // read data and send NACK
    }
    return;
}

#1
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA 2018/09/19 17:09:49 (permalink) ☄ Helpfulby soleil_sword 2018/09/20 18:44:03
+2 (2)
soleil_sword
...
Hardware Setup: I'm using the curiosity development

Which one? There are many Curiosity boards.
Is it the DM164137 ?
https://www.microchip.com...etails/PartNO/DM164137
 

Right now the code is in C, after getting this to work, I will translate it to assembly. The reason I don't write in assembly from the beginning is because I need to pick up assembly again after almost 10 years...

Why bother re-coding? The output from XC8 is pretty compact.
It's easy to write small functions in inline assembly if you need the best possible speed for any small bit of code.
 

#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)

This oscillator mode requires an external crystal.
If it is the Curiosity board I think you have, there is no crystal on the board, and you should use one of the internal oscillator modes.
Have you tried just flashing an LED slowly first?
 

    ANSELC = 0x0C;

This is good. My first guess was you had left RC0/SCL and/or RC1/SDA in analog mode.
They do both have to be in digital mode.
 
I2C_wait (void)
{
    while ((SSP1STAT & 0x08) || (SSP1CON2 & 0x1F)); // wait for start bit to clear in SSPSTAT and bits 0 to 4 in SSPCON2
}

Is this straight from some tutorial code?
The S bit will be set in the middle of any normal transaction, so waiting for it to be clear is NOT correct.
 
void I2C_start (void)
{
    I2C_wait ();
    SSP1CON2 |= 0x01; // SEN=1 -> initiate the START condition on SDA and SCL pins
}

There are much easier ways to access bits in C, which also allow syntax checking to work
With the I2C peripheral, I find it makes much more sense to initiate an action, then wait for that action to finish.
So:
void I2C_start (void)
{
    SSP1CON2bits.SEN = 1;    // initiate a START condition
    while (SSP1CON2bits.SEN);    // wait until the START condition has been sent
}
void I2C_stop (void)
{
    SSP1CON2bits.PEN = 1;    // initiate a STOP condition
    while (SSP1CON2bits.PEN);    // wait until the STOP condition has been sent
}
void I2C_restart (void)
{
    SSP1CON2bits.RSEN = 1;    // initiate a REPEATED START condition
    while (SSP1CON2bits.RSEN);    // wait until the REPEATED START condition has been sent
}



Where you i nthe middle of writing this code?
        // read data
        I2C_start (); // start I2C
        I2C_write (0x76); // using 0x76 slave address
        I2C_repeated_start(); // repeated start
        I2C_write (0x1E); // reset
        I2C_stop ();
        
        __delay_ms (2000); // 2sec delay

        //data = I2C_read (1); // read data and send NACK

The repeated start should NOT be before the reset command. You are still writing commands, you don't send a repeated start until you want to swap to read mode. The next byte sent after a repeated start MUST be a slave address (usually the "read" form of the address, so 0x77 in your case)
If you have to wait a long time before doing the read, I woudn't bother using repeated start at all. Just do the command the initiates a read, then do a standalone read cycle (START/slave address+read/READ/STOP).
 
Also, you SHOULD check the ACKSTAT bit after sending a slave address. That is what tells you if the slave acknowledged the address, which is what tells you it is all working.
 

Nearly there...
#2
soleil_sword
Starting Member
  • Total Posts : 35
  • Reward points : 0
  • Joined: 2009/03/12 14:03:59
  • Location: 0
  • Status: offline
Re: need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA 2018/09/20 18:55:02 (permalink)
0
thanks qhb for your help !
I changed the code, but still nothing works.
I realized something might have gone wrong, so I took a step back, and I tried to use mcc to generate everything instead of me writing the code. The codes are below.
 
I tried to use the ACKSTAT as you suggested, as shown in the main.c file, I first check i2c_driver_isStart(), and I get a callback of 1, which seems good, and then I got a callback 0 from i2c_driver_isNACK(), which seems good as well. As a result, the two LEDS (associated with pin RA5 and RA1 always toggles. 
 
However, I CANT see anything on my scope, my scope shows SCL pin having a constant voltage of about 0.2 V. FUrthermore, even when I changed the address from 0x76 to anything, I keep getting i2c_driver_isNACK() callback of 0, it seems I always get an acknowledge no matter which address I'm writing. 
 
I believe there is something very fundamental that is wrong.
 
main.c:

#include "mcc_generated_files/mcc.h"

void main(void)
{
    // initialize the device
    SYSTEM_Initialize();
    if (i2c_driver_open())
    {
        IO_RA1_Toggle();
    }
   
    while (1)
    {
        i2c_driver_start();
        if (i2c_driver_isStart()){
            i2c_driver_TXData(0x76);
            IO_RA1_Toggle();
            if (!i2c_driver_isNACK()){
                IO_RA5_Toggle();
            }
            __delay_ms (1000);
        }
    }
}

 
pin_manager.c:

#include <xc.h>
#include "pin_manager.h"
#include "stdbool.h"

void PIN_MANAGER_Initialize(void)
{
    LATA = 0x00;
    LATC = 0x00;

    TRISA = 0x1D;
    TRISC = 0x3F;

    ANSELC = 0x0C;
    ANSELA = 0x17;

    WPUA = 0x00;
    WPUC = 0x00;
    OPTION_REGbits.nWPUEN = 1;

    APFCON = 0x00;
}
  
void PIN_MANAGER_IOC(void)
{
}

 
i2c_driver.c:

 
#include <stdio.h>
 
#include <stdint.h>
#include <stdbool.h>
#include "mcc.h"
#include "i2c_driver.h"

#pragma warning disable 520

inline void i2c_driver_close(void)
{
    SSP1CON1bits.SSPEN = 0;
}

/* Interrupt Control */
inline void mssp_enableIRQ(void)
{
    PIE1bits.SSP1IE = 1;
}

inline __bit mssp_IRQisEnabled(void)
{
    return PIE1bits.SSP1IE;
}

inline void mssp_disableIRQ(void)
{
    PIE1bits.SSP1IE = 0;
}

inline void mssp_clearIRQ(void)
{
    PIR1bits.SSP1IF = 0;
}

inline void mssp_setIRQ(void)
{
    PIR1bits.SSP1IF = 1;
}

inline __bit mssp_IRQisSet(void)
{
    return PIR1bits.SSP1IF;
}

inline void mssp_waitForEvent(uint16_t *timeout)
{
    //uint16_t to = (timeout!=NULL)?*timeout:100;
    //to <<= 8;

    if(PIR1bits.SSP1IF == 0)
    {
        while(1)// to--)
        {
            if(PIR1bits.SSP1IF) break;
            __delay_us(100);
        }
    }
}

__bit i2c_driver_open(void)
{
    if(!SSP1CON1bits.SSPEN)
    {
        SSP1STAT = 0x00;
        SSP1CON1 = 0x28;
        SSP1CON2 = 0x00;
        SSP1ADD = 0x13;
        return true;
    }
    else
        return false;
}

__bit i2c_driver_initSlaveHardware(void)
{
    if(!SSP1CON1bits.SSPEN)
    {
        SSP1CON1 |= 0x06; //setup I2C Slave (7-bit Addressing)
        SSP1STAT = 0x00;
        SSP1CON2 = 0x00;
        
        SSP1CON1bits.SSPEN = 1;
        return true;
    }
    return false;
}

inline void i2c_driver_resetBus(void)
{
    
}

inline void i2c_driver_start(void)
{
    SSP1CON2bits.SEN = 1;
}

inline void i2c_driver_restart(void)
{
    SSP1CON2bits.RSEN = 1;
}

inline void i2c_driver_stop(void)
{
    SSP1CON2bits.PEN = 1;
}

inline __bit i2c_driver_isNACK(void)
{
    return SSP1CON2bits.ACKSTAT;
}

inline void i2c_driver_startRX(void)
{
    SSP1CON2bits.RCEN = 1;
}

inline char i2c_driver_getRXData(void)
{
    return SSP1BUF;
}

inline void i2c_driver_setAddr(char addr)
{
    SSP1ADD = addr;
}

inline void i2c_driver_setMask(char mask)
{
    SSP1MSK = mask;
}

inline void i2c_driver_TXData(char d)
{
    SSP1BUF = d;
}

inline char i2c_driver_getAddr(void)
{
    return SSP1ADD;
}

inline void i2c_driver_sendACK(void)
{
    SSP1CON2bits.ACKDT = 0;
    SSP1CON2bits.ACKEN = 1; // start the ACK/NACK
}

inline void i2c_driver_sendNACK(void)
{
    SSP1CON2bits.ACKDT = 1;
    SSP1CON2bits.ACKEN = 1; // start the ACK/NACK
}

inline void i2c_driver_releaseClock(void)
{
    SSP1CON1bits.CKP = 1;
}

inline __bit i2c_driver_isBufferFull(void)
{
    return SSP1STATbits.BF;
}

inline __bit i2c_driver_isStart(void)
{
    return SSP1STATbits.S;
}

inline __bit i2c_driver_isAddress(void)
{
    return !SSP1STATbits.D_nA;
}

inline __bit i2c_driver_isStop(void)
{
    return SSP1STATbits.P;
}

inline __bit i2c_driver_isData(void)
{
    return SSP1STATbits.D_nA;
}

inline __bit i2c_driver_isRead(void)
{
    return SSP1STATbits.R_nW;
}

inline __bit i2c_driver_isWriteCollision(void)
{
    return SSP1CON1bits.WCOL;
}

inline __bit i2c_driver_isReceiveOverflow(void)
{
    return SSP1CON1bits.SSPOV;
}

inline void i2c_driver_clearBusCollision(void)
{
    PIR2bits.BCL1IF = 0; // clear the bus collision.
}

inline void i2c_driver_setBusCollisionISR(interruptHandler handler){
    i2c_driver_busCollisionISR = handler;
}

inline void i2c_driver_setI2cISR(interruptHandler handler){
    i2c_driver_i2cISR = handler;
}

post edited by soleil_sword - 2018/09/20 19:07:29
#3
David
Pic User
  • Total Posts : 1286
  • Reward points : 0
  • Joined: 2007/12/17 23:19:53
  • Location: uk sussex
  • Status: offline
Re: need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA 2018/09/20 21:20:32 (permalink)
+1 (1)
https://cdn.sparkfun.com/datasheets/Sensors/Weather/MS5803-14BA_Breakout_v10.pdf
you do have the solder across "PU" to enable the pull ups for I2c ?

David
I support http://picforum.ric323.com because this forum is often too broken to use!
#4
David
Pic User
  • Total Posts : 1286
  • Reward points : 0
  • Joined: 2007/12/17 23:19:53
  • Location: uk sussex
  • Status: offline
Re: need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA 2018/09/20 22:36:39 (permalink)

David
I support http://picforum.ric323.com because this forum is often too broken to use!
#5
soleil_sword
Starting Member
  • Total Posts : 35
  • Reward points : 0
  • Joined: 2009/03/12 14:03:59
  • Location: 0
  • Status: offline
Re: need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA 2018/09/21 13:48:50 (permalink)
0
thanks David.
David
https://cdn.sparkfun.com/datasheets/Sensors/Weather/MS5803-14BA_Breakout_v10.pdf
you do have the solder across "PU" to enable the pull ups for I2c ?

yes, I have this 2.2 kOhm pull ups for both SCL and SDA. i double checked with a multimeter.
 
#6
RISC
Super Member
  • Total Posts : 5376
  • Reward points : 0
  • Status: offline
Re: need help with I2C using PIC16F1823 to read a pressure sensor MS5803-14BA 2018/09/21 14:37:07 (permalink)
0
Hi,
 
If you use MCC there are drivers for lots of clicks boards on which many external peripheral devices have I2C interface. This will show you lots of examples
Just download the Microelektronika_Click_libraries for MCC : http://www.microchip.com/MCC
 
Regards
 
#7
Jump to:
© 2019 APG vNext Commercial Version 4.5