ADS1256+FFT+卡尔曼滤波 F4

ADS1256+FFT+卡尔曼滤波 F4

引脚初始化

void bsp_InitADS1256(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

#ifdef SOFT_SPI

	/* 打开GPIO时钟 */
	RCC_AHB1PeriphClockCmd(RCC_SCK | RCC_DIN | RCC_DOUT | RCC_CS | RCC_DRDY, ENABLE);

	/* 配置几个推完输出IO */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出	/* 设为推挽输出口 */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	/* IO口最大速度 */

	GPIO_InitStructure.GPIO_Pin = PIN_SCK;
	GPIO_Init(PORT_SCK, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = PIN_DIN;
	GPIO_Init(PORT_DIN, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = PIN_CS;
	GPIO_Init(PORT_CS, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = PIN_SYNC;
	GPIO_Init(PORT_SYNC, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = PIN_RST;
	GPIO_Init(PORT_RST, &GPIO_InitStructure);
	/* 配置GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//浮空
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	GPIO_InitStructure.GPIO_Pin = PIN_DOUT;
	GPIO_Init(PORT_DOUT, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = PIN_DRDY;
	GPIO_Init(PORT_DRDY, &GPIO_InitStructure);
	
	RST_0();
	delay_ms(1);
	SYNC_1();
	RST_1();
	delay_ms(100);

	//
	CS_1();
	SCK_0();		/* SPI总线空闲时,钟线是低电平 */
	DI_1();
	
#endif

	//ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_1000SPS);	/* 配置ADC参数: 增益1:1, 数据输出速率 1KHz */
}
#define RCC_SCK 	RCC_AHB1Periph_GPIOC
#define PORT_SCK	GPIOC
#define PIN_SCK		GPIO_Pin_11

#define RCC_DIN 	RCC_AHB1Periph_GPIOC
#define PORT_DIN	GPIOC
#define PIN_DIN		GPIO_Pin_10

#define RCC_CS 		RCC_AHB1Periph_GPIOC
#define PORT_CS		GPIOC
#define PIN_CS		GPIO_Pin_12

#define RCC_DRDY 	RCC_AHB1Periph_GPIOC
#define PORT_DRDY	GPIOC
#define PIN_DRDY	GPIO_Pin_3

#define RCC_DOUT 	RCC_AHB1Periph_GPIOC
#define PORT_DOUT	GPIOC
#define PIN_DOUT	GPIO_Pin_9

#define RCC_SYNC 	RCC_AHB1Periph_GPIOC
#define PORT_SYNC	GPIOC
#define PIN_SYNC	GPIO_Pin_8

#define RCC_RST 	RCC_AHB1Periph_GPIOC
#define PORT_RST	GPIOC
#define PIN_RST	GPIO_Pin_13

GPIO配置

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

推挽输出

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出	/* 设为推挽输出口 */

速率

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

浮空输入

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//浮空
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

其余配置与例程相同

外部中断配置

/* Enable AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

/* Connect EXTI3 Line to PC3 pin */
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, GPIO_PinSource3);

采集数据

放ads1256.c

uint8_t ch_num;
int32_t adc[8];
int32_t volt[8];
uint8_t i;
int32_t iTemp;
char infoShowLCD[64];
void ADS_GetAllValue(void)
{
//	if(g_tADS1256.ReadOver)											//所有通道读取完毕
//		{
			g_tADS1256.ReadOver = 0;
			for (i = 0; i < ch_num; i++)							//数据提取与转换
			{
				adc[i] = ADS1256_GetAdc(i);							/* 从全局缓冲区读取采样结果。 采样结果是在中断服务程序中读取的。*/
				/* 4194303 == (2^24)/4, ADS1256芯片采集电压范围为正负2*Vref,2.5V只占其量程1/4,故4194303对应2.5V*/
				volt[i] = ((int64_t)adc[i] * 2523200) / 4194303;	
				/*2509700,单位uV,为基准源芯片真实电压值。如需采集准确,需采得基准源芯片输出的准确电压值进行换算*/
				/*基准源是U1左上角的铜点,测量时正接铜点,负接地*/
			}
			for (i = 0; i < ch_num; i++)							//打印到串口
			{
				iTemp = volt[i];												/* uV  */
				if (iTemp < 0)													//采集到负电压
				{
					iTemp = -iTemp;
					//if(i==0)
					//printf("%d=(-%d.%03d %03d V),\t\r\n", i, iTemp /1000000, (iTemp%1000000)/1000, iTemp%1000);//%6X  adc[i]
				}
				else																		//采集到正电压
				{
					//if(i==0)
					//printf("%d=( %d.%03d %03d V),\t\r\n", i, iTemp/1000000, (iTemp%1000000)/1000, iTemp%1000);//%6X  adc[i]
				}
			}
			for(i=0;i<4;i++)
			{
					switch(i)
					{
						case 0:
							sprintf(infoShowLCD, "DIFCH%d:", i);
							LCD_ShowString(0,20,10*15,16,16,( char*)infoShowLCD);
							if(volt[i]<0)
							{
									volt[i] = -volt[i];
									sprintf(infoShowLCD, "-%07duV", volt[i]);
									LCD_ShowString(70,20,10*15,16,16,(char*)infoShowLCD);
							}
							else
							{
									sprintf(infoShowLCD, "+%07duV", volt[i]);
									LCD_ShowString(70,20,10*15,16,16,(char*)infoShowLCD);
							}
						break;
						case 1:
							sprintf(infoShowLCD, "DIFCH%d:", i);
							LCD_ShowString(0,40,10*15,16,16,( char*)infoShowLCD);
							if(volt[i]<0)
							{
									volt[i] = -volt[i];
									sprintf(infoShowLCD, "-%07duV", volt[i]);
									LCD_ShowString(70,40,10*15,16,16,(char*)infoShowLCD);
							}
							else
							{
									sprintf(infoShowLCD, "+%07duV", volt[i]);
									LCD_ShowString(70,40,10*15,16,16,(char*)infoShowLCD);
							}
						break;
						case 2:
							sprintf(infoShowLCD, "DIFCH%d:", i);
							LCD_ShowString(0,60,10*15,16,16,( char*)infoShowLCD);
							if(volt[i]<0)
							{
									volt[i] = -volt[i];
									sprintf(infoShowLCD, "-%07duV", volt[i]);
									LCD_ShowString(70,60,10*15,16,16,(char*)infoShowLCD);
							}
							else
							{
									sprintf(infoShowLCD, "+%07duV", volt[i]);
									LCD_ShowString(70,60,10*15,16,16,(char*)infoShowLCD);
							}
						break;
						case 3:
							sprintf(infoShowLCD, "DIFCH%d:", i);
							LCD_ShowString(0,80,10*15,16,16,( char*)infoShowLCD);
							if(volt[i]<0)
							{
									volt[i] = -volt[i];
									sprintf(infoShowLCD, "-%07duV", volt[i]);
									LCD_ShowString(70,80,10*15,16,16,(char*)infoShowLCD);
							}
							else
							{
									sprintf(infoShowLCD, "+%07duV", volt[i]);
									LCD_ShowString(70,80,10*15,16,16,(char*)infoShowLCD);
							}
						break;								
					}
			}

//		}
	}

定时器3中断

float ADSValue_CH0[fft_adc_n] = {0};		//通道0的数据,用于FFT处理
void TIM3_IRQHandler(void)
{
	static int i = 0;
	static int adc_n = 0;
	float temp;
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
	{
		if(g_tADS1256.ReadOver)
		{
			ADS_GetAllValue();					//采集4个通道的电压值
			ADSValue_CH0[adc_n] = volt[0];		//存放通道0的数据
			//ADSValue_CH0[adc_n] = ADS1256_GetAdc(0);
			//printf("adc_n:%d\t%.0f\r\n",adc_n,ADSValue_CH0[adc_n]);
			adc_n++;
			if(adc_n == fft_adc_n)
			{
				adc_n = 0;
				ad_flag = 1;			//判断完成一次数据采集
			}
		}
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
}

main.c

int main(void)
 {		
		u8 lcd_id[12];			//存放LCD ID字符串

		uint8_t id;
	 
		delay_init(168);	    	 //延时函数初始化	  
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
		uart_init(115200);	 	//串口初始化为115200
		LED_Init();			     //LED端口初始化
		KEY_Init();
		LCD_Init();//默认打印LCD,ID到串口
		POINT_COLOR=RED;
		
		AD985x_Init(AD9851,SERIAL);		//初始化控制AD9851需要用到的IO口,设置为串行通讯	
		sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。	
		POINT_COLOR=RED;	  
		LCD_ShowString(70,0,200,16,16,(uint8_t*)"2019-C TEST"); 
		delay_ms(1000);	 /* 等待上电稳定,等基准电压电路稳定, bsp_InitADS1256() 内部会进行自校准 */
		
		bsp_InitADS1256();	/* 初始化配置ADS1256.  PGA=1, DRATE=30KSPS, BUFEN=0, 输入正负5V */
		id = ADS1256_ReadChipID();
		if (id != 3)
			printf("Error, ASD1256 Chip ID = 0x%X\r\n", id);
		else
			printf("Ok, ASD1256 Chip ID = 0x%X\r\n", id);
		
	 	ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_2000SPS);	/* 配置ADC参数: 增益1:1, 数据输出速率 100Hz */
		ADS1256_StartScan(1);														/*0表示单端8路,1表示差分4路 */
		ch_num = 4;	
	TIM3_Int_Init(5000-1,168-1);		/* 通道数 = 8*/
  	while(1)
	{		 
		getFreq();
		//ADS_GetAllValue();
	} 
}

FFT

fft.h

#ifndef _FFT_H
#define _FFT_H

#include "sys.h"
#include "arm_math.h"

#define fft_adc_n 256


extern float fft_in_adc_data[fft_adc_n*2]; // FFT输入,实部是ADC值,虚部补0
extern float fft_out_adc_data[fft_adc_n];  // FFT输出
extern uint16_t fft_flag[fft_adc_n];			// 数据处理时暂时存放数据


extern int ad_flag;					//数据采集完成标志位

float getFreq(void);				//获取频率


#endif

fft.c

#include "fft.h"
#include "usart.h"
#include "ADS1256.h"
#include "arm_const_structs.h"

float fft_in_adc_data[fft_adc_n*2] = {0};
float fft_out_adc_data[fft_adc_n] = {0};
uint16_t fft_fft_flag[fft_adc_n] = {0};
int fft_i,fft_j;
int k = 1;  							// 找第k大值
float32_t maxValue = 0;					// ADC最大值
uint32_t maxIndex = 0;						// 最大值下标
float freq = 0;							// 频率
int ad_flag = 0;
uint16_t fft_flag[fft_adc_n] = {0};

float getFreq(void)
{
	if(ad_flag == 1)
	{
		printf("finish\r\n");
		ad_flag = 0;
		for(fft_i=0;fft_i<fft_adc_n;fft_i++)
		{
			fft_in_adc_data[fft_i*2] = (double)ADSValue_CH0[fft_i]/1000000;		//实部
			fft_in_adc_data[fft_i*2+1] = 0;							//虚部
			printf("%d\t%.7f\r\n",fft_i,fft_in_adc_data[fft_i*2]);
		}
		printf("\r\n");
		arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_in_adc_data,0,1);
		arm_cmplx_mag_f32(fft_in_adc_data,fft_out_adc_data,fft_adc_n);
			  
		fft_out_adc_data[0]/=256;
		for(fft_i=1;fft_i<fft_adc_n;fft_i++)
			fft_out_adc_data[i]/=128;
		for(fft_i=0;fft_i<fft_adc_n;fft_i++)
		{
			printf("%.5f\t",fft_out_adc_data[fft_i]);
		}
		printf("\r\n");
		//arm_max_f32(&fft_out_adc_data[1],fft_adc_n/2-1,&maxValue,&index);

		/****查找最大值及其下标*********/
		memset(fft_flag, 0, sizeof(fft_flag));
		for (fft_i = 1; fft_i <= k; fft_i++)
		{
			maxValue = 0.0;
			for (fft_j = 1; fft_j < fft_adc_n / 2; fft_j++)
			{
				if (fft_flag[fft_j] == 1)
					break;
				if (fft_out_adc_data[fft_j] > maxValue)
				{
					maxValue = fft_out_adc_data[fft_j];
					maxIndex = fft_j;
				}
			}
			fft_flag[maxIndex] = 1;
		}
		printf("index:%d\r\n",maxIndex);
		printf("Vmax:%.2f\r\n",maxValue);
		freq = (float)2000/fft_adc_n * (maxIndex) /2;		//2000为采样率
		
		printf("freq: %.3f",freq);
		ad_flag = 0;
	}
	return freq;
}

问题总结

  • (如果采集全部通道并进行数据处理)ADS1256采样速率慢,TIM3配置ARR为5000时才能实现数据不重复(PSC:168)
  • 输入正弦波03V,实际采样0.8V2.1V;输入正弦波-2.5V2.5V,实际采样-1.351.35V
  • 配置ARR为2,有2~3个数重复,频率不准

再改

中断

float ADSValue_CH0[fft_adc_n] = {0};
void TIM3_IRQHandler(void)
{
	static int i = 0;
	static int adc_n = 0;
	float temp;
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
	{
//		if(LED1 == 1)
//		AD985x_SetFre_Phase(500,0);
//		else
//		AD985x_SetFre_Phase(5000,0);
//		LED1 = !LED1;
//		if(g_tADS1256.ReadOver)
//		{
			//ADS_GetAllValue();					//采集4个通道的电压值
			//ADSValue_CH0[adc_n] = volt[0];		//存放通道0的数据
			ADSValue_CH0[adc_n] = ((int64_t)ADS1256_GetAdc(0)* 2523200) / 4194303;
			//printf("adc_n:%d\t%.0f\r\n",adc_n,ADSValue_CH0[adc_n]);
			adc_n++;
			if(adc_n == fft_adc_n)
			{
				adc_n = 0;
				ad_flag = 1;
			}
//		}
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
}

不进行全部通道的采集,直接调用采集数据函数

最后

采集函数不需要放在中断中

ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_2000SPS);	/* 配置ADC参数: 增益1:1, 数据输出速率 100Hz */

初始化时已经设置了采样率ADS1256_2000SPS
因此只需放在while(1)中即可

 int main(void)
 {		
		u8 lcd_id[12];			//存放LCD ID字符串

		uint8_t id;
		static int i = 0;
		static int adc_n = 0;
		KF_Struct KFS1;
		KF_Struct_Init(&KFS1);
		delay_init(168);	    	 //延时函数初始化	  
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
		uart_init(115200);	 	//串口初始化为115200
		LED_Init();			     //LED端口初始化
		KEY_Init();
		LCD_Init();//默认打印LCD,ID到串口
		POINT_COLOR=RED;
		
		AD985x_Init(AD9851,SERIAL);		//初始化控制AD9851需要用到的IO口,设置为串行通讯	
		sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。	
		POINT_COLOR=RED;	  
		LCD_ShowString(70,0,200,16,16,(uint8_t*)"2019-C TEST"); 
		delay_ms(1000);	 /* 等待上电稳定,等基准电压电路稳定, bsp_InitADS1256() 内部会进行自校准 */
		
		bsp_InitADS1256();	/* 初始化配置ADS1256.  PGA=1, DRATE=30KSPS, BUFEN=0, 输入正负5V */
		id = ADS1256_ReadChipID();
		if (id != 3)
			printf("Error, ASD1256 Chip ID = 0x%X\r\n", id);
		else
			printf("Ok, ASD1256 Chip ID = 0x%X\r\n", id);
		
	 	ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_2000SPS);	/* 配置ADC参数: 增益1:1, 数据输出速率 100Hz */
		ADS1256_StartScan(0);														/*0表示单端8路,1表示差分4路 */
		ch_num = 1;							/* 通道数 = 8*/
	TIM3_Int_Init(1000-1,168-1);		
  	while(1)
	{		 
		if(g_tADS1256.ReadOver)
		{
			ADS_GetAllValue();
			ADSValue_CH0[adc_n] = volt[0]/1000000;		//存放通道0的数据
			//KMFilter(&KFS1,ADSValue_CH0[adc_n]);
			printf("%.5f\n",ADSValue_CH0[adc_n]);
			adc_n++;
			if(adc_n == fft_adc_n)
			{
				adc_n = 0;
				ad_flag = 1;
				
//				for(i=0;i<fft_adc_n;i++)
//				{
//					ADSValue_CH0[i] = KMFilter(&KFS1,ADSValue_CH0[i]);
//				}
			}
		}
		
		//filter(&KFS1,ADSValue_CH0,fft_adc_n);
		getFreq();
		
	} 
}

附:卡尔曼滤波

kalman.h

#ifndef _KALMAN_H
#define _KALMAN_H

typedef struct Kalman_Filter{
	float x_last;
	float P_now;
	float P_last;
	float K;
	float R_cov;
	float Q_cov;
	float out;
}KF_Struct; 

void KF_Struct_Init(KF_Struct* KFS);			//初始化函数
float KMFilter(KF_Struct* KFS,float z);			//对一个数据滤波
void filter(KF_Struct* KFS,float *data,int n);	//对数组滤波

#endif

kalman.c

#include "kalman.h"
#include "usart.h"
void KF_Struct_Init(KF_Struct* KFS)
{
	KFS->x_last	=0;
	KFS->P_now	=0;
	KFS->P_last	=0.02;
	KFS->K		=0;
	KFS->Q_cov	=0.1;//过程激励噪声协方差,参数可调							//调大:实际占比多
	KFS->R_cov	=30;//测量噪声协方差,与仪器测量的性质有关,参数可调		//调大:预测占比多
	KFS->out	=0;
}

/*
* @brief    卡尔曼滤波器
* @param    KFS:卡尔曼滤波器结构体指针
* @param    z:测量仪器的输入量
* @return   当前时刻的最优估计值
*/
float KMFilter(KF_Struct* KFS,float z)
{
	
	KFS->P_now = KFS->P_last + KFS->Q_cov;
    KFS->K = KFS->P_now / (KFS->P_now + KFS->R_cov );
    KFS->out = KFS->out + KFS->K * (z - KFS->out);
	//printf("%.2f\t",z);
    KFS->P_last = (1 - KFS->K)* KFS->P_now;
    
    return KFS->out;
}

void filter(KF_Struct* KFS,float *data,int n)
{
	static int i;
	//KF_Struct KM;
	for(i=0;i<n;i++)
	{
		data[i] = KMFilter(KFS,data[i]);
	}
}

参考文章:手把手教你学-卡尔曼滤波(附代码)
参数调制方法
主要调节R_covQ_cov
R_cov调大,数据倾向实际采样
Q_cov调大,数据倾向预测值
参考文章:卡尔曼滤波参数是如何影响滤波效果的?——KF第一篇笔记

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值