给新手的两轮自平衡小车开发实战指南
  • Introduction
  • 前言
    • 前言
    • 推荐学习路线
  • 套件上手
    • 必看开箱组装
    • 参数特点一览
    • 蓝牙遥控模式
    • 红外循迹模式
    • 超声跟随模式
    • 超声避障模式
    • 提起识别
    • 着陆识别
    • OLED显示说明
    • 烧录程序指南
    • 修改遥控APP
    • 修改开机模式
    • 硬件组成概述
    • IO引脚分配
    • 软件功能说明
    • 程序框架介绍
    • 分析排除故障
  • 原理分析篇
    • 平衡原理
    • 动力学分析
    • 数学建模
    • Simulink仿真
  • 电路开发篇
    • 系统框架一览
    • 主控模块
    • 稳压模块
    • 驱动模块
    • 蓝牙模块
    • 传感器模块
    • 底板功能说明
    • 电机参数说明
    • 洞洞板底板手工焊接教程
  • 软件开发篇
    • 约定代码编写规则
    • 安装MDK-ARM软件
    • 使用MDK-ARM软件编译代码
    • 安装STM32CubeMX软件
    • STM32CubeMX软件生成工程
    • GPIO与LED闪烁的仪式感
    • Timer与按键消抖的应用
    • Usart与Printf函数重定向
    • Timer编码器模式读取编码器
    • PWM与TB6612FNG驱动电机
    • 硬件I2C读取MPU6050原始数据
    • 加速度计、陀螺仪的工作原理和数据融合
    • MPU6050姿态解算和数据融合
    • PID控制原理与增量式PID算法
    • 电机速度闭环内环PID控制
    • 直立平衡角度环PID控制
    • 运动速度闭环外环PID控制
    • 提高PWM频率为24kHz
    • 移植U8g2单色图形库驱动OLED
  • 其他
    • 扩展投稿
由 GitBook 提供支持
在本页
  • 预先了解
  • 具体步骤

这有帮助吗?

  1. 软件开发篇

Timer编码器模式读取编码器

上一页Usart与Printf函数重定向下一页PWM与TB6612FNG驱动电机

最后更新于5年前

这有帮助吗?

本小节教你使用 STM32CubeMX 配置定时器的编码器模式,读取电机编码器的脉冲数据,并且利用 Printf 函数把数据发送到电脑端上位机显示出来。

预先了解

通过上面的电机接口电路原理图可以看到,「小霸王Lite」两轮自平衡小车使用两个通用定时器(TIM2、TIM4)分别捕获两个电机上的编码器的脉冲数据。

STM32F10x 系列 MCU 的所有通用定时器及高级定时器都集成了编码器接口。定时器的两个输入 TI1 和 TI2 分别与增量式编码器的 A 相、B 相相接。当定时器设为编码器模式时,这两个信号的边沿作为计数器的时钟。在这个模式下,计数器依照增量编码器的速度和方向被自动地修改。

具体步骤

接下来,我们以通用定时器 TIM4 配置编码器模式为例,进行具体的讲解。定时器 TIM4 的正交模式与 TIM2 的配置过程是一样的。

进入我们上一小节修改过的 MiaowLabs-Demo 文件夹,找到 MiaowLabs-Demo.ioc 工程文件,双击,打开工程。在左侧 Pinout&Configuration 界面中的 Timers 下拉中点击 TIM4,然后在 TIM4 Mode and Configuration 的 Mode 中将 Combined Channels 选择为 Encoder Mode,即编码器模式。

在 Configuration 中选择 Parameter Setting 选项卡,进行基本参数配置。其中,Counter Mode 默认为 Up,即向上计数。Counter Period 设置为 65535,即计数器周期,这是一个 16 位的自动加载寄存器,填写范围为 0~65535。Encoder Mode 设置为 Encoder Mode TI1 and TI2,即两个输入 TI1 和 TI2 都被用来作为增量编码器的接口。Polarity 默认为 Rising Edge,即为捕获上升沿。其他参数默认即可。

值得注意的是,Encoder Mode 设置为 Encoder Mode TI1 and TI2 模式时,AB 两相的上升沿和下降沿都会计数,所以计数值是实际脉冲值的 4 倍,即四倍频。Channel1 和 Channel2 的 Polarity 参数默认是 Rising Edge,意思是在检测到上升沿的时候就触发编码器模式接口捕获 AB 相的值,并不是指只检测 AB 相的上升沿,下降沿还是同样会计数的。

点击 GENERATE CODE,重新生成代码。

打开 MDK-ARM 工程,左侧 Application/User 里多个 tim.c 源文件,而且在 main 函数里可以看到多出一个 TIM4 初始化函数: MX_TIM4_Init();。

打开 MDK-ARM 工程,按下组合键 Ctrl+N(按住 Ctrl 键再按 N 键),新建一个文件,再按下组合键 Ctrl+S,文件名改为 encoder.c,保存到 MiaowLabs-DEMO 的 Src 文件夹里,接着在 MDK-ARM 工程界面左侧 Project 栏目双击 Application/User 文件夹,把 encoder.c 加进来。

双击 encoder.c 文件,把下面代码敲进去。(尽可能手动敲一遍)

#include "tim.h"//包含tim头文件
#include "encoder.h"

int iTim4Encoder;//存放从TIM4定时器读出来的编码器脉冲

int GetTim4Encoder(void)//获取TIM4定时器读出来的编码器脉冲
{
    iTim4Encoder = (short)(__HAL_TIM_GET_COUNTER(&htim4));//先读取脉冲数
    __HAL_TIM_SET_COUNTER(&htim4,0);//再计数器清零
    return iTim4Encoder;//返回脉冲数
}

再新建一个文件 encoder.h 头文件,把文件保存到 Inc 文件夹。然后,把下面代码敲进去。

#ifndef __ENCODER_H
#define __ENCODER_H

int GetTim4Encoder(void);//声明函数

#endif

打开 main.c 文件,在 main 函数中的 /* USER CODE BEGIN 1 */ 和 /* USER CODE END 1 */ 之间定义一个变量:

int iTempTim4Encoder; //临时存放从TIM4编码器接口捕获到的脉冲数据

在主循环里,把以下代码敲进去:

HAL_Delay(5000);//延时5秒
iTempTim4Encoder = GetTim4Encoder();//捕获TIM4脉冲数据
printf("TIM4定时器编码器模式捕获脉冲 = %d \n",iTempTim4Encoder);//把脉冲数据打印出来
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);//翻转指示灯LED的电平

上面这段加入主循环的代码的意思,是 TIM4 定时器的编码器捕获脉冲,每隔 5 秒累计输出一次脉冲数据,通过串口显示在上位机上。

这里补充一个知识点:M 法测速,就是数固定时间内产生的脉冲数,像这里每隔 5 秒数一次累计起来的脉冲数据,就是用了 M 法测速。

代码还没写完,有一句重要的代码必须要加进去,TIM4 的编码器接口模式才会启用。我们在 main 函数的 /* USER CODE BEGIN 2 */ 和 /* USER CODE END 2 */ 之间加入:

HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);//开启TIM4的编码器接口模式

然后在 MDK-ARM 中重新编译代码,把代码烧录进 MiaowLabs-STM32F1-Micro 核心板,将核心板插到「小霸王Lite」两轮自平衡小车底板上,将整辆小车组装好,并用数据线连接核心板和电脑,打开喵呜地面站或其他串口助手软件,我们用手转动 TIM4 对应的电机轮子,就能够看到小车每隔 5 秒就将脉冲数据发送上去。

我们上面提到,得到的数据是四倍频的数据,「小霸王Lite」上用的电机单相单圈脉冲为 384 个脉冲,四倍频后,即 384 x 4 = 1536 个脉冲。可以从上面看到,当我们用手转动轮子半圈时,得到了 768 个脉冲,当然,我们用手转动轮子是无法精准地转动到理想位置(比如无法精确地转动半圈),会有偏差,但我们只要得到个大概的数据,判断能否正常读取编码器数据就可以了。

反应快的好奇宝宝在这里可能会提问:在上面的代码中,并没有对数值进行正负判断赋值,脉冲值怎么会出现负数?

如果 TI1 和 TI2 分别接电机的 A 相和 B 相的话,那么,当电机正转的时候,如下图计数器会向上计数,反转的时候会向下计数,但是向下计数并不会出现负的值,依旧是从(0-ARR)计数。

但是为什么出现一个负数呢?计数器的技术范围明明是 0~ARR。让我们回头看获取编码器脉冲的代码:

int GetTim4Encoder(void)//获取TIM4定时器读出来的编码器脉冲
{
    iTim4Encoder = (short)(__HAL_TIM_GET_COUNTER(&htim4));//先读取脉冲数
    __HAL_TIM_SET_COUNTER(&htim4,0);//再计数器清零
    return iTim4Encoder;//返回脉冲数
}

注意看,上面的代码 iTim4Encoder = (short)(__HAL_TIM_GET_COUNTER(&htim4)); 使用了强制类型装换,把寄存器的值读出来了之后,转换成了 short 型(2 字节),范围为(-32768-32767),此时当我们把计数器的初始值设置为 0 之后,如果出现反转,它就会从 0 开始向下计数(0,65535,65534,...)但是经过强制类型转换之后就变成了(0,-1,-2,...)。

有些好奇宝宝可能不明白,为什么 65535 会变成 -1 ?此时我们回到 short 的表示范围(-32768-32767),也就是说原来 int 型变量当读出来的值为 32767, 32768, 32769,...,65535,65536,65537... 的时候会因为强制转换成 short 型变量而溢出转换为 32767,-32768,-32767,...,-1,0,1 就这样不断地循环下去。所以电机反转的时候读出的数就是反方向的速度值,不需要用 65535 去减去读出的值再加上负号才可以得到方便观察的值,我们只需要巧妙地运用一个强制类型转换就可以了。

电机接口电路原理图
将 Combined Channels 选择为 Encoder Mode
Parameter Settings 选项卡的参数配置
重新生成代码
代码截图
代码截图
代码截图
代码截图
串口助手截图
串口助手截图
计数方向与编码器信号的关系