嵌入式C代码审查

all*_*nnn 4 c

我需要编写一个函数,该函数使用查找表来获取温度传感器模拟输入的ADC值,并通过"插值" - 线性近似来找出给定ADC值的温度.我已经创建了一个函数并为它编写了一些测试用例,我想知道是否有一些你可以建议改进代码的东西,因为这应该是嵌入式uC,可能是stm32.

我发布我的代码并附加我的C文件,它将编译并运行.

如果您有任何改进意见/建议,请与我们联系.

我还想知道一些关于从uint32_t到我正在做的浮动的转换,如果它是有效的代码方式.

#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;
}
Run Code Online (Sandbox Code Playgroud)

old*_*mer 5

我通常离线计算查找表,运行时代码归结为:

temp = table[dac_value];

特别是如果嵌入式,你不想要浮点,通常不需要它.预先计算表也解决了这个问题.

预计算也解决了拥有高效算法的问题,你可以像你想要的那样草率和慢,你只需要很少进行这种计算.没有算法能够在运行时与查找表竞争.只要你有查询表的空间,这是双赢的.如果你没有在舞台上为256位dac说256个位置,例如你可能有128个位置,你可以做一些实时插值:

//TODO add special case for max dac_value and max dac_value-1 or make the table 129 entries deep
if(dac_value&1)
{
  temp=(table[(dac_value>>1)+0]+table[(dac_value>>1)+1])>>1;
}
else
{
   temp=table[dac_value>>1];
}

我经常发现被喂食的餐桌可以并且会改变.你的可能是一成不变的,但同样的计算是通过校准设备实现的.而且你通过检查数据是否在正确的总体方向上做了正确的事情(相对于dac相对于dac值增加或增加而减少),更重要的是检查除以零.尽管是一个硬编码表开发习惯,期望它会变成一个不同的硬编码表,希望不必每次都改变你的插值代码.

我也相信原始dac值是这里最重要的值,计算出的温度可以随时发生.即使转换为某种味道的程度已经一成不变,最好显示或存储原始dac值以及计算出的温度.您始终可以从dac值重新计算温度,但不能始终准确地从计算值中重现原始dac值.这取决于你自然建造的是什么,如果这是他们家中公共使用的恒温器,他们不希望在显示器上有一些十六进制值.但是,如果这是任何类型的测试或工程环境,您正在收集数据以供以后分析或验证某些产品的好坏,那么携带该dac值可能是一件好事.对于为您提供表格的工程师而言,只需要一次或两次,声称它是最终表格然后更改它.现在,您必须返回使用不正确表的所有日志,使用先前表计算回dac值,并使用新表重新计算临时值并写入新的日志文件.如果您在那里有原始dac值,并且每个人都接受过培训,可以根据dac值进行思考,并且温度只是一个参考,那么您可能不必修复每个新校准表的旧日志值.最糟糕的情况是只有日志文件中的温度,并且无法确定哪个校准表用于该日志文件,日志文件变为无效,测试的单元成为风险项目等.现在,您必须返回使用不正确表的所有日志,使用先前表计算回dac值,并使用新表重新计算临时值并写入新的日志文件.如果您在那里有原始dac值,并且每个人都接受过培训,可以根据dac值进行思考,并且温度只是一个参考,那么您可能不必修复每个新校准表的旧日志值.最糟糕的情况是只有日志文件中的温度,并且无法确定哪个校准表用于该日志文件,日志文件变为无效,测试的单元成为风险项目等.现在,您必须返回使用不正确表的所有日志,使用先前表计算回dac值,并使用新表重新计算临时值并写入新的日志文件.如果您在那里有原始dac值,并且每个人都接受过培训,可以根据dac值进行思考,并且温度只是一个参考,那么您可能不必修复每个新校准表的旧日志值.最糟糕的情况是只有日志文件中的温度,并且无法确定哪个校准表用于该日志文件,日志文件变为无效,测试的单元成为风险项目等.如果您在那里有原始dac值,并且每个人都接受过培训,可以根据dac值进行思考,并且温度只是一个参考,那么您可能不必修复每个新校准表的旧日志值.最糟糕的情况是只有日志文件中的温度,并且无法确定哪个校准表用于该日志文件,日志文件变为无效,测试的单元成为风险项目等.如果您在那里有原始dac值,并且每个人都接受过培训,可以根据dac值进行思考,并且温度只是一个参考,那么您可能不必修复每个新校准表的旧日志值.最糟糕的情况是只有日志文件中的温度,并且无法确定哪个校准表用于该日志文件,日志文件变为无效,测试的单元成为风险项目等.