|
|
View previous topic :: View next topic |
Author |
Message |
dsaari
Joined: 05 May 2006 Posts: 25
|
write_eeprom data corruption issue |
Posted: Mon May 20, 2013 4:49 pm |
|
|
Hi,
I'm having a problem with the onboard eeprom data becoming corrupt on my Pic. It is very intermittent but I can recreate it. The device is 16F876. I have scoured the forums and reviewed many related posts. I have struggled for long enough and I've decided to expose my stupidity in hopes for some guidance.
My program is a simple count down timer. The Pic drives two 7-segment display drivers. There is one button input which is active low. The theory is that the timer will start up and read the eeprom for the value to begin counting down from. Once it reaches zero, that's it. if the button is pressed, the program scrolls, ever faster, through the numbers and when the button is released the new value is then stored in eeprom.
I've tried several things to help with the data corruption issues. I've tried different fuses, PUT, BROWNOUT, both on and off to no avail. I currently have a scheme in place to write the value to 8 locations in eeprom and on startup I find the value which occurs most and use that. I've tried then to fix the corrupt values, but ultimately it ends in failure and all or most of my locations have 0xFF in them.
The main switched power to my board is 24VAC. This goes through a bridge and then a 12VDC switcher (for powering the LEDs). The 12VDC supplies a linear 5VDC regulator, which is my control circuit power. After reading on this forum I got the idea to use a voltage divider on the 12VDC power and bring it into an analog input. This way I could test for low voltage prior to performing any writes.
The kicker is that this corruption happens without anyone ever pressing the button. I'll set the timer for 10sec, power it on and after ten or so seconds I turn power off. This is done in my test with an external PLC. After several hours of cycling, the data is corrupt. I can't fathom how the program is even getting into the routine which writes to the eeprom. If there's any ideas out there, please help.
Thank you,
Compiler 5.003
Code: |
//****************************************************************************//
#include <16F876.h> //PIC microcontroller driver
#device CCSICD=TRUE //Use the ICD debugger
#device WRITE_EEPROM=NOINT
#device adc=10
#fuses NOWDT, HS, PUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, DEBUG
#use delay(crystal=19883495)
#use rs232(debugger)
#define RSTC1 0x4C //Reset var for seconds timer
#define RSTC2 0x017C //Reset var for timeout timer
#define RSTC3 0x3C //Reset var for blinker timer
#define RSTC4 0x26 //Reset var for err blink timer
#define Input3 PIN_B0
#define LED PIN_C4
//****************************************************************************//
//***************************Global Variables*********************************//
int8 timer_seconds = RSTC1; //Variable to count down within the ISR approx 1sec
int16 timer_timeout = RSTC2; //Variable to count down within the ISR approx 5sec
int8 timer_blinker = RSTC3; //Variable to count down within the ISR approx <1sec
int8 timer_err = RSTC4; //Variable to count down within the ISR approx 500ms
int8 digit1 = 0; //Tens digit
int8 digit2 = 0; //Ones digit
int8 seconds = 0; //Seconds counter
int8 EEPROM_address = 0x00; //Starting address to store preset
int8 count_occurance = 0; //count of EEPROM saved time occurance freq.
int8 err_flash_count = 0; //tracks how many times we have flashed LED
short time_out = 0; //Initialize timeout flag (not done setting time)
short Start_up_complete = 0; //Used to skip button effects at startup
int16 duration_timer =0; //Initialize the button press duration timer
int16 duration_scale = 0; //Scales the button scroll speed
#include <segment.h>
//****************************************************************************//
//**************************Interrupt Service Routine*************************//
#int_TIMER0
void TIMER0_isr()
{
if (timer_seconds > 0) timer_seconds -= 1;//Decrement counter1
if (timer_timeout > 0) timer_timeout -= 1;//Decrement counter2
if (timer_blinker > 0) timer_blinker -= 1;//Decrement counter3
if (timer_err > 0) timer_err -= 1; //Decrement counter4
}
//****************************************************************************//
//**********************************MAIN**************************************//
void main()
{
setup_adc_ports(NO_ANALOGS); //Make these I/O pins A/D inputs
setup_adc(ADC_OFF); //Make the A/D clock an internal reference
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256); //Initialize the internal Timer0
setup_timer_1(T1_DISABLED); //Disable timer1
setup_timer_2(T2_DISABLED,0,1); //Disable timer2
enable_interrupts(GLOBAL); //interrupt to update display
enable_interrupts(INT_TIMER0); //interrupt to update display
display(0xff,0xff);
delay_ms(250); //upped this from 50ms
get_time();
if (seconds >99 || seconds == 0) seconds = 99;
while (TRUE) //Main loop
{
digit1 = seconds/10; //Ten digit
digit2 = seconds - (digit1*10); //Ones digit
display(digit1,digit2);
if (timer_seconds == 0) //interrupt driven countdown timer
{
if (seconds > 0) seconds -= 1; //Each second subtract one
timer_seconds = RSTC1; //Resets counter1 for another sec
Start_up_complete = 1; //After a second expires set this true
}
if (!input(Input3) && Start_up_complete) set_time();//If the PB is
//pressed go set time
if (timer_err == 0)
{ //if there's an EEPROM memory corruption issue flash the extent
if (err_flash_count < (count_occurance * 2))
{
output_toggle(LED);
err_flash_count++;
}
else err_flash_count = 0;
timer_err = RSTC4;
}
}
}
//****************************************************************************//
//***************************Function Prototypes******************************//
void set_time(); //Function to set the countdown time
void get_time(); //Function to get stored time
void display(int8 MSB, int8 LSB); //displays the segment information
//****************************************************************************//
//****************************************************************************//
void set_time()
{
int8 x = 0;
int16 volts = 0;
int8 tempsecs = seconds;
timer_timeout = RSTC2; //Set timer2 to 5 seconds
while (!time_out) //Stay in the loop while there is activity
{
duration_scale = 0;
duration_timer = 500; //Measures the duration the button is pressed
while (!input(Input3)) //Stay in the loop while the button is pressed
{
if (duration_scale < 140) duration_scale += 5;
timer_timeout = RSTC2; //resets timeout to 5 seconds
if (duration_timer > 0 && duration_timer <= 500)
duration_timer = duration_timer -= duration_scale;//decrement timer
if (duration_timer <= 150 ) duration_timer = 150;//lowest we can go is 100
//printf("%Lu,%Lu\n\r",duration_scale,duration_timer);
delay_ms(duration_timer); //Delay interval based on button press duration
if (timer_blinker == 0)
{
if (seconds > 0) seconds -= 1;
if (seconds <= 0) seconds =99;
digit1 = seconds/10;
digit2 = seconds - (digit1*10);
display(digit1,digit2);
}
}
if (timer_timeout == 0) time_out = 1;
if (timer_blinker == 0) timer_blinker = RSTC3; //Resets counter3 for another 1.5 sec
if (timer_blinker >= 30) display(digit1,digit2);
if (timer_blinker < 30) display(0xff,0xff);
}
time_out = 0;
if (tempsecs != seconds)
{
display(digit1,digit2);
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
while (volts < 90)
{
delay_ms(50);
set_adc_channel(4);
volts = read_adc();
//printf("volts = %Lu\n\r", volts);
//delay_ms(50);
}
if (seconds <= 99 || seconds > 0)
{
for(x = 0; x < 8;x++) WRITE_EEPROM(EEPROM_address + x, seconds);
}
printf("Normal write \n\r");
printf("volts = %Lu\n\r", volts);
setup_adc_ports(NO_ANALOGS); //Make these I/O pins A/D inputs
setup_adc(ADC_OFF); //Make the A/D clock an internal reference
delay_ms(50);
}
}
//****************************************************************************//
//****************************************************************************//
void get_time()
{
int8 count[8] = 0,0,0,0,0,0,0,0;
int8 value[8] = 0,0,0,0,0,0,0,0;
int8 hival = 0;
int8 x = 0;
for (x = 0; x < 8; x++) value[x] = READ_EEPROM(EEPROM_address + x);
for (x = 0; x < 8; x++) if(value[0] == value[x] && value[x] <= 99) count[0]++;
if (count[0] != 8)//speeds it up if all = we can skip this
{
for (x = 0; x < 8; x++) if(value[1] == value[x] && value[x] <= 99) count[1]++;
for (x = 0; x < 8; x++) if(value[2] == value[x] && value[x] <= 99) count[2]++;
for (x = 0; x < 8; x++) if(value[3] == value[x] && value[x] <= 99) count[3]++;
for (x = 0; x < 8; x++) if(value[4] == value[x] && value[x] <= 99) count[4]++;
for (x = 0; x < 8; x++) if(value[5] == value[x] && value[x] <= 99) count[5]++;
for (x = 0; x < 8; x++) if(value[6] == value[x] && value[x] <= 99) count[6]++;
for (x = 0; x < 8; x++) if(value[7] == value[x] && value[x] <= 99) count[7]++;
for (x = 0; x < 8; x++) if(count[0] > count[x]) hival = 0;
for (x = 0; x < 8; x++) if(count[1] > count[x]) hival = 1;
for (x = 0; x < 8; x++) if(count[2] > count[x]) hival = 2;
for (x = 0; x < 8; x++) if(count[3] > count[x]) hival = 3;
for (x = 0; x < 8; x++) if(count[4] > count[x]) hival = 4;
for (x = 0; x < 8; x++) if(count[5] > count[x]) hival = 5;
for (x = 0; x < 8; x++) if(count[6] > count[x]) hival = 6;
for (x = 0; x < 8; x++) if(count[7] > count[x]) hival = 7;
/* if (count[hival] < 5)
{
delay_ms(500);
for (x = 0; x < 8; x++) if(value[x] != value[hival])
WRITE_EEPROM(EEPROM_address + x, value[hival]);
}*/
seconds = value[hival];
}
else seconds = value[0];
count_occurance = count[hival]; //Added to display for troubleshooting
}
//****************************************************************************//
//****************************************************************************//
void display(MSB,LSB)
{
//output_A(MSB); //Display digit1
output_bit(PIN_A0,MSB&1); //New Way
output_bit(PIN_A1,MSB&2);
output_bit(PIN_A2,MSB&4);
output_bit(PIN_A3,MSB&8);
//output_C(digit2); //Display digit2//Old Way
output_bit(PIN_C0,LSB&1); //New Way
output_bit(PIN_C1,LSB&2);
output_bit(PIN_C2,LSB&4);
output_bit(PIN_C3,LSB&8);
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 20, 2013 5:11 pm |
|
|
Quote: | if (seconds <= 99 || seconds > 0)
{
for(x = 0; x < 8;x++) WRITE_EEPROM(EEPROM_address + x, seconds);
}
|
What's your intention for this logical expression ? As written, it will
execute the for() loop for any value of 'seconds'.
You are doing development with a Beta test version of the compiler. |
|
|
dsaari
Joined: 05 May 2006 Posts: 25
|
|
Posted: Mon May 20, 2013 5:28 pm |
|
|
My intention with the for loop was implemented today as a final surrender attempt. It was intended to limit what is written to EEPROM to be between 1 and 99. Since an int can hold 255, I wanted to rule out some possible invalid values. I do see the flaw now, but I've been up all nite working on this, so I'm not all with it.... I'll fix and try it again. results to follow... |
|
|
dsaari
Joined: 05 May 2006 Posts: 25
|
|
Posted: Mon May 20, 2013 7:59 pm |
|
|
Ok, I've been cycling two of these timers with the corrected code, as pointed out, for about two hours. They have each turned on, counted down from 10, and turned off, more that 300 times each. There is an LED that I flash when the timer is running. The LED flashes a count representing the number of EEPROM locations that contain the same number in the location that I store the 8 values.
One timer is flashing 8 times (good). The other is flashing 7 times (indicating there is one corrupt value). My suspicion is that by morning they will both be starting from 99 rather than 10, which is what I've been having a problem with.
It appears my last ditch effort to limit what values can be written is still not enough to prevent corrupt data. I temporarily stopped my test to read the actual EEPROM and see what was stored. 00, 0a, 0a, 0a, 0a, 0a, 0a, 0a, and FF for all the rest. So it appears the first location was written with bad data. Bear in mind, the button is not being pressed either.
Here's the change I made in response to your suggestion;
Code: | if (seconds <= 99 && seconds > 0)
{
for(x = 0; x < 8;x++) WRITE_EEPROM(EEPROM_address + x, seconds);
} |
Any thoughts? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 20, 2013 8:14 pm |
|
|
Without looking at your code, just a question. How many tests have you
already done ? Is it possible that you have exceeded the write endurance
limits of location 0, and the eeprom in that particular PIC is now bad ? |
|
|
dsaari
Joined: 05 May 2006 Posts: 25
|
|
Posted: Mon May 20, 2013 8:48 pm |
|
|
For the tests I have run today; I just changed the starting location to address zero from a different memory area. I had the same thought as you and I am forever conscious of this limitation. These writes should only happen after the button is pressed and then only if the stars line up right as far as the checks I am making for valid data and conditions. Your point is well received, however I do not feel this is the issue.
I just observed my ongoing test again after restarting the timers and reloading the EEPROMs locations about an hour ago. One of the locations in the same timer is corrupt again. Bear in mind, no writes should be occurring during my tests at all. I am simply turning power on for ten seconds and then off for ten seconds.
For sanity sake, I've just restarted the test using new locations for both timers. No writes should occur aside from my initial load which I'll do manually instead of through my code. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 20, 2013 9:05 pm |
|
|
Quote: | #fuses NOWDT, HS, PUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, DEBUG |
The PIC is running at +5v, so why not use Brownout ? Unless you are
putting the PIC into Sleep mode and wish to reduce the sleep current.
You could at least do the eeprom corruption tests with Brownout enabled.
Are you compiling and running the PIC in Debug mode ? I would run
the tests in Release mode. |
|
|
dsaari
Joined: 05 May 2006 Posts: 25
|
|
Posted: Mon May 20, 2013 9:15 pm |
|
|
I've tried the fuse settings in every possible combination and still had the issue. I'm not putting it to sleep or anything. Can you direct me on how to do corruption tests if I enable brownout? I only go to debug mode to see what's actually in the eeprom. My test are completely standalone.
I just realized I'm doing a really poor job at debouncing that button. I'm going to fix that and restart my tests. If this is my issue, I'm gonna cry! |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Mon May 20, 2013 9:41 pm |
|
|
Hi,
I think PCM programmer has given you the solution by suggesting that you enable brownout protection.
Here is what I suspect is happening - when power is removed from your circuit, the falling voltage is interpreted by the PIC as a key press, and the EEPROM writing begins. This occurs before the low-voltage operating threshold is crossed and the PIC actually stops executing instructions. Brownout will do internally what you are trying to do externally with your voltage divider and A/D.
You can test the fix by writing a value to the specific EEPROM location written to by the switch closure, and then reading it back each time the PIC is restarted. Of course, this value should never change! Perform this test a number of times until you are satisfied the EEPROM is not being written to when power is lost.
Search the Code Library for PCM programmers 'button' routine. It's an easy to implement solution to the switch bounce problem!
John |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 20, 2013 10:22 pm |
|
|
Do a Google search like this on the Microchip forums:
Quote: | site:microchip.com/forums data eeprom corruption power |
They recommend things like doing a delay as the first thing after power-up.
The also suggest that using a reset supervisor chip on the MCLR line will
help. (Microchip sells those chips).
They recommend setting the EEADRx registers to an unused eeprom
address after each time you access data eeprom.
If you want to narrow it down to your PIC or family of PICs, you can
also do this search:
Quote: |
site:microchip.com/forums data eeprom corruption power 16F877 OR 16F876 |
|
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Tue May 21, 2013 11:29 am |
|
|
If the issue is that the PIC thinks the input is being pressed while 'powering down' try changing your code so the input is active High. This way the inputs wouldn't detect a 'press' when the supply is turned off.
Ronald |
|
|
dsaari
Joined: 05 May 2006 Posts: 25
|
|
Posted: Tue May 21, 2013 3:36 pm |
|
|
I think I've gotten to the bottom of my issue with corrupt data EEPROM writes. I did re-enable BROWNOUT also. I feel that the issue was in the button debounce which I simply overlooked when I wrote the code. I've also added a condition to the for loop which necessitates the button is not being pressed along with the other conditions. Since the button should not be active during the EEPROM write this was a good choice. Since the button is active low; on powerdown, if my code detects the button press, it would never write since the button state would need to be high to allow the write to proceed.
My test code has been cycling since last night and still no corrupt data. It would have certainly failed by now. Thank you all for your guidance. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|