CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

dsPIC33 I2C Hardware Issue
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

dsPIC33 I2C Hardware Issue
PostPosted: Fri Dec 01, 2017 12:38 pm     Reply with quote

I'm having trouble with Hardware I2C on a dsPIC33EP512GP502
Compiler v5.075

Based on other forum discussions I was trying to use
Code:
#pin_select SCL2OUT = PIN_B6
#pin_select SCL2IN  = PIN_B6
#pin_select SDA2OUT = PIN_B5
#pin_select SDA2IN  = PIN_B5
#use i2c(FORCE_HW,I2C2,FAST=400000)


I get "Error#7 Invalid Pre-Processor directive Invalid Pin ID" for each of the pin_select statements.

On the data sheet pin 14 is PGED2/ASDA2/RP37/RB5 and pin 15 is PGEC2/ASCL2/RP38/RB6

So I'm confused why there is no SDA2/SCL2 for this chip and only the Alternates (also the case for I2C1)?

The following compiles and runs fine:
Code:
#define i2c_SCL PIN_B6
#define i2c_SDA PIN_B5
#use i2c(SCL=i2c_SCL,SDA=i2c_SDA,FAST=400000)


If I try to compile this I get the error "Option invalid Wrong pins for H/W":
Code:
#define i2c_SCL PIN_B6
#define i2c_SDA PIN_B5
#use i2c(FORCE_HW,SCL=i2c_SCL,SDA=i2c_SDA,FAST=400000)


This will compile but RB5/6 stay high all the time:
Code:
#define i2c_SCL PIN_B6
#define i2c_SDA PIN_B5
#use i2c(FORCE_HW,I2C2,FAST=400000)


In 33EP512GP502.h I don't see the pins SCL2OUT/SCL2IN/SDA2OUT/SDA2IN defined:
Code:
////////////////////////////////////////////////////////////////// PIN_SELECT
// #pin_select function=pin
// Valid Pins:
//    PIN_A4,PIN_B0,PIN_B1,PIN_B2,PIN_B3,PIN_B4,PIN_B5,PIN_B6,PIN_B7,PIN_B8,
//    PIN_B9,PIN_B10,PIN_B11,PIN_B12,PIN_B13,PIN_B14,PIN_B15
// Virtual Pins for input peripherals only:
//    C1OUT,C2OUT,C3OUT,C4OUT,PTGO30,PTGO31,INDX1,HOME1
// Input Functions:
//    INT1,INT2,T2CK,IC1,IC2,IC3,IC4,OCFA,FLT1,FLT2,QEA1,QEB1,INDX1,HOME1,U1RX,
//    U2RX,SDI2,SCK2IN,SS2IN,C1RX,SYNCI1,DTCMP1,DTCMP2,DTCMP3
// Output Functions:
//    NULL,U1TX,U2TX,SDO2,SCK2OUT,SS2OUT,C1TX,OC1,OC2,OC3,OC4,C1OUT,C2OUT,C3OUT,
//    SYNCO1,QEI1CCMP,REFCLK,C4OUT
//


I'd appreciate any suggestions
Ttelmah



Joined: 11 Mar 2010
Posts: 12538

View user's profile Send private message

PostPosted: Fri Dec 01, 2017 2:10 pm     Reply with quote

The reason is that the I2C, doesn't support PPS.

PPS is the system where peripherals can be moved to any RP pin. It is this that #PIN SELECT drives.
The I2C, only supports two sets of pins, and these are selected in the fuses.
Just use the fuse ALTI2C2, and then don't specify the pins in the setup. Use:

#use i2c(I2C2,FAST=400000)

Using the device name automatically selects 'FORCE_HW'.
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 10:14 am     Reply with quote

Thanks for your feedback Ttelmah, I'm still having some problems. I tried to simplify the program as basic as possible (see below). When I compile with the FORCE_SW line I can see the clock on B6 and data on B5. When I compile with hardware I see the clock high and data low.

I noticed with hardware I2C enabled, if I remove my I2C device I can then see both data and clock (sw can communicate with the device fine). My supply is 3.3v. Initially my pull-ups were 1k, I also tried 2k. My device spec shows a SCL clock frequency of 100-400kHz (I also tried 200).

Code:
#include <33EP512GP502.h>
#FUSES NOWDT                    //No Watch Dog Timer (must be off to control from firmware)
#FUSES NOJTAG                   //JTAG disabled
#FUSES CKSFSM                   //Clock Switching is enabled, fail Safe clock monitor is enabled
#FUSES FRC_PLL
#FUSES ALTI2C2
#build (stack=1024)

#use delay(internal=40000000)

#define LED PIN_A0
#define HU01ADDR 0x27            // address of humidity sensor

#define i2c_SCL PIN_B6
#define i2c_SDA PIN_B5
//#use i2c(STREAM=i2cM,FORCE_SW,SCL=i2c_SCL,SDA=i2c_SDA,FAST=400000)
#use i2c(STREAM=i2cM,I2C2,FAST=400000)

void main()
{
   int ack=0;
   
    set_tris_a(0b1111111111101011);
    set_tris_b(0b0000001101101111);
    setup_adc_ports(NO_ANALOGS);
    i2c_init(i2cM,true);
 
    while(true){
        output_high(LED);
           i2c_start();
           ack = i2c_write((HU01ADDR << 1)|1);
           i2c_stop();
        output_low(LED);
    }           
}
Ttelmah



Joined: 11 Mar 2010
Posts: 12538

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 11:47 am     Reply with quote

Tell us the part number of the sensor?.

That the I2C works when disconnected, suggests the device is holding the lines. It might be a signalling level problem. The hardware has different signalling levels than the software.....
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 11:59 am     Reply with quote

It's the Honeywell HIH9000 series humidity sensor.
https://sensing.honeywell.com/sensors/humidity-sensors/HIH9000-series

From the data sheet:
Low Level Output voltage = max 20% Vdd
High Level Output voltage = min 80% Vdd
Low Level Input voltage = max 20% Vdd
High Level Input voltage = min 80% Vdd

Supply = 2.3-5.5V

I2C pullup resistor = typ 2.2kOhm


Last edited by daveh on Mon Dec 04, 2017 12:25 pm; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 12538

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 12:17 pm     Reply with quote

Code:

void main()
{
   int ack=0;
   
    set_tris_a(0b1111111111101011);
    set_tris_b(0b0000001101101111);
    setup_adc_ports(NO_ANALOGS);
    //i2c_init(i2cM,true);
    //You only need I2C INIT if you specify NOINIT in the setup
    delay_ms(100);
 
    while(true){
        output_high(LED);
           i2c_start();
           ack = i2c_write((HU01ADDR << 1)|1);
           i2c_stop();
        output_low(LED);
    }           
}


The chip specifically says it needs 60mSec after power on, before it can be used. The PIC will also probably start earlier than the chip does, so try 100mSec as shown.
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 12:48 pm     Reply with quote

Thanks Ttelmah, I tried adding the delay as you suggested and I'm getting the same thing.

When I unplug the sensor I can see clk/data on the scope. The levels look the same as when I switch to SW. Interestingly, if I remove either the CLK or the DATA line to the sensor the HW will send, if both are connected then CLK goes to 3.3v and DATA to 0v.
temtronic



Joined: 01 Jul 2010
Posts: 5763
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 1:28 pm     Reply with quote

hmm...basic question

Have you compiled and run PCM P's I2C Scanner program from the code library ?

With EVERY program using I2C devices this is the FIRST program to run ! It'll confirm basic hardware and device access.

Jay
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 2:11 pm     Reply with quote

In my searches I did come across the I2C Scanner Program http://www.ccsinfo.com/forum/viewtopic.php?t=49713&highlight=i2c+scanner which seemed like it could come in handy.

I'm not sure it would be helpful with my current problem. If I compile my code with FORCE_SW it works fine (and my more complete program is able to get valid data from the I2C device w/o a problem). However when I attempt to compile the basic program shown above with the hardware I2C module the SCL simply stays at 3.3v and never moves and the SDA stays at 0v and never moves.

If I disconnect the SDA line to the sensor the hardware module starts sending data, or, if I disconnect the SCL line from the sensor the hardware module starts sending data. If both are connected nothing changes.
temtronic



Joined: 01 Jul 2010
Posts: 5763
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 2:15 pm     Reply with quote

hmm I'm wondering if the batch of PICs you're using have an 'errata' as it sounds like you've narrowed down to PIC I2C hardware.
Any chance you've got another PIC to try ? Maybe yours is damaged ??
Ttelmah



Joined: 11 Mar 2010
Posts: 12538

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 2:33 pm     Reply with quote

Looking at the data, I'm not actually sure it is legal to read without first having triggered an acquisition. The sensor is 'powered down' until this is sent.
Now the software I2C might well 'plough through' an invalid state after the I2C command....
Quote:

By default, the digital output humidity sensor performs humidity
measurement and temperature measurement conversions
whenever it receives a Measurement Request (MR) command;
otherwise, the digital output humidity sensor is always powered
down. The results are stored after each measurement in output
registers to be read using a Data Fetch (DF) command.


Being powered down, I don't think it'll give the ACK.
Code:


#define LED PIN_A0
#define HU01ADDR 0x27            // address of humidity sensor

#use i2c(STREAM=i2cM,I2C2,FAST=400000)

HU01ADDR8 = HU01ADDR*2

void main()
{
    int ack=0;
    int8 MSB, LSB;
    int16 value;
   
    set_tris_a(0b1111111111101011);
    set_tris_b(0b0000001101101111);
    setup_adc_ports(NO_ANALOGS);
    delay_ms(100);
 
    while(true)
    {
        output_high(LED);
        i2c_start(i2cM);
        ack=i2c_write(i2cM,HU01ADDR8); //trigger
        i2c_stop(i2cM);
        if (ack==0) //chip has responded to the write
        {
            delay_ms(40); //typical 36mSec
            while (TRUE)
            {
                i2c_start(i2cM);
                i2c_write(i2cM,HU01ADDR8 | 1); //start a read
                MSB=i2c_read(i2cM);
                LSB=i2c_read(i2cM);
                i2c_stop(i2cM);
                if ((MSB & 0xC0)==0)
                    break; //there has been a legitimate reply
             }//retry read if not
             value=make16(MSB,LSB);
         }
         output_low(LED); 
    }           
}


Reading the sheet, the chip doesn't respond as a normal I2C device is meant to. The write is used as a trigger to start the chip, not as a flag for the actual write, and then the read has to be repeated till the status bytes are 00, before the data is legitimate. Given the 'sleep' comment I don't think it will respond correctly without the initial write.
The software is allowing the invalid response, but with the hardware it hangs the port...

There have been threads in the past where people have tried doing things like this, and found the hardware I2C doesn't like it.
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 3:53 pm     Reply with quote

Ok, so this is sort of interesting, so using this as my main:
Code:
void main()
 {
    int ack=0;
     
     set_tris_a(0b1111111111101011);
     set_tris_b(0b0000001101101111);
     setup_adc_ports(NO_ANALOGS);
     delay_ms(100);


     while(true)
     {
         delay_ms(5000);
         output_high(LED);
         i2c_start(i2cM);
         ack=i2c_write(i2cM,HU01ADDR8); //trigger
         i2c_stop(i2cM);
 
         delay_ms(5000);
         output_low(LED);
         i2c_start(i2cM);
         ack=i2c_write(i2cM,HU01ADDR8|1); //trigger
         i2c_stop(i2cM);
     }           
 }


Starting with both the SDA & SCL high, when the PIC sends HU01ADDR8 I'll see data on the scope. If I reset the PIC before it sends HU01ADDR8|1 it will continue to send the address after each reset(visible on the scope). If however I allow it to send the second address 'HU01ADDR8|1' then the HU sensor holds SDA low and won't release it. It will continue to hold SDA low until I remove the HU sensor and SDA goes high.

Any thoughts on why it would be holding SDA low and never release it?

Also I think with the software enabled I'd actually only get an 'ACK' every other read, so maybe it would push the write out even if SDA was held low?

I've seen I2C code where before they start writing they toggle the SCL while watching for the SDA line to go high. Is this good practice? I'm guessing it's for a situation like this. Can I do something like this while using the hardware module?
Ttelmah



Joined: 11 Mar 2010
Posts: 12538

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 12:07 am     Reply with quote

Before doing anything else, stop, and triple check your connections. The clock is clocked once for the ACK. The only line that is _held_, is the SCL, which the slave will hold low till it is is ready.
daveh



Joined: 30 Aug 2013
Posts: 17

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 10:56 am     Reply with quote

I checked connections again, everything seems good. To verify, I re-loaded my software I2C program and with the same connections I can read valid data from the sensor.

Ok, I think I figured out the problem. So when a read is requested from this particular sensor an i2c_read MUST take place, and the last read MUST be followed with a NACK before the STOP or else it will hold the SDA line low!

Apparently with the hardware I2C module it will not attempt to write or clock if the SDA is held low. With the software it seems it will write even if the SDA is held low.

So from the I2C bus spec https://www.nxp.com/docs/en/user-guide/UM10204.pdf in section 3.1.16 the second paragraph states:
Quote:
If the data line (SDA) is stuck LOW, the master should send nine clock pulses. The device
that held the bus LOW should release it sometime within those nine clocks. If not, then
use the HW reset or cycle power to clear the bus.


With software I2C this can happen but it doesn't seem possible with the hardware I2C module. It appears that once the I2C slave holds the SDA low the hardware module will no longer output anything.

Any suggestions?
Ttelmah



Joined: 11 Mar 2010
Posts: 12538

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 11:31 am     Reply with quote

The hardware will accept the SDA being held low.

You need to test the SDA line after the command, and if it is held low (the error), clock nine pulses out the clock line (disable the port and send these), then re-enable the port.
The software doesn't care that the line is held, so sends 9 clocks when you next transmit a byte. the hardware stops because it knows the bus shouldn't be in this state, waiting for you to fix it....
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group