电机测速(理论+代码)/测距(仅理论)及编码器

目录

一、简单了解编码器

二、电机参数

三、减速比

四、编码器倍频  

五、电机测速思路及两种计算方法

六、电机测距

七、写代码


写作目的:学习成果检验,同时也是出于网上资源太杂(且很多收费等这里不赘述,大家都懂...)以初学者的角度进行总结。

一、简单了解编码器

1.依据检测原理分类:光电编码器,磁性编码器(霍尔编码器),感应式编码器,电容式编码器等(常用的是前两种,这里有相关资料供大家参考—用百度网盘)编码器使用教程与测速原理.pdf—— 提取码: ceq1

依据输出数据类型分类:增量式/绝对式编码器。

增量式:检测脉冲来计算转速及位置,输出有关旋转轴运动的信息由于采用脉冲计数的方式,因此它的测量结果是相对的。另外增量型编码器的数据断电后会丢失。

为了克服增量型编码器的缺点,绝对编码器便应运而生了,绝对编码器是通过旋转轴的位置来输出具体角度即数字量(一个位置一个角度,因此即使断电也不会产生影响)详细介绍增量与绝对式

参照​​​​​​作者---异步时刻的---增量式编码器详解,​​​​​​作者---蚂蚁取经的---增量式编码器和绝对式编码器区别

二、电机参数

下面用到的电机MG310所含霍尔编码器(右边的GMR编码器线数500是左边的38倍在下面介绍可知这种编码器更为精确)只有A、B两相的正交信号,没有Z轴的机械零点信号(用于对位置计数器进行复位,从而确定绝对位置)

a424dd017b174650b52b8021c62d2e63.png

f89aea76a08e4f38beac8ca5648e915d.png

0220e7ff5f084572ac13092012ef6288.png其中堵转就是通电但是不转(电流过大有危害)

三、减速比

输入速度与输出速度的比值。其直接影响转速和扭矩。

减速比不是单纯的越大或者越小越好,取决于你想要的效果。

越大:加速度大,由于P=Fv→最大速度小

越小:加速度小,最大速度大。(由上面减速比34和20的空载速度就能看出)

四、编码器倍频  

参照作者---岚林的---电机编码器学习与pid

详细介绍了使用编码器计数方式3_四倍频的原因并把单圈脉冲数与减速比联系起来(没想到居然有这个关系),但要注意的是文章说的基数脉冲数13是指线数(我还特意问了一下客服,哈哈)

963ddb298692428f83cf74f4d25e37b3.jpeg

于是我们得到单圈脉冲数C=线数*减速比*倍频

下面电机使用MG310的线数是13,减速比1:20,倍频采用最精确的四倍频(外部中断法是一倍频)

b2f531ebf2714d55b858b3e8cef777b4.png

__同时也参照http://www.openedv.com/forum.php?mod=viewthread&tid=297456&page=1

五、电机测速思路及两种计算方法

1.M法(测频法):规定时间T内脉冲数N,知单圈脉冲数C→转速n=N/(C*T)=N*f/C

2.T法(测周法):规定脉冲N内所需时间T,式子与M法相同

T法下相邻脉冲间隔不能太短,即适合低速,故本篇我们采用M法

法一:回顾江科大-旋转编码器计数-外部中断法(虽然只是用来累计计数,但拓展一下。再加上一个定时器,在给定周期内T的计数值N即可实现计算方法1)

下面是中断流程

4b57a33693644003891174e7352e42cd.png

通过下图来确定是正转还是反转(其实主要取决于自己规定,这算是个标准)

44fb47a134bf4bbd905f539cf24644a5.png

我当时遇到的问题:

问题1:有两个中断函数,对应两个引脚,哪个才是A哪个才是B呢?

不是由中断函数推哪个是AB,而是看你AB接的哪个。且正转是+,还是反转是+,看自己定义(Pin1下降沿时Pin0低电平count++→看上面的方波图如果A是Pin1则反转+,如果B是Pin1则正转+__注意这里是我被动去分析中断函数内操作得出结论而不是我主动定义哪个是A/B)

问题2:怎么进入的中断?是引脚高电平?

触发方式在结构体配置中有:上升沿或者下降沿或者上升及下降沿。

法二:回顾江科大旋转编码器测速--利用stm32的输入捕获功能+定时器中断(定时器配置部分及主函数如何调用不再展示)

我当时遇到的问题:在Encode_Get(void)函数里,获取N就置0万一定时器中断读取的正好是0怎么办?

我当时学编码器测速也是一知半解,其实这个置0,是调用以后把值传递给中间变量才置0。所以不存在调用是0的情况

N由编码器接口控制也就是只有旋转编码器转动N才变化,此时可以理解为编码器模式下的定时器(注意这个定时器不是用来中断的那个定时器)为一个计数器。

还有一些很重要的注意事项大家自行参照作者---清雨夜NWC_的---如何用编码器测速部分

另外N≤ARR(自动重载值,为什么设置成65535也是一个难点)

1.ARR的范围是0~65535,设置最大以免溢出。同时也便于反向计数(通过测试我发现假设ARR设置50000,反转时就是-15535+负的速度,所以只有65535才能直接得到负的速度)

2.有Z向信号时,ARR设置成一圈的脉冲总数,即N-1,如果考虑四倍频即(N-1)*4

参照作者---nomil9_OskarBot的---小车驱动(三)_软件配置注意事项还有http://www.openedv.com/forum.php?mod=viewthread&tid=297456&page=1

六、电机测距

首先要知道轮子的半径R,则一圈走过路程为周长2πR,对应一圈的脉冲数Num。

一个脉冲走过路程为2πR/Num

则路程S=2πR/Num*N(N是上面说的脉冲总数)

七、写代码

其实写代码的过程中也会出现各种各样的问题:

1.经过测试我发现tb6612的AIN1/2不能接在B口,只能接A口,否则电机不转

2.在方法二中由于要配置PWM,相当于有三个时基单元,如果用三个定时器太过浪费。

我考虑定时器和PWM用一个时基单元,但是通过测试发现时基单元频率太小那么电机堵转且抖动剧烈,太大电机调速没有反应,一直以高速运行。因此还是只能用三个定时器,鉴于c8t6只有定时器1-4(其中1是高级定时器,其余为通用定时器,2/3用了,我看了看4的复用引脚居然插上OLED了→就是被当成VCC/GND,其实可以用公对母的线把OLED屏放一边去,当然还是用高级定时器1吧,当学习新知识了哈哈)

需要注意的是高级定时器1多了3个反向输出(名字有N的)的通道,但这里用不到,哈哈,不过高级定时器和通用配置有一些区别,详见下面法二的代码

参照Stig_Q介绍TIM1_CH1与TIM_CH1N的区别___

法一:外部中断+定时器法

PWM波配置:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟,这里是为了展示完整流程
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;              
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;           
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);            
	
	/*输出比较初始化*/ 
	TIM_OCInitTypeDef TIM_OCInitStructure;							
	TIM_OCStructInit(&TIM_OCInitStructure);                         	                                                               
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;              
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   
	TIM_OCInitStructure.TIM_Pulse = 0;								
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);                       
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		
}

中断配置:

#include "stm32f10x.h"                  // Device header
int16_t Encoder_Count;					

void Encoder_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);						
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7);
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						
	EXTI_InitStructure.EXTI_Line = EXTI_Line6 | EXTI_Line7;		//选择配置外部中断的6号线和7号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						
	NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQn;	//这里不同型号的5-15在"stm32f10x.h" 中是分开的,而0-4是通用的		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			
	NVIC_Init(&NVIC_InitStructure);							

	NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQn;			
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			
	NVIC_Init(&NVIC_InitStructure);							
}


int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

void EXTI9_5_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line6) == SET)		//6的引脚发声跳变--设置的是下降沿触发
	{
		if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == 0) 
		{
			if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7) == 1)		//A相下降沿B相高电平是正转  
			{
				Encoder_Count ++;					
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line6);		//注意清除标志位的位置,只要进入中断即清除	
													//否则中断将连续不断地触发,导致主程序卡死
	}

	if (EXTI_GetITStatus(EXTI_Line7) == SET)		
	{
		if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == 1)		
			{
				Encoder_Count --;					
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line7);															
													
	}
}

 定时器配置:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);   //PWM用的TIM2这里避免重复用3
	//配置时钟源
	TIM_InternalClockConfig(TIM3);
	//配置时基单元
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;    
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;   //计数模式——向上计数
	TIM_TimeBaseInitStruct.TIM_Period=1000-1;       //Period和Prescaler都<=65535
	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;     //频率=72MHz/((Period+1)*(Prescaler+1))=10Hz--每0.1秒读取一次
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;    //重复计数器,高级寄存器才有
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	//清除标志位,如果没有此步骤,开启中断就会先进行一次中断
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);
	
	//中断输出控制
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);

	//配置NVIC
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
	NVIC_InitTypeDef Structure;
	Structure.NVIC_IRQChannel=TIM3_IRQn;
	Structure.NVIC_IRQChannelPreemptionPriority=2;
	Structure.NVIC_IRQChannelSubPriority=1;
	Structure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&Structure);
	
	//定时器使能
	TIM_Cmd(TIM3,ENABLE);
}	

由于我是先写的法二这里的AIN1/2及IO口控制方向的配置是一样的,不再重复 

主函数配置:

#include "stm32f10x.h"                  
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
#include "Encoder.h"
#include "Timer.h"
uint8_t KeyNum;		
int16_t Gear;	//Gear用来表示档位
int16_t Speed,N; //N表示一个周期内的计数值
const uint16_t C=260;  //13*20*1

int main(void)
{
	/*模块初始化*/
	OLED_Init();	
	Motor_Init();		
	Key_Init();			
	Encoder_Init();
	Timer_Init();
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Gear:");
	OLED_ShowString(2, 1, "Speed:");				
	OLED_ShowString(2, 11, "r/min");
	while (1)
	{
		KeyNum = Key_GetNum();
		N+=Encoder_Get();
		if (KeyNum == 1)					
		{
			Gear += 20;					
			if (Gear > 100)				
			{
				Gear = 0;				
			}
		}
		if (KeyNum == 2)					
		{
			Gear -= 20;					
			if (Gear<-100)				
			{
				Gear = 0;				
			}
		}
		Motor_SetSpeed(Gear);				
		OLED_ShowSignedNum(1, 6, Gear/10, 1);	
		OLED_ShowSignedNum(2, 7, Speed, 3);
	}
}

void TIM3_IRQHandler(void)  //每0.1秒调用一次
{
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
	{
		Speed=N/(C*0.1)*60;
		N=0;
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}

 实验现象:(大家注意空载速度实际上是500这里由于我用的是stilink的5v供电,电压达不到所以最大速度只有290左右)

f6749d5fceee40269273237ffef2e235.png

法二:输入捕获+定时器

PWM波配置(使用高级定时器1):

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);			
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM1);		
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                 
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;              
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;           
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);            
	
	//清除标志位
	TIM_ClearFlag(TIM1,TIM_FLAG_Update);
	
	//中断输出控制
	TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE); 
	
	/*输出比较初始化*/ 
	TIM_OCInitTypeDef TIM_OCInitStructure;							
	TIM_OCStructInit(&TIM_OCInitStructure);                         
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;              
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   
	TIM_OCInitStructure.TIM_Pulse = 0;								
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);                       
	
	//TIM外设主要输出
	TIM_CtrlPWMOutputs(TIM1,ENABLE);  //这一步必须有
	
	/*TIM使能*/
	TIM_Cmd(TIM1, ENABLE);			
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM1, Compare);		
}

AIN1/2及电机方向的配置 :

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

/**
  * 函    数:直流电机初始化
  * 参    数:无
  * 返 回 值:无
  */
void Motor_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3| GPIO_Pin_4;      //注意这个引脚必须是A口的
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//将你想配置的引脚初始化为推挽输出	
	
	PWM_Init();	//此函数不是一定要加,你也可以在主函数里单独加												
}

/**
  * 函    数:直流电机设置速度
  * 参    数:Speed 要设置的速度,范围:-100~100
  * 返 回 值:无
  */
void Motor_SetSpeed(int16_t Speed)  //这里的正负方向取决于自己规定
{
	if (Speed >= 0)							
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_3);	
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);	
		PWM_SetCompare1(Speed);				
	}
	else									
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_3);	
		GPIO_SetBits(GPIOA, GPIO_Pin_4);
		PWM_SetCompare1(-Speed);			
	}
}

编码器接口配置: (有些博主的通道1和2不配置也不算错,经过试验效果一样

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:编码器初始化
  * 参    数:无
  * 返 回 值:无
  */
	
void Encoder_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInitStructure.TIM_Period =65535  - 1;               
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;               
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             
	
	/*输入捕获初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							
	TIM_ICStructInit(&TIM_ICInitStructure);							
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							
	TIM_ICInit(TIM2, &TIM_ICInitStructure);							
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							
	TIM_ICInit(TIM2, &TIM_ICInitStructure);							
	/*编码器接口配置*/
	TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);																	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			
}

/**
  * 函    数:获取编码器的增量值
  * 参    数:无
  * 返 回 值:自上此调用此函数后,编码器的增量值
  */
int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM2);
	TIM_SetCounter(TIM2, 0);
	return Temp;
}

定时器配置:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);   
	//配置时钟源
	TIM_InternalClockConfig(TIM3);
	//配置时基单元
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;    //不分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;   //计数模式——向上计数
	TIM_TimeBaseInitStruct.TIM_Period=1000-1;       //Period和Prescaler都<=65535
	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;     //频率=72MHz/((Period+1)*(Prescaler+1))=10Hz
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;    //重复计数器,高级寄存器才有
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	//清除标志位,如果没有此步骤,开启中断就会先进行一次中断
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);
	
	//中断输出控制
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);

	//配置NVIC
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
	NVIC_InitTypeDef Structure;
	Structure.NVIC_IRQChannel=TIM3_IRQn;
	Structure.NVIC_IRQChannelPreemptionPriority=2;
	Structure.NVIC_IRQChannelSubPriority=1;
	Structure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&Structure);
	
	//定时器使能
	TIM_Cmd(TIM3,ENABLE);
}	


 主函数配置

#include "stm32f10x.h"                  
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
#include "Encoder.h"
#include "Timer.h"
uint8_t KeyNum;		
int16_t Gear;	//Gear用来表示档位
int16_t Speed,N; //N表示一个周期内的计数值
const uint16_t C=1040;  //13*20*4

int main(void)
{
	OLED_Init();	
	Motor_Init();		
	Key_Init();	
	Encoder_Init();
	Timer_Init();
	OLED_ShowString(1, 1, "Gear:");		
	OLED_ShowString(2, 1, "Speed:");				
	while (1)
	{
		KeyNum = Key_GetNum();
		N+=Encoder_Get();
		if (KeyNum == 1)					
		{
			Gear += 20;					
			if (Gear > 100)				
			{
				Gear = 0;				
			}
		}
		if (KeyNum == 2)					
		{
			Gear -= 20;					
			if (Gear<-100)				
			{
				Gear = 0;				
			}
		}
		Motor_SetSpeed(Gear);				
		OLED_ShowSignedNum(1, 6, Gear/10, 2);	
		OLED_ShowSignedNum(2, 7, Speed, 3);
	}
}

void TIM3_IRQHandler(void)  //每0.1秒调用一次
{
	//不要忘记清除标志位
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //这个判断没有其实也没事,因为你已经进入中断了,为了标准还是写上
	{
		Speed=N/(C*0.1)*60;  //转速单位一般是r/min,故乘以60
		N=0;
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}

实验现象(第三行是N,第四行是TIM_GetCounter__都是出问题用来检查用的)

682abbafef8a423b914d4e739f35a161.png

当然除了一开始分析的问题,还有一些粗心导致的,比如说应该是TIM3,但是代码里那么多TIM3就一不小心设置成了TIM2,又或者是忘了在主函数开始写初始化函数.还有我在外部中断法法里移植代码的时候AFIO中断引脚选择的时候GPIOB忘了改GPIOA我说为啥中断没反应.....

以上就是编码电机测试的内容了,后续我会学习PID闭环控制,毕竟是初学者,有很多地方解释的不到位,还请多多见谅

  • 40
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
霍尔编码器电机的搭配可以用于电机的速度信息。在给定的引用中,使用了一个13线的霍尔编码器电机,其减速比为30:1。这意味着电机转动一圈会输出390个脉冲。同时,引用中还提到了轮胎的直径为75mm,轮胎的周长为225mm。定时器采用四倍频计数,因此一圈输出1560个脉冲。通过读取定时器的计数值,可以获取编码器脉冲值。根据编码器脉冲值,可以计算出实际速度。在给定的代码中,使用了GetEncoderPulse函数来读取编码器脉冲值,并使用CalActualSpeed函数来计算速度值。这样,通过霍尔编码器电机的搭配,可以准确获取小车的速度信息。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [stm32霍尔编码器电机测速原理](https://blog.csdn.net/m0_67318127/article/details/124470127)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [STM32机器人控制开发教程No.2 霍尔编码器电机测速以及增量式PID控制(基于HAL库)](https://blog.csdn.net/COONEO/article/details/125909782)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值