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 CCS Technical Support

I2C issue on PIC16F877A
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
OldPhart



Joined: 04 Dec 2017
Posts: 8
Location: Canada

View user's profile Send private message

I2C issue on PIC16F877A
PostPosted: Mon Dec 04, 2017 3:24 pm     Reply with quote

Hello....
I am working on a timer project, using a PIC16F877A, and a LCD readout. I am using an I2C bus RTC (DS3231). I am using the demo (V.5.074d) compiler.
The LCD is working, and I can write to it. Next is the I2C interface with the RTC. The PIC has hardware I2C, so I entered the following in MAIN.h:

#use I2C(MASTER, slow, scl=PIN_C3, sda=PIN_C4, FORCE_HW)

The compiler returns: ERROR Option invalid Bad Option 59

if I enter: #use I2C(MASTER, slow, FORCE_HW) it is happy and compiles. However, if I run the program, it hangs at:

i2c_write(0xD1); // 7 bit slave address (RTC, DS3231) plus read bit0=1

where I send the RTC address and the write bit (required before I can send the address of the byte to read). Documentation is very thin, and not much help. No mention of the error code. I read somewhere in the forum that i2c_write waits for data to arrive, so from that I suspect the I2C hardware was not initialized properly, which brings me back to the #use I2C issue. That statement is straight out of the book as far as I can tell. Any suggestions???

Thanks,
OldPhart.
Ttelmah



Joined: 11 Mar 2010
Posts: 19486

View user's profile Send private message

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

Show us the processor declaration, fuses and clock setup that come before the I2C setup.
What compiler version?.
OldPhart



Joined: 04 Dec 2017
Posts: 8
Location: Canada

View user's profile Send private message

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

in MAIN.h :
Code:

#include <16F877A.h>
#FUSES NOBROWNOUT,LVP,NOWDT,HS,NOCPD,NOWRT,NOPROTECT,NOPUT

#use delay (clock=4000000)
#use FIXED_IO( B_outputs=PIN_B5,PIN_B4,PIN_B3,PIN_B2,PIN_B1 )

#define LCD_Blight  PIN_A5
#define RTC_INT     PIN_B0
#define SSR_OUT     PIN_B1
#define JP4_4       PIN_B2
#define JP4_3       PIN_B3
#define JP4_2       PIN_B4
#define JP4_1       PIN_B5
#define PGC         PIN_B6
#define PGD         PIN_B7
#define SCL         PIN_C3
#define SDA         PIN_C4
#define KEY_COL1    PIN_D0
#define KEY_COL2    PIN_D1
#define KEY_COL3    PIN_D2
#define KEY_COL4    PIN_D3
#define KEY_ROW1    PIN_D4
#define KEY_ROW2    PIN_D5
#define KEY_ROW3    PIN_D6
#define KEY_ROW4    PIN_D7
#define RATE_LOW    PIN_E0
#define RATE_MED    PIN_E1
#define RATE_HI     PIN_E2


#use I2C(MASTER, slow, scl=PIN_C3, sda=PIN_C4, FORCE_HW)  //scl=PIN_C3, sda=PIN_C4,

int8 RTC_Val[13][7]; // 13 rows of 7 byte Data arrays for RTC reads and
// schedules. first data element is RTC_Val[0][0]. the array may be initialized
// as follows: RTC_Val[13][7] = {0,1,2,3,4,5,6,7,8,9,10,11...etc};

int8 Col=0;
int8 Row=0;  //
int8 KeyPress;     //contains last button pressed
int8 KeyPressA;    //shadow register
int8 EE_add;
int8 Flags;        //0=DST enabled, 1=DST_NOW, 2=SUN1, 7=new second
int8 ts;           //row number of the time / schedule data array
int8 X;
int8 XX;           //row counter value
int8 XY;           //byte counter
int8 addr;         //EEPROM address

//(3,A,2,1)(6,B,5,4)(9,C,8,7)(#,D,0,*)
int8 KeyArray[4][4]= {{0x03,0x0A,0x02,0x01}, {0x06,0x0B,0x05,0x04},
{0x09,0x0C,0x08,0x07}, {0x0F,0x0D,0x00,0x0E}};

The variable declarations are used for time data storage and keypad functions and don't seem to cause any problems (keypad etc. works). MAIN.h is included in MAIN.c. Compiler is listed in my earlier message, (I am using the demo (V.5.074d) compiler. )

Thanks,
OldPhart
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 4:06 pm     Reply with quote

Quote:
I am working on a timer project, using a PIC16F877A, and a LCD readout.
I am using an I2C bus RTC (DS3231). I am using the demo (V.5.074d) compiler.

16F877A is a 5v part. DS3231 is a 3.3v part.

You have two easy options to make this work:

1. Use hardware i2c on pins C3 and C4, and use the SMBUS parameter
in your #use i2c() statement. Also use 3.3K or 2.2K pullups, and connect
them to 3.3v.

2. Use software i2c on any two PortB pins. (The SMBUS parameter is
not used, and has no effect with software i2c). Also use 3.3K or 2.2K
pullups and connect them to 3.3v.
OldPhart



Joined: 04 Dec 2017
Posts: 8
Location: Canada

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 4:23 pm     Reply with quote

The DS3231 is a very flexible part; the following is from the DS3231 manual:

ELECTRICAL CHARACTERISTICS
(VCC = 2.3V to 5.5V, VCC = Active Supply

The part will work fine on 3.3V as well as 5V, both are permitted by the manual. I am using 4.7K pull-up resistors on the i2c buss. I have several of these great chips working happily in 5V projects done with Microchip XC8 as well as the PIC native ASSEMBLY. This is my first attempt using CCS though (hence the demo version, 22 free days left).

I will read up on the SMBUS parameter though.

Thanks for the reply,
OldPhart.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 4:39 pm     Reply with quote

You're right. I started typing my post after reading the front page which
says: 3.3V Operation.
SMBus has TTL levels which allow a 5v PIC to work with a 3.3v i2c slave.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 5:32 pm     Reply with quote

To fix the bad option error, do this:
Code:

#use I2C(MASTER, slow, I2C1) 

That will use hardware i2c.

Then run this program to verify the i2c address of the ds3231:
http://www.ccsinfo.com/forum/viewtopic.php?t=49713
temtronic



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

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 8:37 pm     Reply with quote

thought I'd 'chime in' here as I've been bench testing the DS3231 on a combo board with an EEPROM + battery for $2.50 Canadian( I can't buy the parts for that !!). It's connected to my goto PIC18F46K22 and happily running on 5 volts. Have to admit it's better than the DS1307 for keeping time.
Also ALWAYS run PCM P's I2C scanner program when using I2C devices. It will CONFIRM both your hardware and PIC are running right. After that, it'll be a 'coding' issue...perhaps that old 7 bit address vs current 8 bit philosophy.
OldPhart



Joined: 04 Dec 2017
Posts: 8
Location: Canada

View user's profile Send private message

PostPosted: Mon Dec 04, 2017 9:21 pm     Reply with quote

Thanks, PCM programmer,
Using the I2C1 did solve the error issue. However, I am still locking up on the second line; being the first write command. My read code is:
Code:

void I2C_RTC_Read(int8 a2,int8 n2) //a2<-starting data address, n2<-number of bytes
{
  int8 n;                // create temp register for loop counter
  i2c_start();           // Send start condition
  i2c_write(0xD0);       // 7 bit slave address (RTC, DS3231) with write bit0=0
  i2c_write(a2);         // Write target byte address   
  i2c_start();           // Send repeated start condition
  i2c_write(0xD1);       // 7 bit slave address (RTC, DS3231) with read bit0=1
  for (n=0; n<n2; n++)   // Loop n2 times
   {
    while (!i2c_poll())  // wait here for data to arrive
    RTC_Val[0][n] = i2c_read(); // store data in RTC_Val array
   }
  i2c_stop();
}

The code is called with two parameters; a2 is the address where I want the read to start, and n2 is the number of bytes to be read. In this case I want to read all the time registers, so starting at address 0x00, reading 7 bytes. The RTC slave address is 0x68, which with the data write bit becomes 0xD0 (works in all my projects). By inserting a line (not shown) that turns a LED on on my board, I can see where the code hangs, and it still hangs on the line i2c_write(0xD0);. I know the hardware is good, so I am looking at the ports or setup again.
A curiosity side note to the CCS folks, you provide a full-featured demo for new customers to try out, but you give them only a few (15%) of the demo and driver software. New and prospective customers really need them to get going. If frustrated by their absence (in my case the I2C examples) of this info may leave me frustrated enough to move on. Sorry for the impromptu rant, I really don't want to appear ungrateful.

Any further suggestions??

Thanks,
OldPhart
Ttelmah



Joined: 11 Mar 2010
Posts: 19486

View user's profile Send private message

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

Are you sure it is there it is hanging?.

This:
while (!i2c_poll())

Won't work.

You are a master device. You initiate _every_ transaction.
i2c_poll is only for a slave device.

You can't receive a byte till you clock it in using read.

Look at the examples.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

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

Quote:
for (n=0; n<n2; n++) // Loop n2 times
{
while (!i2c_poll()) // wait here for data to arrive
RTC_Val[0][n] = i2c_read(); // store data in RTC_Val array
}

In the code above, you are not doing a NACK on the last read.
The ds3231 data sheet says you must do this. See this diagram on pg 16:
Figure 4. Data Read—Slave Transmitter Mode
https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
See Ttelmah's sample code at the end of the following post for one way
to do this: http://www.ccsinfo.com/forum/viewtopic.php?t=55088&start=1


Quote:
#include <16F877A.h>
#FUSES NOBROWNOUT,LVP,NOWDT,HS,NOCPD,NOWRT,NOPROTECT,NOPUT

In the code above you have the Low Voltage Programming fuse selected.
If you're using a conventional programmer such as PicKit3, ICD-U64, etc.,
those don't use LVP. In that case, you should set it to NOLVP.
Leaving it in LVP mode can cause the PIC to lock-up.

Thirdly, if none of the above fixes the problem, then locking up on an
i2c_write() typically happens if you're missing the pull-up resistors.
It's either that or some other hardware defect.

I don't like that your test program is so complicated. I would strip it down
to the bare essentials. Just write one byte. Read one byte. Write the
code to do only that. Don't write code to fill an array. Don't use an array.
Just make a simple test program. Don't use Fixed i/o.
temtronic



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

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 5:51 am     Reply with quote

OK, make your life easier...
You can use the DS1307 RTC driver to communicate with the DS3231. There may be a couple of tweaks( base address) but I KNOW it works...it's in front of me showing time on a 4x20 LCD !

Jay
drh



Joined: 12 Jul 2004
Posts: 192
Location: Hemet, California USA

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 8:48 am     Reply with quote

You might want to change LVP to NOLVP in your FUSES.
_________________
David
OldPhart



Joined: 04 Dec 2017
Posts: 8
Location: Canada

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 9:16 am     Reply with quote

Thanks everyone for the responses!

Ttelmah:
In order to determine where my code hangs, I insert a line that turns on a LED on my project. If it lights, I know the code reached that point. If it doesn't, the code hangs prior to that point.
The snippet: while (!i2c_poll()) is valid on hardware I2C, and simply waits until the requested byte has arrived in the receive buffer. It should work unless I grossly misunderstand the CCS documentation. The PIC I2C hardware includes a receive buffer SSPBUF which is polled in that line of code. The line:
"i2c_write(0xD1); // 7 bit slave address (RTC, DS3231) with read bit0=1"
initiates the first data read, polling starts in the loop, and the next read is initiated when the previous byte has been copied. No matter how I look at it, it should work.

With respect to the examples, that was the subject of my little rant earlier. CCS in their infinite wisdom decided not to provide most of the examples in the demo version. If they had, perhaps this whole mess would have been solved a long time ago. I don't know how they think people will buy the software if they can't get it working.

PCM programmer:
I took a close look at the DS3231 data sheet again, and I am working with the diagram on Pg.17. I want to have control over which byte to read from the RTC, so if I want to to read for instance the temperature, I don't have to read 18 bytes and sort it out later. With adjusting (setting) the time it is much nicer if I can set the correct byte directly. That seems simpler to me.
Please keep in mind I am an assembler guy, used to explicitly directing EVERY step on the way. Higher languages such as "C" do a lot behind the scenes, which often leaves me with a feeling of not being in control since it is not always clear to me what a function does or doesn't do. With that in mind....
Ttelmah seems to be taking three lefts to make a right in the code snippet referred to. From the CCS documentation a read is simply initiated by: data = i2c_read(), where () defaults to ACK. My loop is primarily to advance the array so the read data is placed in the correct place.
You are correct in that this loop does not send a NACK on the last read. I have to look at that somehow. However, in the worst case it would leave the I2C bus in a undetermined state after the read cycle was completed; I should still have my data the first time it runs. I will remove the fixed IO code; it was created by the project wizard. It should have no bearing on the I2C stuff though; it is assigned to a different port.

Temtronic,
Indeed, looking at that DS1307 driver may help. As per my earlier rant though.... it is not provided with the demo version.

I thank everyone for the suggestions, it certainly made me look at the code again. However, my problem is still that the first write instruction does not return. The instruction i2c_write() is supposed to return a 1 (ACK) or 0 (NACK). It does not return at all, it hangs. The hardware is good, it works with XC8, and ASSEMBLER. That leaves port configuration or code. I found the reason for my earlier Bad Option 59. I had SDA and SCL defined twice. Once in the #define, and once in the #use_i2c. That is now fixed.

Thanks again,
OldPhart
Ttelmah



Joined: 11 Mar 2010
Posts: 19486

View user's profile Send private message

PostPosted: Tue Dec 05, 2017 9:54 am     Reply with quote

You are fundamentally missing the point.....

while (!i2c_poll()) // wait here for data to arrive

You cannot do this.

You can't wait for data to arrive. The master controls all data transactions. _You_ clock the data. It can't arrive until you send the clock.

I2C_poll is the instruction for a _slave_ device to wait until the _master_ sends it data. It won't work on a master device...

Code:

oid I2C_RTC_Read(int8 a2,int8 n2) //a2<-starting data address, n2<-number of bytes
{
  int8 n;                // create temp register for loop counter
  i2c_start();           // Send start condition
  i2c_write(0xD0);       // 7 bit slave address (RTC, DS3231) with write bit0=0
  i2c_write(a2);         // Write target byte address   
  n2--; //just update the count to save maths in the loop

  i2c_start();           // Send repeated start condition
  i2c_write(0xD1);       // 7 bit slave address (RTC, DS3231) with read bit0=1
  for (n=0; n<=n2; n++)   // Loop n2 times
   {
      if (n<n2)
         RTC_Val[0][n] = i2c_read(); // store data in RTC_Val array
      else
         RTC_Val[0][n] = i2c_read(0); //and NACK the last byte
   }
  i2c_stop();
}
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