作为最简单实用的滑动平均滤波器部署如下 , 该实验以加了50Hz的工频干扰的心电信号为实验对象。
思路如下:怎么处理50Hz信号呢?用1分钟除以50Hz: 1000 / 50 = 20ms ,20ms是工频干扰的一个周期,一个周期内的所有采样点相加即为0,那我们就得刚好设置adc 采样时间为这20ms内,假设我的adc采样时间为4ms,那么就有4*5 = 20 , 所以就只需要有5个点就能完成一次滤波,这样我们就可以设置一个5长度的队列或者数组,当进来一个数据的时候出去一个数据,再对新进来的与之前的4个数据进行求和取平均运算。
配置adc为定时器3触发:
/*********************************************************************************************************
* 函数名称:ConfigADC1
* 函数功能:配置ADC1
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:ADC123_IN1-PA1
**********************************************************************************************************/
static void ConfigADC1(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
ADC_InitTypeDef ADC_InitStructure; //ADC_InitStructure用于存放ADC的参数
//使能RCC相关时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频,ADCCLK=PCLK2/6=12MHz
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE); //使能ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIOA的时钟
//配置ADC1的GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //设置输入类型
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据参数初始化GPIO
//配置ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //设置为独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //禁止连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; //使用TIM3触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置为右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //设置ADC的通道数目
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_239Cycles5); //设置采样时间为239.5个周期
ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA
ADC_ExternalTrigConvCmd(ADC1, ENABLE); //使用外部事件启动ADC转换
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_ResetCalibration(ADC1); //启动ADC复位校准,即将RSTCAL赋值为1
while(ADC_GetResetCalibrationStatus(ADC1)); //读取并判断RSTCAL,RSTCAL为0跳出while语句
ADC_StartCalibration(ADC1); //启动ADC校准,即将CAL赋值为1
while(ADC_GetCalibrationStatus(ADC1)); //读取并判断CAL,CAL为0跳出while语句
}
TIM3设置为4ms:
/*********************************************************************************************************
* 函数名称:InitADC
* 函数功能:初始化ADC模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
**********************************************************************************************************/
void InitADC(void)
{
ConfigTimer3(399, 719); //100KHz,计数到400为4ms
ConfigADC1(); //配置ADC1
ConfigDMA1Ch1(); //配置DMA1的通道1
InitU16Queue(&s_structADCCirQue, s_arrADCBuf, ADC1_BUF_SIZE); //初始化ADC缓冲区
InitU16Queue(&s_structFilter,s_arrFilterBuf,FILTER_BUF_SIZE); //初始化滑动滤波器缓冲区大小
}
最后写一个滑动平均滤波器的滤波:
/*********************************************************************************************************
* 函数名称:u16 WriteFilterBuf(u16 d)
* 函数功能:向Filter缓冲区写入数据
* 输入参数:d-待写入的数据
* 输出参数:滤完波后的输出mooth
* 返 回 值:成功标志位,1为成功,0为不成功
* 创建日期:2018年01月01日
* 注 意:
**********************************************************************************************************/
u16 WriteFilterBuf(u16 d)
{
u8 i ;
u16 mooth = 0; //将读取成功标志位的值设置为0
EnU16Queue(&s_structFilter, &d, 1); //入队
if(U16QueueLength(&s_structFilter) == 5)
{
for(i = 0 ; i < 5 ; i++ )
{
mooth += s_structFilter.pBuffer[i];
}
DeU16Queue(&s_structFilter, &d, 1);
mooth = mooth/5;
printf("%d,\r\n",mooth);
}
return mooth; //返回读取成功标志位的值
}
心电未处理前:
心电处理后: