CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

write_eeprom data corruption issue

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
dsaari



Joined: 05 May 2006
Posts: 25

View user's profile Send private message AIM Address

write_eeprom data corruption issue
PostPosted: Mon May 20, 2013 4:49 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon May 20, 2013 5:11 pm     Reply with quote

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'.


Quote:
Compiler 5.003

You are doing development with a Beta test version of the compiler.
dsaari



Joined: 05 May 2006
Posts: 25

View user's profile Send private message AIM Address

PostPosted: Mon May 20, 2013 5:28 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Mon May 20, 2013 7:59 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon May 20, 2013 8:14 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Mon May 20, 2013 8:48 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon May 20, 2013 9:05 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Mon May 20, 2013 9:15 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon May 20, 2013 9:41 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon May 20, 2013 10:22 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue May 21, 2013 11:29 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Tue May 21, 2013 3:36 pm     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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