In a PIC16F627 application I recently developed, I am having a code-hang problem. My best diagnosis so far, is that the code is getting stuck in the EEPROM write routine [code posted at the end of this email] which is called from a main code loop. Interrupt processing seems to be working fine still, even though this main loop is locked up.
In the write routine, I'm waiting on the EEIF flag to be set, indicating that the write operation is complete, but I'm guessing that this is where it's getting stuck.
The EEPROM writes happen in groups of 5 (all calling this routine), and I'm not verifying that the write operation was successful by checking the actual value saved (at least, not yet). I'm also not using a watchdog timer yet. The 2 other interrupts are TMR0 which updates the multiplexed display, and TMR2 which checks for and processes an external signal.
Any idea what could be going wrong here?
Cheers,
-Neil.
;-----------------------------------------------------------------
; Write data to EEPROM (location supplied in W, data in E_DATA)
;-----------------------------------------------------------------
WriteEEData:
bcf PIR1,EEIF ; Reset write-complete flag
bsf STATUS,RP0 ; Bank 1
movwf EEADR
bcf STATUS,RP0 ; Bank 0
movf E_DATA,W ; Get data to be stored
bcf PIR1,EEIF ; Ensure interrupt flag off while in bank 0
bsf STATUS,RP0 ; Bank 1
movwf EEDATA
Still looking over your code, but just a few comments right now:
I don't see anywhere in your code where you are clearing bit 7 of EEADR --
it should be set to zero for the device you are using.
Are you ANDing the W register (EEPROM address) with 0x7F before calling your
write routine?
Also, it might be better to re-enable interrupts only if they were
previously enabled when you entered the EEPROM write routine instead of
unconditionally enabling them. It might not matter in this specific
application of yours, but this might be something that will bite you in the
future.
Perhaps you could toggle an I/O pin in your write routine and 'scope to see
if you are actually getting stuck there.
Also, for safety, I feel it would be best to explicitly clear STATUS, RP1 in
your EEPROM WRITE routine.
Your routine makes the assumption that the RP1 bit is already cleared.
But we are not seeing the entire code so maybe this is one of your function
preconditions?
For now, you might just want to use BANKSEL even though it will be somewhat
wasteful in terms of redundancy in certain cases.
Note that I added BANKSEL directives to your code.
I'll leave it up to you to see which ones could be removed because of
redundancy.
I may have made some boo-boos, so double-check things carefully.
Like I said before, you can add code to this routine to see if interrupts
were enabled or not.
Then based on this boolean condition, you can conditional enable or leave
interrupts disabled upon exit.
You might also want to investigate the state of WRERR upon power-up.
Have you checked all errata notes on the PIC16F627?
Just for kicks, you also might want to check the WR bit to ensure it is
getting cleared after a write completes.
Also (depending upon the situation) it is good practice to point the EEPROM
address register to another unused location upon exit as one of several
measures to help prevent corruption of the EEPROM location you just wrote.
;-----------------------------------------------------------------
; Write data to EEPROM (location supplied in W, data in E_DATA)
;-----------------------------------------------------------------
WriteEEData:
BANKSEL PIR1
bcf PIR1,EEIF ; Reset write-complete flag
BANKSEL EEADR
movwf EEADR
BANKSEL E_DATA
movf E_DATA,W ; Get data to be stored
BANKSEL PIR1
bcf PIR1,EEIF ; Ensure interrupt flag off while in bank 0
BANKSEL EEDATA
movwf EEDATA
; Previous BANKSEL also takes care of the following registers
I wonder if there's a complication with the timers in your app ?
Maybe when you re-enable the interupts there's a flag set that
causes an ISR to be executed. Perhaps test the flags before
you re-enable GIE
>Hi all,
>
>In a PIC16F627 application I recently developed, I am having a code-hang
>problem. My best diagnosis so far, is that the code is getting stuck in the
>EEPROM write routine [code posted at the end of this email] which is called
>from a main code loop. Interrupt processing seems to be working fine still,
>even though this main loop is locked up.
>
>In the write routine, I'm waiting on the EEIF flag to be set, indicating that
>the write operation is complete, but I'm guessing that this is where it's
>getting stuck.
>
>The EEPROM writes happen in groups of 5 (all calling this routine), and I'm
>not verifying that the write operation was successful by checking the actual
>value saved (at least, not yet). I'm also not using a watchdog timer yet.
>The 2 other interrupts are TMR0 which updates the multiplexed display, and
>TMR2 which checks for and processes an external signal.
>
>Any idea what could be going wrong here?
>
>
Here's some working EE code on a '628.
;********************************************************************
;* eeprom interrupt handler *
;********************************************************************
int_ee: ;
bcf eeif ;clear the interrupt that called us
bcf ee_busy ;clear our eeprom busy flag
bank1 ;bank 1
bcf wren ;disable eeprom writes
bank0 ;bank 0
goto exit_int ;
;********************************************************************
;* 'ee_write' writes one byte. *
;* enter with eeprom address in w and eeprom data in ee_temp. *
;********************************************************************
ee_write: ;
btfsc ee_busy ;is the eeprom busy from a previous write?
goto ee_write ;yes, loop & wait
bcf gie ;no, disable the global interupt
bank1 ;bank 1
movwf eeadr ;set up eeprom address
movf ee_temp,w ;get data for eeprom saved in calling
routine
movwf eedata ;set eeprom data
bsf wren ;enable eeprom writing
movlw h'55' ;
movwf eecon2 ;eeprom write unlock sequence
movlw h'aa' ;
movwf eecon2 ;eeprom write unlock sequence
bsf wr ;initiate data eeprom write
bank0 ;bank 0
bsf ee_busy ;set flag to say eeprom is busy
bsf gie ;re-enable the global interupt
return ;exit
I do the first write, then exit back to the main loop right away
to deal with other tasks. Checking the eeprom busy state at
the start of the eeprom write is a more efficient way to do it.
Clearing the write enable during writing *may* be causing
your problem. I wait until the write is finished and that works.
David...
On Tuesday 24 February 2004 12:24 am, Ken Pergola scribbled:
> I don't see anywhere in your code where you are clearing bit 7 of EEADR --
> it should be set to zero for the device you are using.
It defaults to this when I set the address into the W before calling the EEPROM-write routine.
> Are you ANDing the W register (EEPROM address) with 0x7F before calling
> your write routine?
Not specifically, but I just tried that, and no change.
> Also, it might be better to re-enable interrupts only if they were
> previously enabled when you entered the EEPROM write routine instead of
> unconditionally enabling them. It might not matter in this specific
> application of yours, but this might be something that will bite you in the
> future.
Good idea. Unfortunately for this app, they must always be enabled,
> Perhaps you could toggle an I/O pin in your write routine and 'scope to see
> if you are actually getting stuck there.
Holding off on this for now, since all pins are in use, and the circuit is on a PCB already. Luckily I socketed the chip.
On Tuesday 24 February 2004 12:40 am, Ken Pergola scribbled:
> Hi Neil,
>
> Also, for safety, I feel it would be best to explicitly clear STATUS, RP1
> in your EEPROM WRITE routine.
> Your routine makes the assumption that the RP1 bit is already cleared.
> But we are not seeing the entire code so maybe this is one of your function
> preconditions?
I'm sure that RP1 does not change, but I added explicit code to do this in the write routine just now, and no change to the problem still.
>
> For now, you might just want to use BANKSEL even though it will be somewhat
> wasteful in terms of redundancy in certain cases.
I still prefer explicit instructions to set/clear the appropriate bits, since I feel it's a bit more readable/familiar to me.
On Tuesday 24 February 2004 01:04 am, Ken Pergola scribbled:
> Hi Neil,
>
> Note that I added BANKSEL directives to your code.
> I'll leave it up to you to see which ones could be removed because of
> redundancy.
> I may have made some boo-boos, so double-check things carefully.
> Like I said before, you can add code to this routine to see if interrupts
> were enabled or not.
> Then based on this boolean condition, you can conditional enable or leave
> interrupts disabled upon exit.
>
> You might also want to investigate the state of WRERR upon power-up.
This actually won't tell me much for this problem, since the chip does not reset ... the main loop just locks up (my guess is that something causes the wait-for-write complete loop not to exit), but the timer interrupts still get called properly. It feels like I have a stack problem or other timing issue.
> Have you checked all errata notes on the PIC16F627?
Not recently, but a couple months ago I did, and don't remember anything major issues around this that could cause this problem. Perhaps I should re-read them.
I did try a brand new chip, but no change still.
> Just for kicks, you also might want to check the WR bit to ensure it is
> getting cleared after a write completes.
Hmmm.... I'll go experiment.
> Also (depending upon the situation) it is good practice to point the EEPROM
> address register to another unused location upon exit as one of several
> measures to help prevent corruption of the EEPROM location you just wrote.
I always thought that the write sequence would prevent that, but it sounds fine to me if this has ever been a problem still.
> > Also, it might be better to re-enable interrupts only if they were
> Unfortunately for this app, they must always be enabled
Can you arrange for the code to be executed in between timer
interrupts ? ie wait until both TMR0 and TMR2 IRQs have just
occured before entering the EE routine
In your original code you clear WREN after setting WR. All examples
I've seen (eg 13.2 in the 16F628 manual amongst others) simply set
WREN. Have you tried it with that taken out or putting it somewhere
else? However, once WR is set, anything short of a reset won't stop
the write so that could be chasing rainbows
IMHO I think you should verfiy that program flow really is getting
stuck in this routine
> Can an interrupt that occurs while waiting for write completion
> attempt to read from EEPROM or read/write program memory?
AFAIK no manual or EEPROM d/s specifically says you can't
read EE whilst a write is occuring. As the actual write on a micro
is done in h/w on a single byte by a timer/charge pump and doesn't
rely on or affect s/w, maybe you can read other locations during the
~4ms that the write takes. I've never tried that and unfortunately not
able to at the moment
> > Can an interrupt that occurs while waiting for write completion
> > attempt to read from EEPROM or read/write program memory?
>
> AFAIK no manual or EEPROM d/s specifically says you can't
> read EE whilst a write is occuring.
The datasheets say, "After a write sequence has been initiated,
EECON1, EEADR and EEDATA cannot be modified."
As the actual write on a micro
> is done in h/w on a single byte by a timer/charge pump and doesn't
> rely on or affect s/w, maybe you can read other locations during the
> ~4ms that the write takes. I've never tried that and unfortunately not
> able to at the moment
My question could have been worded better; I was asking whether the OP
was actually trying to do that, since it would be a problem. You
can't read EEPROM or program memory while an EEPROM write is in
progress.
> > AFAIK no manual or EEPROM d/s specifically says you can't
> > read EE whilst a write is occuring.
>
> The datasheets say, "After a write sequence has been initiated,
> EECON1, EEADR and EEDATA cannot be modified."
Where does it say that ? I've looked in the EE sections of the
628 and Mid-range manuals and don't see it
> > The datasheets say, "After a write sequence has been initiated,
> > EECON1, EEADR and EEDATA cannot be modified."
>
> Where does it say that ? I've looked in the EE sections of the
> 628 and Mid-range manuals and don't see it
That's actually from an 18F datasheet, but I would expect the same
behavior in a 16F. I've observed program memory reads failing on a
16F during an EEPROM write; I'm assuming that this is because the
EEDATA/EEADR registers are simply non-functional, as noted in the 18F
datasheet, while an EEPROM write is in progress.
Ah, I see it, section 6.4. Seems a little late in the piece for
MC to be saying that. I've checked in the 877 manual in case
it was something to do with being able to R/W Flash as well
but no
> I'm assuming that this is because the EEDATA/EEADR
>registers are simply non-functional
There might be an alternative interpretation of
"After a write sequence has been initiated, EECON1, EEADR
and EEDATA cannot be modified."
_could_ mean that once the write has been initiated, the contents
of the registers have been passed on to the h/w for writing and so
that's what will be written. IOW, too late to change them. Perhaps
it's just sloppy writing, notwithstanding your observations
On Tuesday 24 February 2004 01:29 am, Jinx scribbled:
> Neildude, I use this 628 routine, a little different to yours but it
> works OK. Note that bank0 and bank1 macros or BANKSEL
> address both RP bits
>
> ...
>
> I wonder if there's a complication with the timers in your app ?
This is the biggest thing I suspect now. I just tried your routine above, and it totally froze now.... no display updates, etc. Odd!? However, I moved the line that re-enables INTCON (after the EEPROM) write in my routine to now be outside/after the wait-for-EEIF loop, and the whole app works properly. But interrupts are disabled for so long that there are no display updates during this time, leading to a nasty display flicker ... actually display "blinking". :-(
> Maybe when you re-enable the interupts there's a flag set that
> causes an ISR to be executed. Perhaps test the flags before
> you re-enable GIE
I need to check for this carefully. Let me go bash my head against the wall a bit more.
David's reply/code brought me to thinking about the architecture of my app, so here's some more detail on what I'm doing...
The application is an odometer. It shows the current mileage (to the nearest mile), but every 1/10th mile (which it gets as a pulse from an external source) it saves the new data to EEPROM. It takes 4 EEPROM locations to save a new distance value (0-999,999.9 miles). On startup it reads the EEPROM to know where it left off. Display intensity changes are also stored in EEPROM, but that happens very rarely.
To address the concern of EEPROM write endurance, I have setup the EEPROM as a 30-unit circular buffer, of 4 bytes each. It just moves to the next index/location of the circular buffer and writes to that (set of 4 bytes) location. Each distance value uses 4 bytes, but 5 EEPROM writes -- The first byte is marked as dirty, the distance is then written to the other 3 and the first is then marked as valid. On startup, all 30 locations are read, and the highest *valid* value is taken as the distance it left off with.
For a 1-million mile odometer, that means 10-million distances written. Byte 1 of each 4-byte set would get the most abuse at 20 million writes. But splitting it up across 30 sets means 666,667 max writes for any single EEPROM location. This is well within the datasheet's EEPROM read/write endurance limits (10mil cycles typical, and 1mil minimum). For greater comfort level, I'm thought of moving this to an 'F648 (256 bytes of EEPROM), so I can drop the EEPROM abuse to half of this by spreading it over 60 sets of 4 bytes, but another idea I had was that above a certain speed, I could set it to write every mile or so, since the vehicle has to come back to a lower speed before stopping, and that would let it catch back up to the nearest 1/10-mile. TBD later.
The main code does its initialization stuff, scans for highest saved distance-set, enables 2 timer interrupts, then goes into a loop that updates the display if necessary, and writes a distance-set to EEPROM if necessary. One of the timer interrupts just updates the multiplexed display periodically, and another polls for pulses at the ports and updates the distance variables if necessary. If so, it sets a flag to trigger the main loop to write that to EEPROM.
It all works except for the EEPROM write issue, which was also working some time ago during development, but not sure what's causing this problem now.
... just in case you were wondering.
Cheers,
-Neil.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
On Tuesday 24 February 2004 05:57 am, David Duffy scribbled:
> Here's some working EE code on a '628.
>
> ...
>
>
> I do the first write, then exit back to the main loop right away
> to deal with other tasks. Checking the eeprom busy state at
> the start of the eeprom write is a more efficient way to do it.
> Clearing the write enable during writing *may* be causing
> your problem. I wait until the write is finished and that works.
> David...
Interesting way to do it. In my case, I would just wait in a busy-loop until that flag is set, and then continue with the rest of the code. This is cause the main routine really does not *need* to do anything else during this time.
I can see an easy way to set a timeout with this ... ie: while waiting until the flag is set, I could/should add a "timeout" counter which would assume that something went wrong if the flag was not set in a certain amount of time. What's a reasonable amount of time for this? Hopefully it's less than the time between EEPROM writes in my app. I've calculated that my app can write a new value to EEPROM as fast as every 0.29 secs. [ Hmmm ... that seems pretty short now that I think about it, but it's the very *worst* case. ]
Cheers,
-Neil.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
On Wednesday 25 February 2004 07:03 pm, Jinx scribbled:
> > > Also, it might be better to re-enable interrupts only if they were
> >
> > Unfortunately for this app, they must always be enabled
>
> Can you arrange for the code to be executed in between timer
> interrupts ? ie wait until both TMR0 and TMR2 IRQs have just
> occured before entering the EE routine
That will take a bit of thought. See my other post from a minute ago, where I explain how my code is architected.
> In your original code you clear WREN after setting WR. All examples
> I've seen (eg 13.2 in the 16F628 manual amongst others) simply set
> WREN.
My impression is that disabling it helps prevent spurious writes. I think I just put it there so that the code can get some work done and out of the way while the EEPROM was off doing its thing.
> Have you tried it with that taken out or putting it somewhere
> else? However, once WR is set, anything short of a reset won't stop
> the write so that could be chasing rainbows
I remember something (from the datasheet or an app note) that said that clearing WREN will not stop a write operation once it has been initiated. Of course that's the perfect scenario, if the chips operated exactly as intended.
> IMHO I think you should verfiy that program flow really is getting
> stuck in this routine
I really should re-breadboard it, but so far my test of re-enabling interrupts after waiting for the write operation to be completed seems to re-affirm that this is the problem area. Let me run off and think of an easy way to do this in the existing environment.
Cheers,
-Neil.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
On Wednesday 25 February 2004 07:02 pm, spamBeGonepiclistSTOPspamEraseMEXARGS.COM scribbled:
> Can an interrupt that occurs while waiting for write completion
> attempt to read from EEPROM or read/write program memory?
Nope. The EEPROM location is only written to during continuous operation. Reading is done only at the beginning during initialization when the circuit is first powered up.
Cheers,
-Neil.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
On Wednesday 25 February 2004 07:41 pm, Ken Pergola scribbled:
> Hi Neil,
>
> The part you are having trouble with is a PIC16F627 not a PIC16F627A
> correct?
>
> The reason I'm asking is because the PIC16F62XA has an errata problem with
> EEPROM writes.
>
> Just checking with you.
>
> Best regards,
>
> Ken Pergola
Hmmm.... I've been interchanging these chips as available. Currently it is a PIC16F627A. The latest errata sheet I had previously read for these devices was 80073f, but I just got a new one (80151g) that shows what you're talking about. Let me swap chips and see if it has any effect. I'll be back.
Thanks,
-Neil.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
Bingo! Changed the 16F627A to a 16F628 (using the same code that exhibited the hanging problem), and the odometer works properly again! I'm going to run an extended overnight test and will post the results tomorrow.
Thanks for all the help.
Cheers,
-Neil.
On Wednesday 25 February 2004 11:28 pm, Picdude scribbled: {Quote hidden}
> On Wednesday 25 February 2004 07:41 pm, Ken Pergola scribbled:
> > Hi Neil,
> >
> > The part you are having trouble with is a PIC16F627 not a PIC16F627A
> > correct?
> >
> > The reason I'm asking is because the PIC16F62XA has an errata problem
> > with EEPROM writes.
> >
> > Just checking with you.
> >
> > Best regards,
> >
> > Ken Pergola
>
> Hmmm.... I've been interchanging these chips as available. Currently it is
> a PIC16F627A. The latest errata sheet I had previously read for these
> devices was 80073f, but I just got a new one (80151g) that shows what
> you're talking about. Let me swap chips and see if it has any effect.
> I'll be back.
>
> Thanks,
> -Neil.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
Do you need to be updating that often ? Presumably using
TMR0 means a fairly short IRQ period, when a refresh rate
as slow as 100Hz would probably be fine. That would put
the update period outside of the 8ms max spec for an EE
write
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
>>Can an interrupt that occurs while waiting for write completion
>>attempt to read from EEPROM or read/write program memory?
>>
>>
>
>AFAIK no manual or EEPROM d/s specifically says you can't
>read EE whilst a write is occuring. As the actual write on a micro
>is done in h/w on a single byte by a timer/charge pump and doesn't
>rely on or affect s/w, maybe you can read other locations during the
>~4ms that the write takes. I've never tried that and unfortunately not
>able to at the moment
>
AFAIK, changing EEADR during a write is not good!
IIRC, this one caught me last year. The code I posted
yesterday definately does work correctly and we have
hundreds of units out there working hard all the time.
David...
--
___________________________________________
David Duffy Audio Visual Devices P/L
U8, 9-11 Trade St, Cleveland 4163 Australia
Ph: +61 7 38210362 Fax: +61 7 38210281
New Web: http://www.audiovisualdevices.com.au
___________________________________________
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
>On Tuesday 24 February 2004 05:57 am, David Duffy scribbled:
>
>
>>Here's some working EE code on a '628.
>>
>> ...
>>
>>
>>I do the first write, then exit back to the main loop right away
>>to deal with other tasks. Checking the eeprom busy state at
>>the start of the eeprom write is a more efficient way to do it.
>>Clearing the write enable during writing *may* be causing
>>your problem. I wait until the write is finished and that works.
>>David...
>>
>>
>
>Interesting way to do it. In my case, I would just wait in a busy-loop until
>that flag is set, and then continue with the rest of the code. This is cause
>the main routine really does not *need* to do anything else during this time.
>
>I can see an easy way to set a timeout with this ... ie: while waiting until
>the flag is set, I could/should add a "timeout" counter which would assume
>that something went wrong if the flag was not set in a certain amount of
>time. What's a reasonable amount of time for this? Hopefully it's less than
>the time between EEPROM writes in my app. I've calculated that my app can
>write a new value to EEPROM as fast as every 0.29 secs. [ Hmmm ... that
>seems pretty short now that I think about it, but it's the very *worst* case.
>
My application has to monitor a RF remote receiver, a push button, flash
an led and do serial comms in between the eeprom writing and reading
so the wait-while-previous-byte-writing was the best way to go for me.
I think the eeprom byte write time on a 'F628 is in the order of 8-10ms.
David...
--
___________________________________________
David Duffy Audio Visual Devices P/L
U8, 9-11 Trade St, Cleveland 4163 Australia
Ph: +61 7 38210362 Fax: +61 7 38210281
New Web: http://www.audiovisualdevices.com.au
___________________________________________
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
On Thursday 26 February 2004 12:40 am, David Duffy scribbled:
> I think the eeprom byte write time on a 'F628 is in the order of 8-10ms.
> David...
Using the 16F628 instead now, I had tested for a day at a simulated ~1000 MPH, and it worked well, and I've also run it for a day at ~1600MPH ! In both cases, it works well, with the EEPROM saving the 0.1-mile distances reliably. I've logged 60,000+ simulated miles already. So the write time is more than fast enough for my original intention of supporting speeds up to 250mph. :-)