今天和大家分享一下,定时器的输入捕获功能,用来检测编码器行走的圈数。
一:编码器知识:
编码器一般是正交编码器或者是拉线式编码器,通过两个信号线(A、B)的输出一定频率的脉冲输出来进行数据输出,编码器在转动时,利用2000r/rpm,意思就是编码器每转动一圈,会发出2000个脉冲,意味着编码器一个输出脉冲信号就意味着一个固定角度,单片机在通过读取单位时间脉冲信号的数量,通过定时器的执行时间就可以得到编码器行走的速度,间接的我们得到加载机构或者电机实际行走的距离。
编码器硬件介绍:
VCC :供电正极 一般是DC 5-24V
GND:供电负极
A:脉冲输出A相
B:脉冲输出B相
Z:脉冲输出Z相
其中:编码器在工作时,A相、相输出的脉冲信号总是相差90°相位差。反之如果两个信号相位差为90度,则这两个信号称为正交。由于两个信号相差90度,因此可以根据两个信号哪个先哪个后来判断方向、并且可以根据AB相脉冲信号数量测得速度,位移等;
正转的时候信号线A先输出信号,信号线B后输出 A相超前B相90度 证明是正转
反转的时候信号线B先输出信号,信号线A后输出 B相超前A相90度 证明是反转
二:STM32定时器输入捕获知识:

输入捕获总结工作过程:通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。我们只需要在其他定时器回调函数中,读取该定时器的计数值就可以,同时每次读取时将数据清零,否则会导致数据的溢出;
STM32的编码器模式共有三种:仅在TL1计数(A相),仅在TL2计数(B相),在TL1和TL2都计数(A相和B相都计数))
在使用三种定时器模式时的注意事项如下:
1.需要增加测量的精度时,可以采用4倍频方式,即分别在A、B相波形的上升沿和下降沿计数,分辨率可以提高4倍,
2.如果只是测速,不要求方向,那么只需要用单片机随意选择一个信号线就行了,,然后定时器边沿触发,检测脉冲计数即可
3.一般是定时器的通道1和2才能作为编码器输入口,对应编码器输出的两相。
4.GPIO配置为配置为上拉输入模式
5.一个定时器做一种工作,如果你配置了编码器模式,那么剩下的通道就不能配置其他模式 H! d7 N. c) u
6.两相计数模式下, 读出来数需要/4 一个脉冲信号对应四次计数
三:STM32cube MX 软件配置

如上图所示:说明如何在编码器模式下配置 TIM1 外设以确定旋转方向。
为了模拟正交编码器,TIM3 配置为切换模式,以在 10KHz 的(PA6 和 PB5)上生成 2 个正交信号。每 1 秒,信号会改变相位 (+90°/-90°) 以模拟向前/向后旋转。
TIM1 在编码器模式接口中配置,依靠 TI1 和 TI2。计数方向对应于所连接传感器的旋转方向(由 TIM3 信号模拟)。可以通过在 Live Watch 窗口中放置 “uwDirection” 变量来监控旋转方向。当 uwDirection = 0 时,根据“计数方向与编码器信号”表,旋转方向为“向前”。当 uwDirection = 1 时,根据“计数方向与编码器信号”表,旋转方向为“向后”。同时在程序中读取定时器1的内部寄存器的计数值。
程序编写步骤:
① 初始化定时器和通道对应IO的时钟。
② 初始化IO口,模式为输入:
③初始化定时器ARR,PSC
④初始化输入捕获通道
⑤如果要开启捕获中断
⑥使能定时器
⑦编写中断服务函数:
四:部分代码分享:
4.1输入捕获定时器1 初始化部分:
/**
* @brief TIM1 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_Encoder_InitTypeDef sConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 0;
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 0;
if (HAL_TIM_Encoder_Init(&htim1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}4.2 定时器3 输出PWM配置参数:
static void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = EMU_PERIOD;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_OC_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = PULSE1_VALUE;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_OC_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sConfigOC.Pulse = PULSE2_VALUE;
if (HAL_TIM_OC_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
HAL_TIM_MspPostInit(&htim3);
}4.3 主程序如下:
/* Start the encoder interface */
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* Step 1: */
/* Emulate a Forward direction */
Emulate_Forward_Direction(&htim3);
/* Insert 1s delay */
HAL_Delay(1000);
/* Get the current direction */
uwDirection = __HAL_TIM_DIRECTION_STATUS(&htim1);
/* Step 2: */
/* Emulate a Backward direction */
Emulate_Backward_Direction(&htim3);
/* Insert 1s delay */
HAL_Delay(1000);
/* Get the current direction */
uwDirection = __HAL_TIM_DIRECTION_STATUS(&htim1);
temp = __HAL_TIM_GET_COUNTER(&htim1);
__HAL_TIM_SET_COUNTER(&htim1,0);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}实物测试图如下:

如上图所示:在读取到定时器1中的计数值时,我们需要将定时器中的计数值清零,否则数据异常溢出时候,会导致数据不准确。
我要赚赏金
