编码器接口测速

编码器接口测速接线部分

接线图:

这里PA6和PA7两个引脚可以交换一下,A相接PA6,B相接PA7,或者A相接PA7,B相接PA6,都是可以的,就是正转和反转的极性不一样而已,但是PA6和PA7这两个引脚不能随便更换

PA6和PA7是TIM3的通道1和通道2,我们计划用TIM3接编码器,所以需要接在PA6和PA7这两个引脚

代码部分

第一步,RCC开启时钟,开启GPIO和定时器的时钟

c 复制代码
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

第二步, 配置GPIO,这里需要把PA6和PA7配置成输入模式

c 复制代码
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);

第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装,一般给最大65535, 只需要个CNT执行计数就行了

c 复制代码
//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,自动重装值, 自前还是给65536-1,也就是满量程计数,这样计数的范围是最大的,而且方便换算为负数
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;		//PSC,预分频器,这里改成1-1,预分频给0, 就是不分频,编码器的时钟,直接驱动计数器
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //最后初始化TIM3

第四步, 配置输入捕获单元,不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关

c 复制代码
//看流程图发现,输入捕获单元并没有全部使用到,编码器接口只使用了通道1和通道2的滤波器和极性选择,所以我们只需要配置这两部分的参数即可,那在代码这里,就是后面这两个参数(上节代码部分),与编码器无关,删掉,或者你留看也行,只是它们目前没有作用,那删掉之后,目前结构体的配置是不完整的,为了防止结构体中出现不确定值可能会造成的问题,我们最好用structinit给结构体赋一个初始值,加个Structlnit也是提醒一下我们,结构体并没有配置完整
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure); 
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道为1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //滤波器为0xF
//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //电平极性在默认赋值里面就是上升沿,上一小节我们说过,这里的上升沿并不代表上升沿有效,因为编码器接口始终都是上升沿、下降沿都有效的,这里的上升沿参数代表的是高低电平极性不反转,也就是我们PPT这里演示的TI1,TI2是否反相,对应通道,给上升沿,就是不反相,给下降沿 就是反相,其实这里的这个极性参数,等会儿我们配置编码器接口的时候也有,属于重复配置了,这里这个其实也可以删掉
TIM_ICInit(TIM3, &TIM_ICInitStructure); //最后传给ICInit,这些参数写到通道1,调用ICInit函数之后,就写入到硬件的寄存器了,所以Init之后,这个结构体我们可以换个值继续使用,不需要重新定义新的结构体了
//下面通道2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);

第五步,配置编码器接口模式,这个直接调用一个库函数就可以了

c 复制代码
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); //这后两个参数和上面代码极性选择部分是一样的,实际效果确实一样,这两个地方的参数,其实都配置的是同一个寄存器,属于重复配置了,后配置的参数,会覆盖前面的参数,所以我们可以把这前面那个极性的参数也删掉,只使用这个函数来配置极性,不过要注意,这时一定要保证,这个Encoder函数位于ICInit函数的下面,否则的话,就是ICInit覆盖Encoder函数的配置了
c 复制代码
/**
  * @brief  Configures the TIMx Encoder Interface.
  * @param  TIMx: where x can be  1, 2, 3, 4, 5 or 8 to select the TIM peripheral.
  * @param  TIM_EncoderMode: specifies the TIMx Encoder Mode. 选择编码器模式
  *   This parameter can be one of the following values:
  *     @arg TIM_EncoderMode_TI1: Counter counts on TI1FP1 edge depending on TI2FP2 level.仅在TI1计数
  *     @arg TIM_EncoderMode_TI2: Counter counts on TI2FP2 edge depending on TI1FP1 level.仅在TI2计数
  *     @arg TIM_EncoderMode_TI12: Counter counts on both TI1FP1 and TI2FP2 edges depending 在TI1和TI2都计数
  *                                on the level of the other input.
  * @param  TIM_IC1Polarity: specifies the IC1 Polarity 选择IC1的极性
  *   This parameter can be one of the following values:
  *     @arg TIM_ICPolarity_Falling: IC Falling edge. 选择Falling就是这个通道反相
  *     @arg TIM_ICPolarity_Rising: IC Rising edge. 选择Rising就是这个通道不反相
  * @param  TIM_IC2Polarity: specifies the IC2 Polarity 选择IC2的极性
  *   This parameter can be one of the following values:
  *     @arg TIM_ICPolarity_Falling: IC Falling edge.
  *     @arg TIM_ICPolarity_Rising: IC Rising edge.
  * @retval None
  */
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity)

最后,调用TIM_Cmd,启动定时器, 就完事了

c 复制代码
TIM_Cmd(TIM3, ENABLE);

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

流程看完, 我们来看一下库函数

c 复制代码
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity); //定时器编码器接口配置,第二个参数选择编码器模式,然后后面两个参数 分别选择通道1和通道2的电平极性

调用一下这个Encoder_Init函数,编码器旋转就能控制CNT自增自减了

再写一个Get函数

c 复制代码
int16_t Encoder_Get(void)
{
	int16_t Temp; //为了显示负数,就直接把uint16_t类型强制转换成int16_t就行了
	Temp = TIM_GetCounter(TIM3); //得到CNT的值
	TIM_SetCounter(TIM3, 0); //为了测速度,读完后清零CNT
	return Temp;
}

编码器每转一格(段落感)

A相提前还是滞后90度,取决于正转还是反转

那可以看出,现在编码器转动一格,A、B相各出现了一个下降沿和上升沿,所以计次总共加了4次,当然如果你是电机的编码器,那就不会有这个段落感了

main.c

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"

int16_t Speed;

int main(void)
{
	OLED_Init();
	Timer_Init();
	Encoder_Init();
	
	OLED_ShowString(1, 1, "Speed:");
	
	while (1)
	{
		OLED_ShowSignedNum(1, 7, Speed, 5); //这个函数可以显示负数
		//Delay_ms(1000);
		//可以这个地方加delay函数,测这个delay时间段内计次的个数,如果你是编码电机飞速旋转的话,闸门时间就可以给短点,这样可以提高速度刷新的频率,而且防止计数器溢出,最好不要在主循环加入过长的Delay,这样会阻塞主循环的执行,那比较好的方法,就是用定时中断
	}
}

void TIM2_IRQHandler(void) //这里定时中断自前是每隔1s执行一次,你可以修改定时中断的时间 来调整闸门时间
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Speed = Encoder_Get(); //每隔1s读取一下速度,存在Speed变量里,然后主循环,就可以快速刷新显示speed了
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

研究一下极性问题

自前向右转是增,向左转是减,如果这个方向和你想要的不一致的话,可以修改一下极性

在硬件层面,我们可以这样,把A、B相两根线换一下

在硬件层面,我们可以修改这里的(TIM_EncoderInterfaceConfig), 两个输入通道的极性,把任意一个极性反转一下, 方向就会反过来,如果两个极性都反转,那极性还是保持不变

相关推荐
清风66666617 分钟前
基于单片机的双档输出数字直流电压源设计
单片机·mongodb·毕业设计·nosql·课程设计
牛马大师兄39 分钟前
STM32独立看门狗IWDG与窗口看门狗WWDG知识梳理笔记
笔记·stm32·单片机·嵌入式硬件·嵌入式·看门狗
夜月yeyue1 小时前
STM32 Flash 访问加速器详解(ART Accelerator)
linux·单片机·嵌入式硬件·uboot·bootloard
A9better1 小时前
嵌入式开发学习日志37——stm32之USART
stm32·嵌入式硬件·学习
国科安芯5 小时前
ASP4644芯片低功耗设计思路解析
网络·单片机·嵌入式硬件·安全
充哥单片机设计5 小时前
【STM32项目开源】基于STM32的智能厨房火灾燃气监控
stm32·单片机·嵌入式硬件
CiLerLinux12 小时前
第四十九章 ESP32S3 WiFi 路由实验
网络·人工智能·单片机·嵌入式硬件
时光の尘12 小时前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin12 小时前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远202113 小时前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos