STM32F401 定时器做编码器使用 采集电机旋转速度、旋转方向

定时器做编码器

STM32F401的所有定时器都可以作为编码器使用(STM32F401在某宝20RMB以内买到的,晶振不同,不知道怎么操作的看我上一篇博文,里面有介绍怎么移植,经过本人验证,目前没有任何问题)

下面步入正题:
TIMER都可以作为编码器采集通道,但是需要十分注意的是:
定时器只有通道1和通道2可以作为编码器使用,当采用编码器的2个通道后,剩余的2个通道一般而言就没什么用了,当然,对于高手而言,或许有他们的办法。
我使用的方式,以TIM4的通道1(CH1)和通道2(CH2)作为电机编码器采集通道,以TIM2作为定时中断,TIM3作为PWM信号控制,实现了编码器的数据采集。可以参考这篇博文的做法

硬件电机驱动采用了某宝3.5RMB的TB6612电机驱动,这里需要注意的是STBY引脚一定要接到3.3v-5v, 如果悬空的话,是不会输出的,AIN1和AIN2引脚为方向控制,PWM引脚接PWM输出信号,AO1和AO2引脚是电机驱动引脚,VM是接12V的,VCC是接3.3v-5v的, 这个千万不能接错。当然,TB6612模块的胆电容其实我觉得不怎么好用,被我弄爆炸了2个,当然这个电容的目的是保护电路不受上电掉电时的电流冲击,爆了之后依然可以用,只要不会短路就行,TB6612模块还是很皮实的。

方向控制表:
在这里插入图片描述
tb6612连线图:
在这里插入图片描述

上面这个图上的电机在某宝某小车买的,太贵了,如果不是RMB玩家建议买其他的。
重点:
TIM4作为编码器通道后,PB6和PB7作为CH1和CH2,由于是复用的,在配置GPIO时候一定要配置为复用的方式。同时,在采集数据的时候一定要注意TIM4->CNT的数据格式。

实际代码:

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "oled.h"
#include "bmp.h"
#include "timer32bit.h"
#include "iwdg.h"
#include "motor.h"
/*====================================================================================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
/*****************************************************程序改动说明*****************************************************/
//根据采用不同晶振 STM32F401 改动的地方有:
//OptionForTarget         xtal(改为和实际晶振大小一致)
//delay.c 								121、187、206行(降低计时误差 不改动定时误差为5% 改动后延时为偶数无误差,为奇数误差为1/n *100%)
//system_stm32f4xx.c			317行(根据system_stm32f4xx.c 137-181行的说明改动)
//stm32f4xx.h							123行(将HSE晶振数值更新为与实际一样)
/*====================================================================================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
double zhankongbi = 0.6;
u16 	time2_count_threshold = 0;
u8 Direction = 0;
int main(void)
{ 
	delay_init(84);		  //初始化延时函数
	LED_Init();		        //初始化LED端口
	KEY_Init();
	OLED_Init();
	OLED_ShowPicture(0,0,128,64,BMP4,1);//LOGO
	LED_Shine(300,3);
	delay_ms(1000);
	ShowFrequence();//显示时钟频率
	delay_ms(1000);
	OLED_Clear();
	TIM2_Int_Init(10000-1,840-1);//周期 10^(5-6) s
	TIM3_Int_Init(1000-1,84-1);//计时周期 (PWM周期)1^(3-6) s 
	Encoder_Init_TIM4();
	Motor_Init();
	Direction_Front;
  /**下面是通过直接操作库函数的方式实现IO控制**/	
	while(1)
	{
		if(KEY_Scan(0)==KEY0_PRES)
		{			
			Direction = Direction==1?0:1;			
			if(Direction==0)
			{
				Direction_Front;
			}
			else
			{
				Direction_Back;
			}				
		}
		time2_count_threshold = (u16)(1000*zhankongbi);//调整PWM比较值
		//PWM观察  因为PC13没有PWM功能 采用软PWM
 		if(TIM3->CNT < time2_count_threshold)
		{
			MotorPWM = 1;
			LED0 = 0;
		}
		else
		{
			MotorPWM = 0;
			LED0 = 1;
		}
	}
}

motor.c

#include "motor.h"

void Motor_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOC时钟
  //GPIOF9,F10初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//LED对应IO口
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//下拉
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO	
	GPIO_SetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10);//关闭PWM
	Direction_Front;
}

void Encoder_Init_TIM4(void)
{
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	// GPIO口初始化
	GPIO_InitTypeDef GPIO_InitStructure;  // GPIO口的输入模式配置很重要,不正确的话会读不到数据
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);    
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_TIM4);
	
	// 定时器编码器模式初始化
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	TIM_DeInit(TIM4);
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	TIM_TimeBaseStructure.TIM_Prescaler = 0;
	TIM_TimeBaseStructure.TIM_Period = 65535;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 这里可以自己设置,或使用默认值
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
	
	TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising ,TIM_ICPolarity_Rising); // 这里配置了编码器模式
	

	TIM_ICStructInit(&TIM_ICInitStructure);
	//TIM_ICInitStructure.TIM_ICFilter = 10; // 这里可以自己设置,或使用默认值
	TIM_ICInit(TIM4, &TIM_ICInitStructure);
	
	TIM_SetCounter(TIM4, 0);
	TIM_Cmd(TIM4, ENABLE);
}

motor.h

#ifndef _MOTOR_H
#define _MOTOR_H
#include "sys.h"

#define MotorPWM PAout(8)
#define Motor_AIN1 PAout(9)
#define Motor_AIN2 PAout(10)
#define Direction_Front  {Motor_AIN1 = 0 ; Motor_AIN2 = 1;}
#define Direction_Back   {Motor_AIN1 = 1 ; Motor_AIN2 = 0;}

void Motor_Init(void);
void Encoder_Init_TIM4(void);//32位定时器

#endif

timer32bit.c

#include "timer32bit.h"
#include "led.h"
#include "oled.h"
float counter = 0;//Time2 计数值
u32 times = 0;//Time3 时间
int Encoder_TIM = 0;
void TIM3_Int_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟
	
  TIM_TimeBaseInitStructure.TIM_Period = arr; 	//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向下计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
	TIM_Cmd(TIM3,ENABLE); //使能定时器3	
}

//定时器2中断服务函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中断
	{
		extern double zhankongbi;
		zhankongbi += 0.01;
		if(zhankongbi >= 1)
			zhankongbi = 0.2;		

		Encoder_TIM = (int)((int16_t)(TIM4->CNT));  
		TIM4->CNT=0;
		Encoder_TIM *= 10;
		Encoder_TIM = (int)(Encoder_TIM /26);//每分钟转速  Encoder_TIM*10/1556*60 r/min
		counter += (float)((float)(Encoder_TIM)/600);//100ms 圈数
		
		OLED_ShowString(5,0,"Zhuan Shu :",8,1);
		OLED_ShowNum(25,10,(u32)(counter),10,8,1);
		OLED_ShowString(92,10,"r",8,1);
		
		OLED_ShowString(5,20,"Shu Du    :",8,1);
		OLED_ShowNum(25+6*5,30,Encoder_TIM,5,8,1);
		OLED_ShowString(92,30,"r/min",8,1);
		
		OLED_ShowString(5,40,"ZhanKongBi:",8,1);
		OLED_ShowNum(25+8*6,50,zhankongbi*100,2,8,1);
		OLED_ShowString(92,50,"%",8,1);
		
		OLED_Refresh();
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //清除中断标志位
}


void TIM2_Int_Init(u32 arr,u16 psc)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); 

	TIM_TimeBaseInitStructure.TIM_Period = arr; //0xffffffff 可以计数到 2^32 次方
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //84分频  us 级别计数
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	TIM_Cmd(TIM2,ENABLE); //使能定时器2
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //允许定时器3更新中断
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //定时器3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02; //子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

timer32bit.h

#ifndef __TIMER32BIT__
#define __TIMER32BIT__
#include "sys.h"
	
void TIM2_Int_Init(u32 arr,u16 psc);//32位定时器
void TIM3_Int_Init(u16 arr,u16 psc);//16位定时器

#endif

在这里插入图片描述
在这里插入图片描述

效果图

完整工程链接
提取码:vdh2

在这里插入图片描述

  • 7
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值