2
\$\begingroup\$

I am trying to design an ON-OFF Keying decoder for an RFID reader project I've been working on. Below is the code that runs on a PIC16F887 @20MHZ. The compiler is Mikroelektronika mikroC PRO for PIC.

What I was trying to do is to toggle PORTC.1 every 200uS. The code runs as wished, but only for about 5.8 msec, as I measured with the oscilloscope. After that, PORTC.1 do not toggle.

I am guessing that somehow it is related to the overflow of the fiftymicrosand data_time variables. If we increase fiftymicrosvariable every 50 usec - actually this is what progam do, it will overflow after 255*50 microseconds which is equal to 12.75 msec.

Whenever I change the type of the variables fiftymicrosand data_time to integer, the problem diminishes.

Could you help me understand what is the reason behind this behavior?

volatile unsigned char fiftymicros=0;
volatile unsigned char fiftymicroscounter=0;
volatile unsigned char data_time=0;
volatile unsigned int milis=0;
volatile unsigned int milis_ctr=0;

void calculate_data_time()
{
     static volatile unsigned char ctr=0;
     static volatile unsigned char ind=0;
     if(fiftymicros-data_time>3)
     {
       data_time=fiftymicros;
       PORTC.F1=~PORTC.F1;
     }
}

void interrupt()
{
     if(INTCON.T0IF)
     {
      TMR0=198;
      INTCON.T0IF=0;
      fiftymicros++;
      if(++fiftymicroscounter==20) { fiftymicroscounter=0; milis++; }
     }
    if(INTCON.RBIF)
     {
      //data_time=fiftymicros;
      INTCON.RBIF=0;
     }
}

void main()
{
     ANSEL =0;
     ANSELH=0;
     TRISA=0; PORTA=0;
     TRISB=0x01; PORTB=0;
     TRISC=0; PORTC=0;
     TRISD=0; PORTD=0;
     TRISE=0; PORTE=0;
     OPTION_REG.INTEDG=1;
     OPTION_REG.T0CS=0;
     OPTION_REG.PSA=0;
     OPTION_REG.PS0=1;
     OPTION_REG.PS1=0;
     OPTION_REG.PS2=0;
     INTCON.TMR0IE=1;
     INTCON.RBIE=1;
     INTCON.GIE=1;
     UART1_Init(9600);
     Delay_ms(100);
     milis_ctr=milis;
     while(1)
     {
      calculate_data_time();
     }
}
\$\endgroup\$
  • \$\begingroup\$ This is in fact a C programming question. You might be much better off asking on StackOverflow. \$\endgroup\$ – sharptooth Mar 12 '12 at 7:27
  • 1
    \$\begingroup\$ @sharptooth, you may be right, but I love this community more :) \$\endgroup\$ – abdullah kahraman Mar 12 '12 at 9:46
  • \$\begingroup\$ I thought the whole purpose of Stack Exchange is to ask a good question on a site where there's enough hardcore experts that can provide a brilliant answer. I'm sure you love your parents but unless they are proficient in C programming you won't ask them this question, will you? \$\endgroup\$ – sharptooth Mar 12 '12 at 9:58
  • \$\begingroup\$ @sharptooth, nice simile :) To be serious, this is not an exclusive C question, in fact, maybe there was a hardware problem? Maybe there was an electrical problem? You won't know, so I thought it would be more suitable to ask it here, since it is not exclusively C related. \$\endgroup\$ – abdullah kahraman Mar 12 '12 at 10:17
1
\$\begingroup\$

If the compiler is ANSI C compliant then the two unsigned chars will be promoted to integers based on the Usual Arithmetic Conversions specified in the ANSI C standard. If data_time is greater than fiftymicros due to fiftymicros overflowing, then the result of fiftymicros - data_time will be a negative number.

With the original code, if fiftymicros = 0 and data_time = 251, the result of fiftymicros - data_time will be -251

You can force the compiler to do what you want by changing the code to this:

if ((unsigned char)(fiftymicros - data_time) > 3)
{
    ...
}

With this code, if fiftymicros = 0 and data_time = 251, the result of (unsigned char)(fiftymicros - data_time) will be 5.

\$\endgroup\$
  • \$\begingroup\$ Comment after long time, sorry :) Just popped into my mind, will the result be 5 or 4? Because -251+255=4. If I am incorrect, how to determine the result? \$\endgroup\$ – abdullah kahraman Apr 18 '12 at 11:48
  • 1
    \$\begingroup\$ -251 is 1111 1111 0000 0101 in binary. If you cast that as an unsigned char you are only left with the lower 8 bits, giving you 0000 0101 in binary or 5. \$\endgroup\$ – Louis Davis Apr 20 '12 at 21:42
  • \$\begingroup\$ Ah, right, I remember that from my 8086 class. Thanks. I hate digital by the way :) \$\endgroup\$ – abdullah kahraman Apr 21 '12 at 12:51
2
\$\begingroup\$

I think it all boils down to:

 if(fiftymicros-data_time>3)
 {
   data_time=fiftymicros;
   PORTC.F1=~PORTC.F1;
 }

I don't know how that compiler handles the >3, but it may be performing a signed comparison. So, here's the scenario: Assume fiftymicros just incremented to 255, and data_time is 251 (these numbers could be slightly smaller and the result would be the same). The if would be true so data_time would be set to 255. Then interrupt() increments fiftymicros, which causes it to roll over to 0 (because fiftymicros is an unsigned char). Now, all future calculations fiftymicros-data_time will result in a negative (signed) number, which will always be less than 3.

A simple solution would be:

 if(fiftymicros>3)
 {
   fiftymicros=0;
   PORTC.F1=~PORTC.F1;
 }

If you still want data_time for other reasons, then add data_time+=fiftymicros; before fiftymicros=0;.

\$\endgroup\$
  • \$\begingroup\$ Hmm, I thought fiftymicros-data_time would result in an unsigned number and be 252(or 251?) for 251-255. So that means when it is an integer, it will not give it enough time for this scenario to happen. Btw, I really don't want to touch fiftymicros. I guess I will just change those characters to integers.. \$\endgroup\$ – abdullah kahraman Mar 9 '12 at 21:44
  • 1
    \$\begingroup\$ My entire theory is based on the idea that the >3 comparison may be signed. If you can ensure that it is unsigedn then the negative number becomes a very large unsinged number, which will always be >3. This may result in PORTC.F1 toggling an extra time when the variables roll over. The easiest way to guarantee that you are doing unsigned math (without me knowning that compiler), is for you to put the result of fiftymicros-data_time into an unsigned char temporary variable and then using that in the comparison. \$\endgroup\$ – Klox Mar 9 '12 at 21:56
  • \$\begingroup\$ If you want to use my "simple solution" from my original answer, then you could also create a new variable just like fiftymicros that gets incremented in interrupt() (so you have two variables: fiftymicros, which always increments, and a new variable that gets reset). My biggest concern is that your solution to increase the size of fiftymicros will only postpone the problem. It doesn't solve it. \$\endgroup\$ – Klox Mar 9 '12 at 21:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.