从你提供的代码来看,问题可能出在以下代码行:
```c
fVin = (adcvalue * VCC_VALUE_MV * (390 + 110000 + 33000.0)) / 33000.0 / 4096.0; //todo: check reference voltage //MV
```
执行这段代码后直接进入了 FPU(浮点单元)中断处理程序,通常是因为浮点运算中出现了非法操作或异常。以下是可能的原因及解决方法:
---
### 1. **浮点运算异常**
- **问题**:浮点运算中可能出现非法操作,例如:
- 除以零。
- 结果超出浮点数的表示范围(溢出)。
- 结果为非数字(NaN,Not a Number)。
- **分析**:
- `33000.0` 是浮点数,`adcvalue` 和 `VCC_VALUE_MV` 是整数。
- 如果 `adcvalue` 或 `VCC_VALUE_MV` 的值异常(例如 `adcvalue` 超出 `ADC_MAX_VALUE` 或为负值),可能导致非法浮点运算。
- 如果 `33000.0` 或 `4096.0` 为零(理论上不可能,但需要确认),会导致除以零异常。
- **解决方法**:
在计算前添加检查,确保所有参与运算的值都在合理范围内。例如:
```c
if (adcvalue > ADC_MAX_VALUE || adcvalue < 0 || VCC_VALUE_MV <= 0)
{
// 错误处理
return;
}
```
---
### 2. **浮点硬件未正确初始化**
- **问题**:如果你的 MCU 使用了硬件浮点单元(FPU),但在系统初始化时未正确启用 FPU,任何浮点运算都会触发硬件异常。
- **解决方法**:
确保在系统启动时正确初始化 FPU。例如,在 Cortex-M 系列 MCU 中,通常需要在启动代码中启用 FPU:
```c
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); // 启用 FPU
```
如果你使用的是裸机开发,请检查启动文件(`startup_xxx.s`)是否包含 FPU 初始化代码。如果使用的是 RTOS(如 FreeRTOS),请确保在配置中启用了 FPU 支持。
---
### 3. **浮点数精度问题**
- **问题**:浮点数运算可能因为精度问题导致意外的结果。例如,`33000.0` 和 `4096.0` 的运算可能因为精度丢失导致结果不准确。
- **解决方法**:
- 确保所有常量和变量的类型一致。例如,将 `390` 和 `110000` 显式转换为浮点数:
```c
fVin = (adcvalue * VCC_VALUE_MV * (390.0 + 110000.0 + 33000.0)) / 33000.0 / 4096.0;
```
---
### 4. **非法内存访问**
- **问题**:如果 `fVin` 或其他变量的内存地址非法(例如未正确初始化或被意外覆盖),可能导致异常。
- **解决方法**:
- 确保 `fVin` 和其他变量已正确声明并初始化。
- 检查是否有其他代码意外修改了这些变量的内存。
---
### 5. **调试方法**
- **启用浮点异常捕获**:
在调试环境中启用浮点异常捕获,查看具体的异常类型(如除以零、溢出或 NaN)。
- **打印调试信息**:
在计算前打印所有参与运算的变量值,确认它们是否在预期范围内:
```c
printf("adcvalue: %d, VCC_VALUE_MV: %d\n", adcvalue, VCC_VALUE_MV);
```
---
### 总结
最可能的原因是浮点运算中出现了非法操作(如除以零或溢出),或者 FPU 未正确初始化。建议按照以下步骤排查问题:
1. 检查 `adcvalue` 和 `VCC_VALUE_MV` 的值是否在合理范围内。
2. 确保 FPU 在系统启动时已正确初始化。
3. 确保所有常量和变量的类型一致,避免隐式类型转换问题。
4. 在调试环境中启用浮点异常捕获,定位具体的异常原因。
最后发现是计算温度是除数里有ADC_MAX_VALUE-adcvalue操作。导致当sensor不接时,两者相等出现除0操作,进而触发FPU中断死机。(假如不再FPU 加while 1循环,则会在下一次的循环中触发hardfault中断)
static unsigned char ADC_2_NTC3K_Rin(uint16_t adcvalue, float *pResistanceVal)
{
float fResistance;
if (pResistanceVal == NULL)
{
return 0;
}
if (adcvalue >= ADC_MAX_VALUE || adcvalue < 0)
{
*pResistanceVal = -20.0;
return 0;
}
fResistance = (adcvalue * 3000.0);
fResistance = fResistance / (ADC_MAX_VALUE - adcvalue); //4096-adc)/3000 = adc / fresistance;
*pResistanceVal = fResistance;
return 1;
}
另外一例触发FPU中断的例子:
当NTC值为-20的时候,会触发FPU中断死机。
float NTC_val;
temp_val = (uint16_t)(NTC_val * 10.0);
memcpy(&temp[0], &temp_val, sizeof(uint16_t));
问题分析 在代码中,以下部分可能导致问题:
1. 问题原因:浮点数到无符号整数的转换
• NTC_val 是一个 float 类型的变量,当其值为负数(如 -20)时,乘以 10.0 的结果为 -200.0。 • 将 -200.0 转换为 uint16_t 类型时,会发生未定义行为,因为 uint16_t 是无符号整数,无法表示负值。 • 这种非法转换可能触发 FPU 异常,导致进入 FPU 中断。
2. FPU 中断的触发条件
• Cortex-M 系列的 FPU(浮点单元)可能会在以下情况下触发中断:
◦ 浮点运算结果为 NaN(非数字)。
◦ 浮点运算结果超出表示范围(溢出)。
◦ 非法的浮点数到整数的转换。
修复方法 为了避免进入 FPU 中断,需要在进行浮点数到整数的转换之前,确保值在合法范围内。以下是修复方法:
方法 1:
检查 NTC_val 的值范围 在进行浮点数到整数的转换之前,检查 NTC_val 是否为负数。如果是负数,可以将其限制为 0 或其他合理的值。
if (NTC_val < 0)
{
temp_val = 0; // 将负值限制为 0
}
else
{
temp_val = (uint16_t)(NTC_val * 10.0);
}
memcpy(&temp[0], &temp_val, sizeof(uint16_t));
方法 2:
使用有符号整数类型 如果需要保留负值,可以将 temp_val 的类型改为 int16_t,以支持负数的表示。 同时,确保接收端能够正确解析有符号整数。
int16_t temp_val = (int16_t)(NTC_val * 10.0);
memcpy(&temp[0], &temp_val, sizeof(int16_t));
方法 3:
启用浮点异常捕获(调试用) 在调试阶段,可以启用浮点异常捕获,以便更清楚地了解问题的根源。以下代码可以在系统初始化时启用 FPU 异常捕获: 这将帮助你捕获浮点异常并定位问题。
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); // 启用 FPU
FPU->FPCCR |= (1 << 4); // 启用浮点异常捕获
方法 4:限制浮点数的范围 如果 NTC_val 的值可能超出预期范围,可以在计算之前对其进行限制。
例如:
float clamped_NTC_val = NTC_val;
if (clamped_NTC_val < -100.0)
{
clamped_NTC_val = -100.0; // 限制最小值
}
else if (clamped_NTC_val > 100.0)
{
clamped_NTC_val = 100.0; // 限制最大值
}
temp_val = (uint16_t)(clamped_NTC_val * 10.0);
memcpy(&temp[0], &temp_val, sizeof(uint16_t));
推荐使用 方法 2,将 temp_val 的类型改为 int16_t,以支持负值的表示。这是最直接的修复方法,且不会丢失负值信息。
修改后的代码如下:
int16_t temp_val = (int16_t)(NTC_val * 10.0);
memcpy(&temp[0], &temp_val, sizeof(int16_t));
同时,确保接收端能够正确解析有符号整数。
总结
• 问题原因:NTC_val 为负数时,浮点数到无符号整数的转换导致非法操作,触发 FPU 中断。
• 修复方法: 1. 检查并限制 NTC_val 的值范围。 2. 使用有符号整数类型(推荐)。 3. 启用浮点异常捕获(调试用)。 4. 限制浮点数的范围。