900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 【平衡小车制作】(三)编码器讲解(超详解)

【平衡小车制作】(三)编码器讲解(超详解)

时间:2024-01-10 21:20:47

相关推荐

【平衡小车制作】(三)编码器讲解(超详解)

大家好,我是小政。本篇文章我将针对平衡小车电机上的编码器进行讲解。让每位小伙伴能够对编码器的硬件结构和软件编程有更加清晰的理解。

一、硬件结构

1.什么是编码器?

编码器是一种将角位移或者角速度转换成一串电数字脉冲的旋转式传感器。编码器又分为光电编码器和霍尔编码器。

2.编码器的工作原理

这里以霍尔编码器为主介绍(光电编码器精度比霍尔高38倍)。霍尔编码器是有霍尔马盘和霍尔元件组成。霍尔马盘是在一定直径的圆板上等分的布置有不同的磁极。霍尔马盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。示意图如下:

图1 霍尔编码器

3.编码器接口引脚

我用的电机是GM25-370直流减速电机(带霍尔编码器),其对应编码器引脚图如下:

图2 霍尔编码器接口引脚

4.编码器的测速原理

单位时间内,根据脉冲走过的距离计算电机实际速度。

5.采集数据方式

通常有两种方式,第一种软件技术直接采用外部中断进行采集,根据AB相位差的不同可以判断正负。第二种硬件技术直接使用定时器的编码器模式,这里采用第二种。也是大家常说的四倍频,提高测量精度的方法。其实就是把AB相的上升沿和下降沿都采集而已,所以1变4。自己使用外部中断方式实现就比较占用资源了,这里建议使用定时器的编码器模式来测量脉冲变化值。

二、软件编程

1.编码器函数——Encoder.c

1)编码器初始化函数

 入口参数:无

① 时钟配置

② GPIO配置

③ 定时器配置

④ 编码器配置

⑤ 输入捕获配置

⑥ 清除定时器溢出更新标志位

⑦ 定时器溢出更新

⑧ 定时数据清零

⑨ 定时器使能

这里具体讲解一下编码器配置函数

// 编码器配置函数: 定时器2,模式3,上升沿TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

① 第一个参数是定时器选择。

② 第二个参数是编码器启动模式;TIM_EncoderMode_TI12:在T1和T2的每个跳变沿均计数。

【模式】:

Tl1模式:在T1的所有边沿计数。

Tl2模式:在T2的所有边沿计数。

Tl1l2模式:在T1和T2的所有边沿计数。

简单举个例子:Tl1处于上升沿时,Tl2处在低电平,即对应Tl1FP1信号向上计数。依次类推,下图我圈起来的就是根据编码器计数操作推导出的计数模式。

图3 T1与T2极性图4 对应计数模式

③ 第三和第四个参数是极性配置;这里分为上升沿和上升沿,为什么说设置极性变成设置上升沿和上升沿呢?通过进入stm32f10x.tim.c函数中可以查找到,设置极性实际上与TIM_CCER_CC1P这个寄存器是有关联的。我们查找STM32中文参考手册可以了解到。

这里可以看到,我们设置为不反相,对应IC1上升沿

图5 IC极性查找

编码器函数代码

// 编码器1初始化函数void Encoder_TIM2_Init(void){TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;GPIO_InitTypeDef GPIO_InitStruct;TIM_ICInitTypeDef TIM_ICInitStruct;// 1.时钟配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE); // 开启定时器2时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE); // 开启GPIO时钟// 2.GPIO配置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // 编码器1:PA0/PA1GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA ,&GPIO_InitStruct);// 3.定时器配置TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 不分频TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数TIM_TimeBaseInitStruct.TIM_Period = 65535; // 重装载值65535TIM_TimeBaseInitStruct.TIM_Prescaler =0; // 分频系数0(自动加1)TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);// 4.编码器配置: 定时器2,模式3,上升沿TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); // 5.输入捕获配置TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_ICFilter = 10; // 滤波器设置为10TIM_ICInit(TIM2,&TIM_ICInitStruct);// 6.清除定时器溢出更新标志位(清除计数值)TIM_ClearFlag(TIM2,TIM_FLAG_Update);// 7.定时器2,溢出更新,使能TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // 8.定时数据清零(输入捕获的值从0开始计数)TIM_SetCounter(TIM2,0); // 9.定时器2使能TIM_Cmd(TIM2,ENABLE); }// 编码器2初始化函数void Encoder_TIM4_Init(void){TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;GPIO_InitTypeDef GPIO_InitStruct;TIM_ICInitTypeDef TIM_ICInitStruct;// 1.时钟配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 ,ENABLE); // 开启定时器4时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE); // 开启GPIO时钟// 2.GPIO配置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // 编码器2:PB6/PB7GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB ,&GPIO_InitStruct);// 3.定时器配置TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;// 不分频TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数TIM_TimeBaseInitStruct.TIM_Period = 65535;// 重装载值65535TIM_TimeBaseInitStruct.TIM_Prescaler = 0; // 分频系数0(自动加1)TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);//重装载值设置为65535代表能够装载最大的脉冲数计数值// 4.编码器配置: 定时器4,模式3,上升沿TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); // 5.输入捕获配置TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_ICFilter = 10; // 滤波器设置为10TIM_ICInit(TIM4,&TIM_ICInitStruct);// 6.清除定时器溢出更新标志位(清除计数值)TIM_ClearFlag(TIM4,TIM_FLAG_Update);// 7.定时器4,溢出更新,使能TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 8.定时数据清零(输入捕获的值从0开始计数)TIM_SetCounter(TIM4,0); // 9.定时器4使能TIM_Cmd(TIM4,ENABLE); }

2)编码器数据读取函数

 入口参数:定时器x

① 选择定时器x

② 采集编码器的计数值并保存

③ 将定时器的计数值清零

④ 返回定时器计数值

注:因为TIM_GetCounter得到的是短整型(short int),所以这里我们要进行一个强制转换。

// 编码器速度读取函数// 入口参数:定时器// 编码器产生的是脉冲,计数器计脉冲数(位移)int Read_Speed(int TIMx){int value_1;switch(TIMx){case 2:// 单周期位移作为速度值value_1 = (short)TIM_GetCounter(TIM2); // 采集编码器的计数值并保存TIM_SetCounter(TIM2,0); // 将定时器的计数值清零break;case 4:// 单周期位移作为速度值value_1 = (short)TIM_GetCounter(TIM4); // 采集编码器的计数值并保存TIM_SetCounter(TIM4,0); // 将定时器的计数值清零break; default: value_1 = 0;}return value_1;}

3)定时器中断服务函数

// 定时器2中断服务函数void TIM2_IRQHandler(){if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) // 中断标志位置1{TIM_ClearITPendingBit(TIM2,TIM_IT_Update); // 清楚中断标志位}}// 定时器4中断服务函数void TIM4_IRQHandler(){if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) // 中断标志位置1{TIM_ClearITPendingBit(TIM4,TIM_IT_Update); // 清楚中断标志位}}

2.编码器函数头文件——Encoder.h

#ifdef _ENCODER_H#define _ENCODER_Hvoid Encoder_TIM2_Init(void); // 编码器1初始化函数void Encoder_TIM4_Init(void); // 编码器2初始化函数int Read_Speed(int TIMx); // 编码器速度读取函数void TIM2_IRQHandler(void);// 定时器2中断服务函数void TIM4_IRQHandler(void);// 定时器4中断服务函数#endif

以上就是平衡小车系列文章第三讲——编码器,包括硬件结构讲解和STM32软件编程的讲解,文章中出现错误或者小伙伴对以上内容有所疑问,欢迎大家在评论区留言,小政看到后会尽快回复大家!

【平衡小车制作】(四)陀螺仪MPU6050(超详解)/weixin_44270218/article/details/113656398

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。