View previous topic :: View next topic |
Author |
Message |
s_mack
Joined: 04 Jun 2009 Posts: 107
|
Does read_eeprom need a delay? |
Posted: Thu Oct 15, 2009 2:33 pm |
|
|
I'm trying to introduce code that will allow users of my device to modify certain variable values by sending messages via CANBUS to the device, which the device stores in EEPROM to be read when it powers on. Everything appears to work, except that I get very erratic behavior since introducing the "get" function below. It is behaving as though at least some of the variables are getting zeros instead of their expected values. When I comment this function out (it runs in main() before the while()) everything operates just fine on the default values, which are declared at the beginning of the script.
Either I have some flawed logic and the defaults are getting wiped by this function... or read_eeprom needs a delay? or ??? I've looked at it a hundred times, so perhaps some fresh eyes can help.
Thanks in advance:
PIC 18F4480
So to reiterate/clarify... if I DONT use this function, then the defaults are used and everything works great. If I DO use this function with a BLANKED eeprom (all values 0xFF) it behaves as though the values are not the defaults and like they are probably zeros. I can't confirm what they are as there is no output on this device. I have confirmed, by bringing the device back in and reading the eeprom, that all values are in fact 0xFF.
Last edited by s_mack on Thu Oct 15, 2009 3:36 pm; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 15, 2009 3:02 pm |
|
|
Your test program is too complicated. Strip it down to a few lines
of code. Add the #include, #fuses, #use delay(), main(), variable
declarations and #defines. Add some printf statements to show
the output, to show if it's working or failing. You don't need any
CAN bus stuff for this test. The code in main can be less than 10 lines,
I would guess. Also post your compiler version.
The reason I make this kind of post is:
Sometimes a problem can be solved by inspection. This is made more
difficult by having to read tons of code.
If it can't be solved by inspection, and requires either reading the .LST
file or doing an actual hardware test, it's much easier if the code is short.
In both cases, a complete test program (with fuses) is best, because
quite often the problem is in the fuses or #use statements. |
|
|
s_mack
Joined: 04 Jun 2009 Posts: 107
|
|
Posted: Thu Oct 15, 2009 3:12 pm |
|
|
The code is mostly irrelevant anyway. At least to answer the question in the title: "Does read_eeprom need a delay". The code was there to show how read_eeprom gets called in rapid fashion and I wondered if there was a problem with doing that.
Do I need to put in a delay_ms(?) after each one or does it wait until it has the value. I guess I'm wondering if "val" is being presumed to be zero upon initialization and staying that way for some reason as its not getting valid reads?
I suppose its easy enough for me to test... but an arbitrary ms delay seems... clumsy. I'll start there. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 15, 2009 3:32 pm |
|
|
Look in the 18F4480 data sheet, in this section:
Quote: | 7.3 Reading the Data EEPROM Memory
To read a data memory location, the user must write the
address to the EEADR register, clear the EEPGD
control bit (EECON1<7>) and then set control bit, RD
(EECON1<0>). The data is available on the very next
instruction cycle; therefore, the EEDATA register can
be read by the next instruction.
|
Answer: No delay is required.
To investigate the compiler's generated code, make a test program
with one line in main() that reads the eeprom:
Code: | .................... result = read_eeprom(0);
0018: MOVFF INTCON,@@07
001C: BCF INTCON.GIE
001E: CLRF EEADR
0020: BCF EECON1.CFGS
0022: BCF EECON1.EEPGD
0024: BSF EECON1.RD
0026: MOVF EEDATA,W
0028: BTFSC @@07.7
002A: BSF INTCON.GIE
002C: MOVWF result |
Answer: The compiler does not put in any delay loop after reading
the EEDATA register. |
|
|
s_mack
Joined: 04 Jun 2009 Posts: 107
|
|
Posted: Thu Oct 15, 2009 3:37 pm |
|
|
Thanks for that. It must be something else then. I'll figure it out, thanks. |
|
|
s_mack
Joined: 04 Jun 2009 Posts: 107
|
|
Posted: Fri Oct 16, 2009 4:44 pm |
|
|
Thanks again for your help. I just wanted to follow up in case it helps anyone else.
The code I showed above actually wasn't the problem. It certainly seemed like it because when I commented it out, the problems went away. However it was the code immediately preceding that was the problem... and that was a function full of eeprom_write calls. THOSE need a delay! I wasn't aware they were causing a problem because there wasn't an issue until I subsequently read them.
Essentially (this is over-simplifying) the routine went like this:
Code: | int8 setting = 0x09; //set a default value
...
write_eeprom( 0x00, 0xFF ); //write a new value to the eeprom
var = read_eeprom( 0x00 ); //retrieve that value
if ( var < 0xFF) setting = var; |
The "oops" in there is that write_eeprom ERASES the eeprom THEN writes to it, and in the meantime the code keeps executing. So sometimes what is happening is the eeprom is cleared giving var a null value (interpreted as a zero?) and that is less than 0xFF so setting gets a zero value.
To fix:
I'm sure an arbitrary delay_ms would work but I hate doing that. Make use of the INT_EEPROM interrupt instead. I replace write_eeprom with write_eeprom_wait and add the function to my code:
Code: | void write_eeprom_wait( int8 _id, int8 _val )
{
eeprom_is_busy = true;
write_eeprom( _id, _val );
while( eeprom_is_busy )
{
//loop until eeprom_write has finished (see INT_EEPROM interrupt)
}
} |
And the interrupt as well:
Code: | #INT_EEPROM
void EEPROMIsr()
{
eeprom_is_busy = false;
} |
and of course declare the global boolean at the top of my code:
Code: | bool eeprom_is_busy = false; |
That cleared up my issue entirely.
- Steven |
|
|
|