#include "spi.h" #include "APPDEF.H" #include "math.h" #include "string.h" #include "stdio.h" // AD7190寄存器地址定义 #define AD7190_REG_COMM 0x00 << 3 #define AD7190_REG_STAT 0x00 << 3 #define AD7190_REG_MODE 0x01 << 3 #define AD7190_REG_CONF 0x02 << 3 #define AD7190_REG_DATA 0x03 << 3 #define AD7190_REG_GPOC 0x05 << 3 // AD7190命令定义 #define AD7190_CMD_NOP 0xFF #define AD7190_CMD_WR 0x00 #define AD7190_CMD_RD 0x40 // AD7190配置寄存器位掩码 #define AD7190_CONF_CHOP 0x800000 #define AD7190_CONF_REFSEL2 0x100000 // AD7190配置寄存器通道选择 #define AD7190_CONF_CH7 0x008000 #define AD7190_CONF_CH6 0x004000 #define AD7190_CONF_CH5 0x002000 #define AD7190_CONF_CH4 0x001000 #define AD7190_CONF_CH3 0x000800 #define AD7190_CONF_CH2 0x000400 #define AD7190_CONF_CH1 0x000200 #define AD7190_CONF_CH0 0x000100 // AD7190配置寄存器 #define AD7190_CONF_BURNOUT 0x000080 #define AD7190_CONF_REFDET 0x000040 #define AD7190_CONF_BUF 0x000010 #define AD7190_CONF_UB 0x000008 // AD7190配置寄存器增益定义 #define AD7190_CONF_GAIN_1 0x000000 #define AD7190_CONF_GAIN_8 0x000003 #define AD7190_CONF_GAIN_16 0x000004 #define AD7190_CONF_GAIN_32 0x000005 #define AD7190_CONF_GAIN_64 0x000006 #define AD7190_CONF_GAIN_128 0x000007 // AD7190配置寄存器工作模式定义 #define AD7190_CONF_MODE_CONT 0x000000 #define AD7190_CONF_MODE_ZEROSEL 0x800000 #define AD7190_CONF_MODE_FullSEL 0xA00000 #define AD7190_CONF_MODE_FS9_0 0x0003FF // 4.7Hz #define AD7190_CONF_MODE_FS9_02 0x0001E0//10Hz #define AD7190_CONF_MODE_FS9_03 0x000096//50Hz #define AD7190_CONF_MODE_InCLK 0x080000// 4.92 MHz内部时钟 MCLK2引脚为三态 // AD7190 电桥开关 #define AD7190_GPOCON_BDPSW 0x40 void AD7190_WriteRegister(uint8_t regAddr, uint32_t regValue) { SPI1_ReadWrite(AD7190_CMD_WR | regAddr); SPI1_ReadWrite((regValue >> 16) & 0xFF); SPI1_ReadWrite((regValue >> 8) & 0xFF); SPI1_ReadWrite(regValue & 0xFF); } uint32_t AD7190_ReadRegister(uint8_t regAddr) { uint32_t regValue = 0; SPI1_ReadWrite(AD7190_CMD_RD | regAddr); regValue = SPI1_ReadWrite(0xFF); regValue = (regValue << 8) + SPI1_ReadWrite(0xFF); regValue = (regValue << 8) + SPI1_ReadWrite(0xFF); return regValue; } void AD7190_WriteRegisterOnce(uint8_t regAddr, uint8_t regValue) { SPI1_ReadWrite(AD7190_CMD_WR | regAddr); SPI1_ReadWrite(regValue); } uint8_t AD7190_ReadRegisterOnce(uint8_t regAddr) { uint32_t regValue = 0; SPI1_ReadWrite(AD7190_CMD_RD | regAddr); regValue = SPI1_ReadWrite(0xFF); return regValue; } bool AD7190_WaitForReady(uint32_t timeout) { uint32_t count = 0; while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) && (count < timeout)) { delay_us(1); count++; } if (count >= timeout) { return false; } else { return true; } } //extern uint16_t Read_Internal_Temperature(); //// AD7190函数:读取AD转换结果 //float V25 = 1.43; // 25 度时的传感器电压(mV) // float Avg_Slope = 4.3; // 传感器电压与温度之间的平均斜率(mV/°C) // // 获取 ADC 参考电压值(一般为 3.3V) // float Vref = 3.3; // 参考电压值(V) //float TemperatureIn; uint8_t ErrCount; uint16_t DifErrCount; uint32_t lastdata = 0; uint32_t UseuLBuf[20]; uint32_t UseuLBuf2[20]; uint16_t BufIndex = 0; // 计算一组数字的平均值 float calculateAverage(uint32_t arr[], int size) { double sum = 0.0; // 计算总和 int i; for (i = 0; i < size; i++) { sum += arr[i]; } // 计算平均值 double average = sum / size; return average; } void bubbleSort(uint32_t *arr, int n) { int i, j, temp; for (i = 0; i < n-1; i++) { for (j = 0; j < n-i-1; j++) { if (arr[j] > arr[j+1]) { // 交换 arr[j] 和 arr[j+1] temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } /** *********************************************************** * @brief printf函数默认打印输出到显示器,如果要输出到串口, 必须重新实现fputc函数,将输出指向串口,称为重定向 * @param * @return *********************************************************** */ int fputc(int ch, FILE *f) { while (!(READ_BIT(USART1->SR, USART_SR_TXE))) { delay_us(100); } USART1->DR = ch; return ch; } float average; float center; int ErrcountAdd = 0; int DifErrCountAdd = 0; uint32_t AD7190_ReadData( void ) { uint32_t data = 0; if(AD7190_WaitForReady(50000)) { data = AD7190_ReadRegister(AD7190_REG_DATA); if(fabs((int)data - (int)lastdata) > 100000 ) { DifErrCount ++; DifErrCountAdd++; } else { DifErrCount = 0; } lastdata = data; if((data > 8388608) || (data < 100 )) { ErrCount++; ErrcountAdd++; } else { ErrCount = 0; } } else { ErrCount++; ErrcountAdd++; } if(ErrCount > 3 || DifErrCount > 20) { uint32_t modeReg; SPI1_Configuration(); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); // 40个脉冲复位ADC osDelay(50); // 配置寄存器:通道选择、参考电压源、增益和工作模式 // 打开PSW uint8_t GPOC = AD7190_ReadRegisterOnce(AD7190_REG_GPOC); GPOC |= AD7190_GPOCON_BDPSW; AD7190_WriteRegisterOnce(AD7190_REG_GPOC, GPOC); osDelay(5); uint32_t config = AD7190_CONF_GAIN_128 | AD7190_CONF_UB | AD7190_CONF_BUF | AD7190_CONF_CH0;// 0x0000011F; AD7190_WriteRegister(AD7190_REG_CONF, config); osDelay(5); // 连续转换模式 modeReg = AD7190_CONF_MODE_FS9_0 | AD7190_CONF_MODE_InCLK; AD7190_WriteRegister(AD7190_REG_MODE, modeReg); ErrCount = 0; osDelay(500); if(AD7190_WaitForReady(50000)) { data = AD7190_ReadRegister(AD7190_REG_DATA); } } return data; } // 定义卡尔曼滤波器的参数结构体 typedef struct { double x; // 状态估计值 double P; // 状态协方差 double Q; // 过程噪声协方差 double R; // 观测噪声协方差 double K; // 卡尔曼增益 } KalmanFilter; KalmanFilter kf2; // 初始化卡尔曼滤波器 void kalman_filter_init2(KalmanFilter *kf, float initial_x, float initial_P, float process_noise, float measurement_noise) { kf->x = initial_x; kf->P = initial_P; kf->Q = process_noise; kf->R = measurement_noise; } // 更新卡尔曼滤波器状态 void kalman_filter_update2(KalmanFilter *kf, float measurement) { // 预测步骤 float x_pred = kf->x; // 预测的状态值 float P_pred = kf->P + kf->Q; // 预测的状态协方差 // 更新步骤 kf->K = P_pred / (P_pred + kf->R); // 计算卡尔曼增益 kf->x = x_pred + kf->K * (measurement - x_pred); // 更新状态估计值 kf->P = (1 - kf->K) * P_pred; // 更新状态协方差 } uint8_t FollowCount = 0; // 零点跟踪计数 uint8_t TimeCount = 0; // 蠕变时间计数 uint32_t FliterCount = 0; // 长期抑制计数 uint16_t SetZeroCount = 0; // 卸载归零计数 int32_t ADvalue2 = 0; // ADC采样原始值 double ADvalue2filter = 0.0f; // ADC采样卡尔曼滤波值 double ADvalue2filterOld = 0; // 上次ADC采样卡尔曼滤波值 double WeightData2Temp = 0.0f; // 本次ADvalue2filter计算重量值 double WeightData2TempOld; // 上次ADvalue2filter计算重量值 double WeightData2Finally = 0.0f; // 最终输出前重量值 double UninstiallRefWeight= 0.0f; // 卸载重物时的重量值 double WeightZeroOld = 0; // 上次稳定状态时零点值 struct uCalibrateWeight CalibrateWeight2; //传感器 斜率 零点 double WeightData2 = 0.0f; // 最终输出重量值 bool weightChanging = false; // 传感器稳定标志 bool weight11g = false; // 传感器稳定标志 float ZeroWeightOld; float ZeroWeightChange; float LowFecAlpha = 0.95f; // 超低频波动抑制系数,发生突变时偏向跟踪新值,相对稳定时跟踪旧值 float WeightRefStart = 0; uint8_t i; #define ChangeMax 10 #define ZeroTrackMax 0.050f #define FollowMax 0.030f #define FliterCountMax 200 #define FollowDlteaMax 0.001f #define FollowAlpha 0.9999f // 长期数据跟踪系数 void AD7190_Run(const void *p_arg) { uint8_t FilterQOld, SensorQOld, ZeroTrackOld, RuBianLiangOld, ruBianTimeOld; int8_t RubianLiang, RubianTime; float ZeroTrackTime, ZeroTrackRange; osDelay(600); ADvalue2 = AD7190_ReadData(); osDelay(200); ADvalue2 = AD7190_ReadData(); ADvalue2filterOld = ADvalue2filter = ADvalue2; kalman_filter_init2(&kf2, ADvalue2, 1.0, 0.001, 0.1); WeightZeroOld = CalibrateWeight2.WeightZero; for (;;) { if (FilterQOld != Set.FilterQ) { FilterQOld = Set.FilterQ; switch (Set.FilterQ) // 滤波器信任度 信任度越高 反应越慢数据显示越稳定 { case 0: kf2.Q = 0.1; break; case 1: kf2.Q = 0.08; break; case 2: kf2.Q = 0.06; break; case 3: kf2.Q = 0.04; break; case 4: kf2.Q = 0.02; break; case 5: kf2.Q = 0.009; break; case 6: kf2.Q = 0.007; break; case 7: kf2.Q = 0.005; break; case 8: kf2.Q = 0.003; break; case 9: kf2.Q = 0.001; break; default: break; } } if (SensorQOld != Set.SensorQ) { SensorQOld = Set.SensorQ; switch (Set.SensorQ) // 传感器信任度 信任度越高 反应越迅速,数据波动越大 显示越不稳定 { case 0: kf2.R = 0.5; break; case 1: kf2.R = 0.3; break; case 2: kf2.R = 0.1; break; case 3: kf2.R = 0.08; break; case 4: kf2.R = 0.06; break; case 5: kf2.R = 0.04; break; case 6: kf2.R = 0.02; break; case 7: kf2.R = 0.009; break; case 8: kf2.R = 0.005; break; case 9: kf2.R = 0.001; break; default: break; } } if (ZeroTrackOld != Set.ZeroTrack) // 零点跟踪 跟踪级别越高 原始数据发生缓慢变化时,最终数据不会发生变化的可能性越大。例如 当跟踪级别设置为9时,那么如果原始数据在4秒内变化小于2.5mg,那么最终数据不会发生变化。原理是cpu会将这4秒内变化的数据叠加到零点上,相当于动态调零了。 { ZeroTrackOld = Set.ZeroTrack; switch (ZeroTrackOld) { case 0: ZeroTrackTime = 20; // 连续20秒 ZeroTrackRange = 0; // 相邻两次采样数据小于0.000g break; case 1: ZeroTrackTime = 15; ZeroTrackRange = 0.0005; break; case 2: ZeroTrackTime = 10; ZeroTrackRange = 0.0005; break; case 3: ZeroTrackTime = 7; ZeroTrackRange = 0.0005; break; case 4: ZeroTrackTime = 4; ZeroTrackRange = 0.0005; break; case 5: ZeroTrackTime = 4; ZeroTrackRange = 0.0008; break; case 6: ZeroTrackTime = 4; ZeroTrackRange = 0.001; break; case 7: ZeroTrackTime = 4; ZeroTrackRange = 0.0015; break; case 8: ZeroTrackTime = 4; ZeroTrackRange = 0.002; break; case 9: ZeroTrackTime = 4; ZeroTrackRange = 0.0025; break; default: break; } } if (ruBianTimeOld != Set.RuBianTime) // 蠕变时间 级别越高 蠕变时间越短,蠕变越快 零为不蠕变 { ruBianTimeOld = Set.RuBianTime; switch (Set.RuBianTime) { case 0: RubianTime = 100; break; case 1: RubianTime = 60; break; case 2: RubianTime = 30; break; case 3: RubianTime = 20; break; case 4: RubianTime = 15; break; case 5: RubianTime = 10; break; case 6: RubianTime = 8; break; case 7: RubianTime = 6; break; case 8: RubianTime = 4; break; case 9: RubianTime = 2; break; default: break; } } if (RuBianLiangOld != Set.RuBianLiang) //蠕变量 可分正向蠕变和反向蠕变 零为不蠕变 当零点跟踪为0时 调零后观察一段时间的数据变化 如果数据持续变化为负数 则为负蠕变。反之为正蠕变。 负蠕变调整蠕变量为负数级别(谨慎调整 建议不调整) { RuBianLiangOld = Set.RuBianLiang; switch (Set.RuBianLiang) { case 0: RubianLiang = -5; break; case 1: RubianLiang = -4; break; case 2: RubianLiang = -3; break; case 3: RubianLiang = -2; break; case 4: RubianLiang = -1; break; case 5: RubianLiang = 0; break; case 6: RubianLiang = 1; break; case 7: RubianLiang = 2; break; case 8: RubianLiang = 3; break; case 9: RubianLiang = 4; break; case 10: RubianLiang = 5; break; default: break; } } osDelay(200); ADvalue2 = AD7190_ReadData(); //4.7Hz 213ms kalman_filter_update2(&kf2, ADvalue2); // 更新步骤 if(fabs(ADvalue2filter - kf2.x) < ChangeMax ) { LowFecAlpha = 0.95; // 数据处于稳定状态 未有重量突变或小幅度连续单向改变时信任上次值 } else { LowFecAlpha = 0.5; // 数据处于较大波动状态 有重量突变或小幅度连续单向改变时信任本次值 } ADvalue2filter = kf2.x *(1-LowFecAlpha)+ADvalue2filter*LowFecAlpha; // if(pageNum != 1) // { // if(WeightData2TempOld - WeightData2Temp > 0.500f) // 卸载重物 // { // if(UninstiallRefWeight < 0.005f) // 如果未触发归零条件 // { // UninstiallRefWeight = WeightData2TempOld; // WeightZeroOld = CalibrateWeight2.WeightZero; // 备份零点原始值,以备不稳定状态下重新加载重物导致数据不准确 // } // } // else if(WeightData2TempOld - WeightData2Temp < -0.500f)//加载重物 // { // UninstiallRefWeight = 0.0f; // if( WeightZeroOld != CalibrateWeight2.WeightZero) // { // CalibrateWeight2.WeightZero = WeightZeroOld; // 置零未完成或零点跟踪未完成,恢复至改变前零点值,防止数据不准确 // } // SetZeroCount = 0; // } // if(UninstiallRefWeight > 0.005f) // 卸载重物后接近归零时进行归零操作 // { // if(SetZeroCount++ > 50 ) // 归零后重置触发归零条件 // { // // UninstiallRefWeight = 0.0f; // WeightZeroOld = CalibrateWeight2.WeightZero; // SetZeroCount = 0; // } // if(fabs(WeightData2Temp) < 0.100f && fabs(WeightData2Temp) > 0.004f) // 符合归零区间范围则缓慢归零 // { // SetZeroCount = 0; // CalibrateWeight2.WeightZero += WeightData2Temp / CalibrateWeight2.WeightSlope / 10; // ZeroWeightChange = CalibrateWeight2.WeightSlope * (ADvalue2filter - CalibrateWeight2.WeightZero); // } // ZeroWeightOld = CalibrateWeight2.WeightSlope * (ADvalue2filter - WeightZeroOld); // if( fabs(ZeroWeightOld) <= fabs(ZeroWeightChange) ) // { // weight11g=0; // CalibrateWeight2.WeightZero = WeightZeroOld; // } // else // { // weight11g=1; // } // } // } WeightData2TempOld = WeightData2Temp; WeightData2Temp = CalibrateWeight2.WeightSlope * (ADvalue2filter - CalibrateWeight2.WeightZero); // printf("%f,%f,%f,%.4f\n",Temperature,ADvalue2filter,WeightData2Temp,WeightData2Finally); if( RubianLiang != 0 ) // 蠕变 { if (TimeCount++ > 4 * RubianTime) // 动态称重时蠕变跟踪 { TimeCount = 0; if (CalibrateWeight2.WeightZero == WeightZeroOld) { CalibrateWeight2.WeightZero += RubianLiang; } WeightZeroOld = CalibrateWeight2.WeightZero; } } if (fabs(WeightData2TempOld - WeightData2Temp) < ZeroTrackRange && fabs(WeightData2Temp) < ZeroTrackMax && ( pageNum != 1)) // 符合跟踪范围 型评 当示值为零或相当于毛重为0时 负的净重值,且未运行,且水分测定仪处于平衡稳定状态 才允许运行 { if (FollowCount++ > 4 * ZeroTrackTime) // 零点动态跟踪 { FollowCount = 0; TimeCount = 0; CalibrateWeight2.WeightZero += ADvalue2filter - ADvalue2filterOld; WeightZeroOld = CalibrateWeight2.WeightZero; ADvalue2filterOld = ADvalue2filter; } } else { FollowCount = 0; ADvalue2filterOld = ADvalue2filter; } if ( fabs(WeightData2Temp) > ZeroTrackMax && fabs(WeightData2TempOld - WeightData2Temp) < FollowDlteaMax && fabs(WeightData2Temp - WeightRefStart) < FollowMax && ( pageNum != 1)) //重量不变且大于零点跟踪范围未加热进行长期数据跟踪 { if(FliterCount < FliterCountMax) FliterCount++; else FliterCount = FliterCountMax; WeightData2Finally = WeightData2Finally + (WeightData2Temp - WeightData2Finally) * ( 1.00000f - FollowAlpha * FliterCount / FliterCountMax); } else { WeightData2Finally = WeightRefStart = WeightData2Temp; FliterCount = 0; } if (fabs(WeightData2 - WeightData2Finally) > 0.006f) // 变化大于50mg/s 置位传感器不稳定标志 { weightChanging = 1; } else { weightChanging = 0; } if (fabs(WeightData2 - WeightData2Finally) > 0.0002f) // 数据滤波 { WeightData2 = (int32_t)(WeightData2Finally * 10000) / 10000.0f; } else if(fabs(WeightData2Finally) < 0.001f) { WeightData2 = (int32_t)(WeightData2Finally * 10000) / 10000.0f; } } } osThreadDef(AD7190_Run, osPriorityNormal, 1, 0); // AD7190函数:初始化AD7190 void AD7190_Init() { uint32_t modeReg; SPI1_Configuration(); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); SPI1_ReadWrite(0xFF); // 40个脉冲复位ADC osDelay(50); // 配置寄存器:通道选择、参考电压源、增益和工作模式 // 打开PSW uint8_t GPOC = AD7190_ReadRegisterOnce(AD7190_REG_GPOC); GPOC |= AD7190_GPOCON_BDPSW; AD7190_WriteRegisterOnce(AD7190_REG_GPOC, GPOC); osDelay(5); uint32_t config = AD7190_CONF_GAIN_128 | AD7190_CONF_UB | AD7190_CONF_BUF | AD7190_CONF_CH0;// 0x0000011F; AD7190_WriteRegister(AD7190_REG_CONF, config); osDelay(5); modeReg = AD7190_CONF_MODE_FS9_0 ;// 滤波器最低速率输出 执行上电校准 AD7190_WriteRegister(AD7190_REG_MODE, modeReg); osDelay(5); //连续转换模式 modeReg = AD7190_CONF_MODE_FS9_0 | AD7190_CONF_MODE_InCLK; AD7190_WriteRegister(AD7190_REG_MODE, modeReg); CalibrateWeight2.WeightSlope = 0.0001530345f; // CalibrateWeight2.WeightZero = 745430; osThreadCreate(osThread(AD7190_Run), NULL); }