|
|
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: 19542
|
|
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: 9244 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: 19542
|
|
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: 9244 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: 192 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: 19542
|
|
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
|