• AVR Freaks

Helpful ReplyHot!Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus.

Page: 12 > Showing page 1 of 2
Author
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
2018/10/12 11:00:51 (permalink)
0

Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus.

Hello, as the title says I am attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus for my college capstone project. The first thing I attempted to do was to read the MPU-9250's registers so that I can see if I need to change any configuration bits. However I am running into the problem that the SDA and SCL pins seem to be hovering around 0.8 V. They should be pulled high by the 4.7 K ohm resistors that are connected to Vdd, which is 3.3V. I don't think it's a problem with my hardware, so I came here to check if I wrote my code correctly. I have included my code below.
 
#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration (Allow only one reconfiguration)
#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow only one reconfiguration)
#pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB Module)
#pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB Module)

// DEVCFG2
#pragma config FPLLIDIV = DIV_12 // PLL Input Divider (12x Divider)
#pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier)
#pragma config UPLLIDIV = DIV_12 // USB PLL Input Divider (12x Divider)
#pragma config UPLLEN = OFF // USB PLL Enable (Disabled and Bypassed)
#pragma config FPLLODIV = DIV_256 // System PLL Output Clock Divider (PLL Divide by 256)

// DEVCFG1
#pragma config FNOSC = FRCDIV // Oscillator Selection Bits (Fast RC Osc w/Div-by-N (FRCDIV))
#pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled)
#pragma config IESO = ON // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_8 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = ON // Watchdog Timer Enable (WDT Enabled)
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)

// DEVCFG0
#pragma config JTAGEN = OFF // JTAG Disable (JTAG Port Disabled)
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF // Program Flash Write Protect (Disable)
#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF // Code Protect (Protection Disabled)

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

#include <xc.h>
#include <sys/attribs.h>

void IMUTestRead(int* REG1, int* REG2)
{
    I2C1CONbits.SEN = 1; //Enable a I2C start condition
    while(I2C1CONbits.SEN); //Wait for the start condition to end
    I2C1TRN = 0b11010000; //Transmit the IMU address followed by the write bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    I2C1TRN = 107; //Transmit the address of the IMU register that you want to read data from
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    I2C1CONbits.RSEN = 1; //Enable a I2C repeated start condition
    while(I2C1CONbits.RSEN); //Wait for the repeated start condition to end
    I2C1TRN = 0b11010001; //Transmit the IMU address followed by the read bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    I2C1CONbits.RCEN == 1; //Enable receive mode
    while(I2C1STATbits.RBF == 0); //Wait until the data is transfered into the I2C1RCV register
    *REG1 = I2C1RCV; //Load the data into the REG1 variable
    I2C1CONbits.ACKDT = 0; //Make it so that the PIC will send and ACK sequence
    I2C1CONbits.ACKEN = 1; //Start the ACK sequence
    while(I2C1CONbits.ACKEN); //Wait until an acknowledge sequence is finished
    I2C1CONbits.RCEN == 1; //Enable receive mode
    while(I2C1STATbits.RBF == 0); //Wait until the data is transfered into the I2C1RCV register
    *REG2 = I2C1RCV; //Load the data into the REG1 variable
    I2C1CONbits.ACKDT = 1; //Make it so that the PIC will send and NACK sequence
    I2C1CONbits.ACKEN = 1; //Start the NACK sequence
    while(I2C1CONbits.ACKEN); //Wait until an not acknowledge sequence is finished
    I2C1CONbits.PEN = 1; //Enable a I2C stop condition
    while(I2C1CONbits.PEN); //Wait for the stop condition to end
    return;
}

int main()
{
    int REG1, REG2; //Variables that hold the values obtained by the test read
    REG1 = 0;
    REG2 = 0;
    //REG1 = DEVCFG1bits.FPBDIV; //FPBDIV is set to 3, so PBCLK is 6 MHz
    I2C1BRG = 0x00E; //Set I2C1 clock to 400kHz
    I2C1CONbits.ON = 1; //Enable the SDA1 and SCL1 pins
    
     IMUTestRead(&REG1, &REG2);

    return;
    
}

#1
daveX
Starting Member
  • Total Posts : 40
  • Reward points : 0
  • Joined: 2013/07/20 15:20:21
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/12 16:11:14 (permalink)
0
I see you were spammed Crock, and that you have no replies so here's a quick hint.
 
If you don't set up your port bits they will have defaulted to output with contents = 0.
This fragment has nothing to do with I2C but it shows you the registers involved (here PortB )
    TRISB   = 0xA003; // Input Pins: RB0,1,13,15; Includes Pull-Ups
    ANSELB  = 0x0000; // Make all inputs PORT inputs (not A-D)
    LATBCLR = 0x043C; // Output Pins: unused RB2,3,4,5,9,10 to Zero outputs
    LATBSET = 0x1000; // Preset RB12 Panel D/C to Data


The name of the A-D disable register varies - look it up in the IO Port description for your part.
#2
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/12 18:17:06 (permalink)
0
     The other I/O ports don't really matter for this. They will matter later though, so thank you. I have learned that I hooked up the MPU-9250 wrong such that it was pulling the lines low. I have since corrected this and the lines are now at 3.3 V like they are supposed to be.
     I have also updated my code so that it's more readable. However, the integers I am using to store the read data remain at 0, the analog discovery I'm using doesn’t detect a start condition when I connect it to the SDA and SCL lines, and the program gets stuck waiting for the I2C1CONbits.ACKEN bit to clear, which it never does. I’m almost to the point where I’m thinking that my PIC’s I2C module is simply not functional. I also fear that I may be setting I2C1BRG to an incorrect value.
 
// DEVCFG3
// USERID = No Setting
#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration (Allow only one reconfiguration)
#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow only one reconfiguration)
#pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB Module)
#pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB Module)

// DEVCFG2
#pragma config FPLLIDIV = DIV_12 // PLL Input Divider (12x Divider)
#pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier)
#pragma config UPLLIDIV = DIV_12 // USB PLL Input Divider (12x Divider)
#pragma config UPLLEN = OFF // USB PLL Enable (Disabled and Bypassed)
#pragma config FPLLODIV = DIV_256 // System PLL Output Clock Divider (PLL Divide by 256)

// DEVCFG1
#pragma config FNOSC = FRCDIV // Oscillator Selection Bits (Fast RC Osc w/Div-by-N (FRCDIV))
#pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled)
#pragma config IESO = ON // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_8 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = ON // Watchdog Timer Enable (WDT Enabled)
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)

// DEVCFG0
#pragma config JTAGEN = OFF // JTAG Disable (JTAG Port Disabled)
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF // Program Flash Write Protect (Disable)
#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF // Code Protect (Protection Disabled)

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

#include <xc.h>
#include <sys/attribs.h>

void IMUTestRead(int* REG1, int* REG2);
void initI2C1(void);
void waitForIdle(void);
void start(void);
void stop(void);
void restart(void);
void ack(void);
void nack(void);

int main()
{
    ODCBbits.ODCB8 = 1; //open drain enable on SCA and SCL pins
    ODCBbits.ODCB9 = 1;
    TRISBbits.TRISB8 = 0; //set to outputs
    TRISBbits.TRISB9 = 0;
    LATBbits.LATB8 = 0; //clear outputs
    LATBbits.LATB9 = 0;
    TRISBbits.TRISB8 = 1; //set to inputs
    TRISBbits.TRISB9 = 1;
    int REG1, REG2; //Variables that hold the values obtained by the test read
    REG1 = 0;
    REG2 = 0;
    initI2C1();

    IMUTestRead(&REG1, &REG2);
    
    return;
    
}

void IMUTestRead(int* REG1, int* REG2)
{
    waitForIdle();
    start();
    I2C1TRN = 0b11010000; //Transmit the IMU address followed by the write bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
    I2C1TRN = 107; //Transmit the address of the IMU register that you want to read data from
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
    restart();
    I2C1TRN = 0b11010001; //Transmit the IMU address followed by the read bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
    I2C1CONbits.RCEN == 1; //Enable receive mode
    while(I2C1CONbits.RCEN); //Wait for the receive operation to complete
    *REG1 = I2C1RCV; //Load the data into the REG1 variable
    ack();
    I2C1CONbits.RCEN == 1; //Enable receive mode
    while(I2C1CONbits.RCEN); //Wait for the receive operation to complete
    *REG2 = I2C1RCV; //Load the data into the REG1 variable
    nack();
    stop();
    return;
}

void initI2C1(void)
{
    I2C1BRG = 6; //Set I2C1 clock to 400kHz
    I2C1CONbits.ON = 1; //Enable the SDA1 and SCL1 pins
    I2C1CONbits.DISSLW = 0; //Enable Slew Rate control
    I2C1CONbits.SMEN = 0; //Disable SMB bus input levels
}

void waitForIdle(void)
{
    while(I2C1CON & 0x1F); // Acknowledge sequence not in progress
                                // Receive sequence not in progress
                                // Stop condition not in progress
                                // Repeated Start condition not in progress
                                // Start condition not in progress
    while(I2C1STATbits.TRSTAT); // Bit = 0 ? Master transmit is not in progress
}

void start(void)
{
    waitForIdle();
    I2C1CONbits.SEN = 1;//Initiate Start condition
    while(I2C1CONbits.SEN);// Hardware clear at end of master Start sequence
    // 0 = Start condition not in progress
}

void stop(void)
{
    waitForIdle();
    I2C1CONbits.PEN = 1;//Initiate Stop condition on SDAxand SCLxpins
    // Hardware clear at end of master Stop sequence
    while(I2C1CONbits.PEN);// bit = 0 ---Stop condition not in progress
}
void restart(void)
{
    waitForIdle();
    I2C1CONbits.RSEN = 1; // Initiate Repeated Start condition on SDAxand SCLxpins
                                // Hardware clear at end of master Repeated Start sequence
    while(I2C1CONbits.RSEN); // bit = 0 ---Repeated Start condition not in progress
}

void ack(void)
{
    waitForIdle();
    I2C1CONbits.ACKDT = 0; // 0 = Send ACK during Acknowledge
    I2C1CONbits.ACKEN = 1; // 1 = Initiate Acknowledge sequence on SDAxand SCLxpins and
                                // transmit ACKDT data bit
                                // Hardware clear at end of master Acknowledge sequence
    while(I2C1CONbits.ACKEN); // bit = 0 ---Acknowledge sequence not in progress
}

void nack(void) // Acknowledge Data bit
{
    waitForIdle();
    I2C1CONbits.ACKDT = 1; // 1 = Send NACK during Acknowledge
    I2C1CONbits.ACKEN = 1; // 1 = Initiate Acknowledge sequence on SDAxand SCLxpins and
                                // transmit ACKDT data bit
                                // Hardware clear at end of master Acknowledge sequence
    while(I2C1CONbits.ACKEN); // bit = 0 ---Acknowledge sequence not in progress
}

#3
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/12 21:12:57 (permalink)
4 (1)
You don't need any of this
    ODCBbits.ODCB8 = 1; //open drain enable on SCA and SCL pins
    ODCBbits.ODCB9 = 1;
    TRISBbits.TRISB8 = 0; //set to outputs
    TRISBbits.TRISB9 = 0;

It may not matter, but it would be a good idea just to discard those lines.
If you were using the second I2C channel, you WOULD have to switch SDA2 and SCL2 from analog to digital mode (via ANSB), but there's no need for SDA1 and SCL1, they are not analog capable.
 
Here:
    start();
    I2C1TRN = 0b11010000; //Transmit the IMU address followed by the write bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();

you are not checking the ACK status from the Slave.
Try:
    start();
    I2C1TRN = 0b11010000; //Transmit the IMU address followed by the write bit
    while(I2C1STATbits.TRSTAT); //Wait for the transfer to complete
    if (I2C1STATbits.ACKSTAT)
    {
        //Report that we did not receive a NAK. Abort and send STOP.
    }


Nearly there...
#4
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/13 15:33:07 (permalink)
0
So you're staying I should make my code look like this.
 
void IMUTestRead(int* REG1, int* REG2)
{
    waitForIdle();
    start();
    I2C1TRN = 0b11010000; //Transmit the IMU address followed by the write bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
 while (!I2C1STATbits.ACKSTAT); //Wait for an achknolage bit from the slave
    I2C1TRN = 107; //Transmit the address of the IMU register that you want to read data from
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
 while (!I2C1STATbits.ACKSTAT); //Wait for an achknolage bit from the slave
    restart();
    I2C1TRN = 0b11010001; //Transmit the IMU address followed by the read bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
    I2C1CONbits.RCEN == 1; //Enable receive mode
    while(I2C1CONbits.RCEN); //Wait for the receive operation to complete
    *REG1 = I2C1RCV; //Load the data into the REG1 variable
    ack();
    I2C1CONbits.RCEN == 1; //Enable receive mode
    while(I2C1CONbits.RCEN); //Wait for the receive operation to complete
    *REG2 = I2C1RCV; //Load the data into the REG1 variable
    nack();
    stop();
    return;
}

#5
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 00:25:14 (permalink)
4 (1)
Crock101
So you're staying I should make my code look like this.

No.
Why did you change my
if (!I2C1STATbits.ACKSTAT)
to
while (!I2C1STATbits.ACKSTAT) ?
Your version is just plain wrong.
 
If the bit shows a NAK at that instant, it will NEVER change, so the while is totally pointless.
 

Nearly there...
#6
rodims
Super Member
  • Total Posts : 1491
  • Reward points : 0
  • Joined: 2009/02/10 11:08:59
  • Location: 51.9627, 7.6262
  • Status: online
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 03:42:16 (permalink)
0
I have learned that I hooked up the MPU-9250 wrong such that it was pulling the lines low.

 
Likely not your problem, but ensure that your AD0/SD0 input, which sets the i2c address, is set correctly.
Since you are using 0b1101000x as address you must ensure that AD0 is tied to VSS (low).
 
And for testing I would suggest to read register 117 (Who Am I) which always should return value 0x71.
 
 
post edited by rodims - 2018/10/14 03:47:05
#7
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 07:56:54 (permalink)
0
qhb
Crock101
So you're staying I should make my code look like this.

No.
Why did you change my
if (!I2C1STATbits.ACKSTAT)
to
while (!I2C1STATbits.ACKSTAT) ?
Your version is just plain wrong.
 
If the bit shows a NAK at that instant, it will NEVER change, so the while is totally pointless.
 


 
Isn't that what the
 
while(I2C1STATbits.TRSTAT);

Statement in the waitForIdle() function is for? It gets set when a transfer is in progress, and gets cleared when an ACK is received from the slave?
 
Also by the way, does writing something into the I2CTRN register automatically start a transfer process, or do I have to set/clear a bit to start the process? I ask because neither the I2C1STATbits.TRSTAT or the I2C1STATbits.TBF bits get set when I monitor them using the MPLAB's debugger.
#8
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 08:54:43 (permalink)
0
Quick update, I disconnected the SDA and SCL lines and the PIC was transmitting the signals correctly, the MPU-9250 was just holding the lines high because I connected pin 21 to ground instead of pin 20. So now everything is going swimmingly until the second ack after the restart condition.
 
I've attached an image of the logic signal that my analog discovery captured.
 
I'm not sure what exactly is going on here, any suggestions?
 
Edit:
I've determined that my value for I2C1BRG is probably not correct. I've tried to use the formulas in the in the family reference manual, but I still get an incorrect value. Does anyone have a formula that works?
post edited by Crock101 - 2018/10/14 09:56:35

Attached Image(s)

#9
davekw7x
Entropy++
  • Total Posts : 1769
  • Reward points : 0
  • Joined: 2012/01/16 12:01:07
  • Location: Left Coast, USA
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 12:10:12 (permalink) ☄ Helpfulby Crock101 2018/10/16 19:33:03
5 (1)
Crock101
 
I've attached an image of the logic signal that my analog discovery captured.
 
I'm not sure what exactly is going on here, any suggestions?

I'm not sure either.  Looks like things proceeded at some point without waiting for previous operation  to complete, but, since I don't know exactly what you changed from your original post,  I'm not going to try to debug it for you.  Instead, I'll offer some suggestions based on my experience (with I2C in general, and with the MPU9850 in particular).
 
Suggestions:
  • For testing, use a direct sequence for reading a single byte from the "WHO_AM_I" register.
  • Make all operations visible at the point of use instead of calling functions.
  • Use simplest operations to control progress. Previous suggestions by qhb would make a good start.  See attachment for the way I did it for my PIC32MX270F256 circuit.
    Bottom line (so far): Aim for consistency: Eyeballs + Brain, yielding "Correct at a Glance" debugging are much more efficient than PICkit3 + MPLABX.  Often my "Correct at a Glance" approach gives code that is pretty enough for my mom to post on the refrigerator (unlike my drawings of Peter Rabbit, which are just dreadful).
  • After verifying that you can read a byte, put the sequence in a function so that it can be used for further debugging as well as being used in the actual application.  The function will have parameters consisting of the device address and the register address.  Later on, if you need it, you can use that sequence as a basis for reading multiple bytes (in  successive register addresses) into an array.
  • Use same approach to writing a single byte.  For starters maybe set the MPU9850 for "Pass Through" operation by writing the appropriate bit to MCU9850 register 55 (0x37).  Then read it back using the function that you previously validated.
  • Finally, before getting to the "Good Stuff" of your application, afrer setting "Pass Through" operation, read the "WHO_AM_I" register of the AK9863 module using your previous function to read a byte
 
See attachment for the sequence I used for initial evaluation of MPU9850 with my PIC32MX270F256B.
 
Output:
 
Value of MPU9850 register 117 = 0x71: Good show!


Crock101
I've determined that my value for I2C1BRG is probably not correct.

According to the code in your original post, the system clock is 4 Mhz and you divide it by 8 to get the Peripheral Bus Clock
 
According to my calculations (and measurements) the BRG value of 0x0E (in your original post) gives a I2C bus clock period something in the neighborhood of 64 us (a bus frequency of something between 15 and 16 kHz).
I'll leave it to you to calculate the bus speed for the BRG value of 6 that you use in a later post.
 
Bottom line for bus speed: With a peripheral bus frequency of 500 kHz, there ain't no way to get an I2C bus clock of 400 kHz.  Period.  Full Stop.
 
Note that this (unexpectedly) slow I2C bus clock will not create any problems (as far as I can imagine), since there is, practically, no minimum bus speed.
 
Regards,

Dave
post edited by davekw7x - 2018/10/14 12:53:42

Sometimes I just can't help myself...
#10
rodims
Super Member
  • Total Posts : 1491
  • Reward points : 0
  • Joined: 2009/02/10 11:08:59
  • Location: 51.9627, 7.6262
  • Status: online
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 12:18:01 (permalink)
0
I've determined that my value for I2C1BRG is probably not correct. I've tried to use the formulas in the in the family reference manual, but I still get an incorrect value.

You need to be more specific, what you think is "not correct" and "incorrect"
 
What are your input parameters ? Which value do you calculate ? Which value do you expect ? Which value do you measure ? Which formula do you use ?
Especially which is your PBCLK (one comment says 6 MHz, but are you sure) ?
Your SCL clock in the diagram looks something like 4000 Hz.
#11
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/14 12:26:12 (permalink)
0
Crock101
...
Isn't that what the
 
while(I2C1STATbits.TRSTAT);

Statement in the waitForIdle() function is for? It gets set when a transfer is in progress, and gets cleared when an ACK is received from the slave?

TRSTAT gets cleared when the ACK/NAK status is received.
TRSTAT tells you the transfer is complete.
If ACKSTAT is low after that, you got an ACK.
If ACKSTAT is high after that, you got a NAK.
Your modified code will lock up if you ever get a NAK.
 

Also by the way, does writing something into the I2CTRN register automatically start a transfer process,

Yes.
 
or do I have to set/clear a bit to start the process? I ask because neither the I2C1STATbits.TRSTAT or the I2C1STATbits.TBF bits get set when I monitor them using the MPLAB's debugger.

You will never see them set, because by the time you look the transfer has finished.

Nearly there...
#12
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/16 19:49:22 (permalink)
0
davekw7x
 
According to the code in your original post, the system clock is 4 Mhz and you divide it by 8 to get the Peripheral Bus Clock
 
According to my calculations (and measurements) the BRG value of 0x0E (in your original post) gives a I2C bus clock period something in the neighborhood of 64 us (a bus frequency of something between 15 and 16 kHz).
I'll leave it to you to calculate the bus speed for the BRG value of 6 that you use in a later post.
 
Bottom line for bus speed: With a peripheral bus frequency of 500 kHz, there ain't no way to get an I2C bus clock of 400 kHz.  Period.  Full Stop.
 
Note that this (unexpectedly) slow I2C bus clock will not create any problems (as far as I can imagine), since there is, practically, no minimum bus speed.
 
Regards,

Dave




So, the only one who was helpful here was Dave (thank you Dave).
 
I need the I2C bus clock to be at 400 kHz because occording to the IMU's data sheet
"Communication with all registers of the device is performed using either I2C at 400kHz or SPI at 1MHz. For applications requiring faster communications, the sensor and interrupt registers may be read using SPI at 20MHz."
 
So according to Dave's comment, and the PIC32 family referance manual, I need to decrease the PBCLK's devisor such that the PBCLK is above 10 MHz.
 
Also Dave, the PIC32MX270F256D has a clock speed of 40 MHz, not 4 MHz. So the PBCLK is actually 5 MHz.
 
I crunched the numbers, and with a PBCLK of 5MHz, I2C1BRG needs to be set to 4. With a PBCLK of 10MHz, I2C1BRG needs to be set to 10. I'll try out both configurations tomorrow.
#13
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/16 19:56:41 (permalink)
0
qhb
 
Your modified code will lock up if you ever get a NAK.
 
You will never see them set, because by the time you look the transfer has finished.




So you're saying I can remove the 
while(I2C1STATbits.TBF); 
statement.
 
Also, I don't really care about getting a NACK right now since this is still the breadboarding phase, my group will worry about error conditons once we start to write the actual code for this thing.
#14
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/16 20:06:07 (permalink)
0
Crock101
qhb
 
Your modified code will lock up if you ever get a NAK.
 
You will never see them set, because by the time you look the transfer has finished.




So you're saying I can remove the 
while(I2C1STATbits.TBF); 
statement.

No.
You asked why you never saw it set in the debugger. The debugger is too slow to see something that is only set for a few milliseconds.
That doesn't mean your code doesn't need to wait for the operation to complete.
 
 

Also, I don't really care about getting a NACK right now since this is still the breadboarding phase, my group will worry about error conditons once we start to write the actual code for this thing.

When trying to debug I2C code, the ACK/NAK response to sending the Slave address is the FIRST thing you should be getting working. The secret to efficient debugging is to check each simple step, and move on to the next when you have each one working.
Just running a long complex bit of code, and scratching your head when it doesn't work, is not an efficient way to debug.
 
However, I note that you think Dave is the only one giving you any useful help, so I'll bow out at this stage.
 

Nearly there...
#15
davekw7x
Entropy++
  • Total Posts : 1769
  • Reward points : 0
  • Joined: 2012/01/16 12:01:07
  • Location: Left Coast, USA
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/16 22:22:03 (permalink) ☄ Helpfulby Crock101 2018/10/20 13:26:00
5 (1)
Crock101
...
Also Dave, the PIC32MX270F256D has a clock speed of 40 MHz, not 4 MHz. So the PBCLK is actually 5 MHz.

I rarely post specifics that I have not tested, but, of course you couldn't know for sure that I did it right
 
Here's the deal:
The code you showed in your post #1 and #5 has this:

#pragma config FNOSC = FRCDIV // Oscillator Selection Bits (Fast RC Osc w/Div-by-N (FRCDIV))

Note that the frequency of FRC is 8 MHz.

Since you did not do anything to change the system clock, and since the default value of  OSCCONbits.FRCDIV is 0b001 (Divide by 2), the system clock frequency is 4 MHz.Then you have

#pragma config FPBDIV = DIV_8 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)

So the Peripheral Bus Clock (PBCLK) is 4 MHz / 8 = 500 kHz.

I tested your setup by using PBCLK to drive a timer and toggling an LED when the timer overflowed. My results were consistent with a PBCLK = 500 kHz.  (See Footnote)
 
I could have just measured the I2C clock at this point, but that would be cheating, so I calculated the I2C clock frequency using Equation 24-1 in Section 24, DS61116F, of the PIC32 Family Reference manual:
   I2CxBRG = ((1 / (2 * Fsck) - TPGD) * PBCLK) - 2

Where Fsck is the I2C clock, PBCLK is the Peripheral Bus Clock, and TPGD is a somewhat-nefarious delay whose "typical" value is said to be 104 ns.
 
I solved for Fsck and I plugged in I2CXBRG = 6 (the value in your post #5), PBCLK = 500e3 and TPGD = 104e-9:
   Fsck = 1.0 / (2.0 * (TPGD + (I2CxBRG + 2.0)/PBCLK)) = (approximately) 31048.2 Hz


When I did observe the I2C clock on my 'scope, the results were in the neighborhood of 30 kHz, so, I am satisfied that, using your configuration, the observation on my system is consistent with the calculation based on PBCLK = 500 kHz
 
Now to try to make sense of your results, I looked at the scope shot in your post #11:
 
Looking at that picture between 0.08 ms and 0.24 ms, I see that there are something like a little more than five cycles of I2C clock in that 160 us.

Now, 5 cycles in 160 us corresponds to a frequency of 32250 Hz.

So I think that the observation is close enough to the formula that I can say that PBCLK is also 500 kHz in your system.
 
Bottom line:
    Bada-bing, bada-boom.  Back to the drawing board to get your System Clock to a reasonable value.  40 MHz is reasonable.  By using my own set of configuration pragmas, I ran my chip PBCLK at 48 MHz, and got approximately 400 kHz with BRG register setting of 53.  (In the "real" project, PBCLK was lower, but for evaluation purposes, I run things all-out.)
 
Finally (yes, really, finally):
Since I already had my little calculation script set up for Equation 24-1 I ran it for a few different PBCLK values
 
For PBCLK =  5 MHz: I2CxBRG =  3.730 ==>  4
For PBCLK = 10 MHz: I2CxBRG =  9.460 ==>  9
For PBCLK = 20 MHz: I2CxBRG = 20.920 ==> 21
For PBCLK = 40 MHz: I2CxBRG = 43.840 ==> 44
 
Regards,

Dave
Footnote: With a new project I always (yes, always) test system clock and peripheral bus clock before doing anything else.  With the PIC32, I usually use the CP0 counter for initial testing, but since I almost always have a millisecond counter using a timer peripheral in my projects, I sometimes do the timer first.
post edited by davekw7x - 2018/10/17 00:21:48

Sometimes I just can't help myself...
#16
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/20 13:33:39 (permalink)
0
davekw7x
  Back to the drawing board to get your System Clock to a reasonable value.  40 MHz is reasonable.  By using my own set of configuration pragmas, I ran my chip PBCLK at 48 MHz, and got approximately 400 kHz with BRG register setting of 53.  (In the "real" project, PBCLK was lower, but for evaluation purposes, I run things all-out.)



Ok, so the obvious question is, how do I do that? I've never had to change the system clock on a PIC before. I assume I need to change FNOSC, but to what value?
 
Update:
While debugging my code I noticed a fairly glaring flaw that no one noticed.
 
instead of writing
I2C1CONbits.RCEN = 1; //Enable receive mode

to enable receive mode, I wrote
I2C1CONbits.RCEN == 1; //Enable receive mode

which did nothing.
 
I have now changed this, and everything works perfectly now. (note the included screen shot below)
 
This is a huge relief, however I still want to learn how to change the oscillator settings. So if you could help me with that, that would be fantastic.
 
Here is the version of my code that was able to achieve this result:
 
// DEVCFG3
// USERID = No Setting
#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration (Allow only one reconfiguration)
#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow only one reconfiguration)
#pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB Module)
#pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB Module)

// DEVCFG2
#pragma config FPLLIDIV = DIV_12 // PLL Input Divider (12x Divider)
#pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier)
#pragma config UPLLIDIV = DIV_12 // USB PLL Input Divider (12x Divider)
#pragma config UPLLEN = OFF // USB PLL Enable (Disabled and Bypassed)
#pragma config FPLLODIV = DIV_256 // System PLL Output Clock Divider (PLL Divide by 256)

// DEVCFG1
#pragma config FNOSC = FRCDIV // Oscillator Selection Bits (Fast RC Osc w/Div-by-N (FRCDIV))
#pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled)
#pragma config IESO = ON // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_1 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = ON // Watchdog Timer Enable (WDT Enabled)
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)

// DEVCFG0
#pragma config JTAGEN = OFF // JTAG Disable (JTAG Port Disabled)
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF // Program Flash Write Protect (Disable)
#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF // Code Protect (Protection Disabled)

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

#include <xc.h>
#include <sys/attribs.h>

void IMUTestRead(int* REG1, int* REG2);
void initI2C1(void);
void waitForIdle(void);
void start(void);
void stop(void);
void restart(void);
void ack(void);
void nack(void);
/*
void IMUConfig(void);
void IMUSleep(void);
void IMUWake(void);
void IMUReadSensors(void);
*/

int main()
{
    int REG1, REG2; //Variables that hold the values obtained by the test read
    REG1 = 0;
    REG2 = 0;
    initI2C1();
    waitForIdle();
    /*
    IMUConfig();
    
    IMUSleep();
    */
    while(1)
    {
       //IMUWake();
       IMUTestRead(&REG1, &REG2);
       //IMUSleep();
    }
    return; //The PIC will never reach this.
    
}

void IMUTestRead(int* REG1, int* REG2)
{
    waitForIdle();
    start();
    I2C1TRN = 0b11010000; //Transmit the IMU address followed by the write bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
 while (I2C1STATbits.ACKSTAT); //Wait for an achknolage bit from the slave
    I2C1TRN = 117; //Transmit the address of the IMU register that you want to read data from
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
 while (I2C1STATbits.ACKSTAT); //Wait for an achknolage bit from the slave
    restart();
    I2C1TRN = 0b11010001; //Transmit the IMU address followed by the read bit
    while(I2C1STATbits.TBF); //Wait for the transfer to complete
    waitForIdle();
    I2C1CONbits.RCEN = 1; //Enable receive mode
    while(I2C1CONbits.RCEN); //Wait for the receive operation to complete
    while(!I2C1STATbits.RBF); //Wait for the receive operation to complete
    *REG1 = I2C1RCV; //Load the data into the REG1 variable
    ack();
    I2C1CONbits.RCEN = 1; //Enable receive mode
    while(I2C1CONbits.RCEN); //Wait for the receive operation to complete
    while(!I2C1STATbits.RBF); //Wait for the receive operation to complete
    *REG2 = I2C1RCV; //Load the data into the REG1 variable
    nack();
    stop();
    return;
}

void initI2C1(void)
{
    I2C1BRG = 3; //Set I2C1 clock to 400kHz
    I2C1CONbits.ON = 1; //Enable the SDA1 and SCL1 pins
    I2C1CONbits.DISSLW = 0; //Enable Slew Rate control
    //I2C1CONbits.SMEN = 0; //Disable SMB bus input levels
}

void waitForIdle(void)
{
    while(I2C1CON & 0x1F); // Acknowledge sequence not in progress
                                // Receive sequence not in progress
                                // Stop condition not in progress
                                // Repeated Start condition not in progress
                                // Start condition not in progress
    while(I2C1STATbits.TRSTAT); // Bit = 0 ? Master transmit is not in progress
}

void start(void)
{
    waitForIdle();
    I2C1CONbits.SEN = 1;//Initiate Start condition
    while(I2C1CONbits.SEN);// Hardware clear at end of master Start sequence
    // 0 = Start condition not in progress
}

void stop(void)
{
    waitForIdle();
    I2C1CONbits.PEN = 1;//Initiate Stop condition on SDAxand SCLxpins
    // Hardware clear at end of master Stop sequence
    while(I2C1CONbits.PEN);// bit = 0 ---Stop condition not in progress
}
void restart(void)
{
    waitForIdle();
    I2C1CONbits.RSEN = 1; // Initiate Repeated Start condition on SDAxand SCLxpins
                                // Hardware clear at end of master Repeated Start sequence
    while(I2C1CONbits.RSEN); // bit = 0 ---Repeated Start condition not in progress
}

void ack(void)
{
    waitForIdle();
    I2C1CONbits.ACKDT = 0; // 0 = Send ACK during Acknowledge
    I2C1CONbits.ACKEN = 1; // 1 = Initiate Acknowledge sequence on SDAxand SCLxpins and
                                // transmit ACKDT data bit
                                // Hardware clear at end of master Acknowledge sequence
    while(I2C1CONbits.ACKEN); // bit = 0 ---Acknowledge sequence not in progress
}

void nack(void) // Acknowledge Data bit
{
    waitForIdle();
    I2C1CONbits.ACKDT = 1; // 1 = Send NACK during Acknowledge
    I2C1CONbits.ACKEN = 1; // 1 = Initiate Acknowledge sequence on SDAxand SCLxpins and
                                // transmit ACKDT data bit
                                // Hardware clear at end of master Acknowledge sequence
    while(I2C1CONbits.ACKEN); // bit = 0 ---Acknowledge sequence not in progress
}

/*
void IMUConfig(void)
{
    
}

void IMUSleep(void)
{
    
}

void IMUWake(void)
{
    
}

void IMUReadSensors(void)
{
    
}
*/

post edited by Crock101 - 2018/10/20 14:23:13

Attached Image(s)

#17
davekw7x
Entropy++
  • Total Posts : 1769
  • Reward points : 0
  • Joined: 2012/01/16 12:01:07
  • Location: Left Coast, USA
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/20 17:28:21 (permalink) ☄ Helpfulby Crock101 2018/10/28 07:43:32
5 (1)
Crock101
davekw7x
  Back to the drawing board to get your System Clock to a reasonable value

Ok, so the obvious question is, how do I do that?



Suppose I want to run my PIC32MX270 at a System Clock frequency of 48 MHz.

It is possible to set up clock frequencies using configuration bit settings that don't  require any initialization software.

Look at the block diagram for the Oscillator in the Data Sheet or in Section 6 of the PIC32 Family Reference Manual.  Specifically, look at the PLL.

Restrictions: Input clock to PLL must be 4 to 5 MHz.  Output frequency must be no greater than the specification on the Data Sheet.


Clock source is selected by COSC<2:0>

To keep things simple, I'll  use FRC with the PLL

#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)


Since the FRC has a frequency of 8 MHz, we can get the proper input frequency by setting FPLLIDIV to 2:

#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)


Now, the PLL will multiply this 4 MHz by a value from 15 to 24 by setting the appropriate value of PLLMULT.  Since I can't multiply by 12 (to get 48 MHz) am I out of luck?

No, because...
If I multiply 4 MHz by 24 I will get 96 MHz and then I can divide that by 2 to get 48 MHz
 
So...

#pragma config FPLLMUL = MUL_24         // PLL Multiplier (24x Multiplier)


And

#pragma config FPLLODIV = DIV_2         // System PLL Output Clock Divider (PLL Divide by 2)


Summary: System Clock Frequency = 8 MHz / 2 * 24 / 2 = 48 MHz

I can set the Peripheral Bus Clock frequency to a sub-multiple of this by the setting of FPBDIV

If I set FPBDIV to 1, peripheral bus clock will be 48 MHz

#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)


To save power (and, perhaps, reduce system noise), you can set FPBDIV to lower values as appropriate.

Left as an exercise to the student: What changes would you make to get a System Clock frequency of 40 MHz and a Peripheral Bus Clock frequency of 20 MHz?

Regards,

Dave
post edited by davekw7x - 2018/10/20 21:34:59

Sometimes I just can't help myself...
#18
Crock101
New Member
  • Total Posts : 13
  • Reward points : 0
  • Joined: 2018/10/01 14:09:27
  • Location: 0
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/28 07:49:25 (permalink)
0
Thank you Dave, just out of curiosity, what are the different types of oscillators used for?
 
Also, one of my other group members is attempting to use the PIC's USB functionality to connect it to a desktop computer in order to transfer data to a program on the computer. When looking at the Oscillator block diagram, I noticed that the PIC has a dedicated PLL for the USB clock. However my group member has been telling me that we need to use an external oscillator in order to make the computer recognize the PIC. Can you provide any insights into this?
post edited by Crock101 - 2018/10/28 09:57:52
#19
davekw7x
Entropy++
  • Total Posts : 1769
  • Reward points : 0
  • Joined: 2012/01/16 12:01:07
  • Location: Left Coast, USA
  • Status: offline
Re: Attempting to make a PIC32MX270F256D and a mpu-9250 communicate via an I2C bus. 2018/10/28 10:20:04 (permalink)
0
Crock101
... one of my other group members is attempting to use the PIC's USB functionality to connect it to a desktop computer in order to transfer data to a program on the computer

USB 2.0 Full Speed clock accuracy should be +/- 0.5%.  Since the internal FRC oscillator does not meet this specification, you should always use an external crystal or oscillator for USB applications with this chip.  With crystals you can easily get accuracy within a few tens of parts per million at a very low cost.  For wide temperature ranges, it might be necessary to use an external temperature-compensated oscillator (TCXO) rather than a crystal (at somewhat higher cost).

With this device, 8 MHz is a reasonable value of input clock.  Other, more primitive, devices may require 12 MHz clock inputs to work with USB, and I have used 12 MHz crystals for lots of different projects.
 
My colleague in Shenzhen came up with several bushel baskets full of 12 MHz crystals at a crazy low price.  (I'm thinking they might have "fell off a truck," as we used to say, before I went legit.)

You can use the same input clock source to set up CPU frequency.  Doesn't have to be directly related to the USB clock.  That's why it is a separate PLL.

Bottom line: Look at the data sheet.  See what settings you can come up with to get proper USB frequency  with an 8 MHz crystal. Questions?  Tell us what you tried, and what happened that you don't understand.

Note that I typically start out using internal oscillator and do some self-tests before switching to crystal-pll operation in application code.


Regards,

Dave
post edited by davekw7x - 2018/10/28 10:22:16

Sometimes I just can't help myself...
#20
Page: 12 > Showing page 1 of 2
Jump to:
© 2019 APG vNext Commercial Version 4.5