Author Topic: PIC24FJ128GA110 programming for I2C  (Read 899 times)

0 Members and 1 Guest are viewing this topic.

Offline juicycrunchwrapTopic starter

  • Contributor
  • Posts: 21
  • Country: au
PIC24FJ128GA110 programming for I2C
« on: June 01, 2023, 07:36:10 pm »
Hello everyone !
I want to make a PIC24FJ128GA110 communicate via I2C on an ADC Maxim MAX11603. I am really new to PIC programming so apologies in advance if I'm not clear. I tried to initialize the I2C2 bus (will join my functions) so I can get an SCL signal on my scope, but despite my initialization, it is not working. I checked the pull-ups resistors and their value is 3.3ohms which is what I want. I reckon my problem would come from the clock configuration as I am really not sure about it. Does someone has any ideas of what I could try to indeed have a SCL signal on my scope please ?

This is my i2c.c file :
#include <stdint.h>
#include <stdbool.h>
//#include "system.h"
#include "i2c.h"



#ifndef _XTAL_FREQ
#define _XTAL_FREQ  8000000UL
#endif

#ifndef FCY
#define FCY _XTAL_FREQ/2
#endif


#include <libpic30.h>

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

//We give a start condition on SDA and SCL
#define SDA TRISAbits.TRISA3
#define SCL TRISAbits.TRISA2
#define SDA_IN PORTAbits.RA3
#define SCL_IN PORTAbits.RA2

#define I2C_WADDR(x) (x & 0xFE) //clear R/W
#define I2C_RADDR(x) (x | 0x01) //set R/W


////////////////////////////////////////////////////////////////////////////////
//Timer and interrupt for i2c
void myTimer() {
    //i2c2_init(100);
    T1CONbits.TON = 1;
    T1CONbits.TCS = 0;
    T1CONbits.TCKPS = 0b10;
    PR1 = 10000; // period to read I2C, This could probably be chosen more intelligently
    TMR1 = 0;
}

//Configuration of the oscillator
void init_OSC() {
   
    OSCCONbits.COSC = 0b001;
    OSCCONbits.NOSC = 0b001;
    // Configure PLL prescaler, PLL multiplier, and PLL postscaler
    CLKDIVbits.RCDIV = 0;  // PLL prescaler: N1 = 2
    CLKDIVbits.DOZE = 0; // PLL postscaler: N2 = 2
   
    // Configure oscillator selection bits
    __builtin_write_OSCCONH(0x01);  // Fast RC oscillator with PLL
    __builtin_write_OSCCONL(OSCCON | 0x01); // Enable clock switch
   
    // Wait for PLL to lock
    while (OSCCONbits.LOCK != 1) {}
   
}


//Function initiates I2C2 module to baud rate BRG
void i2c2_init(int BRG) {
   
    //I2C2ADD = 0b1101101;
    I2C2ADD = 0b00000001;
    I2C2BRG = BRG;
   
    //Enable open-drain mode on SDA2
    ODCAbits.ODA2 = 1;
    ODCAbits.ODA3 = 1;
    //Enable pull up for SCL and SDA
    CNPD3bits.CN35PDE = 1; //  SCL2 pin
    CNPD3bits.CN36PDE = 1; // SDA2 pin
   
    SDA = 0;
    SCL = 0;
    SCL_IN = SDA_IN = 0 ;
   
    I2C2CONbits.I2CEN = 0; // Disable I2C Mode
    // Clear Interrupt
    IFS3bits.MI2C2IF = 0;
    //Continue module operation in Idle mode
    I2C2CONbits.I2CSIDL = 0;
    //IPMIEN mode disabled
    I2C2CONbits.IPMIEN = 1;
    //7 bits slave address
    I2C2CONbits.A10M = 0;
    //Slew rate enabled
    I2C2CONbits.DISSLW = 1;
    //General call address disabled
    I2C2CONbits.GCEN = 0;
    //Disable SMBus input thresholds
    I2C2CONbits.SMEN = 1;
    //Software or receive call stretching disabled
    I2C2CONbits.STREN = 0;
    // Enable I2C Mode
    I2C2CONbits.I2CEN = 1;

}

//Function initiates a start condition on bus
void i2c2_start(void) {
    I2C2CONbits.ACKDT = 0; //Reset any previous Ack
    __delay_us(10);
    I2C2CONbits.SEN = 1; //Initiate Start condition
    SDA = 1;
    __delay_us(10);
    SCL = 1;
    __delay_us(10);
    SDA = 0;

}

void i2c2_restart(void) {
    I2C2CONbits.RSEN = 1; //Initiate restart condition
    __delay_us(10);
}


//Resets the I2C bus to Idle
void reset_i2c2_bus(void) {
    I2C2CONbits.PEN = 1; //initiate stop bit
    I2C2CONbits.RCEN = 0;
    IFS3bits.MI2C2IF = 0; // Clear Interrupt
    I2C2STATbits.IWCOL = 0;
    I2C2STATbits.BCL = 0;
    __delay_us(10);
}


//Basic I2C byte send
char send_i2c2_byte(int data) {
    IFS3bits.MI2C2IF = 0; // Clear Interrupt
    I2C2TRN = data; // load the outgoing data byte
    // Check for NO_ACK from slave, abort if not found
    if (I2C2STATbits.ACKSTAT == 1) {
        reset_i2c2_bus();
        return (1);
    }
    __delay_us(10);
    return (0);
}

//Function that write
void I2C2write(char addr, char value) {
    i2c2_start();
    send_i2c2_byte(addr);
    send_i2c2_byte(value);
    reset_i2c2_bus();
}

//Function reads data, returns the read data, no ack
char i2c2_read(void) {
    char data = 0;
    i2c2_start();
    I2C2CONbits.RCEN = 1; //set I2C module to receive
    data = I2C2RCV; //get data from I2CRCV register
    return data; //return data
}


//function reads data, returns the read data, with ack

char i2c2_read_ack(void) {
    char data = 0;
    I2C2CONbits.RCEN = 1; //set I2C module to receive
    data = I2C2RCV; //get data from I2CRCV register
    I2C2CONbits.ACKEN = 1; //set ACK to high
    __delay_us(10); //wait before exiting 
    return data; //return data
}

This is my i2c.h file :
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "xc.h"
#include <p24FJ128GA110.h>

void myTimer();
void init_OSC();
void i2c2_init(int BRG);
void i2c2_start(void);
void i2c2_restart(void);
void reset_i2c2_bus(void);
char send_i2c2_byte(int data);
void I2C2write(char addr, char value);
char i2c2_read(void);
char i2c2_read_ack(void);

This is my main file :
#include <stdio.h>
#include <stdlib.h>
#include "i2c.h"



#define ADC_SLAVE_ADDRESS_WRITE  0b11001010
#define ADC_SLAVE_ADDRESS_READ  0b11001011
#define SETUP_REGISTER_ADDRESS 0b11001101
#define CONFIGURATION_REGISTER_ADDRESS 0b01101010

/*
 *
 */
int main() {
    //myTimer();
    init_OSC();
    i2c2_init(0x27);

    while (1) {
        //i2c2_start();
        //send_i2c2_byte(ADC_SLAVE_ADDRESS_WRITE);
        //send_i2c2_byte(SETUP_REGISTER_ADDRESS);
        //reset_i2c2_bus();
    }
    return (EXIT_SUCCESS);
}

As I said, I am really new to this  :'(. I would take any advice, ideas or comments in consideration to improve my work.
Thank y'all for your time !
Juicycrunchwrap
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5835
  • Country: es
Re: PIC24FJ128GA110 programming for I2C
« Reply #1 on: June 02, 2023, 02:44:26 am »
. I checked the pull-ups resistors and their value is 3.3ohms which is what I want.
I hope you meant 3.3K, otherwise there you have the problem.

Make sure to read: www.microchip.com/DS70000195

I don't think you need setting the pins in open drain mode, the i2c module should take care of everything:
Quote
The user software need not be concerned with the state of the port I/O of the pins as the module overrides the port state and direction. At initialization, the pins are tri-stated (released).

Try adding external pullups (1K...10K will be fine), it might be completely ignoring the port configuration.
« Last Edit: June 02, 2023, 03:01:51 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline juicycrunchwrapTopic starter

  • Contributor
  • Posts: 21
  • Country: au
Re: PIC24FJ128GA110 programming for I2C
« Reply #2 on: June 02, 2023, 02:12:44 pm »
Hello, thank you for your reply !
Indeed, I made a mistake, it's indeed 3.3 Kohms, I am sorry.
Okay, I will delete these two lines then, thank you !
It is on a PCB so I am not really sure I can add any pull-ups but I will look into it, thank you !
Do I have to do this in my code to set the pins as inputs ?

#define SDA   TRISAbits.TRISA3
#define SCL   TRISAbits.TRISA2
#define SDA_IN   PORTAbits.RA3
#define SCL_IN   PORTAbits.RA2
SDA = 0;
SCL = 0;
SCL_IN = SDA_IN = 0 ;

I will try what you suggested. Thank you very much for your help !

I added what I am seeing on my scope. I am pretty sure that it's not what I am supposed to get though...

Sorry, I am editing again
I am getting my signals on the scope from resistors that are connected to the slave and not the master. Could it also be why I am not getting any signal ?
I have attached a capture from the datasheet you sent me and I just noticed that...
 Again sorry for the editing. Thank you for the time !
« Last Edit: June 02, 2023, 03:40:08 pm by juicycrunchwrap »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5835
  • Country: es
Re: PIC24FJ128GA110 programming for I2C
« Reply #3 on: June 02, 2023, 05:39:37 pm »
At 2mv/div, It means nothing, clearly there're no pullups or you code is stuck low somehow.
Set your scope to 2V/div.

Pins are set as input after a reset, so no need of anything.

Try adding a 5 second delay before starting any test, just after the i2c init, or debug it, halt after any i2c tests.
Check voltages,  I2c lines should read high on idle.

Are you sure the CPU itself is running properly?
Try toggling a led, 500ms high, 500ms low, see if it does something.
Is it a custom board or a commercial dev/testing pcb?
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline juicycrunchwrapTopic starter

  • Contributor
  • Posts: 21
  • Country: au
Re: PIC24FJ128GA110 programming for I2C
« Reply #4 on: June 02, 2023, 06:54:52 pm »
Thank you for your reply. Yes I figured it wasn't working because the voltage is really low.
Okay, I deleted the lines when i set the pins as inputs.
So far on idle, the lines are at 3.3 Volts, last time I checked.
I reckon the problem would come from the CPU or the clock, but I don't really know how to find what's wrong. I tried to "simulate" the clock but putting myself the SCL signal low and waiting, and then putting it high. But I will try to see if I can do it with a LED, thank you.
It's a custom board, I'm a new intern in a company and I have to set up an I2C communication with the PIC and the ADC.
Thank you very much for your time. I will try to see if the CPU is working with the LED and I will let you know !


Edit :
I can toggle a LED. So I think the CPU is running properly. I am going to use what I did for the LED to the SCL. The ADC chip has never been used, is it possible that the problem also comes from that ?
Thank you !
« Last Edit: June 02, 2023, 07:38:08 pm by juicycrunchwrap »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5835
  • Country: es
Re: PIC24FJ128GA110 programming for I2C
« Reply #5 on: June 02, 2023, 08:55:37 pm »
For now remove any interrupts or fancy stuff, make it as simple as possible.
Your job now is to create a START condition, nothing else. Don't disturb SCL/SDA in your code after i2c was configured.
Use proper flag checks, avoid simple delays.

To catch the start condiotion, set the scope for normal trigger (Only updates when a trigger happens, not free running), falling edge mode, at SCL.
If working you should see something like this:



Attach your whole project if possible (In ZIP! Don't paste 400 lines of code  ;) ), I'll compile it and simulate in Proteus.

ALso check:
https://github.com/govind-mukundan/WebSiteDownloads/tree/master/C/PICxi2cDriver
« Last Edit: June 02, 2023, 09:00:09 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline juicycrunchwrapTopic starter

  • Contributor
  • Posts: 21
  • Country: au
Re: PIC24FJ128GA110 programming for I2C
« Reply #6 on: June 02, 2023, 09:32:03 pm »
Thank you very much. I am going to try this right now and I will post an edit if I managed to get something !
And I have attached the project, again thank you so much !!
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5835
  • Country: es
Re: PIC24FJ128GA110 programming for I2C
« Reply #7 on: June 02, 2023, 11:33:57 pm »
I was already trying by my own. This example interfacing a 24C02 works for me.

Code: [Select]
#include <xc.h>

void i2c2_init() {
   
    //I2C2ADD = 0b1101101;
    //I2C2ADD = 0b00000001;
    I2C2BRG = 255;
     
   
    I2C2CONbits.I2CEN = 0; // Disable I2C Mode
    // Clear Interrupt
    IFS3bits.MI2C2IF = 0;
    //Continue module operation in Idle mode
    I2C2CONbits.I2CSIDL = 0;
    //IPMIEN mode disabled
    I2C2CONbits.IPMIEN = 1;
    //7 bits slave address
    I2C2CONbits.A10M = 0;
    //Slew rate enabled
    I2C2CONbits.DISSLW = 1;
    //General call address disabled
    I2C2CONbits.GCEN = 0;
    //Disable SMBus input thresholds
    I2C2CONbits.SMEN = 1;
    //Software or receive call stretching disabled
    I2C2CONbits.STREN = 0;
    // Enable I2C Mode
    I2C2CONbits.I2CEN = 1;
}


void i2c2_wait(void){
    while(!IFS3bits.MI2C2IF);
    IFS3bits.MI2C2IF = 0;                                   // Reset flag
}

void i2c2_start(void) {
    I2C2CONbits.SEN = 1; //Initiate Start condition
    i2c2_wait();
}

void i2c2_restart(void) {
    I2C2CONbits.RSEN = 1; //Initiate restart condition
    i2c2_wait();
}

void i2c2_stop(void) {
    I2C2CONbits.PEN = 1; //Initiate Start condition
    i2c2_wait();
}

void reset_i2c2_bus(void) {
    I2C2CONbits.PEN = 1; //initiate stop bit
    while(I2C2CONbits.PEN);
    I2C2CONbits.RCEN = 0;
    IFS3bits.MI2C2IF = 0; // Clear Interrupt
    I2C2STATbits.IWCOL = 0;
    I2C2STATbits.BCL = 0;
}

char send_i2c2_byte(unsigned char data) {
    I2C2TRN = data;                                         // load the outgoing data byte
    i2c2_wait();
    return (I2C2STATbits.ACKSTAT == 0);                     // return 1 if ACK   
}

unsigned char receive_i2c2_byte(char send_ack) {
    unsigned char data;
   
    if(send_ack)       
        I2C2CONbits.ACKDT = 0;
    else       
        I2C2CONbits.ACKDT = 1;
   
    I2C2CONbits.RCEN = 1;                                   // Start reception
    i2c2_wait();
    data = I2C2RCV;                                         // Read data   
    I2C2CONbits.ACKEN = 1;                                  // Send ACK/NAK
    i2c2_wait();
    return data;
}

unsigned char I2C2write(unsigned char dev, unsigned char reg, unsigned char value){
    dev <<= 1;                                              // RW=0
    i2c2_start();                                           // Send start
    if(!send_i2c2_byte(dev) || !send_i2c2_byte(reg)){       // Send device address and reg address
        i2c2_stop();                                        // Cancel if NACK
        return 0;
    }
    send_i2c2_byte(value);                                  // Write data
    i2c2_stop();                                            // Send stop
    return 1;
}

char I2C2read(unsigned char dev, unsigned char reg) {   
    unsigned char data;
    dev = (dev<<1);                                         // RW=0   
    i2c2_start();                                           // Send start
    if(!send_i2c2_byte(dev) || !send_i2c2_byte(reg)){       // Send device address and reg address
        i2c2_stop();                                        // Cancel if NACK
        return 0;
    }
    dev |= 1;                                               // RW=1
    i2c2_restart();                                         // Send restart
    if(!send_i2c2_byte(dev)){                               // Send device address
        i2c2_stop();                                        // Cancel if NACK
        return 0;
    }
    data = receive_i2c2_byte(0);
    i2c2_stop();                                            // Send stop
    return data;                                            // Return read data
}

int main(void)
{
    unsigned char data;
    //init_OSC();
    i2c2_init();           
    I2C2write(0x50, 0x01, 0xF1);                            // EEPROM ID 0x50, write value 0x83 to byte address 0x11     
    data = I2C2read(0x50, 0x01);                            // Read back byte at reg 0x01
    if(data != 0xF1){                                       // Verify
        Nop();                                              // Data mismatch
    }

    while(1);
    return 1;
}

I'm running it very slow (BRG=255) and no PLL, for now forget about high speed, first make it work, tweak later.

As seen in the picture, removing the eeprom or changing its address will cause NACK, no further transfers will be made, but the first transfer (Device address) will always be there. If it ACKs back, it'll send the reg address and data.


Edit: I forgot the full read+write waveforms.
« Last Edit: June 03, 2023, 01:18:33 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline juicycrunchwrapTopic starter

  • Contributor
  • Posts: 21
  • Country: au
Re: PIC24FJ128GA110 programming for I2C
« Reply #8 on: June 05, 2023, 02:19:04 pm »
Hello ! Thank you so much for your reply !
I will try to lower the BRG value today. I will keep you updated if it works. Again thank you a lot for your help !

Edit : After 8 long hours of retries , I still cannot visualize and SCL signal. I don't know if it's my ADC that is deficient, but in any case, when I probe the SCL and SDA signals on the pullups resistors, I don't see anything at all on my scope. I tried using a LED to debug my code, to put it on and off line after line to have like a "breakpoint", and apparently the code is running smoothly. Yet, the absence of clock signal makes me wonder what I could have forgotten.
There is another I2C bus that I could use on my PCB, the bus 1. It's connected to a Open-Drain Capable  Logic-Level Translator that is itself connected to a LCD. If I can get myself a LCD, I will try that way. Else, I am really out of ideas about why it doesn't work. But thank you so much for your help though, really appreciate it !


Edit 2 : My bad, it's working. I just feel very stupid, I wasn't probing on the right side of the resistors (towards the bus) but rather on the side towards the 3.3V. Thank y'all for your help. I will make a github rep with my library and post the link here so people will be abe to use it if they want to.
« Last Edit: June 07, 2023, 09:26:03 pm by juicycrunchwrap »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf