STM32—TIMx实现编码器四倍频
一.储备知识
通过STM32的定时器编码器接口模式对编码器进行四倍频,并使用M法测速得到小车电机的速度信息。
编码器的相关知识之前介绍过:编码器s
M法测速:读取每10ms的脉冲数,以脉冲数的多少代表速度的快慢。
二.TIMx的编码器模式介绍
TIMx的编码器模式,每个定时器只能测量一组AB相的值(编码器的AB相),分别使用CH1和CH2接AB相,通过判断CH1和CH2的输入信号,来实现编码器的测速。需要配置TI1和TI2的极性、计数边沿、自动装载值等信息来驱动编码器模式。在实现编码器后,电机的转速会以计数器的值来表示,然后在另一个TIMx的10ms中断程序中读取编码器计数器的值(读取完要置零)。
下面大概总结一下配置编码器模式的信息:
1.计数边沿设置
我是使用编码器四倍频技术测速,所以要对AB相的上下沿都要计数,也就是说TI1和TI2的上下沿都要触发计数器计时。
关于编码器计数模式,通过配置TIMx_SMCR寄存器中的SMS[2:0]位可以设置,参考手册中的原图如下:


所以,要配置编码器模式3才可以对TI1和TI2的上下沿都计数,即SMS=011。
2.选择极性和使能
设置TIMx_CCER寄存器中的CC1P和CC2P位,可以选择TI1和TI2极性,如图:


3.使能
TIMx_CR1寄存器中的CEN=’1’用来使能计数器:


4.计数方向
在工作时,计数器只在0到TIMx_ARR寄存器的自动重装值之间进行连续计数,所以计数开始前要配置TIMx_ARR。
通过对AB相的输入捕获,可以得到电机的转动方向和转速,是通过计数器的计数方向和计数值来表示的,计数方向和编码器信号的对应关系如图:

四倍频配置如下:

得到的计数器计数过程就如图:

三.代码部分
在STM32中,可以用TIM2、TIM4的CH1、CH2来连接俩个电机的AB相,进行编码器测速,然后在TIM3进行10ms的中断读取计数器的值,这样就实现了编码器的四倍频测速,代码如下:
TIM2、TIM4初始化代码
/* 测量编码器输出的TIM初始化,TIMx编码器模式
TIM2、TIM4编码器模式测速
A电机:PA0、PA1(TIM2的CH1、CH2)
B电机:PB6、PB7(TIM2的CH1、CH2)
*/
void Encoder_TIM2_TIM4_Init( void )
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE );
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2|RCC_APB1Periph_TIM4, ENABLE );
/* GPIO初始化 */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_Init( GPIOA, &GPIO_InitStruct );
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_Init( GPIOB, &GPIO_InitStruct );
/* 配置时基结构体 */
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65535;//定时器自动重装值
TIM_TimeBaseInitStruct.TIM_Prescaler = 0;
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStruct );
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseInitStruct );
/* 编码器模式3,极性上升沿 */
TIM_EncoderInterfaceConfig( TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising );
TIM_EncoderInterfaceConfig( TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising );
/* 配置输入捕获结构体 */
TIM_ICStructInit( &TIM_ICInitStruct );
/*CCMR1寄存器位7:4是IC1F[3:0]:这几位定义了TI1输入的采样频率及数字滤波器长度。数字滤波器由一个事件计数器组成,它记
录到N个事件后会产生一个输出的跳变:*/
TIM_ICInitStruct.TIM_ICFilter = 10;//1010:采样频率fSAMPLING=fDTS/16, N=5
TIM_ICInit( TIM2, &TIM_ICInitStruct );
TIM_ICInit( TIM4, &TIM_ICInitStruct );
/* 中断配置 */
TIM_ClearFlag( TIM2, TIM_IT_Update );
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE );
TIM_ClearFlag( TIM4, TIM_IT_Update );
TIM_ITConfig( TIM4, TIM_IT_Update, ENABLE );
/* 配置计数器的值 */
TIM_SetCounter( TIM2, 0 );
TIM_SetCounter( TIM2, 0 );
/* 使能TIM */
TIM_Cmd( TIM2, ENABLE );
TIM_Cmd( TIM4, ENABLE );
}
读取编码器计数值
/* 读取编码器计数 */
int Read_Encoder( uint8_t TIMx )
{
int Encoder;
/* 读取相应TIM的计数器值CNT,然后清零 */
switch(TIMx)
{
case 2:Encoder = (short)TIM2->CNT;TIM2->CNT=0;break;
case 4:Encoder = (short)TIM4->CNT;TIM4->CNT=0;break;
default:Encoder = 0;break;
}
return Encoder;
}
TIM2、TIM4中断程序
void TIM2_IRQHandler(void)
{
/* SR位:当捕获事件发生时该位由硬件置’1’,它由软件清’0’或通过读TIMx_CCR1清’0’。
0:无输入捕获产生;
1:计数器值已被捕获(拷贝)至TIMx_CCR1(在IC1上检测到与所选极性相同的边沿)。 */
if( TIM2->SR&0x0001 )
;
/* 清除中断标志位 */
TIM2->SR&=~(1<<0);
}
void TIM4_IRQHandler(void)
{
/* SR位:当捕获事件发生时该位由硬件置’1’,它由软件清’0’或通过读TIMx_CCR1清’0’。
0:无输入捕获产生;
1:计数器值已被捕获(拷贝)至TIMx_CCR1(在IC1上检测到与所选极性相同的边沿)。 */
if( TIM4->SR&0x0001 )
;
/* 清除中断标志位 */
TIM4->SR&=~(1<<0);
}
TIM3配置、中断读取计数器值
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig( //使能或者失能指定的TIM中断
TIM3, //TIM2
TIM_IT_Update ,
ENABLE //使能
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
Encoder_Left=(short)TIM2->CNT;
TIM2->CNT=0;
}
}
STM32—TIMx实现编码器四倍频的更多相关文章
- STM32—TIMx输出PWM信号驱动MG996R舵机
文章目录 一.前言 二.MG996R舵机简介 三.TIM定时器简介 四.通用定时器TIMx 1.TIMx主要功能 2.TIMx框图 3.计数单元 4.时钟选择 5.输出比较PWM 五.TIM3输出双路 ...
- stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】
stm32可选的时钟源 在STM32中,可以用内部时钟,也可以用外部时钟,在要求进度高的应用场合最好用外部晶体震荡器,内部时钟存在一定的精度误差. 准确的来说有4个时钟源可以选分别是HSI.LSI.H ...
- STM32学习笔记(四)——串口控制LED(中断方式)
目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...
- STM32延时函数的四种方法
单片机编程过程中经常用到延时函数,最常用的莫过于微秒级延时delay_us()和毫秒级delay_ms().本文基于STM32F207介绍4种不同方式实现的延时函数. 1.普通延时 这种延时方式应该是 ...
- STM32应用实例十四:利用光敏二极管实现光度测量
最近我们在开发臭氧发生器时,需要监测生成的臭氧的浓度,于是想到使用光度计来测量.因为不同浓度的臭氧对管的吸收作用是不相同的,于是检测光照强度的变化就可以得到相应的浓度数据. 1.硬件设计 此次光照度检 ...
- STM32输入捕获TIM2四通道
相比于一通道,原子的例程里因为清了计数时间,所以要对程序进行修改. 记录上升沿后的计数,然后记录下降沿的计数.相减后计算高电平时间,对于定时器中断间隔的边界要分开处理. 这里因为我的接收机时间是1ms ...
- STM32 HAL库学习系列第5篇 定时器TIM---编码器接口模式配置
cube基本配置,外设开启编码器,串口2 可能大家在设置的时候有这个错误 错误:error: #20: identifier "TIM_ICPOLARITY_BOTHEDGE" ...
- 直流电机、减速器、编码器与TB6612FNG知识整理
之前把MPU6050的相关知识了解了一下,可以做到测量欧拉角,在平衡小车的开发中需要测量小车的倾角来调整小车姿态.接下来该学习小车的电机驱动部分了,这里整理一下直流电机.减速器.编码器以及TB6612 ...
- STM32f103 定时器之编码器接口模式
背景 买了个Arduino的旋转编码器模块,配合STM32定时器的编码器模式实现了旋转角度以及圈数的计数.这种旋转编码器我能想到的实际应用场景暂时只有实体音量旋钮,鼠标的滚轮等,所以只实现了计数.阅读 ...
随机推荐
- 在spring boot使用总结(九) 使用yaml语言来写配置文件
yaml是专门用来写配置文件的语言.使用yaml来写配置文件扩展性比较强而且十分方便.spring boot支持使用yaml语言来写配置文件,使用snakeyaml库来读取配置文件.spring bo ...
- PHP实现的解汉诺塔问题算法示例
问题描述: 相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏.该游戏是在一块铜板装置上,有三根杆(编号A.B.C),在A杆自下而上.由大到小按顺序放置64个金盘(如下图).游戏的目标:把A杆 ...
- 『心善渊』Selenium3.0基础 — 26、unittest测试框架的断言
目录 1.断言介绍 2.常用的断言方法 3.断言示例 1.断言介绍 在执行测试用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果和预期结果是否相等决定的,这时会用到断言方法. 本着没有消 ...
- HanLP使用教程——NLP初体验
话接上篇NLP的学习坑 自然语言处理(NLP)--简介 ,使用HanLP进行分词标注处词性. HanLP使用简介 HanLP是一系列模型与算法组成的NLP工具包,目标是普及自然语言处理在生产环境中的应 ...
- kali中设置共享文件夹
1.在虚拟机设置共享目录 2.查看共享目录命令 root@kali:~# vmware-hgfsclient 3.新建文件夹 root@kali:~# mkdir /mnt/hgfs/ShareDir ...
- C++ 11 智能指针(shared_ptr)类成员函数详解
C++ 11 模板库的 <memory> 头文件中定义的智能指针,即 shared_ptr 模板类,用来管理指针的存储,提供有限的内存回收函数,可同时与其他对象共享该管理功能. share ...
- C语言内存:大端小端及判别方式
大端和小端是指数据在内存中的存储模式,它由 CPU 决定:1) 大端模式(Big-endian)是指将数据的低位(比如 1234 中的 34 就是低位)放在内存的高地址上,而数据的高位(比如 1234 ...
- 【译】.NET 对象分配工具
随着 Visual Studio 16.10 的发布,性能分析器又有了一个新的分析引擎,.NET 对象分配工具是第一个加入的工具.这为该工具提供了一些新特性,并显著提高了 perf 性能.在你的 C# ...
- 【LeetCode】1207. 独一无二的出现次数
1207. 独一无二的出现次数 知识点:set:哈希表 题目描述 给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数. 如果每个数的出现次数都是独一无二的,就返回 true:否则返回 fal ...
- 两万字Vue.js基础学习笔记
Vue.js学习笔记 目录 Vue.js学习笔记 ES6语法 1.不一样的变量声明:const和let 2.模板字符串 3.箭头函数(Arrow Functions) 4. 函数的参数默认值 5.Sp ...