Search code examples
c++cembeddedmicrocontrolleriar

Defining integer in C resolves to unsigned char, or produces unexpected behavior in case of math operations


I am working on a micro-controller and I want to implement a simple average filter on the resulted values to filter out noise (or to be honest to not let values dance on LCD!).

The ADC result is inserted into memory by DMA. I have (just for sake of ease of debugging) an array with size 8. To make life even easier I have wrote some defines to make my code editable with minimum effort:

#define FP_ID_POT_0             0  //identifier for POT_0
#define FP_ID_POT_1             1  //identifier for POT_1
#define FP_ANALOGS_BUFFER_SIZE  8 //buffer size for filtering ADC vals
#define FP_ANALOGS_COUNT        2  // we have now 2 analog axis
#define FP_FILTER_ELEMENT_COUNT  FP_ANALOGS_BUFFER_SIZE / FP_ANALOGS_COUNT
//means that the DMA buffer will have 4 results for each ADC

So, the buffer has size of 8 and its type is uint32_t. And I am reading 2 ADC channels. in the buffer I will have 4 result for Channel A and 4 result for Channel B (in a circular manner). A simple dump of this array is like:

INDEX    0      1      2       3     4      5      6    7
CHNL     A      B      A       B     A      B      A    B
VALUE   4017    62    4032    67    4035    64    4029  63  

It means that the DMA puts results for ChA and ChB always in a fixed place.

Now to calculate the average for each channel I have the function below:

uint32_t filter_pots(uint8_t which) {

  uint32_t sum = 0;
  uint8_t i = which;

  for( ; i < FP_ANALOGS_BUFFER_SIZE; i += FP_ANALOGS_COUNT) {
    sum += adc_vals[i];
  }

  return sum / (uint32_t)FP_FILTER_ELEMENT_COUNT;
}

If I want to use the function for chA I will pass 0 as argument to the funtion. If I want chB I pass 1...and if I happen to have chC I will pass 2 and so on. This way I can initiate the for-loop to point to the element that I need.

The problem is, at the last step when I want to return the result, I do not get the correct value. The function returns 1007 when used for chA and 16 when used for chB. I am pretty sure that the sum is calculated OK (I can see it in debugger). The problem, I beleive, is in the division by a value defined using #define. Even casting it to uint32_t does not help. The sum is being calculated OK, but I can no see what type or value FP_FILTER_ELEMENT_COUNT has been assigned to by compiler. Mybe its an overflow problem of dividing uint32 by uint8?

#define FP_FILTER_ELEMENT_COUNT  FP_ANALOGS_BUFFER_SIZE / FP_ANALOGS_COUNT
//means FP_FILTER_ELEMENT_COUNT  will be 8 / 2 which results in 4

What causes this behaviour and if there is no way that #define would work in my case, what other options I have?

The compiler is IAR Embedded Workbench. Platform is STM32F103


Solution

  • For fewer surprises, always put parenthesis around your macro definitions

    #define FP_FILTER_ELEMENT_COUNT (FP_ANALOGS_BUFFER_SIZE / FP_ANALOGS_COUNT)

    This prevents oddball operator precedence issues and other unexpected syntax and logic errors from cropping up. In this case, you're returning sum/8/2 (i.e. sum/16) when you want to return sum/4.