| 
	
	|  |  |  
	
		| View previous topic :: View next topic |  
		| Author | Message |  
		| OldPhart 
 
 
 Joined: 04 Dec 2017
 Posts: 8
 Location: Canada
 
 
			    
 
 | 
			
				| I2C issue on PIC16F877A |  
				|  Posted: Mon Dec 04, 2017 3:24 pm |   |  
				| 
 |  
				| 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: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 3:40 pm |   |  
				| 
 |  
				| 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
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 3:50 pm |   |  
				| 
 |  
				| 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
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 4:06 pm |   |  
				| 
 |  
				|  	  | 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
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 4:23 pm |   |  
				| 
 |  
				| 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
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 4:39 pm |   |  
				| 
 |  
				| 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
 
 
 
			    
 
 |  |  
		|  |  
		| temtronic 
 
 
 Joined: 01 Jul 2010
 Posts: 9588
 Location: Greensville,Ontario
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 8:37 pm |   |  
				| 
 |  
				| 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
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 9:21 pm |   |  
				| 
 |  
				| 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: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 11:54 pm |   |  
				| 
 |  
				| 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
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Mon Dec 04, 2017 11:58 pm |   |  
				| 
 |  
				|  	  | 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: 9588
 Location: Greensville,Ontario
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Tue Dec 05, 2017 5:51 am |   |  
				| 
 |  
				| 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: 193
 Location: Hemet, California USA
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Tue Dec 05, 2017 8:48 am |   |  
				| 
 |  
				| You might want to change LVP to NOLVP in your FUSES. _________________
 David
 |  |  
		|  |  
		| OldPhart 
 
 
 Joined: 04 Dec 2017
 Posts: 8
 Location: Canada
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Tue Dec 05, 2017 9:16 am |   |  
				| 
 |  
				| 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: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Tue Dec 05, 2017 9:54 am |   |  
				| 
 |  
				| 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();
 }
 
 | 
 |  |  
		|  |  
		|  |  
  
	| 
 
 | 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
 
 |