PIC24FJ128GA110 programming for I2C - Page 1
Forum section: electronics / beginners

author: juicycrunchwrap
author_rank: Contributor
timestamp: June 01, 2023, 07:36:10 pm
content: 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 i2cvoid 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 intelligentlyTMR1 = 0;}//Configuration of the oscillatorvoid init_OSC() {OSCCONbits.COSC = 0b001;OSCCONbits.NOSC = 0b001;// Configure PLL prescaler, PLL multiplier, and PLL postscalerCLKDIVbits.RCDIV = 0; // PLL prescaler: N1 = 2CLKDIVbits.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 lockwhile (OSCCONbits.LOCK != 1) {}}//Function initiates I2C2 module to baud rate BRGvoid i2c2_init(int BRG) {//I2C2ADD = 0b1101101;I2C2ADD = 0b00000001;I2C2BRG = BRG;//Enable open-drain mode on SDA2ODCAbits.ODA2 = 1;ODCAbits.ODA3 = 1;//Enable pull up for SCL and SDACNPD3bits.CN35PDE = 1; // SCL2 pinCNPD3bits.CN36PDE = 1; // SDA2 pinSDA = 0;SCL = 0;SCL_IN = SDA_IN = 0 ;I2C2CONbits.I2CEN = 0; // Disable I2C Mode// Clear InterruptIFS3bits.MI2C2IF = 0;//Continue module operation in Idle modeI2C2CONbits.I2CSIDL = 0;//IPMIEN mode disabledI2C2CONbits.IPMIEN = 1;//7 bits slave addressI2C2CONbits.A10M = 0;//Slew rate enabledI2C2CONbits.DISSLW = 1;//General call address disabledI2C2CONbits.GCEN = 0;//Disable SMBus input thresholdsI2C2CONbits.SMEN = 1;//Software or receive call stretching disabledI2C2CONbits.STREN = 0;// Enable I2C ModeI2C2CONbits.I2CEN = 1;}//Function initiates a start condition on busvoid i2c2_start(void) {I2C2CONbits.ACKDT = 0; //Reset any previous Ack__delay_us(10);I2C2CONbits.SEN = 1; //Initiate Start conditionSDA = 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 Idlevoid reset_i2c2_bus(void) {I2C2CONbits.PEN = 1; //initiate stop bitI2C2CONbits.RCEN = 0;IFS3bits.MI2C2IF = 0; // Clear InterruptI2C2STATbits.IWCOL = 0;I2C2STATbits.BCL = 0;__delay_us(10);}//Basic I2C byte sendchar send_i2c2_byte(int data) {IFS3bits.MI2C2IF = 0; // Clear InterruptI2C2TRN = data; // load the outgoing data byte// Check for NO_ACK from slave, abort if not foundif (I2C2STATbits.ACKSTAT == 1) {reset_i2c2_bus();return (1);}__delay_us(10);return (0);}//Function that writevoid 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 ackchar i2c2_read(void) {char data = 0;i2c2_start();I2C2CONbits.RCEN = 1; //set I2C module to receivedata = I2C2RCV; //get data from I2CRCV registerreturn data; //return data}//function reads data, returns the read data, with ackchar i2c2_read_ack(void) {char data = 0;I2C2CONbits.RCEN = 1; //set I2C module to receivedata = I2C2RCV; //get data from I2CRCV registerI2C2CONbits.ACKEN = 1; //set ACK to high__delay_us(10); //wait before exitingreturn 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

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 02, 2023, 02:44:26 am
content: Quote from: juicycrunchwrap on June 01, 2023, 07:36:10 pmI hope you meant 3.3K, otherwise there you have the problem.Make sure to read:www.microchip.com/DS70000195I don't think you need setting the pins in open drain mode, the i2c module should take care of everything:QuoteTry adding external pullups (1K...10K will be fine), it might be completely ignoring the port configuration.

author: juicycrunchwrap
author_rank: Contributor
timestamp: June 02, 2023, 02:12:44 pm
content: 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.RA2SDA = 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 againI 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 !

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 02, 2023, 05:39:37 pm
content: 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?

author: juicycrunchwrap
author_rank: Contributor
timestamp: June 02, 2023, 06:54:52 pm
content: 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 !

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 02, 2023, 08:55:37 pm
content: 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

author: juicycrunchwrap
author_rank: Contributor
timestamp: June 02, 2023, 09:32:03 pm
content: 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 !!

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 02, 2023, 11:33:57 pm
content: I was already trying by my own. This example interfacing a 24C02 works for me.Code:#include <xc.h>void i2c2_init() {//I2C2ADD = 0b1101101;//I2C2ADD = 0b00000001;I2C2BRG = 255;I2C2CONbits.I2CEN = 0; // Disable I2C Mode// Clear InterruptIFS3bits.MI2C2IF = 0;//Continue module operation in Idle modeI2C2CONbits.I2CSIDL = 0;//IPMIEN mode disabledI2C2CONbits.IPMIEN = 1;//7 bits slave addressI2C2CONbits.A10M = 0;//Slew rate enabledI2C2CONbits.DISSLW = 1;//General call address disabledI2C2CONbits.GCEN = 0;//Disable SMBus input thresholdsI2C2CONbits.SMEN = 1;//Software or receive call stretching disabledI2C2CONbits.STREN = 0;// Enable I2C ModeI2C2CONbits.I2CEN = 1;}void i2c2_wait(void){while(!IFS3bits.MI2C2IF);IFS3bits.MI2C2IF = 0; // Reset flag}void i2c2_start(void) {I2C2CONbits.SEN = 1; //Initiate Start conditioni2c2_wait();}void i2c2_restart(void) {I2C2CONbits.RSEN = 1; //Initiate restart conditioni2c2_wait();}void i2c2_stop(void) {I2C2CONbits.PEN = 1; //Initiate Start conditioni2c2_wait();}void reset_i2c2_bus(void) {I2C2CONbits.PEN = 1; //initiate stop bitwhile(I2C2CONbits.PEN);I2C2CONbits.RCEN = 0;IFS3bits.MI2C2IF = 0; // Clear InterruptI2C2STATbits.IWCOL = 0;I2C2STATbits.BCL = 0;}char send_i2c2_byte(unsigned char data) {I2C2TRN = data; // load the outgoing data bytei2c2_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;elseI2C2CONbits.ACKDT = 1;I2C2CONbits.RCEN = 1; // Start receptioni2c2_wait();data = I2C2RCV; // Read dataI2C2CONbits.ACKEN = 1; // Send ACK/NAKi2c2_wait();return data;}unsigned char I2C2write(unsigned char dev, unsigned char reg, unsigned char value){dev <<= 1; // RW=0i2c2_start(); // Send startif(!send_i2c2_byte(dev) || !send_i2c2_byte(reg)){ // Send device address and reg addressi2c2_stop(); // Cancel if NACKreturn 0;}send_i2c2_byte(value); // Write datai2c2_stop(); // Send stopreturn 1;}char I2C2read(unsigned char dev, unsigned char reg) {unsigned char data;dev = (dev<<1); // RW=0i2c2_start(); // Send startif(!send_i2c2_byte(dev) || !send_i2c2_byte(reg)){ // Send device address and reg addressi2c2_stop(); // Cancel if NACKreturn 0;}dev |= 1; // RW=1i2c2_restart(); // Send restartif(!send_i2c2_byte(dev)){ // Send device addressi2c2_stop(); // Cancel if NACKreturn 0;}data = receive_i2c2_byte(0);i2c2_stop(); // Send stopreturn 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 0x11data = I2C2read(0x50, 0x01); // Read back byte at reg 0x01if(data != 0xF1){ // VerifyNop(); // 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.

author: juicycrunchwrap
author_rank: Contributor
timestamp: June 05, 2023, 02:19:04 pm
content: 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.