PDA

View Full Version : PIC 16F877 I2C communications



sigmoideng
- 9th July 2004, 22:56
Hello everyone,

I am expermenting with a PIC 16F877 in trying to make a clock with a Dallas Semi DS1307 and a serial LCD. I am having some strange problems. I have the clock and data ports connected to the PIC at portA.0 and port A.1. I have a square wave output from the RTC that is working properly so the chip is oscillating. As a test, I am writing a value to the RTC Ram and then trying to read it back again and displaying it. The problem is that I cannot seem to write and read back values. I have used a simple conversion routine to convert the BCD values to ascii for display and have test this routine and it works fine. I was wondering if there was something about port A that causes it to not work well as an I2C bus. Both data and clock lines have pull up resistors and the battery input on the RTC is grounded. I am using PB Pro compiler. The code is really straight forward using PBP. Any ideas would be really appreciated.

Thanks

Tim

Melanie
- 10th July 2004, 08:34
PortA is just fine... as long as you've remembered to set the Port to DIGITAL as by default it comes up Analogue.

ADCON1=$07

Go see Datasheet ADCON1 Register Section 11.2

The only other gotcha on PortA is RA4, but since you're not using it, then it doesn't apply. Actually RA4 is one that I prefer to use for one of the I2C lines because it needs a pull-up, and you've generally got to provide a pull-up for I2C, so the two go together nicely.

If you have the 16F877A, you've got to do the same to the Comparators as well as Microchip have sneaked those under the hood whilst you weren't looking...

CMCON=$07

16F877A Datasheet Section 12-1

sigmoideng
- 13th July 2004, 19:02
Melanie thanks for replying...

I have done all of the above. Below is an annotated listing of the program. The purpose of the program is very simple, to write a value to the RTC Ram and then read it back to the PIC. The value is then changed from a BCD format using the lookdown to a decimal number. The ascii representation of the decimal number is then displayed on a serial LCD.

I have verified the operation of the serial LCD conversion part and know the RTC is oscillating because I can see the waveform on my oscope (the tick portion of the code below is also a visual indication see below). The square wave output of the RTC is being inputed into the PIC at port A.2. I have 4.7k resisitors on the clock and data ports of the RTC. The battery pin on the RTC is grounded and the source is 5 volts. I also have a 4.7k resistor on the square wave output pin.

I am writing the number 9 to the RTC below.



TRISA = %000100 'Set RA0-RA5 Tristate Pins for port A to output (0) or input (1)

DPIN VAR PORTA.0
CPin VAR PORTA.1
Tick VAR PORTA.2
x VAR BYTE
y VAR BYTE
m1 VAR BYTE 'minutes 1's place 0-9
m10 VAR BYTE 'minutes 10's place 0-5
B1 VAR BYTE
B2 VAR BYTE
sec VAR BYTE

RTC CON %11010000
ADCON0=%11000000
ADCON1=%00000111

init:
Pause 1000
y=0
m1 = 0
m10 = 0
B1=0
sec = %00001001

'Start

I2CWrite DPIN, CPIN, RTC, 0, 0 'Writes to seconds byte and initiate the oscillations of the RTC
Pause 40
'I2CWrite DPIN, CPIN, RTC, 1, 0 'Writes to minutes byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 2, 1 'Writes to hours byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 3, 1 'Writes to day of week byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 4, 1 'Writes to date byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 5, 1 'Writes to month byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 6, 1 'Writes to year byte.
'Pause 40
I2CWrite DPIN, CPIN, RTC, 7, %10010000 'Writes to control byte of RTC and sets the SQWE square wave output
'to one cycle per second
Pause 40


I2CWrite PORTA.0, PORTA.1, %11010000, $11, %00001001 'Test write to RTC Ram location $11, executed only once
Pause 10


loop:

' The tick is a blinking colon that appears on display. Visual verification the RTC is oscillating
IF tick = 0 Then
SerOut PORTD.2,4,[254,131] 'locate cursor 1st line position 3
Pause 40
SerOut PORTD.2,4,[" "] 'print null value on LCD
Else
SerOut PORTD.2,4,[254,131] 'locate cursor 1st line position 3
Pause 40
SerOut PORTD.2,4,[":"] 'print a colon value on LCD
sec = sec + 1
EndIF




'Debug section of program, should be deleted after program is finalized.


'Read the test value from the same ram location as written to the RTC. What should happed is the value read back from the
'RTC is the same as the one written.
I2CRead PORTA.0, PORTA.1, %11010000, $11, [m1]
Pause 10

'This line converts the BCD value to a decimal. The ascii representation of this decimal value will be placed on the
'serial LCD screen.
LookDown m1, [%00000000,%00000001,%00000010,%00000011,%00000100, %00000101,%00000110,%00000111,%00001000,%00001001],B1

SerOut PORTD.2,4,[254,132] 'locate cursor on serial LCD
Pause 40
SerOut PORTD.2,4,[#B1] 'Output
Pause 40

'This section uses a variable called sec that is set at the beginning of the program. This is to verify the proper working of
'the BCD to decimal conversion.
LookDown sec, [%00000000,%00000001,%00000010,%00000011,%00000100, %00000101,%00000110,%00000111,%00001000,%00001001],B2

SerOut PORTD.2,4,[254,192] 'locate cursor on serial LCD
Pause 40
SerOut PORTD.2,4,[#B2] 'Output
Pause 40





GoTo loop

End

On my display, I get a flasing colon (this is good) and a number 1 instead of the number 9 that I have written (variable B1)
For variable B2 I get the number 9 as I should.

Any input would be greatly appreciated. From what I can tell, this really should work. Any ideas why I am unable to read back from the RTC the value that I write?

Thanks

Tim

Melanie
- 13th July 2004, 23:49
Tim

Let's be brutal...

1. You got a Pause 40 fetish? A Pause 10 following a I2CWrite is all that is needed.

2. You're obviously writing correctly to the RTC because you've initialised it and it's ticking...

3. If you're using DPIN, CPIN and RTC (good!), then why change to using PORTA.0, PORTA.1 and %11010000 later? Stick with what you've started - it saves on the possible introduction of typo's and errors.

4. You don't need Pause 10 (or indeed any Pauses) following an I2CRead.

5. Don't use a Constant in the I2C Address... do this instead...

RTC var BYTE
RTC=%11010000

something is nudging the back of my mind that not using a variable causes problems sometimes... but I can't recall the exact details of 'why?' right now, other than the fact that bit 0 is used as a flag, and it's kinda difficult nailing flags into a constant.

6. Why don't you try displaying on your Serial LCD variable (as #m1) immediately following the I2CRead... what is the result?

7. Hmmmm.... this BCD conversion of yours... have a look at what it's doing...

B1=m1
if m1>9 then B1=0

uses less code and basically does what your routine does.


Make the changes, come back and tell us what happened.

sigmoideng
- 22nd July 2004, 04:42
Melanie

Ok, Ok you caught me. My pause 40 fetish is public and now I can come out of the closet :)
I followed your suggestions and made the changes. Below is the revised program...

TRISA = %000100 'Set RA0-RA5 Tristate Pins for port A to output (0) or input (1)

DPIN VAR PORTA.0
CPin VAR PORTA.1
Tick VAR PORTA.2
x VAR BYTE
y VAR BYTE
m1 VAR BYTE 'minutes 1's place 0-9
m10 VAR BYTE 'minutes 10's place 0-5
B1 VAR BYTE
sec VAR BYTE

RTC VAR BYTE
ADCON0=%11000000
ADCON1=%00000111



'*************************************Initiation of variables**********************************
Pause 750 'Allow LCD to power up
m1 = 0
m10 = 0
B1=0
RTC = %11010000


' I saw in one of the responses to a similar problem, someone reset, set and reset again
' the CE bit. The lines below are an attempt at doing this...

I2CWrite DPIN, CPIN, RTC, 0, 0 'Writes to seconds byte.
Pause 10
'I2CWrite DPIN, CPIN, RTC, 1, 0 'Writes to minutes byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 2, 1 'Writes to hours byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 3, 1 'Writes to day of week byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 4, 1 'Writes to date byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 5, 1 'Writes to month byte.
'Pause 40
'I2CWrite DPIN, CPIN, RTC, 6, 1 'Writes to year byte.
'Pause 40
I2CWrite DPIN, CPIN, RTC, 7, %10010000 'Writes to control byte.
Pause 10

I2CWrite DPIN, CPIN, RTC, $11, %0000110 'Test write
Pause 10

'*************************************Program***** ********************************************
loop:

IF tick = 0 Then
SerOut PORTD.2,4,[254,131] 'locate cursor 1st line position 3
Pause 40
SerOut PORTD.2,4,[" "] 'print null value on LCD
Else
SerOut PORTD.2,4,[254,131] 'locate cursor 1st line position 3
Pause 40
SerOut PORTD.2,4,[":"] 'print a colon value on LCD
sec = sec + 1
EndIF


'Debug section of program, should be deleted after program is finalized.

I2CRead PORTA.0, PORTA.1, %11010000, $11, [m1]

SerOut PORTD.2,4,[254,196] 'locate cursor
SerOut PORTD.2,4,[#m1] 'Output the minutes 10's position


B1 = m1 'BCD to ASCII conversion
IF m1>9 Then
B1=0
EndIF

SerOut PORTD.2,4,[254,132] 'locate cursor
SerOut PORTD.2,4,[#B1] 'Output

GoTo loop

End

When I run this program, I am getting the same results as before. I get a flashing colon on the screen (my digital probe confirms the clocking signal from the RTC) and I get the number 1 at position 196 and at position 132 on the LCD screen. As you can see from the program, I am attempting to write a number 6 to the RTC and then read it back and display it on the LCD. As you said, the RTC is being initialized. I like your conversion routine better than the one I came up with so I added this. I simply cannot seem to write a value then read it back. This is really strange. Any other suggestions?

Thanks for your help

Tim

Melanie
- 22nd July 2004, 10:37
I'll thoroughly check what's going on later today when I've a free moment.

Melanie
- 23rd July 2004, 16:36
Let me elucidate (big word for a Friday afternoon!). When I posted point 5 saying not to use a constant in the I2C ADDRESS, I also intended to mean NOT to use a constant in the RTC Register ADDRESS as well. Sometimes it works, sometimes it doesn't. It'll be the subject of an FAQ shortly. However, the PBP manual in the I2CWrite section although mentioning this, isn't too clear either. This is the main problem you've been having.

Don't jump up and down for joy just yet, there's other problems in your code... you're still not using CPin, DPin and RTC consistantly throughout your code (by not being consistant it'll bite you one day). Also... where's the square brackets in your I2Cwrite statements?

I appended some sample code for you to cut, paste, compile, marvel and wonder at. It's basically your own code, rehashed and born again. Do I need any thanks? Of course not... just that big Diamond and Sapphire cluster from Macey's window.



'
' Hardware Pin Defines
' --------------------
CPin var PortA.1 ' I2CLock
DPin var PortA.0 ' I2CData
Tick VAR PORTA.2

'
' RAM Assignments and Variables
' -----------------------------
RTC var BYTE ' RTC Hardware Address
RTCData var BYTE ' RTC Data
RTCRegister var BYTE ' RTC Register Address
Temp var BYTE ' Temporary variable (used in BCD conversions)

'
' Start Program
' =============

'
' Initialise Processor
' --------------------
TRISA=%00000001
TRISB=%11001111
TRISC=%00000000
ADCON0=%11000000
ADCON1=%00000111
RTC = %11010000 ' RTC Hardware Address ($D0)
Pause 750 ' Pause for external LCD to settle


' I saw in one of the responses to a similar problem, someone reset, set and reset again
' the CE bit. The lines below are an attempt at doing this... Cobblers! - see Note below - Melanie

' Cobblers! - A quaint olde English expression
' meaning a group of people that make or mend shoes

'
' Initialise RTC to starting Date & Time 23rd July 2004, 15:10:00
' ---------------------------------------------------------------
RTCData=4:RTCRegister=6:Gosub WriteRTC
' Write Year
RTCData=7:RTCRegister=5:Gosub WriteRTC
' Write Month
RTCData=23:RTCRegister=4:Gosub WriteRTC
' Write Days
RTCData=15:RTCRegister=2:Gosub WriteRTC
' write Hours
RTCData=10:RTCRegister=1:Gosub WriteRTC
' write Minutes
RTCData=0:RTCRegister=0:Gosub WriteRTC
' write Seconds & activate RTC
RTCData=%10010000:RTCRegister=7:Gosub WriteRTC
' write Control Register


RTCData=6:RTCRegister=$11:Gosub WriteRTC
' This is your Test write to RAM


'
' Main Program Loop
' -----------------
Loop:
IF tick = 0 Then
SerOut PORTD.2,4, $FE,$83," " 'locate cursor 1st line position 3
Else
SerOut PORTD.2,4, $FE,$83,":" 'locate cursor 1st line position 3
EndIF


'
' Your Debug Display
' ------------------

RTCRegister=$11:Gosub ReadRTC
SerOut PORTD.2,4, $FE,$C4,#RTCData 'locate cursor & Display Data

'
' New Section - Display rolling time at Cursor
' --------------------------------------------
RTCRegister=2:Gosub ReadRTC ' Get and display Hours
SerOut PORTD.2,4, $FE,$84,DEC2 RTCData
RTCRegister=1:Gosub ReadRTC ' Get and display Minutes
SerOut PORTD.2,4, ":",DEC2 RTCData
RTCRegister=0:Gosub ReadRTC ' Get and display Seconds
SerOut PORTD.2,4, ":",DEC2 RTCData

Goto Loop ' Do it forever

'
' Subroutine Reads Data from RTC converting from BCD where required
' -----------------------------------------------------------------
' RTCData is always in NUMERIC format on exit from this subroutine
' it is NEVER in BCD format.
ReadRTC:
I2CRead DPin,CPin,RTC,RTCRegister,[RTCData]
If RTCRegister < 7 then ' Convert from BCD to Numeric
Temp=RTCData & $7F ' Mask out Bit 7 in all cases
Temp=Temp >> 4
RTCData=(RTCData & $0F)+(Temp*10)
endif
Return

'
' Subroutine Writes Data to RTC converting to BCD where required
' --------------------------------------------------------------
' Caution: RTCData may be converted to BCD on exit from this subroutine
' if the Register address is less than 7.
WriteRTC:
If RTCRegister < 7 then ' Convert from Numeric to BCD
Temp=RTCData DIG 1
Temp=Temp << 4
RTCData=(RTCData DIG 0)+Temp
endif
I2Cwrite DPin,CPin,RTC,RTCRegister,[RTCData]
Pause 10
Return

End



PS. Count the number of Pause statements...

*smiles*
Melanie

buddhi
- 13th July 2007, 11:28
Can u please send me sample I2C code. I need to comminunicate 24c32 or 24c64 EEPROM with PIC 16F877A. I have tried many ways but still I couldn't.