PDA

View Full Version : EEprom and MCP9801 I2C communication problems



aajgss
- 16th April 2011, 02:13
Hi

I am trying to communicate with some I2C chips.
I am using a Low Pin Count USB development board that has an 18F14K50 Pic.
Picbasic Pro as the compiler.
Microchip I2C Serial Eval board. This is connected via the PicKit serial interface. VDD is 3.3V
I put a pullup resistor to PortB.4 and PortB.6. The value was 4K7. This gave me a voltage level of 0.2V low and 0.6V high
I changed the SDA resistor to 1K5 and this then gave a high of 2.4V
By using 'Define SCLOUT 1', I am now at 2.8V on the SCL line

Attached is the Scope trace when it is trying to read back the data from the EEprom.
The second trace is when its doing the write command.
The code is pretty much out of the PBP samples for reading and writing to the EEprom.
On the I2C Eval board is also an MCP9801 temperature sensing chip. Address 0X92 and I had the same results.
At this stage I dont know if the problem is hardware, software or both. One thing I can assure you - in the end I will have been the problem!!

The problem I am having is that when I write to the eeprom, the write value to the address is 100 to 115, but when the read value is returned, I get the value of 16 being read back.
Similar thing with the MCP9801. I was trying to read back the high and low byte of the temperature registers and getting zero returned.
Any help would be appreciated. I have been on this for the last 3 days and nearly ready to give up.
if any more info is required please let me know.


Code for writing and reading eeprom

' PICBASIC PRO program to read and write to I2C SEEPROMs
'
' Write to the first 16 locations of an external serial EEPROM
' Read first 16 locations back and send to LCD repeatedly
' Note: for SEEPROMs with byte-sized address
' Define LCD registers and bits
DEFINE OSC 12
Define I2C_slow 1
Define I2C_SCLOUT 1


SDA Var PORTB.4 ' Data pin
SCL Var PORTB.6 ' Clock pin
B0 Var Byte ' Address
B1 Var Byte ' Data 1
B2 Var Byte ' Data 2

ANSEL = %00000000 ' Set all pins digital
ANSELH = %00000000

For B0 = 0 To 15 ' Loop 16 times
B1 = B0 + 100 ' B1 is data for SEEPROM
I2CWRITE SDA, SCL, $A0, B0, [B1] ' Write each location
Serout PORTB.7,0,[#B0, ": ", #B1, " ", 10,13]
Pause 100 ' Delay 10ms after each write
Next B0
mainloop:

For B0 = 0 To 15 Step 2 ' Loop 8 times
I2CREAD SDA, SCL, $A0, B0, [B1, B2] ' Read 2 locations in a row
Serout PORTB.7,0,[#B0, ": ", #B1, " ", #B2, " ",10,13]
Pause 1000
Next B0
Goto mainloop
End

mackrackit
- 16th April 2011, 14:17
It might help to know what dev board you are using.
Verify that the OSC is 12 Mhz, and post your configs.

aajgss
- 16th April 2011, 15:09
I am using the low pin count USB development board.

The code is written as shown. ie no config.

How do I verify the oscillator - scope?

This is my first attempt at using a PIC and is frustrating to say the least, so any help would be great.

aajgss

Charles Linquis
- 16th April 2011, 17:18
Here is some code that should work with your MCP9801. It is a stripped-down version of some code that does work.



ASM
ifndef __18F2321
error "18F2321 not selected"
endif
ENDASM

ASM
movlw 0x52 ; %0101 0010 = 2Mhz, internal osc block
movwf OSCCON
movlw 0x80 ; %1000 0000 = PLL disabled
movwf OSCTUNE
ENDASM


DEFINE OSC 4 ; We are running at 2, but there is no DEFINE for that
Define USE_LFSR 1
DEFINE I2C_HOLD 1
DEFINE I2C_SLOW 1



DataPin var portc.1
ClockPin var portc.0
LED VAR PORTB.5

TRISA = $FF
TRISB = $FF
TRISC = $FF


MCP9800_ADDR VAR BYTE
MCP9800_CONFIG VAR BYTE
ConfigByte VAR BYTe
TemperatureRaw VAR WORD
TempOutLSB VAR BYTE
TXBuffer VAR BYTE[5]
TempVar VAR BYTE


FinalTemp VAR BYTE
LEDCNTR VAR WORD
SensorErrorFlag VAR BYTE

TEMPERATURES var word
SerialNumber VAR WORD
FinalTempW VAR WORD
TRISB.5 = 0 ; LED pin

RES CON 0 ; low resolution

WDTCON = 1 ; Turn on the WDT

Gosub InitSensor
Gosub ReadSensor ; prime the pump
Pause 200 ; Really 400 msec
Main:

Gosub ReadSensor ; Do it again
Pause 500 ;Really 1 sec
; TEMPERATURE will be in TXBuffer[0]
Goto Main

InitSensor:

MCP9800_ADDR = %10010000


MCP9800_TEMPERATURE CON 0 ; Temperature register
MCP9800_CONFIGURATION CON 1 ; Configuration register
MCP9800_HYSTERESIS CON 2
MCP9800_LIMITSET CON 3

ONESHOT_OFF CON %00000000 ;bits for register configuration
ONESHOT_ON CON %10000000
RESOLUTION_9bits CON %00000000
RESOLUTION_10bits CON %00100000
RESOLUTION_11bits CON %01000000
RESOLUTION_12bits CON %01100000
INT_MODE CON %00000010
SHUTDOWN_OFF CON %00000000
SHUTDOWN_ON CON %00000001


Start:
IF !RES THEN
MCP9800_CONFIG = oneshot_off | Resolution_9bits |shutdown_off
ELSE
MCP9800_CONFIG = oneshot_off | Resolution_12bits |shutdown_off
ENDIF

; Tempout = (TEMPERATURE.LowByte >>5 ) * 125 ; resolution 11 bits
; TempOut = (TEMPERATURE.LowByte >>6 ) * 25 ; resolution 10 bits


INIT_MCP9800:

IF !CLOCKPIN or !DATAPIN then goto SensorError
i2cwrite DATAPIN,CLOCKPIN,MCP9800_ADDR,[mcp9800_configuration,mcp9800_config],SensorError
i2cwrite DATAPIN,CLOCKPIN,MCP9800_ADDR,[mcp9800_temperature],SensorError
PIE1.3 = 1

Pause 10 ; 20
RETURN


;--------------------------------------------------------------------------------

ReadSensor:

SensorErrorFlag.1 = 1 ; set to error condition before we start

IF !CLOCKPIN or !DATAPIN then goto SensorError
i2cread DATAPIN,CLOCKPIN,MCP9800_ADDR,[TemperatureS.HighByte,TEMPERATURES.LowByte],SensorError
IF RES = 0 THEN
IF TempOutLSB.7 THEN TemperatureS.Highbyte = (TemperatureS.HighByte + 1) ; round up
FinalTemp = TemperatureS.HighByte ;
IF FinalTemp = 0 THEN FinalTemp.7 = 1 ; Zero degrees is negative
TxBuffer[0]= FinalTemp
TxBuffer[1] = 0 - FinalTemp
SensorErrorFlag.1 = 0
ELSE
FinalTempW.HighByte = TemperatureS.HighByte ;
FinalTempW.LowByte = Temperatures.LowByte
IF FinalTempW = 0 THEN FinalTempW.15 = 1 ; Zero degrees is negative
TxBuffer[0]= FinalTempW.HighByte
TempVar = FinalTempW.Lowbyte >> 4
TXBuffer[1]= TempVar *66/10
TxBuffer[2] = 0 - (TxBuffer[0] - TXBuffer[1])
SensorErrorFlag.1 = 0
ENDIF
Goto ENDRoutine

SensorError:
LED = 0
PAUSE 100 ; 200
LED = 1
PAUSE 100
LED = 0
PAUSE 100
LED = 1
SensorErrorFlag.0 = 1 ; Restart if error, but flash LED
TXBuffer[0] = $F0 + SensorErrorFlag
ENDRoutine:

Return
;-------------------------------------------------------------------------------------------------------


ASM
ORG 0x8FE ; More than Half full
nop
ENDASM

aajgss
- 17th April 2011, 05:36
Thanks Charles,

The board I am using has a Pic18F14K50. I will need to change the processor type, but is there anything else you think may trip me up?

aajgss

Charles Linquis
- 17th April 2011, 16:05
I believe it should work as-is, as long as you change the processor type.

aajgss
- 21st April 2011, 04:57
THanks Charles.

I was still having trouble, so went back to trying to write to serial EEprom. I was having no luck until I set

SSPCON =%00101011

Everything then started to happen. I thought that would be done in the I2CREAD and I2CWRITE.

Then by merging what you posted all looks good.

Thanks again for your help.

Now onto the next bit - trying to write values from the EEprom to the I2C DAC

This PIC stuff is great. I'm loving it!!

aajgss
- 3rd September 2011, 14:35
Well,

This project has developed a bit from the early start.

The initial bit was a learning curve on communications. The project itself was to create a 3 phase sinewave to drive a reactive power compensation board.

I changed from I2C to SPI and use 3 DACs to output the 3 phases shifted by 120 degrees.

Photos to follow. I used a free PCB software to produce the board. If anyone is looking for a free package, PCB Artist is really good.

Because I needed to have it switchable for 50 and 60 hz and to get the timing correct, I used a PAUSEUS command to get my delays. Is there any issues with that command? It seemed to work ok up to a couple of hundred uSecs, but after that the PIC would not run. I ended up putting in a for next loop and adjusting the number of times the loop executed to get the correct timing.

Sorry cant post the code at the moment as its on my work computer.


Thanks
aajgss

Charles Linquis
- 3rd September 2011, 16:35
There is nothing wroing with the PAUSEUS command, but if you using a variable after the PAUSEUS, it should be a WORD variable.

Without looking at your code, there is no way of knowing what effect the longer delays may have.

Anything that requires close timing should be done with a timer. Even if you aren't using interrupts, you can use a timer by setting up the prescaler and clock source, and turning the timer ON. When you want to start your timing interval, you clear the timer, and later on in the program you can sit in a loop until the timer is > a certain value. The upside is that you can do other tasks after you start the timer, and as long as the tasks don't take longer than your desired time interval, timing will be maintained. And this technique works especially well if you DO use interrupts, because the interrupts can "steal" time from the main loop, and make the PAUSEs take longer than their set value. Using a timer keeps timing perfect.

The downside is that at 40MHz, TMR1 times out in ~52mSec even with a /8 prescaler.

Charles Linquis
- 3rd September 2011, 21:45
I guess I should have also added that TMR0 can have a lot longer time period because it has a /256 prescaler. I just always use TMR0 for my main interrupt timer and TMR1 for the time delays in the loop. You could reverse the operation and use TMR1 for ints and TMR0 for the loop timer. That will get you over 1 second.