Eh.. does this look right? (eeprom question)

General discussion on mikroBasic.
Author
Message
rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

Eh.. does this look right? (eeprom question)

#1 Post by rackley » 16 Aug 2005 03:01

Can you guys/gals see anything wrong with this? It's pretty basic and SHOULD work, but I'm getting bad data from it:

Code: Select all



const EECtrlRead     = %10100001       ' Control bytes to address the chip
const EECtrlWrite    = %10100000

'------------------------------------------------------
' Reads a byte from eeprom at Addr location.
' Returns a byte.
' Increments Addr by 64 automatically.
'------------------------------------------------------

sub function I2C_ReadByte(Dim ByRef Addr as word) as Byte
    I2C_Start
    I2C_Wr(EECtrlWrite) ' Issue write just to set the working address in EEPROM
    I2C_Wr(hi(Addr))
    I2C_Wr(lo(Addr))
    I2C_Repeated_Start  ' Terminate write and issue start of new operation
    I2C_Wr(EECtrlRead)  ' Issue read command
    result = I2C_rd(0)  ' Do not issue ACK since we're done reading and will send stop
    I2C_Stop            ' Send stop
    Addr = Addr + 64
end sub

endimion
Posts: 26
Joined: 27 Apr 2005 17:49
Location: Urziceni, Ialomita, Romania

#2 Post by endimion » 16 Aug 2005 06:29

I had the same problem - and i resolved by using procedures with no param's. Realy...... I dont know why. Try to make all the variables external.

P.S. Sorry for my english.

User avatar
zristic
mikroElektronika team
Posts: 6608
Joined: 03 Aug 2004 12:59
Contact:

Re: Eh.. does this look right? (eeprom question)

#3 Post by zristic » 16 Aug 2005 07:55

Try this:

Code: Select all

sub function I2C_ReadByte(Dim Addr as word) as Byte ' "ByRef" excluded 
if it works, then we have a bug with Lo, Hi, Higher and Highest.

endimion
Posts: 26
Joined: 27 Apr 2005 17:49
Location: Urziceni, Ialomita, Romania

#4 Post by endimion » 16 Aug 2005 19:28

i think i've tried and this (i dont know for sure) - however - this is the code that its works:

Code: Select all

'------------------------------------------------------------------------------
'citeste din eeprom extern valoare
'read from eeprom
'------------------------------------------------------------------------------
sub procedure ee_read
I2C_Start 'Issue I2C start signal
I2C_Wr(ee_address) 'Send byte via I2C
I2C_Wr(hi(ee_adr)) 'Send byte(address for EEPROM)
I2C_Wr(lo(ee_adr)) 'Send byte(address for EEPROM)
I2C_Repeated_Start 'Issue I2C signal repeated start
I2C_Wr(ee_address+1) 'Send byte (request data from EEPROM)
data=I2C_Rd(0) 'Read the data no ack
I2C_Stop 'Issue I2C stop signal
end sub 'sfarsit procedura
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
'scrie in eeprom extern valoare
'------------------------------------------------------------------------------
sub procedure ee_write
i2c_start 'daca linia e libera se transmite start
I2C_Wr(ee_address) 'Send byte via I2C(command to 24c64)
I2C_Wr(hi(ee_adr)) 'se transmite adresa hi pentru eeprom
I2C_Wr(lo(ee_adr)) 'se transmite adresa lo pentru eeprom
I2C_Wr(data) 'Send data(data that will be written)
I2C_Stop 'Issue I2C stop signal
delay_ms(ee_write_delay) 'pauza pentru scriere date
end sub 'sfarsit procedura
'------------------------------------------------------------------------------

const ee_address as byte=$a0 'adresa eeprom extern scriere
const ee_write_delay as byte=10 'pauza scriere eeprom extern



rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#5 Post by rackley » 02 Dec 2005 19:03

Well I had this problem prior to my deployment so I totally forgot I even posted it. Taking out the ByRef doesn't help. I've been troubleshooting it even more and I found out the following:

1) It works correctly the first time
2) The second time and up it always returns 160 (decimal)
3) If I reinitialize the I2C bus each time then the read works fine.

Code: Select all

sub function I2C_ReadByte(Dim Addr as word) as Byte
    I2C_Init(100000)
    I2C_Start
    I2C_Wr(EECtrlWrite) ' Issue write just to set the working address in EEPROM
    I2C_Wr(hi(Addr))
    I2C_Wr(lo(Addr))
    I2C_Repeated_Start  ' Terminate write and issue start of new operation
    I2C_Wr(EECtrlRead)  ' Issue read command
    result = I2C_rd(0)  ' Do not issue ACK since we're done reading and will send stop
    I2C_Stop            ' Send stop
end sub
This is for a 24LC256. I really don't want to make all the variables external because the function being called is in a module. It's also bad practice to do so. I'm also assuming that reinitializing the I2C bus shouldn't be necessary and is probably quite inefficient to do every time you read a byte. Any ideas?

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#6 Post by rackley » 04 Dec 2005 19:29

Bump...I hate to cry "bug!" but it seems that something in here may be going awry. I've triple checked the datasheet for the 24LC256 module and my code for communication with the chip across the bus is correct, at least on the IDE level :-)

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#7 Post by xor » 04 Dec 2005 23:54

  • I read the datasheet and looks like you got the code correct.

    I've used the Soft_I2C library successfully.....you can see if that works. If it does then you may be making a good case for a problem in the I2C library.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#8 Post by rackley » 06 Dec 2005 05:32

I found ONE of the problems. One of them is indeed a problem with passing the eeprom address word via ByRef. If I pass it ByRef the subroutine sometimes returns the eeprom address instead of the variable "result."

Bear with me because this is pretty crazy. Bear in mind the actual return value SHOULD be "68" decimal.

This code does not work, as I need to reinitialize the I2C bus every time I read from it or it returns "139" decimal once, then returns "160" decimal every subsequent read attempt:

Code: Select all

sub function I2C_ReadByte(Dim Addr as word) as Byte
    I2C_Start
    I2C_Wr(EECtrlWrite) ' Issue write just to set the working address in EEPROM
    I2C_Wr(hi(Addr))
    I2C_Wr(lo(Addr))
    I2C_Repeated_Start  ' Terminate write and issue start of new operation
    I2C_Wr(EECtrlRead)  ' Issue read command
    result = I2C_rd(0)  ' Do not issue ACK since we're done reading and will send stop
    I2C_Stop            ' Send stop
end sub
This returns "Addr" as the return value for the sub instead of the byte "result". Addr is "151" decimal. If I throw in the "Addr = Addr + 64" at the end of the sub it will return "139" or "145" or somewhere in that range every time regardless of what Addr is (I can vary it via an ADC input but it's normally constant.) But even if I vary Addr the result stays constant, when it should be refreshing (and therefore changing) every two seconds.

Code: Select all

sub function I2C_ReadByte(Dim ByRef Addr as word) as Byte
    I2C_init(100000)
    Delay_ms(10)
    I2C_Start
    I2C_Wr(EECtrlWrite) ' Issue write just to set the working address in EEPROM
    I2C_Wr(hi(Addr))
    I2C_Wr(lo(Addr))
    I2C_Repeated_Start  ' Terminate write and issue start of new operation
    I2C_Wr(EECtrlRead)  ' Issue read command
    result = I2C_rd(0)  ' Do not issue ACK since we're done reading and will send stop
    I2C_Stop            ' Send stop
    'Addr = Addr + 64
end sub
This is the ONLY way it will function correctly:

Code: Select all

sub function I2C_ReadByte(Dim Addr as word) as Byte
    I2C_init(100000)
    Delay_ms(10)
    I2C_Start
    I2C_Wr(EECtrlWrite) ' Issue write just to set the working address in EEPROM
    I2C_Wr(hi(Addr))
    I2C_Wr(lo(Addr))
    I2C_Repeated_Start  ' Terminate write and issue start of new operation
    I2C_Wr(EECtrlRead)  ' Issue read command
    result = I2C_rd(0)  ' Do not issue ACK since we're done reading and will send stop
    I2C_Stop            ' Send stop
end sub
This version also refreshes and varies the return value, as it should, every two seconds. The return value is not "fixed" for the whole runtime of the PIC until it is reset.

I stumbled on the only working version of my sub by pure trial and error. None of the incorrect results I'm getting back in the other versions make any sense whatsoever, which is why I was so stumped and frustrated. Z, I think you might want to try these out and see if you can reproduce these problems, because technically ANY of these versions of the sub should work 100%, but only the last one really works.

I can send you my whole program if you'd like (which isn't very much since I've been stuck on this) but varying ONLY the read byte sub is what seems to break/fix it.

Let me know if that post made any sense at all :-)

User avatar
zristic
mikroElektronika team
Posts: 6608
Joined: 03 Aug 2004 12:59
Contact:

#9 Post by zristic » 06 Dec 2005 09:28

I am trying to figure out where the problem is, but I do not succeed. It would be great if you send me the source code with a description of the problem. I will probably have more to say when I see the complete code.

dangerous
Posts: 748
Joined: 08 Mar 2005 16:06
Location: Nottinghamshire, UK

#10 Post by dangerous » 08 Dec 2005 09:35

Hello guys, this sounds familiar.

Does the program hang with SDA or SCL low?

If so add
"while I2C_Is_Idle = 0
wend"

after the read statement. It seems that issuing a Stop without this command can make the system hang. It is not usually seen in the EEprom examples as the timing is such that the system has time to recover. It never happens with Soft_I2C routines.

I discovered the problem when trying to read data from a fast source, a Philips PLL chip. The system would hang with SCL low, and if the WDT was on, the system would only release SCL after a WDT T/O. It would then do all of its instructions up to the I2C_Read command and hang again until the next time-out. Adding the above statement cleared the problem.

I think that this is an issue with the way the MSSP port handles I2C commands, rather than MB.

With assembler code for the 80C751 which had a rudimentary I2C port, the program polled a Tx or Rx success bit and if it failed for more than 1mS the current action terminated. This released the bus, but then it was difficult to locate the problem.

Hope this helps

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#11 Post by xor » 08 Dec 2005 16:16

rackley wrote:This is the ONLY way it will function correctly:

Code: Select all

sub function I2C_ReadByte(Dim Addr as word) as Byte
    I2C_init(100000)
    Delay_ms(10)
    I2C_Start
    I2C_Wr(EECtrlWrite) ' Issue write just to set the working address in EEPROM
    I2C_Wr(hi(Addr))
    I2C_Wr(lo(Addr))
    I2C_Repeated_Start  ' Terminate write and issue start of new operation
    I2C_Wr(EECtrlRead)  ' Issue read command
    result = I2C_rd(0)  ' Do not issue ACK since we're done reading and will send stop
    I2C_Stop            ' Send stop
end sub
  • I am wondering how you had set up I2C_INIT() outside of your sub-function in your program. Are you crossing over and inadvertantly changing the state of SDA and SCK if using the same port for other functions? To re-init would indicate that you have possibly changed the last state of SDA and SCK in your program somewhere.

    I haven't seen the source code for this function but mB usually sets up global variables for the Port, SCK, and SDA lines, so that they hold their values for the duration of the program until you intentionally change them.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#12 Post by rackley » 08 Dec 2005 16:55

Thanks for the ideas xor. The hardware I2C port/pins are not configurable, so I imagine they're hard coded depending on which PIC device you select for the project. As far as inadvertently changing the state of the pins, I checked my code and I'm not defining, using or mentioning the pins or even the port anywhere else.

Dangerous, I'll try your suggestion tonight to see if it helps out.

Thanks,
Ray

dangerous
Posts: 748
Joined: 08 Mar 2005 16:06
Location: Nottinghamshire, UK

#13 Post by dangerous » 08 Dec 2005 17:23

Just a thought,

If you are playing with (presumbly) portc, do not globally change the state of TRISC. AND or OR it with XXX11XXX as a mask to change bits other than SDA & SCL. Similar reasoning applies to PortC. Use bit set, bit clear or mask to leave SCL & SDA unchanged.

This also applies to Soft_I2C routines.

I think that once you have initialised the I2C engine upsetting the state of tris or port bits for SDA & SCL will kill the program.

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#14 Post by rackley » 08 Dec 2005 17:42

I'm following your thought pattern. Although my production PCB uses the other PORTC pins, my program/software doesn't have anything implemented for PORTC at all yet. I'm not even initializing TRISC, aside from whatever internal initializtion the I2C_Init() routine does for the SDA and SCL.

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#15 Post by xor » 08 Dec 2005 18:00

  • The rest of your code isn't showing but I can let you in on a problem I had unrelated to I2C but may be pertinent. When I was developing my xLCD library I found problems with passing variables too many times or trying to call a function or procedure from another function or procedure. I don't remember exactly what I did then except to work around it, which I did. Also, in many mBasic library routines some variables are passed ByRef and this can cause problems when sending things around by the unwitting programmer, which you are now aware.

    I wonder if you put your routine 2 or 3 times in linear fashion in the main program with Stops and without re-INIT whether it works?
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Post Reply

Return to “mikroBasic General”