江科大笔记—编码器接口测速

在这里插入图片描述
A相输出接到PA6,B相输出接到PA7,也可以AB相可以互换,但PA6,PA7引脚不能随便更换。
PA6,PA7是TM3的通道1和通道2。

在这里插入图片描述

第一步,RCC开启时钟,开启GPIO和定时器的时钟
第二步,配置GPIO,需将PA6和PA7配置成输入模式
第三步,配置时基单元,预分频器一般选择不分频,ARR一般给最大值655535,只需要CNT执行计数就行了。
第四步,配置输入捕获单元,这里只有滤波器和极性两个参数有用,后面的参数没有用到,与编码器无关。
第五步,配置编码器接口模式,直接调用一个库函数。
最后,调用TIM_Cmd,启动定时器。

定时器编码器接口配置

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);//TIMx第一个参数选择定时器,TIM_EncoderMode第二个参数选择编码器模式,TIM_IC1Polarity,TIM_IC2Polarity,分别选择通道1,通道2的电平极性。
  • 编码器向右旋转,CNT自增,向左旋转,自减。
    向左转到0之后在左转现象,0在自减,计数器反向溢出了,回到自动重装载值65535,然后继续往下减。 这就是使用uint16_t的现象。
    如果想0之后变为-1就要把uint16_t强制转换成int16_t。
  • 如想修改极性,在硬件层面,把A,B相两线对调一下,软件层面,可以修改软件通道的极性,把任意一个极性反转,方向就会反转,如两个极性都反转,极性保持不变。

如想测速的话,在固定的匝门时间读一次CNT然后把CNT清零,CNT的值代表速度,单位是脉冲个数/s(每秒)例:

int16_t Encoder_Get(void)  //直接返回CNT的值
{
	//测速,在固定的匝门时间读一次CNT然后把CNT清零
	int16_t Temp;  //定义一个临时变量Temp,因为要先读取CNT再清零,所以要用temp缓存一下
	Temp = TIM_GetCounter(TIM3); //读取CNT
	TIM_SetCounter(TIM3, 0); //CNT清零
	return Temp;
}

main.c

程序最终现象是:向右旋转,正速度,向左旋转,负速度,停止速度为0。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"

int16_t Speed; //定义一个全局变量Speed

int main(void)
{
	OLED_Init();
	Timer_Init();
	Encoder_Init();
	
	OLED_ShowString(1, 1, "Speed:");
	
	while (1)
	{
		OLED_ShowSignedNum(1, 7, Speed, 5);  //主循环快速刷新显示Speed
	}
}
//定时器2中断函数放在使用中断的main.c文件中,在startup文件中;定时中断每隔1s执行一次
void TIM2_IRQHandler(void)
{
	//检查中断标志位
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Speed = Encoder_Get(); //定时器每隔1s读取一下速度,存在speed变量里
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  //清除标志位
	}
}

Encoder.c

#include "stm32f10x.h"                  // Device header

void Encoder_Init(void)
{
	//1.打开时钟,选择内部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//2.初始化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);
		
	//不需要初始化时基单元下面这个内部时钟函数,因为编码器接口会托管时钟,编码器接口就是一个带方向控制的外部时钟,所以内部时钟就不用了	
	//TIM_InternalClockConfig(TIM3);
	
	//3.配置时基单元
	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    65536 - 1是满量程计数,这样计数的范围是最大的而且方便换算成负数
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;		//PSC   1-1=0,预分频器,不分频,编码器的时钟直接驱动计数器
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //初始化TIM3
	
	//4.配置输入捕获单元(通道),编码器接口只使用了通道1和2的滤波器和极性选择,只配置这两部分代码
	//结构体的配置不完整,需先定义结构体变量,然后StructInit给结构体赋一个初始值,再部分修改我们想要的参数,调用ICInit配置一遍电路,结构体变量的配置在调用ICInit函数之后就写入到硬件的寄存器了,所以ICInit之后这个结构体我们可以换个值继续使用、不需要重新定义新的结构体
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICStructInit(&TIM_ICInitStructure); //结构体初始化,防止结构体中出现不确定值可能造成问题,最好用StructInit给结构体赋一个初始值
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;   //滤波器为0xF
	TIM_ICInit(TIM3, &TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //通道2
	TIM_ICInitStructure.TIM_ICFilter = 0xF;          //滤波器为0x
	//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//电平极性为上升沿,这里的上升沿参数代表的是高低电平极性不反转;等会配置编码器接口的时候也有极性配置,属于重复配置,这个其实可以删掉;这里的上升沿并不代表上升沿有效,因为编码器接口始终都是上升沿、下降沿都有效
	TIM_ICInit(TIM3, &TIM_ICInitStructure);
	
	
	
	//5.配置编码器接口,只需调用一个函数就行了;需保证TIM_EncoderInterfaceConfig在TIM_ICInit函数后面,否则TIM_ICInit覆盖TIM_EncoderInterfaceConfig函数的配置
	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); //TI12是TI1和TI2都计数,选择Rising是通道不反相,选择Falling是通道反相;重复配置TIM_ICPolarity_Rising,后面的参数会覆盖前面的参数配置,所以前面注释掉
	
	//6.启动定时器
	TIM_Cmd(TIM3, ENABLE);
}

int16_t Encoder_Get(void)  //Encoder_Get 函数是用于测速的函数,主要实现了在固定的时间间隔内读取一次计数器的值,并清零计数器。函数返回了测量得到的编码器的变化值,即速度信息
{
	//测速,在固定的匝门时间读一次CNT然后把CNT清零
	int16_t Temp;  //定义一个临时变量Temp,因为要先读取CNT再清零,所以要用temp缓存一下
	Temp = TIM_GetCounter(TIM3); //读取CNT
	TIM_SetCounter(TIM3, 0); //CNT清零
	return Temp;
}

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif

科大STM32笔记是关于STM32单片机的学习笔记,其中涵盖了一些关于按键初始化和按键读取的代码示例。在这些代码中,通过引用中的Key_Init函数来对按键进行初始化,然后通过引用中的Key_GetNum函数来获取按键按下的键码值。代码中使用了STM32的GPIO模块来配置引脚的工作模式和读取引脚的电平状态。此外,引用中提到STM32内部集成了硬件收发电路,可以通过写入控制寄存器CR和数据寄存器DR来实现与外设的通信,并通过读取状态寄存器SR来了解外设电路的当前状态。这些寄存器的使用可以实现对外设的控制和监测,减轻CPU的负担。因此,科大STM32笔记主要是介绍了STM32单片机的相关知识和编程技巧。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [STM32学习笔记 -- I2C(科大)](https://blog.csdn.net/weixin_61244109/article/details/131002266)[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^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [STM32科大学习笔记](https://blog.csdn.net/weixin_38647099/article/details/128337708)[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^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值