STM32f4日记5之AB相编码器测速实验(TIM定时器的编码器模式使用)
板子:stm32f407zgt6正点原子迷你版
电机参数:减速比 1:90
线数:1170(13乘以90)
//
鉴于目前网上的教学形态各异,很多带有迷惑、误导性质,所以写这篇文章交流自己的看法,也权当纪录自己的学习经历。
//
如果你觉得对你有帮助,请点赞同,这对我很重要,谢谢。
//
一、器材介绍
准备:1.L298N,
2.带AB相编码器的电机,
3.给L298N供电的电源(建议保持电压为12V,博主以前用的供电5.6V左右,pwm波输出很离谱的频率100hz电机转的超快)(可以网购12v的电源适配器,大概4~5元左右,把头子切掉,扒开黑胶套,里面白线负极,红线正极,插市电就可以用,但请注意用电安全)
4.杜邦线连接(L298N的介绍可以参考我的STM32日记3之diy小车实验(小车实验一:驱动小车转圈,直走,倒走))
明确:一般L298N输入10khzPWM波,电机功率达到最大
主要器材介绍:
1、电机
最简单判断电机好坏的判断方式就是拿6V左右的电池组加在它两边,看会不会转。(电压太大会烧掉)
2、AB相编码器
网上介绍如下:
编码器分为光电和霍尔编码器是一种将角位移或者角速度转换成一连串电数字脉冲的旋转式传感器,我们可以通过编码器测量到位移或者速度信息。
编码器从输出数据类型上分,可以分为增量式编码器和绝对式编码器。
从编码器检测原理上来分,还可以分为光学式、磁式、感应式、电容式。常见的是光电编码器(光学式)和霍尔编码器(磁式)。两种(以下介绍为复制内容):
光电编码器是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。
光电编码器是由光码盘和光电检测装置组成。光码盘是在一 定直径的圆板上等分地开通若干个长方形孔。由于光电码盘与电动机同轴,电动机旋转时,检测装置检测输出若干脉冲信号,为判断转向,一般输出两组存在一 定相位差的方波信号。
霍尔编码器是一种通过磁电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。
霍尔编码器是由霍尔码盘和霍尔元件组成。霍尔码盘是在一 定直径的圆板上等分地布置有不同的磁极。霍尔码盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。
二、硬件连接
我在做这个实验的时候只使用的L298N的右边一端的out口
1.将L298N的OUT口分别接到电机编码器的M+,M-
2.L298N的ENB接pwm的输出口PF8
3.L298N的两个IN3,IN4接PF2跟PF6
4.单片机跟L298N共地
5.编码器跟单片机共地
6.编码器5V接单片机5V
7.编码器A,B相接单片机的TIM4的CH1跟CH2就是PD12,PD13
三、核心模块TIM定时器编码器模式讲解
参考STM32f4中文参考手册
编码器模式的好处:
编码器模式是TIM自带的对编码处理的一种特殊的输入捕获模式
好处很直接:
1.当编码器出现抖动,它能够防止抖动,不影响计数,并且它可以采用四倍频的方法,使得误差减小4倍。
2.能通过检测TIMx_CR1的第四位DIR来判断电机的转动方向:正转还是反转,这功能很强
3.重要备注:当定时器检测到一个正向脉冲计数值**+1!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**这很重要划重点
所以我们并不能知道检测一个脉冲用了多少时间,所以还要再开一个定时器。
网上搜到的配置STM32F1编码器模式的方法不适用于F4
四、代码编写
思路:TIM13用来提供10khzPWM波跟占空比
TIM4配置编码器模式来实现对AB相编码器的处理
鉴于所以我们并不能知道检测一个脉冲用了多少时间,所以还要再开一个定时器TIM5
配置TIM4代码如下
void Encoder_Init_TIM4(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12| GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_TIM4);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler=psc;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter =0;
TIM_ICInit(TIM4,&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter=0;
TIM_ICInit(TIM4,&TIM_ICInitStructure);
TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising );
NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x02;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM4,ENABLE);
}
最难配置的已经告诉大家了
最重要的是这个函数TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising );
配置的编码器模式,具体模式为TIM_EncoderMode_TI12
其它timer.h里代码如下
void TIM5_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=arr;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM5,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)
{
Encoder_Timer_Overflow++;
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
}
u32 Read_Encoder(void)
{
u32 Count;
u16 Current_Count;
u16 Enc_Timer_Overflow_one;
Enc_Timer_Overflow_one=Encoder_Timer_Overflow;
Current_Count = TIM_GetCounter(TIM4);
Encoder_Timer_Overflow=0;
if((TIM4->CR1&0x0010) == 0x0010)
Count = (u32)((-1*Enc_Timer_Overflow_one)*(4*ENCODER_PPR-4) + (Current_Count - Previous_Count));
else
Count = (u32)(Current_Count - Previous_Count + (Enc_Timer_Overflow_one) * (4*ENCODER_PPR-4));
Previous_Count = Current_Count;
return(Count);
}
这里面
if((TIM4->CR1&0x0010) == 0x0010)
Count = (u32)((-1Enc_Timer_Overflow_one)(4*ENCODER_PPR-4) + (Current_Count - Previous_Count));
else
Count = (u32)(Current_Count - Previous_Count + (Enc_Timer_Overflow_one) * (4乘以ENCODER_PPR-4));
用来得到脉冲的数量并通过DIR位来判断给不给脉冲数量加正负号
主函数代码如下
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "pwm.h"
#include "gpio.h"
#include "math.h"
#include "stdio.h"
#include "control.h"
int encode;
float speed,t;
extern int Encoder_Timer_Overflow;
u8 key;
int duty=50;
void keyscan(void);
int main(void)
{
delay_init(168);
uart_init(115200);
KEY_Init();
_GPIO_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
TIM13_PWM_Init(8400-1,0); // 84mhz->10khz
TIM_SetCompare1(TIM13,(int)(8400*(duty/100.0)));
Encoder_Init_TIM4((ENCODER_PPR-1)*4,1-1);
TIM5_Int_Init(500-1,8400-1);
while(1)
{
keyscan();
delay_ms(200);
}
}
void keyscan(void)
{
key=KEY_Scan(0);
if(key)
{
switch(key)
{
case WKUP_PRES:
GPIO_ToggleBits(GPIOF,GPIO_Pin_6);
GPIO_ToggleBits(GPIOF,GPIO_Pin_4);
break;
case KEY0_PRES:
duty+=5;
if(duty>100)duty=0;
TIM_SetCompare1(TIM13,8400*(duty/100.0));
break;
}
}
}
void TIM5_IRQHandler(void)
{
if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET)
{
encode=Read_Encoder();
printf("编码器脉冲为:%d\r\n",encode);
speed=(float)(encode*1.0/(13*90*0.05));
printf("电机转速为:%.4f\r\n",speed);
}
TIM_ClearITPendingBit(TIM5,TIM_IT_Update);
}
***效果如下:***
我用的串口调试助手是vofa+强烈安利这一款。
五、思考
1.在测速的基础上怎么实现对电机位置的测量
2.对电机速度位置的准确控制(比如使电机转2圈每秒,转动60度)
这个要用到PID调速,我们下一期出。
作者:shawn
可咨询QQ:965798711(要代码可加QQ)
2021.1.28
22:46
All rights reserved