江协科技STM32学习- P20 实验-TIM编码器接口测速
🚀write in front🚀
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝💬本系列哔哩哔哩江科大STM32的视频为主以及自己的总结梳理📚
🚀Projeet source code🚀
💾工程代码放在了本人的Gitee仓库:iPickCan (iPickCan) - Gitee.com
引用:
STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili
Keil5 MDK版 下载与安装教程(STM32单片机编程软件)_mdk528-CSDN博客
STM32之Keil5 MDK的安装与下载_keil5下载程序到单片机stm32-CSDN博客
0. 江协科技/江科大-STM32入门教程-各章节详细笔记-查阅传送门-STM32标准库开发_江协科技stm32笔记-CSDN博客
【STM32】江科大STM32学习笔记汇总(已完结)_stm32江科大笔记-CSDN博客
江科大STM32学习笔记(上)_stm32博客-CSDN博客
STM32学习笔记一(基于标准库学习)_电平输出推免-CSDN博客
STM32 MCU学习资源-CSDN博客
stm32学习笔记-作者: Vera工程师养成记
术语:
英文缩写 | 描述 |
GPIO:General Purpose Input Onuput | 通用输入输出 |
AFIO:Alternate Function Input Output | 复用输入输出 |
AO:Analog Output | 模拟输出 |
DO:Digital Output | 数字输出 |
内部时钟源 CK_INT:Clock Internal | 内部时钟源 |
外部时钟源 ETR:External clock | 时钟源 External clock |
外部时钟源 ETR:External clock mode 1 | 外部时钟源 Extern Input pin 时钟模式1 |
外部时钟源 ETR:External clock mode 2 | 外部时钟源 Extern Trigger 时钟模式2 |
外部时钟源 ITRx:Internal trigger inputs | 外部时钟源,ITRx (Internal trigger inputs)内部触发输入 |
外部时钟源 TIx:external input pin | 外部时钟源 TIx (external input pin)外部输入引脚 |
CCR:Capture/Comapre Register | 捕获/比较寄存器 |
OC:Output Compare | 输出比较 |
IC:Input Capture | 输入捕获 |
TI1FP1:TI1 Filter Polarity 1 | Extern Input 1 Filter Polarity 1,外部输入1滤波极性1 |
TI1FP2:TI1 Filter Polarity 2 | Extern Input 1 Filter Polarity 2,外部输入1滤波极性2 |
正文:
0. 概述
从 2024/06/12 定下计划开始学习下江协科技STM32课程,接下来将会按照哔站上江协科技STM32的教学视频来学习入门STM32 开发,本文是视频教程 P2 STM32简介一讲的笔记。
定时器共四个部分,分为八个小节笔记。本小节为第一部分第一节。
🌳在第一部分,是定时器的基本定时的功能:定时中断功能、内外时钟源选择
🌳在第二部分,是定时器的输出比较功能,最常见的用途是产生PWM波形,用于驱动电机等设备
🌳在第三部分,是定时器的输入捕获功能和主从触发模式,来实现测量方波频率
🌳在第四部分,是定时器的编码器接口,能够更加方便读取正交编码器的输出波形,编码电机测速
1.🚢编码器
编码器接口测速
接线图:
A相可以接PA6或者PA7,这个可以随便交换,但是必须用PA6和PA7这两个引脚,因为我们计划用定时器3来接编码器,PA6和PA7对应的是定时器3的通道一和通道二。
2.🚢编码器接口的初始化步骤
编码器接口的初始化步骤看这个结构图来配置
- 🌾第一步,RCC开启时钟,开启GPIO和定时器的时钟。
- 🌾第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式。
- 🌾第三步,配置时基单元,这里预分频器,一般选择不分频。自动重装一般给最大65535,只需要个CNT执行计数就行了。
- 🌾第四步,配置输入捕获单元,不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关。
- 🌾第五步,配置编码器接口模式,这个直接调用个库函数就可以了。
- 🌾第六步,启动定时器,调用TIM_Cmd启动定时器。
- 🌾电路初始化完成之后,CNT就会随着编码器旋转而自增自减。如果想要测量编码器的位置,直接读出CNT的值就行了。如果想测量编码器的速度和方向,就需要每隔一段固定的闸门时间取出一次CNT,然后再把CNT清零,这样就是测频法测量速度了。
3.🚢编码器使用到的stm32f10x_tim.h中新函数
编码器使用到的stm32f10x_tim.h中新函数
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity); | 定时器编码器接口配置 |
上拉和下拉如何选择?
我们一般可以看一下接在这个引脚的外部模块输出的默认电平。如果外部模块空闲默认输出高电平,我们就选择上拉输入,默认输入高电平。如果外部模块默认输出低电平,我们配置下拉输入,默认输入低电平,和外部模块保持默认状态一致,防止默认电平打架,这是上拉和下拉的选择原则。
不过一般来说,默认高电平是一个习惯的状态,所以一般上拉输入用的比较多。
然后如果不确定外部模块输出的默认状态或者外部信号输出功率非常小,这时就尽量选择浮空输入,浮空输入没有上拉电阻和下拉电阻去影响外部信号。但是缺点就是当引脚悬空时没有默认的电平了,输入就会受噪声干扰,来回不断的跳变。
这就是三种输入模式的选择原则。
4.实验1-编码器接口读取编码器正传反转
源码
Encoder.c
#include "stm32f10x.h" // Device header
#include "Encoder.h"
void Encoder_Init(void)
{
//RCC开启外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //GPIOB外设RCC时钟使能
//GPIO配置
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIOInitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIOInitStructure);
//使用定时器TIM3 CH1
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
//Setp 3.
//配置时基单元
TIM_TimeBaseInitTypeDef TimeBaseInitStruct;
TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //时钟信号滤波使用,滤波的采样频率,采样点数
TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数器向上计数
TimeBaseInitStruct.TIM_Period = 65536 - 1; //ARR, Auto-Reload Register 自动重装载寄存器的值,记得需要减一
TimeBaseInitStruct.TIM_Prescaler = 1 - 1; //PSC, 预分频器的值,记得需要减一
TimeBaseInitStruct.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM3, &TimeBaseInitStruct);
//输入信号滤波
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICStructInit(&TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0xF;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit(TIM3, &TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter = 0xF;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit(TIM3, &TIM_ICInitStruct);
//输入编码器配置
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_Cmd(TIM3, ENABLE);
}
int16_t Encoder_GetCount(void)
{
int16_t tmp;
tmp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0);
return tmp;
}
Timer.c
#include "stm32f10x.h" // Device header
#include "Timer.h"
void Timer_Init(void)
{
//Setp 1.
//RCC APB1的外设时钟控制,因为TIM2在STM32的APB1外设总线上
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//Setp 2.
//选择时基单元的时钟,使用内部RCC时钟 CLK_INT (Clock_Internal)
TIM_InternalClockConfig(TIM2);
//Setp 3.
//配置时基单元
TIM_TimeBaseInitTypeDef TimeBaseInitStruct;
TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //时钟信号滤波使用,滤波的采样频率,采样点数
TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数器向上计数
TimeBaseInitStruct.TIM_Period = 10000/4 - 1; //Auto-Reload Register 自动重装载寄存器的值,记得需要减一
TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; //预分频器的值,记得需要减一
TimeBaseInitStruct.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2, &TimeBaseInitStruct);
//在TIM_TimeBaseInit()之后,在启用中断之前,手动清除一下定时器更新中断标志位,
//就能避免初始化之后就立即进入中断的问题了。
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//Setp 4.
//使能定时中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//Setp 5.
//NVIC配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC优先级分组
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct); //NVIC配置定时器中断优先级
//Setp 6.
//定时器启动
TIM_Cmd(TIM2, ENABLE);
}
uint16_t Speed;
int16_t Encoder_GetCount(void);
//定时器TIM2,中断处理函数
void TIM2_IRQHandler(void)
{
//检查中断标志位
if(SET == TIM_GetITStatus(TIM2, TIM_IT_Update))
{
Speed = Encoder_GetCount();
//清除中断标志位
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
int16_t Encoder_GetSpeed(void){
return Speed;
}
main.c
#include "stm32f10x.h" // Device header
#include "oled.h"
#include "Countersensor.h"
#include "Encoder.h"
#include "Timer.h"
extern uint16_t Num;
int main(int argc, char *argv[])
{
OLED_Init();
OLED_ShowString(1, 1, "Cnt:");
Timer_Init();
Encoder_Init();
while(1)
{
OLED_ShowSignedNum(1,5,Encoder_GetSpeed(),5);
}
return 1;
}
3.1 实验结果
编码器正传,读取出来的TIM计数器的值为正,因为是在"IT1,IT2均计数" 所以每正传一个格子计数器的值增加4.
编码器正传,读取出来的TIM计数器的值为正,因为是在"IT1,IT2均计数" 所以每正传一个格子计数器的值减4.
编码器反转到计数器值为0,下一次自编码器的值为‘0-4’溢出后为(65536-4),如果想要显示为负数可以用补码的知识直接将uin16_t类型的值强制转换为 int16_t。