我需要编写一个函数,它使用一个查找表来测量温度传感器模拟输入的ADC值,并通过“插值”-线性近似找到给定ADC值的温度。
我已经创建了一个函数并为它编写了一些测试用例,我想知道你们是否有什么可以改进代码的建议,因为这应该是一个嵌入式uc,可能是stm32。
我正在张贴我的代码,并附上我的C文件,它将编译和运行。
如果您有任何改进意见/建议,请告诉我。
我还想了解一下从uint32到float的转换,如果这是一种有效的编码方式的话。

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define TEMP_ADC_TABLE_SIZE 15

typedef struct
{
 int8_t temp;
 uint16_t ADC;
}Temp_ADC_t;

const Temp_ADC_t temp_ADC[TEMP_ADC_TABLE_SIZE] =
{
    {-40,880}, {-30,750},
    {-20,680}, {-10,595},
    {0,500}, {10,450},
    {20,410}, {30,396},
    {40,390}, {50,386},
    {60,375}, {70,360},
    {80,340}, {90,325},
    {100,310}
};

// This function finds the indices between which the input reading lies.
// It uses an algorithm that doesn't need to loop through all the values in the
// table but instead it keeps dividing the table in two half until it finds
// the indices between which the value is or the exact index.
//
// index_low, index_high, are set to the indices if a value is between sample
// points, otherwise if there is an exact match then index_mid is set.
//
// Returns 0 on error, 1 if indices found, 2 if exact index is found.
uint8_t find_indices(uint16_t ADC_reading,
                    const Temp_ADC_t table[],
                    int8_t dir,
                    uint16_t* index_low,
                    uint16_t* index_high,
                    uint16_t* index_mid,
                    uint16_t table_size)
{
    uint8_t found = 0;
    uint16_t mid, low, high;
    low = 0;
    high = table_size - 1;

    if((table != NULL) && (table_size > 0) && (index_low != NULL) &&
       (index_mid != NULL) && (index_high != NULL))
    {
        while(found == 0)
        {
            mid = (low + high) / 2;

            if(table[mid].ADC == ADC_reading)
            {
                // exact match
                found = 2;
            }
            else if(table[mid].ADC < ADC_reading)
            {
                if(table[mid + dir].ADC == ADC_reading)
                {
                    // exact match
                    found = 2;
                    mid = mid + dir;
                }
                else if(table[mid + dir].ADC > ADC_reading)
                {
                    // found the two indices
                    found = 1;
                    low = (dir == 1)? mid : (mid + dir);
                    high = (dir == 1)? (mid + dir) : mid;
                }
                else if(table[mid + dir].ADC < ADC_reading)
                {
                    low = (dir == 1)? (mid + dir) : low;
                    high = (dir == 1) ? high : (mid + dir);
                }
            }
            else if(table[mid].ADC > ADC_reading)
            {
                if(table[mid - dir].ADC == ADC_reading)
                {
                    // exact match
                    found = 2;
                    mid = mid - dir;
                }
                else if(table[mid - dir].ADC < ADC_reading)
                {
                    // found the two indices
                    found = 1;
                    low = (dir == 1)? (mid - dir) : mid;
                    high = (dir == 1)? mid : (mid - dir);
                }
                else if(table[mid - dir].ADC > ADC_reading)
                {
                    low = (dir == 1)? low : (mid - dir);
                    high = (dir == 1) ? (mid - dir) : high;
                }
            }
        }
        *index_low = low;
        *index_high = high;
        *index_mid = mid;
    }

    return found;
}

// This function uses the lookup table provided as an input argument to find the
// temperature for a ADC value using linear approximation.
//
// Temperature value is set using the temp pointer.
//
// Return 0 if an error occured, 1 if an approximate result is calculate, 2
// if the sample value match is found.

uint8_t lookup_temp(uint16_t ADC_reading, const Temp_ADC_t table[],
                    uint16_t table_size ,int8_t* temp)
{
    uint16_t mid, low, high;
    int8_t dir;
    uint8_t return_code = 1;
    float gradient, offset;

    low = 0;
    high = table_size - 1;

    if((table != NULL) && (temp != NULL) && (table_size > 0))
    {
        // Check if ADC_reading is out of bound and find if values are
        // increasing or decreasing along the table.
        if(table[low].ADC < table[high].ADC)
        {
            if(table[low].ADC > ADC_reading)
            {
                return_code = 0;
            }
            else if(table[high].ADC < ADC_reading)
            {
                return_code = 0;
            }
            dir = 1;
        }
        else
        {
            if(table[low].ADC < ADC_reading)
            {
                return_code = 0;
            }
            else if(table[high].ADC > ADC_reading)
            {
                return_code = 0;
            }
            dir = -1;
        }
    }
    else
    {
        return_code = 0;
    }

    // determine the temperature by interpolating
    if(return_code > 0)
    {
        return_code = find_indices(ADC_reading, table, dir, &low, &high, &mid,
                                   table_size);

        if(return_code == 2)
        {
            *temp = table[mid].temp;
        }
        else if(return_code == 1)
        {
            gradient = ((float)(table[high].temp - table[low].temp)) /
                       ((float)(table[high].ADC - table[low].ADC));
            offset = (float)table[low].temp - gradient * table[low].ADC;
            *temp = (int8_t)(gradient * ADC_reading + offset);
        }
    }

    return return_code;
}



int main(int argc, char *argv[])
{
  int8_t temp = 0;
  uint8_t x = 0;
  uint16_t u = 0;
  uint8_t return_code = 0;
  uint8_t i;

  //Print Table
  printf("Lookup Table:\n");
  for(i = 0; i < TEMP_ADC_TABLE_SIZE; i++)
  {
      printf("%d,%d\n", temp_ADC[i].temp, temp_ADC[i].ADC);
  }

  // Test case 1
  printf("Test case 1: Find the temperature for ADC Reading of 317\n");
  printf("Temperature should be 95 Return Code should be 1\n");
  return_code = lookup_temp(317, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Temperature: %d C\n", temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 2
  printf("Test case 2: Find the temperature for ADC Reading of 595 (sample value)\n");
  printf("Temperature should be -10, Return Code should be 2\n");
  return_code = lookup_temp(595, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Temperature: %d C\n", temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 3
  printf("Test case 3: Find the temperature for ADC Reading of 900 (out of bound - lower)\n");
  printf("Return Code should be 0\n");
  return_code = lookup_temp(900, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 4
  printf("Test case 4: Find the temperature for ADC Reading of 300 (out of bound - Upper)\n");
  printf("Return Code should be 0\n");
  return_code = lookup_temp(300, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 5
  printf("Test case 5: NULL pointer (Table pointer) handling\n");
  printf("Return Code should be 0\n");
  return_code = lookup_temp(595, NULL, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 6
  printf("Test case 6: NULL pointer (temperature result pointer) handling\n");
  printf("Return Code should be 0\n");
  return_code = lookup_temp(595, temp_ADC, TEMP_ADC_TABLE_SIZE, NULL);
  printf("Return code: %d\n", return_code);

  // Test case 7
  printf("Test case 7: Find the temperature for ADC Reading of 620\n");
  printf("Temperature should be -14 Return Code should be 1\n");
  return_code = lookup_temp(630, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Temperature: %d C\n", temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 8
  printf("Test case 8: Find the temperature for ADC Reading of 880 (First table element test)\n");
  printf("Temperature should be -40 Return Code should be 2\n");
  return_code = lookup_temp(880, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Temperature: %d C\n", temp);
  printf("Return code: %d\n\n", return_code);

  // Test case 9
  printf("Test case 9: Find the temperature for ADC Reading of 310 (Last table element test)\n");
  printf("Temperature should be 100 Return Code should be 2\n");
  return_code = lookup_temp(310, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
  printf("Temperature: %d C\n", temp);
  printf("Return code: %d\n\n", return_code);

  printf("Press ENTER to continue...\n");
  getchar();
  return 0;
}

最佳答案

我通常离线计算查找表,运行时代码可以归结为:
温度=表[dac_值];
特别是如果要嵌入,你不需要浮点,往往不需要它。预先计算表也解决了这个问题。
预计算也解决了有一个高效算法的问题,你可以像你想的那样草率和缓慢,你只需要很少做这个计算。没有任何算法能够在运行时与查找表竞争。只要你有足够的空间去看桌子,这是双赢的。例如,如果8位DAC的PROM中没有256个位置,则可能有128个位置,并且可以进行一些实时插值:
//todo为max dac_value和max dac_value-1添加特殊情况,或使表129项变深
if(dac_值&1)
{
温度=(表[(DAC值>>1)+0]+表[(DAC值>>1)+1])>>1;
}
其他的
{
温度=表[DAC_值>>1];
}
我经常发现被喂进去的桌子会变的。你的计算可能是一成不变的,但这种计算是用经过校准的设备来实现的。您已经做了正确的事情,检查数据是否在正确的一般方向上(相对于dac增加而减少,或者相对于dac值增加而增加),更重要的是检查除以零。尽管是硬编码表,但要养成习惯,期望它会变为不同的硬编码表,希望不必每次都更改插值代码。
我也相信原始的dac值是这里最重要的值,计算出的温度可以随时发生。即使转换成某种味道的度数是用石头做的,显示或存储原始DAC值和计算的温度也是一个好主意。您始终可以从DAC值重新计算温度,但不能始终从计算值准确地再现原始DAC值。这取决于你的建筑自然,如果这是一个公共使用的恒温器在他们的家里,他们不想有一些十六进制值的显示器。但是,如果这是任何一种测试或工程环境,您在其中收集数据,以便以后分析或验证某个产品是好是坏,随身携带DAC值可能是一件好事。如果提供给您表格的工程师声称是最终表格,然后更改表格,则只需要一两次。现在,您必须返回到使用不正确表的所有日志,使用上一个表计算回DAC值,并使用新表重新计算临时值,然后编写一个新的日志文件。如果您在那里有原始的dac值,并且每个人都接受了dac值方面的培训,并且温度只是一个参考,那么您可能不必为每个新的校准表修复旧的日志值。最坏的情况是日志文件中只有温度,无法确定该日志文件使用了哪个cal表,日志文件无效,被测单元成为风险项等。

09-07 09:59