欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > STM32单片机入门学习——第20节: [6-8]编码器接口测速

STM32单片机入门学习——第20节: [6-8]编码器接口测速

2025/9/26 23:42:40 来源:https://blog.csdn.net/qq_44764442/article/details/147027461  浏览:    关键词:STM32单片机入门学习——第20节: [6-8]编码器接口测速

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

本文写于:2025.04.06

STM32开发板学习——第20节: [6-8]编码器接口测速

  • 前言
  • 开发板说明
  • 引用
  • 解答和科普
  • 一、编码器接口测速
  • 问题
  • 总结

前言

   本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。
   欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
   在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
   另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!

开发板说明

   本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。
   原理图如下
1、开发板原理图
在这里插入图片描述
2、STM32F103C6和51对比
在这里插入图片描述
3、STM32F103C6核心板
在这里插入图片描述

视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。

下图是实物图
在这里插入图片描述

引用

【STM32入门教程-2023版 细致讲解 中文字幕】
还参考了下图中的书籍:
STM32库开发实战指南:基于STM32F103(第2版)
在这里插入图片描述
数据手册
在这里插入图片描述

解答和科普

一、编码器接口测速

在这里插入图片描述
A相接PA6,B相输出接到PA7引脚,因为用的是TIM3.
在这里插入图片描述
在这里插入图片描述
第一步,RCC开启时钟,开启GPIO和定时器的时钟;
第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式
第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装,一般给最大65535,只需要个CNT执行计数就行了;
第四步,配置输入捕获单元,不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关;
第五步,配置编码器接口模式,这个直接调用一个库函数就可以了;
最后调用TIM_Cmd,启动定时器,就完事了。

CNT就会随着编码器旋转而自增自减,如果想要测量编码器的位置,那直接读出CNT的值就行了,如果想测量编码器的速度和方向,那就需要每隔一段固定的闸门时间,取出一次CNT,然后再把CNT清零,这样就是测频法测量速度了。

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);

定时器编码器接口配置:第一个参数选择定时器,第二个参数选择编码器模式,后面的参数分别选择通道1和通道2的电平极性;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);	

这里的GPIO模式,可以选择上拉、下拉、或者浮空,我们一般可以看一下接在这个引脚的外部模块输出的默认电平,如果外部模块空闲默认输出高电平,我们就选择上拉输入,默认为低电平,我们就配置下拉输入,默认输出低电平。和外部模块保持默认状态一致,防止默认电平打架,这是上拉和下拉的选择原则。不过一般来说,默认高电平,这是一个习惯的状态。所以一般上拉输入用的比较多。不确定外部模块的默认状态,或者外部信号输出功率非常小,这时就尽量选择浮空输入,浮空输入,没有上拉电阻和下拉电阻去影响外部信号,但是缺点就是当引脚悬空时,没有默认的电平了,输入就会受到噪声干扰,来回不断底跳变。

时基单元配置

TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;				//滤波的分频关系不大TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up ;			//向上计数TIM_TimeBaseInitStructure.TIM_Period=65536-1;									/* ARR 计数最大值,同时方便转化为负数*/TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;								   /* PSC编码器时钟直接驱动计数器*/  TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

输入捕获单元

TIM_ICInitTypeDef  TIM_ICInitStructure;TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel=TIM_Channel_1 ;							//选择通道TIM_ICInitStructure.TIM_ICFilter=0xF;									//滤波TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;			//配置编码器接口的时候也有//上升沿触发代表的是高低电平不翻转(也就是是否反相)TIM_ICInit(TIM3,&TIM_ICInitStructure);		//写入寄存器TIM_ICInitStructure.TIM_Channel=TIM_Channel_2 ;							//选择通道TIM_ICInitStructure.TIM_ICFilter=0xF;									//滤波TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;			//配置编码器接口的时候也有//上升沿触发代表的是高低电平不翻转(也就是是否反相)TIM_ICInit(TIM3,&TIM_ICInitStructure);

配置编码器接口

TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

模式3、通道都不反相,极性不变;

TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;			//配置编码器接口的时候也有//上升沿触发代表的是高低电平不翻转(也就是是否反相)

这种极性祈祷的效果是一样的,其实配置的都是同一个寄存器;后配置的会覆盖前面配置的参数;

启动定时器

	TIM_Cmd(TIM3,ENABLE);
#include "stm32f10x.h"                  // Device headervoid Encoder_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);	// TIM_InternalClockConfig(TIM3);					//编码器会托管时钟,没有用了TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;				//滤波的分频关系不大TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up ;			//向上计数TIM_TimeBaseInitStructure.TIM_Period=65536-1;									/* ARR 计数最大值,同时方便转化为负数*/TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;								   /* PSC编码器时钟直接驱动计数器*/  TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);TIM_ICInitTypeDef  TIM_ICInitStructure;TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel=TIM_Channel_1 ;							//选择通道TIM_ICInitStructure.TIM_ICFilter=0xF;									//滤波TIM_ICInit(TIM3,&TIM_ICInitStructure);		//写入寄存器TIM_ICInitStructure.TIM_Channel=TIM_Channel_2 ;							//选择通道TIM_ICInitStructure.TIM_ICFilter=0xF;									//滤波TIM_ICInit(TIM3,&TIM_ICInitStructure);TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);TIM_Cmd(TIM3, ENABLE);
}int16_t Encoder_Get(void)
{return TIM_GetCounter(TIM3);
}
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Timer.h"
#include "OLED.h"
#include "Encoder.h"//uint16_t Num;
int main(void)
{OLED_Init();//Timer_Init();Encoder_Init();OLED_ShowString(1,2,"Hello STM32 MCU");OLED_ShowString(2,1,"CNT:");while(1){OLED_ShowNum(2,5,Encoder_Get(),5);}
}//void TIM2_IRQHandler(void)
//{
//	if(TIM_GetITStatus(TIM2,TIM_IT_Update)== SET)
//	{//			Num++;
//		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
//	}//}

这个时候每转动一格编码器就会增加4,如果你是电机的编码器就不会有这个段落感了;
0之后左转为65536如何想要变成负数,怎么做呢:就是直接把uint16_t转化为int16_t就行了
借用补码的特性快速完成负数转换的小技巧。

实验现象1

左转不是负数

int16_t Encoder_Get(void)
{return TIM_GetCounter(TIM3);
}
while(1){OLED_ShowSignedNum(2,5,Encoder_Get(),5);}

实验现象2

调整后显示负数

极性问题
1、A、B相两根线换一下,增减方向相反;
向右转是减,向左转是增;

2、可以修改两个输入通道的极性
在这里插入图片描述
把任意一个反转一下,方向就会反过来,如果两个极性都反转,那极性还是保持不变。
实现现象

软件极性反转

目前是测量为止,还要测速度的话,还要进行改进,
测速的话可以在固定的闸门时间,读一次CNT,然后CNT清零,我们修改一下这个Get参数,要求读完后清零CNT;

int16_t Encoder_Get(void)		//先读取后请求 TEMP中间缓存一下
{int16_t Temp;Temp=TIM_GetCounter(TIM3);		TIM_SetCounter(TIM3,0);return  Temp;
}
while(1){OLED_ShowSignedNum(2,5,Encoder_Get(),5);Delay_ms(1000);		//实际电机的话要小点,防止计数器溢出;}

Delay 实验现象

主程序Delayis测速

如果主程序没有其他东西可以这样做,但是如果有其他东西的话,最好就不要再主循环加入过长的Delay();这样会阻塞主循环的执行,可以用定时中断来完成;
1.main

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Timer.h"
#include "OLED.h"
#include "Encoder.h"int16_t Speed;
int main(void)
{OLED_Init();Timer_Init();Encoder_Init();OLED_ShowString(1,2,"Hello STM32 MCU");OLED_ShowString(2,1,"Speed:");while(1){OLED_ShowSignedNum(2,7,Speed,5);}
}void TIM2_IRQHandler(void)			//目前配置是1S进入中断
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)== SET){Speed=Encoder_Get();TIM_ClearITPendingBit(TIM2,TIM_IT_Update);}}

2、Encoder.CH

#include "stm32f10x.h"                  // Device headervoid Encoder_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);	// TIM_InternalClockConfig(TIM3);					//编码器会托管时钟,没有用了TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;				//滤波的分频关系不大TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up ;			//向上计数TIM_TimeBaseInitStructure.TIM_Period=65536-1;									/* ARR 计数最大值,同时方便转化为负数*/TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;								   /* PSC编码器时钟直接驱动计数器*/  TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);TIM_ICInitTypeDef  TIM_ICInitStructure;TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel=TIM_Channel_1 ;							//选择通道TIM_ICInitStructure.TIM_ICFilter=0xF;									//滤波TIM_ICInit(TIM3,&TIM_ICInitStructure);		//写入寄存器TIM_ICInitStructure.TIM_Channel=TIM_Channel_2 ;							//选择通道TIM_ICInitStructure.TIM_ICFilter=0xF;									//滤波TIM_ICInit(TIM3,&TIM_ICInitStructure);TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);TIM_Cmd(TIM3, ENABLE);
}int16_t Encoder_Get(void)		//先读取后请求 TEMP中间缓存一下
{int16_t Temp;Temp=TIM_GetCounter(TIM3);		TIM_SetCounter(TIM3,0);return  Temp;
}
#ifndef    __ENCODER_H
#define    __ENCODER_Hvoid Encoder_Init(void);int16_t Encoder_Get(void);
#endif

3、TImer

#include "stm32f10x.h"                  // Device headerextern uint16_t Num;void Timer_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);TIM_InternalClockConfig(TIM2);		//好多人不写,默认的是内部时钟TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;			//滤波的分频关系不大TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up ;		//向上计数TIM_TimeBaseInitStructure.TIM_Period=7200-1;						//ARRTIM_TimeBaseInitStructure.TIM_Prescaler=10000-1;					//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2,TIM_FLAG_Update);			//手动把更新标志位清除一下,就能避免刚初始化完就进中断的问题了TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);			//更新中断到NVIC的通路NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef	NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2,ENABLE);
}/*
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)== SET){Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);}}
*/
#ifndef  __TIMER_H
#define  __TIMER_H
void Timer_Init(void);#endif

实验现象

定时器1s进入中断,获取速度

问题

1、正交信号示波器的波形
正转:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2、反转
在这里插入图片描述
在这里插入图片描述

总结

本节课主要学习了配置编码器接口,同样的引脚配置,时基单元,然后进入到输入比较配置,最后进行编码器配置,只需要一个函数,然后就完成了获取位置的代码,后来有在一定的时间内,变化的位置就是速度,完成了编码器接口测速度的功能。第一步,RCC开启时钟,开启GPIO和定时器的时钟;
第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式
第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装,一般给最大65535,只需要个CNT执行计数就行了;
第四步,配置输入捕获单元,不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关;
第五步,配置编码器接口模式,这个直接调用一个库函数就可以了;
最后调用TIM_Cmd,启动定时器,就完事了。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词