STM32F103 数字滤波的编码器测速

本文介绍了一种利用编码器进行速度测量的方法,包括差分计算防止上下溢出,以及使用FIR滤波器提高测速精度。通过Matlab设计滤波器,并在C语言中实现,有效降低了速度测量中的噪声。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工作中经常遇到需要连续测速的应用,担心编码器“抖动”,导致速度测不准

一. 简单原理

      编码器计数  ---->  数值取样  ----> 差分  ----> FIR滤波  ---->  比例计算实际速度

二. 硬件接口

  

 

三. 差分计算

  

#define MAXCOUNT1  		20000		//最大计数值 per 0.1s
#define ENCODER_TIM_PERIOD 	39999		//计数自动重装值 值在 0 到 39999之间
/*使用100ms 定时器,定时调用该函数*/
int16_t Enc_GetCount(void)
{
	static uint16_t last_count = 0;
	uint16_t cur_count;

	cur_count = __HAL_TIM_GetCounter(&htim2);      /*获取编码计数值*/
	
	int32_t Angle = cur_count - last_count;
	
	if(Angle > MAXCOUNT1)                          /*判定上溢出*/
	{
		Angle -= ENCODER_TIM_PERIOD;
	}
	if(Angle < -MAXCOUNT1)                         /*判定下溢出*/
	{
		Angle += ENCODER_TIM_PERIOD;
	}
	
	last_count = cur_count;
	
	return Angle;                                  /*返回差分值*/
}

四. FIR滤波器

  使用matlab 的 FDAtool工具,设计FIR滤波器

采样频率Fs = 10Hz  阶数order = 5(过长延迟增加)

导出滤波器参数

h(6) =

   -0.0691    0.0296    0.5204    0.5204    0.0296   -0.0691

C语言实现的FIR滤波器计算

/*简单的FIR滤波器*/
const float h[6] = {-0.0691  ,0.0296 ,0.5204 ,0.5204 ,0.0296 ,-0.0691}
float Fir_Filter(int16_t xn)
{
    static int16_t xx[6] = {0};        /*FIR 长度6*/
    float ret;
    int16_t i;

    xx[0] = xn;
    
    ret = xx[0] * h[0];

    for(i = 5;i > 0;i--)
    {
        ret += h(i) * xx[i];
        xx(i) = xx(i-1);
    }
    
    return ret;
}

5. 计算速度

    结合编码器一圈的脉冲数,可以换算成速度   如角频率,圈每分,圈每秒等

 

 

 

### 使用HAL库实现STM32F103C8T6的编码器测速 #### 配置硬件连接 在使用HAL库进行编码器测速之前,需确保硬件连接正确。STM32F103C8T6微控制器应与带有编码器的电机相连,具体来说,编码器的A相和B相脉冲信号分别接入STM32的不同GPIO引脚[^1]。 #### 初始化时钟 启动定时器前,必须先使能相应的外设时钟。对于TIM3定时器而言,在`main.c`文件中的初始化部分加入如下代码: ```c __HAL_RCC_TIM3_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // 假定PA6, PA7用于接收编码器信号 ``` #### GPIO配置 接着定义并初始化GPIO引脚为输入模式,以便读取来自编码器的脉冲信号。这里以PA6和PA7为例说明设置过程: ```c GPIO_InitTypeDef GPIO_InitStruct = {0}; // 设置GPIO引脚为上拉输入 GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); ``` 值得注意的是,上述代码中将GPIO模式设定为中断触发方式(即上升沿或下降沿均可触发),这样可以更灵敏地捕捉到每一个变化边缘[^5]。 #### 定时器配置 采用输入捕获法来进行编码器计数,则需要对特定定时器通道做进一步配置。下面展示了一个简单的例子,它展示了如何利用TIM3完成此操作: ```c TIM_HandleTypeDef htim3; void MX_TIM3_Init(void) { TIM_Encoder_InitTypeDef sConfig = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 0; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 65535; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; sConfig.EncoderMode = TIM_ENCODERMODE_TI12; // 同时使用TI1和TI2作为增量式编码器输入 sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; // A相极性选择 sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; // 直接映射至TI1 sConfig.IC1Prescaler = TIM_ICPSC_DIV1; // 不分频 sConfig.IC1Filter = 0x0; // 数字滤波系数 sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; // B相极性选择 sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; // 直接映射至TI2 sConfig.IC2Prescaler = TIM_ICPSC_DIV1; // 不分频 sConfig.IC2Filter = 0x0; // 数字滤波系数 HAL_TIM_Encoder_MspInit(&htim3); if (HAL_TIM_Encoder_Init(&htim3, &sConfig) != HAL_OK) { Error_Handler(); } } ``` 以上代码片段完成了针对TIM3定时器的基础参数以及编码器工作模式的具体设定,并调用了底层驱动程序进行了必要的初始化处理[^4]。 #### 获取当前计数值 一旦定时器被成功初始化之后,就可以随时查询其内部寄存器来获取最新的位置信息或者计算瞬时速度了。通常情况下,可以直接访问`Instance->CNT`成员变量获得当前位置值;而要得到每秒钟内的转动圈数,则还需要额外记录一段时间间隔内累积的变化量再除以时间差得出平均速率。 ```c int32_t GetEncoderCount(TIM_HandleTypeDef *htim){ return __HAL_TIM_GET_COUNT(htim); } float CalculateSpeed(int32_t count_diff,float time_interval_seconds,int pulses_per_revolution){ float revolutions = ((float)(count_diff))/(pulses_per_revolution*4); // 计算实际旋转了多少周(考虑四倍频效应) return revolutions/time_interval_seconds; // 得到单位时间内转过的圈数(rps) } ``` 通过这种方式便可以在不依赖于RTOS的情况下简单有效地监测电机运转状态,进而达到精确控制的目的[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值