16 bit substraction compiler error

I’m working on a project based upon the real time operating system, FreeRTOS, on the ATmega328 chip. This next code delivers wrong results:

void vApplicationIdleHook()
{
   static bool led_state = false;

   TickType_t current = xTaskGetTickCount(); 

   TickType_t elapsed_time = current - last_time; // <-- ERROR: Wrong results

   if( 500 < elapsed_time ){

      Serial.print( "E:" );
      Serial.print( elapsed_time );

      // some more serial prints here

      last_time = current;

      digitalWrite( 13, led_state );
      led_state = !led_state;
   }
}

TickType_t type is defined as unsigned 16 bit. In order to see the problem let’s look at the output:

1:44:35.776 -> hooks_1.ino
21:44:35.776 -> E:65535 C:0 B:0 A:0
21:44:35.776 -> E:65535 C:2 B:0 A:2
21:44:35.776 -> E:65535 C:3 B:2 A:3
21:44:35.776 -> E:2 C:5 B:3 A:5
21:44:35.809 -> E:65535 C:8 B:5 A:8
21:44:35.809 -> E:65535 C:9 B:8 A:9
21:44:35.809 -> E:3 C:12 B:9 A:12
21:44:35.809 -> E:65535 C:13 B:12 A:13
21:44:35.809 -> E:65535 C:15 B:13 A:15
21:44:35.809 -> E:65535 C:19 B:15 A:19

‘E’ stands for “elapsed time”. elapsed_time is defined as C-B (current minus last_time). As the second line shows, 0 minus 0 → 65535, What the heck!

2-0=65535
3-2=65535
5-3=65535
12-9=3 ← Weee, it was ok!
19-15=65535
Etc

As per this error the line

if( 500 < elapsed_time ){...}

always evaluates to TRUE.

The assembly output of that arithmetic operation is:

 TickType_t elapsed_time = current - last_time;
     c4e:	80 91 74 01 	lds	r24, 0x0174	; 0x800174 <_ZL9last_time>
     c52:	90 91 75 01 	lds	r25, 0x0175	; 0x800175 <_ZL9last_time+0x1>
     c56:	45 01       	movw	r8, r10
     c58:	88 1a       	sub	r8, r24
     c5a:	99 0a       	sbc	r9, r25

Register r10 is holding the current variable value.

By the way, after some seconds the program resets itself.

Are you aware of a compiler bug? Thank you!

Move the assignment of last_time out of the conditional block.

500 is less than 65535 so, yes, it’s true. Not sure why but I don’t think last_time is valid and the 16 bit values are not being handled atomically.

Hi!

But that 65535 is wrong! In normal conditions the elapsed_time variable should be in the range [0, 500). There’s no way to explain how the expression 0 - 0 yields 65535!

I can’t. last_time must be updated every 500ms (assuming the function is called every 1ms).

This statatement is equivalent to:

if( elapsed_time > 500 ) {...}

Let’s make it more concrete:

if( 500 < 0 ) FALSE
if( 500 < 1 ) FALSE
if( 500 < 2 ) FALSE
...
if( 500 < 500 ) FALSE
if( 500 < 501 ) TRUE 

Any more ideas?

I assure you that subtraction is not broken.

You haven’t posted enough of the code to detect the actual problem.

1 Like

Thank you to @WattsThat for the hint. It is an atomicity problem. It works when the dispatcher and interrupts are disabled. This is the code that works:

void vApplicationIdleHook()
{
   static bool led_state = false;

   TickType_t current;
   TickType_t elapsed_time;

   taskENTER_CRITICAL();
   {
      current = xTaskGetTickCount(); 
      elapsed_time = current - last_time;

      if( elapsed_time >= 500 ){

         Serial.print( "E:" );
         Serial.print( elapsed_time );

         // more serial outputs ...

         last_time = current;

         digitalWrite( 13, led_state );
         led_state = !led_state;
      }
   }
   taskEXIT_CRITICAL();
}

The conditional must be inside the critical section too; otherwise it won’t work (the same atomicity problem).

The output is this (the message “Ok” is emmited by a task every 497ms):

00:15:06.365 -> E:500 C:5284 B:4784 A:5284
00:15:06.464 -> Ok
00:15:06.862 -> E:500 C:5784 B:5284 A:5784
00:15:06.961 -> Ok
00:15:07.392 -> E:500 C:6284 B:5784 A:6284
00:15:07.459 -> Ok
00:15:07.890 -> E:500 C:6784 B:6284 A:6784
00:15:07.989 -> Ok
00:15:08.420 -> E:500 C:7284 B:6784 A:7284
00:15:08.486 -> Ok
00:15:08.917 -> E:500 C:7784 B:7284 A:7784
00:15:09.017 -> Ok
00:15:09.448 -> E:500 C:8284 B:7784 A:8284

What did I learn? Never push 8 bits microcontrollers beyond its limits.

Hi, the code is quite large. I found that the root problem is because of using 16 bit op’s in a 8 bit architecture, running with enabled interrupts. The substraction is not performed atomically, but when I set a critical section, then the program works as expected. You can see the post marked as the solution.

Thank you!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.