声明:学习笔记来自正点原子B站教程,仅供学习交流!!
输出电压 = (6*0.02Ω*I)+ 1.27
I = (旋转时电压 - 偏置电压)/0.12A
VBUS = POWER / (12 +12 + 1)
POWER = VBUS*25
1.3 温度采集原理
VTEMP = 4.7 * 3.3 / (4.7 + 热敏电阻Rt)
Rt = Rp *exp(B*(1/T1-1/T2))
注意: Rt 是热敏电阻在 T1 温度下的阻值; Rp 是热敏电阻在 T2 常温下的标称阻值;
exp 是 e 的 n 次方, e 是自然常数,就是自然对数的底数,近似等于 2.7182818; B 值是热敏电阻的重要参数,教程中用到的热敏电阻 B 值为 3380;
这里 T1 和 T2 指的是开尔文温度, T2 是常温 25℃,即(273.15+25)K
T1 就是所求的温度
为了是曲线更加平滑,往往需要使用到滤波算法
参考:https://blog.csdn.net/qq_46336588/article/details/120272707
方法:取a=0~1,本次滤波结果=(1-a)*本次采样值+a*上次滤波结果
优点:对周期性干扰具有良好的抑制作用适用于波动频率较高的场合
缺点:相位滞后,灵敏度低滞后程度取决于a值大小不能消除滤波频率高于采样频率的1/2的干扰信号*/
方法:连续采样N次(N取奇数)把N次采样值按大小排列取中间值为本次有效值
优点:能有效克服因偶然因素引起的波动干扰;对温度、液位等变化缓慢的被测参数有良好的滤波效果
缺点:对流量,速度等快速变化的参数不宜*/
方法:连续取N个采样值进行算术平均运算;
N值较大时:信号平滑度较高,但灵敏度较低
N值较小时:信号平滑度较低,但灵敏度较高
N值的选取:一般流量,N=12;压力:N=4
优点:试用于对一般具有随机干扰的信号进行滤波。
这种信号的特点是有一个平均值,信号在某一数值范围附近上下波动。
缺点:测量速度较慢或要求数据计算较快的实时控制不适用。
方法:把连续取N个采样值看成一个队列,队列的长度固定为N
每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据(先进先出原则)
把队列中的N个数据进行算术平均运算,就可获得新的滤波结果
N值的选取:流量,N=12;压力:N=4;液面,N=4-12;温度,N=1~4
优点:对周期性干扰有良好的抑制作用,平滑度高;试用于高频振荡的系统
缺点:灵敏度低;对偶然出现的脉冲性干扰的抑制作用较差,不适于脉冲干扰较严重的场合
比较浪费RAM(改进方法,减去的不是队首的值,而是上一次得到的平均值)
方法:相当于“中位值滤波法”+“算术平均滤波法”
连续采样N个数据,去掉一个最大值和一个最小值然后计算N-2个数据的算术平均值
N值的选取:3~14
优点:融合了两种滤波的优点。对于偶然出现的脉冲性干扰,可消除有其引起的
采样值偏差。对周期干扰有良好的抑制作用,平滑度高,适于高频振荡的系统。
缺点:测量速度慢。
优点:对于偶然出现的脉冲性干扰,可消除由其引起的采样值偏差。
对周期性干扰有良好的抑制作用,平滑度高;
试用于高频振荡的系统。
缺点:测量速度慢
取最近的10个值,去掉最大最小值求平均
队列queue中,第0个值换成新值,其余值依次往后移一个位置
方法:相当于“限幅滤波法”+“递推平均滤波法”
每次采样到的新数据先进行限幅处理再送入队列进行递推平均滤波处理
优点:对于偶然出现的脉冲性干扰,可消除有其引起的采样值偏差。
缺点:比较浪费RAM
方法:是对递推平均滤波法的改进,即不同时刻的数据加以不同的权;
通常是,越接近现时刻的数值,权取得越大;
给予新采样值的权系数越大,则灵敏度越高,但信号平滑度越低。
优点:适用于有较大纯滞后时间常数的对象和采样周期较短的系统
缺点:对于纯滞后时间常数较小,采样周期较长,变化缓慢的信号不能迅速反应系统当前所受干扰的严重程度,滤波效果差
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
if(hadc->Instance == ADC1)
{
calc_motor_var(adc_raw_value);
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_raw_value, 3);
}
}
void calc_motor_var(uint16_t adc[])
{
static uint32_t value[3] = {0};
static uint32_t adc_count = 0;
value[0] += adc[0];
value[1] += adc[1];
value[2] += adc[2];
adc_count++;
if(adc_count == ADC_COLL)
{
value[0] /= ADC_COLL;
value[1] /= ADC_COLL;
value[2] /= ADC_COLL;
dc_motor.temp = get_motor_temp(value[0]);
dc_motor.vol = get_motor_vol(value[2]);
{
static uint16_t curt_array[CURT_FILTER_COUNT] = {0};
static uint16_t array_number = 0;
curt_array[array_number++] = value[1];
if(array_number == CURT_FILTER_COUNT)
{
uint16_t temp = 0;
uint32_t sum = 0;
for(int i = 0; i < CURT_FILTER_COUNT; i++)
{
for(int j = 0; j < CURT_FILTER_COUNT - i - 1; j++ )
{
if(curt_array[j] > curt_array[j + 1])
{
temp = curt_array[j];
curt_array[j] = curt_array[j + 1];
curt_array[j + 1] = temp;
}
}
}
for(int i = 1; i < CURT_FILTER_COUNT - 1; i++)
{
sum += curt_array[i];
}
array_number = 0;
dc_motor.curt = get_motor_curt(sum/(CURT_FILTER_COUNT - 2));
}
}
adc_count = 0;
memset(value, 0, sizeof(value));
}
}
/**
* @brief 计算温度值
* @param para: 温度采集对应 ADC 通道的值(已滤波)
* @note 计算温度分为两步:
1.根据 ADC 采集到的值计算当前对应的 Rt
2.根据 Rt 计算对应的温度值
* @retval 温度值
*/
float get_motor_temp(uint32_t para)
{
float Rt;
float temp;
/*
第一步:
Rt = 3.3 * 4700 / VTEMP - 4700 ,其中 VTEMP 就是温度检测通道采集回来的电压
值,VTEMP = ADC 值* 3.3/4096
由此我们可以计算出当前 Rt 的值: Rt = 3.3f * 4700.0f / (para * 3.3f /
4096.0f ) - 4700.0f;
*/
Rt = 3.3f * 4700.0f / (para * 3.3f / 4096.0f ) - 4700.0f;
/*
第二步:
根据当前 Rt 的值来计算对应温度值: Rt = Rp *exp(B*(1/T1-1/T2))
*/
temp = Rt / Rp; /* 解出 exp(B*(1/T1-1/T2)) ,即 temp = exp(B*(1/T1-1/T2)) */
temp = log(temp); /* 解出 B*(1/T1-1/T2) ,即 temp = B*(1/T1-1/T2) */
temp /= Bx; /* 解出 1/T1-1/T2 ,即 temp = 1/T1-1/T2 */
temp += (1.0f / T2); /* 解出 1/T1 ,即 temp = 1/T1 */
temp = 1.0f / (temp); /* 解出 T1 ,即 temp = T1 */
temp -= Ka; /* 计算 T1 对应的摄氏度 */
return temp; /* 返回温度值 */
}
float get_motor_curt(uint32_t para)
{
static uint32_t curt_array[CURT_FILTER_COUNT] = {0};
static int curt_number = 0;
float sum = 0.0f;
if(curr_initial_value < 0.1)
{
curr_initial_value = para;
return 0.0f;
}
else
{
if(curt_number < CURT_FILTER_COUNT) curt_number++;
for(int i = CURT_FILTER_COUNT - 1; i > 0; i--)
{
curt_array[i] = curt_array[i - 1];
sum += curt_array[i - 1];
}
curt_array[0] = para;
sum += para;
if(sum < CURT_FILTER_COUNT) return abs((int)(para - curr_initial_value))*ADC2CURT;
}
return (abs((int)((sum/CURT_FILTER_COUNT) - curr_initial_value)))*ADC2CURT;
}
/* 电压计算公式:
* V_POWER = V_BUS * 25
* ADC 值转换为电压值:电压=ADC 值*3.3/4096
* 整合公式可以得出电压 V_POWER= ADC 值 *(3.3f * 25 / 4096)
*/
#define ADC2VBUS (float)(3.3f * 25 / 4096)
float get_motor_vol(uint32_t para)
{
return para*ADC2VBUS;
}
在get_motor_curt函数里主要做了两件事
/* 电流计算公式:
* I=(最终输出电压-初始参考电压) /(6*0.02) A
* ADC 值转换为电压值:电压=ADC 值*3.3/4096,这里电压单位为 V,我们换算成
mV,4096/1000=4.096,后面就直接算出为 mA
* 整合公式可以得出电流 I= (当前 ADC 值-初始参考 ADC 值) * (3.3 / 4.096 / 0.12)
*/
#define ADC2CURT (float)(3.3f / 4.096f / 0.12f)
#define CURT_FILTER_COUNT 6 //电流过滤次数
float get_motor_curt(uint32_t para)
{
static uint32_t curt_array[CURT_FILTER_COUNT] = {0};
static int curt_number = 0;
float sum = 0.0f;
if(curr_initial_value < 0.1)
{
curr_initial_value = para;
return 0.0f;
}
else
{
if(curt_number < CURT_FILTER_COUNT) curt_number++;
for(int i = CURT_FILTER_COUNT - 1; i > 0; i--)
{
curt_array[i] = curt_array[i - 1];
sum += curt_array[i - 1];
}
curt_array[0] = para;
sum += para;
if(sum < CURT_FILTER_COUNT) return abs((int)(para - curr_initial_value))*ADC2CURT;
}
return (abs((int)((sum/CURT_FILTER_COUNT) - curr_initial_value)))*ADC2CURT;
}