图片均来自江科大STM32教学PPT
中断
中断简介
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
NVIC的作用:
- 中断优先级管理:NVIC允许为每个中断源设置优先级,以确定中断的相对顺序和优先级。较高优先级的中断可以打断正在执行的较低优先级中断。
- 中断向量表管理:NVIC维护一个中断向量表,其中存储了每个中断向量的地址。当发生中断时,中断控制器将执行相应中断向量表中存储的中断处理程序。
- 中断使能和禁用:NVIC允许对不同的中断源进行使能和禁用操作。通过使能或禁用特定中断,可以控制中断是否被触发和处理。
- 中断嵌套管理:NVIC支持嵌套中断,即在处理一个中断时,允许更高优先级的中断打断当前正在执行的中断处理程序。这种机制确保了高优先级任务的及时响应。
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级。
NVIC结构
NVIC优先级分组
- NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
- 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
响应优先级类似于插队
抢占优先级可以决定是否可以中断嵌套
EXTI外部中断
简介
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(像PA1,PB1,PC1这种就不行)- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应(执行中断函数)/事件响应(触发别的外设,比如触发ADC转换,触发DMA)
EXTI基本结构
AFIO:
- AFIO主要用于引脚复用功能的选择和重定义
- 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
- 数据选择器(中断引脚选择),从16个引脚选择其中一个连到EXTI通道上。所以相同的Pin不能同时触发中断
其他外设:就是触发事件响应
EXTI框图
输入线:由AFIO选择后,连接到这里。
边沿检测电路:因为中断的触发模式可以是上升沿触发,下降沿触发,或者双边沿触发
请求挂起寄存器:相当于中断标志,可以判断是哪个通道触发的中断。
中断/事件屏蔽寄存器:类似于开关的作用,置1,就允许中断,置0,就不允许中断。
需要用到外部中断的情况:STM32获取外部驱动的很快的突发信号。 例如旋转编码器的输出信号,不拧就不需要STM32做事情,一拧就要接收信号。信号是突发的,并且是外部。
EXIT代码示例
根据下图配置EXIT, 从GPIO到NVIC 把出现的外设模块都配置好就可以实现。
- 配置RCC时钟,把涉及到的外设时钟都打开(这里就开启GPIO和AFIO,EXTI和NVIC都是一直开启的,不需要我们再开启)。
- 配置GPIO,选择输入模式
- 配置AFIO,选择中断线路
- 配置EXTI
- 配置NVIC
void CountSensor_Init(void)
{
// 使能GPIOB和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 配置GPIO引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 输入上拉模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; // 使用GPIOB的引脚14
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置AFIO中断线路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
// 配置外部中断
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14; // 外部中断线14
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能外部中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_Init(&EXTI_InitStructure);
// 配置NVIC中断分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 配置NVIC中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; // 外部中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级
NVIC_Init(&NVIC_InitStructure);
}
中断函数都是固定 参考启动文件,都是无参无返回值
void EXTI15_10_IRQHandler(void)
{
// 判断是否为14号中断线触发的中断标志位
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
CountSensor_Count++;
// 清除中断标志位,防止程序一直申请中断,导致程序卡死
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
TIM定时中断
简介
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16位
计数器
、预分频器
、自动重装寄存器
的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 - 不仅具备基本的定时中断功能,而且还包含
内外时钟源选择
、输入捕获
、输出比较
、编码器接口
、主从触发模式
等多种功能 - 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
定时器类型
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
基本定时器
- 基本定时器只能选择内部时钟
- PSC预分频器连着内部时钟(72MHz),可以对72MHz进行预分频,这个寄存器写0,就是不分频,或者说1分频;如果写1,就是2分频,即 输出频率=输入频率/2=72MHz/2=36MHz。 实际分频系数=预分频器的值+1。
- CNT计数器可以对分频后的计数时钟进行计数。计数时钟每来一个上升沿,计数器加1。仅支持向上计数
- ARR自动重装载寄存器存储目标值,当CNT的值等于ARR的值,就会产生中断,并且清零CNT。
- 向上折线箭头表示产生中断信号(更新中断),更新中断后通往NVIC,当我们配置好NVIC定时器通道后,就可以得到CPU的响应;
- 向下箭头表示会产生一个事件(更新事件),更新事件不会触发中断,但可以触发其他内部电路工作,这里可以把更新事件映射到触发输出(TRGO),TRGO直接接到DAC触发转换引脚,这样就可以通过硬件自动触发,就不需要频繁中断手动触发DAC转换。
TRGO:代表"Trigger Output"(触发输出)。TRGO是一种特殊的输出信号,用于触发或同步外部设备或其他STM32微控制器的操作。TRGO通常与定时器(Timer)相关,特别是与定时器的触发功能(Trigger)有关。定时器可以设置为根据某些事件触发并生成TRGO信号,以便在特定的时间点上发送触发脉冲.TRGO信号可以由定时器的各种事件触发,例如计数器溢出、捕获/比较匹配、外部触发事件等。通过将TRGO信号连接到其他外部设备或其他STM32微控制器的输入引脚,可以实现各种应用,例如同步多个设备的操作或触发复杂的时间序列。
通用定时器
- CNT可以支持向上计数,向下计数,中央对齐三种模式。
- 时钟源不仅可以选择内部时钟,还可以选择外部时钟。
- 右下方的是输出比较电路,总共有四个通道,CH1~CH4,可以用于输出PWM波形,驱动电机
- 左下方的是输入捕获电路,也是四个通道,可以用于测量输入方波的频率等
- 中间是捕获/比较寄存器,是输入捕获电路和输出比较电路共用寄存器。
高级定时器
- 重复次数计数器:可以实现每隔几个周期触发一次
- DTG是死区生成电路
- 右边引脚由一个变成了两个互补输出,可以输出一对互补的PWM波形(用于驱动三相无刷电机)
- BRK刹车功能,用于在发生故障的时候切断输出,防止意外发生。
定时中断基本结构
预分频器时序
- 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
计数器时序
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)
RCC时钟树
定时器中断代码实例
- 开启RCC时钟
- 选择时基单元的时钟源
- 配置时基单元
- 使能更新中断
- 配置NVIC
- 运行控制
选择内部时钟
void Timer_Init(void)
{
// 1.使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
// 2.选择内部时钟
TIM_InternalClockConfig(TIM2);
// 3.配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; // 定义一个TIM_TimeBaseInitStruct结构体变量,用于配置定时器的基本参数
// 定时1s就是 72MHz/10000/7200=1s
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频系数为1,即不分频
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数器模式为向上计数
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; // [ARR]设置计数器的周期为10000-1,即计数器从0到9999循环计数
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; // [psc]设置预分频系数为7200-1,将时钟频率分频为1 MHz
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 设置重复计数器的值为0,表示不使用重复计数器
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); // 使用TIM2定时器和上述配置参数进行定时器的初始化设置
// 手动清除更新中断标志位,避免刚初始化玩就进入中断
TIM_ClearFlag(TIM2,TIM_IT_Update);
// 4.使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
// 5.设置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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);
// 启动定时器
TIM_Cmd(TIM2,ENABLE);
}
选择外部时钟
void Timer_Init(void)
{
// 1.使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 2.配置ETR外部时钟2
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_Inverted,0x00);
// 3.配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=10-1;
TIM_TimeBaseInitStruct.TIM_Prescaler=1-1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
// 手动清除更新中断标志位,避免刚初始化玩就进入中断
TIM_ClearFlag(TIM2,TIM_IT_Update);
// 4.使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
// 5.设置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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);
// 启动定时器
TIM_Cmd(TIM2,ENABLE);
}
输出比较
- OC(Output Compare)输出比较
- 输出比较可以通过比较CNT与CCR(捕获/比较)寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
- 每个高级定时器和通用定时器都拥有4个输出比较通道
- 高级定时器的前3个通道额外拥有死区生成和互补输出的功能
PWM简介
- PWM(Pulse Width Modulation)脉冲宽度调制
- 在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
- PWM参数:频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
输出比较通道(通用定时器):
输出比较模式:
PWM基本结构
- 黄色线:ARR
- 蓝色线:CNT
- 红色线:CCR
PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比:Duty = CCR / (ARR + 1)
PWM分辨率:Reso = 1 / (ARR + 1)
PWM示例代码
void PWM_Init(void)
{
// 1.使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出,片上外设来输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 2.选择内部时钟
TIM_InternalClockConfig(TIM2);
// 3.配置时基单元
// 输出频率为1KHz,占空比:50%,
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=100-1; // 周期 ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1; // 预分频器 PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
// 初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitStruct;
// 赋初始值
TIM_OCStructInit(&TIM_OCInitStruct);
// 设置输出比较模式
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
// 设置输出比较极性
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
// 设置输出使能
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
// 设置CCR
TIM_OCInitStruct.TIM_Pulse=0; // CCR 这里利用下面的函数来单独修改CCR的值
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
// 启动定时器
TIM_Cmd(TIM2,ENABLE);
}
输入捕获
- IC(Input Capture)输入捕获
- 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
- 每个高级定时器和通用定时器都拥有4个输入捕获通道
- 可配置为PWMI模式,同时测量频率和占空比
- 可配合主从触发模式,实现硬件全自动测量
频率测量
注意:只有第一条路就只能测频率。
从GPIO输入波形信号后,会经过边缘检测,上升沿触发走TI1FP1通道,选择不分频后,CNT的值会存储到CCR寄存器里,然后选择TI1FP1为触发源信号,从模式选择复位操作,自动清零CNT的值,这里CNT的值就是测周法里的N,fc就是PSC/(预分频系数+1)
注意:可以同时测量周期和占空比
当检测到下降沿就会触发TI1FP2,将此时的CNT的值存储到CCR2里,这就是高电平持续时间的计数值,CCR1是整个周期的值,CCR2/CCR1就是占空比。
输入捕获代码示例
这里采用STM32产生PWM波形,然后输入到STM32的输入捕获
产生PWM波形
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
// 1.使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出,片上外设来输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 2.选择内部时钟
TIM_InternalClockConfig(TIM2);
// 3.配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=100-1; // 周期 ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1; // 预分频器 PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
// 初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitStruct;
// 赋初始值
TIM_OCStructInit(&TIM_OCInitStruct);
// 设置输出比较模式
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
// 设置输出比较极性
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
// 设置输出使能
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
// 设置CCR
TIM_OCInitStruct.TIM_Pulse=0; // CCR 这里利用下面的函数来单独修改CCR的值
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
// 启动定时器
TIM_Cmd(TIM2,ENABLE);
}
// 单独更改通道1的CCR的值
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
// 单独修改PSC
void PWM_SetPrescaler(uint16_t Prescaler)
{
// 立即生效
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);
}
输入捕获
#include "stm32f10x.h" // Device header
/*
* 输入捕获模块
*/
void IC_Init(void)
{
// 1.使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 2.选择内部时钟
TIM_InternalClockConfig(TIM3);
// 3.配置时基单元
// 输出频率为1KHz,占空比:50%,
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=65536-1; // 周期 ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=72-1; // 预分频器 PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
// 4.初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter=0XF;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising; // 上升沿触发
TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; // 不分频
TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI; // 直连通道
TIM_ICInit(TIM3,&TIM_ICInitStruct);
// 5.配置TRGI的触发源为TI1FP1
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
// 6.配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
// 7.启动定时器
TIM_Cmd(TIM3,ENABLE);
}
// 读取CCR
uint32_t IC_GetFreq(void)
{
// Fx=Fc/N Fc=72M/(PSC+1) 这里Fc=1MHz
return 1000000/(TIM_GetCapture1(TIM3)+1);
}
编码器
- Encoder Interface 编码器接口
- 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
- 每个高级定时器和通用定时器都拥有1个编码器接口
- 两个输入引脚借用了输入捕获的通道1和通道2
输入捕获两个通道通过GPIO口接入编码器A,B相,这里的PSC就不用内部时钟,由编码器接口接管。
正转向上计数;反转向下计数
实例(均不反相)
实例(TI1反相)
编码器示例代码
利用定时器每隔一秒进一次中断,获得编码器的值,这就相当于旋转编码器的速度。
#include "stm32f10x.h" // Device header
void Encoder_Init(void)
{
// 1.使能时钟,开启GPIO和定时器的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 2.配置GPIO,这里需要把PA6和PA7配置成输入模式
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);
// 这里编码器会托管时钟(带方向的外部时钟,就不需要选择内部时钟
// TIM_InternalClockConfig(TIM3);
// 3.配置时基单元 预分频器选择不分频(PSC),自动重装器(ARR)给65535
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; // 这里没用到,计数模式也是被编码器托管的(默认就是向上向下都计数)
TIM_TimeBaseInitStruct.TIM_Period=65536-1; // ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=1-1; // 预分频器 PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
// 4.初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICStructInit(&TIM_ICInitStruct); // 给结构体赋初值,结构体配置不完整
TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter=0XF;
TIM_ICInit(TIM3,&TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter=0XF;
TIM_ICInit(TIM3,&TIM_ICInitStruct);
// 配置编码器接口 TI1 TI2都计数,TIM_ICPolarity_Rising:代表不反向
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
// 启动定时器
TIM_Cmd(TIM3,ENABLE);
}
// 获取CNT的值
int16_t Encode_Get(void)
{
int16_t Temp;
Temp=TIM_GetCounter(TIM3);
// 给CNT清零
TIM_SetCounter(TIM3,0);
return Temp;
}