- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我需要编写一个函数,该函数使用温度传感器模拟输入的 ADC 值查找表,并通过“插值”(线性近似)找出给定 ADC 值的温度。我已经创建了一个函数并为它编写了一些测试用例,我想知道你们是否可以建议改进代码,因为这应该是针对嵌入式 uC,可能是 stm32。
我正在发布我的代码并附上我的 C 文件,它将编译并运行。
如果您有任何改进意见/建议,请告诉我。
我还想了解一下我正在做的从 uint32_t 到 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;
}
最佳答案
我通常离线计算查找表,运行时代码归结为:
temp = table[dac_value];
特别是如果要嵌入,你不需要 float ,通常不需要它。预先计算表格也可以解决该问题。
预计算也解决了拥有一个高效算法的问题,你可以随心所欲地马虎和慢,你只需要很少做这个计算。没有算法能够在运行时与查找表竞争。只要您有查找表的空间,这就是双赢。例如,如果您没有为 8 位 dac 在 prom 中说 256 个位置,您可能有 128 个位置,并且您可以进行一些实时插值:
//TODO add special case for max dac_value and max dac_value-1 or make the table 129 entries deepif(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 值。这取决于你自然而然地 build 什么,如果这是一个在他们家中供公众使用的恒温器,他们不希望在显示器上显示一些十六进制值。但是,如果这是您正在收集数据以供以后分析或验证某些产品好坏的任何类型的测试或工程环境,那么携带该 dac 值可能是一件好事。为您提供表格的工程师声称这是最终表格然后更改它的情况只需要一两次。现在您必须返回到所有使用不正确表的日志,使用先前的表计算回 dac 值并使用新表重新计算 temp 并写入新的日志文件。如果那里有原始 dac 值,并且每个人都接受过从 dac 值的角度思考的训练,并且温度只是一个引用,那么您可能不必为每个新校准表修复旧的日志值。最坏的情况是日志文件中只有温度,无法确定该日志文件使用了哪个校准表,日志文件变得无效单元测试成为风险项,等等。
关于c - 嵌入式 C 代码审查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3641828/
我尝试理解[c代码 -> 汇编]代码 void node::Check( data & _data1, vector& _data2) { -> push ebp -> mov ebp,esp ->
我需要在当前表单(代码)的上下文中运行文本文件中的代码。其中一项要求是让代码创建新控件并将其添加到当前窗体。 例如,在Form1.cs中: using System.Windows.Forms; ..
我有此 C++ 代码并将其转换为 C# (.net Framework 4) 代码。有没有人给我一些关于 malloc、free 和 sprintf 方法的提示? int monate = ee; d
我的网络服务器代码有问题 #include #include #include #include #include #include #include int
给定以下 html 代码,将列表中的第三个元素(即“美丽”一词)以斜体显示的 CSS 代码是什么?当然,我可以给这个元素一个 id 或一个 class,但 html 代码必须保持不变。谢谢
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
我试图制作一个宏来避免重复代码和注释。 我试过这个: #define GrowOnPage(any Page, any Component) Component.Width := Page.Surfa
我正在尝试将我的旧 C++ 代码“翻译”成头条新闻所暗示的 C# 代码。问题是我是 C# 中的新手,并不是所有的东西都像 C++ 中那样。在 C++ 中这些解决方案运行良好,但在 C# 中只是不能。我
在 Windows 10 上工作,R 语言的格式化程序似乎没有在 Visual Studio Code 中完成它的工作。我试过R support for Visual Studio Code和 R-T
我正在处理一些报告(计数),我必须获取不同参数的计数。非常简单但乏味。 一个参数的示例查询: qCountsEmployee = ( "select count(*) from %s wher
最近几天我尝试从 d00m 调试网络错误。我开始用尽想法/线索,我希望其他 SO 用户拥有可能有用的宝贵经验。我希望能够提供所有相关信息,但我个人无法控制服务器环境。 整个事情始于用户注意到我们应用程
我有一个 app.js 文件,其中包含如下 dojo amd 模式代码: require(["dojo/dom", ..], function(dom){ dom.byId('someId').i
我对“-gencode”语句中的“code=sm_X”选项有点困惑。 一个例子:NVCC 编译器选项有什么作用 -gencode arch=compute_13,code=sm_13 嵌入库中? 只有
我为我的表格使用 X-editable 框架。 但是我有一些问题。 $(document).ready(function() { $('.access').editable({
我一直在通过本教程学习 flask/python http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-wo
我想将 Vim 和 EMACS 用于 CNC、G 代码和 M 代码。 Vim 或 EMACS 是否有任何语法或模式来处理这种类型的代码? 最佳答案 一些快速搜索使我找到了 this vim 和 thi
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve this
这个问题在这里已经有了答案: Enabling markdown highlighting in Vim (5 个回答) 6年前关闭。 当我在 Vim 中编辑包含 Markdown 代码的 READM
我正在 Swift3 iOS 中开发视频应用程序。基本上我必须将视频 Assets 和音频与淡入淡出效果合并为一个并将其保存到 iPhone 画廊。为此,我使用以下方法: private func d
pipeline { agent any stages { stage('Build') { steps { e
我是一名优秀的程序员,十分优秀!