概述
目录
前言
理论学习
一、定时器中断
1.1、时基单元包括:
1.2、通用定时器功能
1.3、计数器模式
1.3 相关寄存器
二、定时器PWM输出
2.1、PWM输入模式
2.2、定时器PWM输出
2.3、PWM输出相关寄存器
三、定时器输入捕获
3.1、简介
3.2、寄存器配置
3.3、发生输入捕获时:
实战演练
一、定时器中断配置步骤
1.1、TIM3 时钟设置与使能
1.2、设置 TIM3_ARR 和 TIM3_PSC的值
1.3、设置 TIM3_DIER 允许更新中断
1.4、允许 TIM3 工作(TIM3_CR1)
1.5、TIM3 中断分组与中断服务函数设置
1.6、主函数的编写
二、定时器PWM输出配置步骤
2.1、配置TIM14的输出端口
2.2、设置 TIM14 的 ARR 和 PSC
2.3、TIM14-CH1设置为PWM输出 模式
2.4、TIMx_CR1控制寄存器 1,使能 TIM14
2.5、主函数的编写和占空比控制
三、定时器输入捕获配置步骤
3.1、配置TIM5输入端口
3.2、设置 TIM5 的 ARR 和 PSC
3.3、TIM5-CH1(TI1)设置为输入捕获模式
3.4、设置 TIM5->DIER,使能捕获和更新中断。
3.5、TIMx_CR1控制寄存器 1,使能 TIM5
3.6、设置中断分组及编写中断函数
3.7、输入捕获主函数
前言
使用的是正点原子的探索者开发板进行学习,芯片:STM32F407ZGTx
学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。
本文参考了《正点原子的寄存器开发指南》、《STM32F4参考手册》
详细介绍的时钟的配置与,定时器时钟如何从时钟树而来的。
理论学习
包括TIM1和TIM8高级控制定时器、TIM2-5和TIM9-14通用定时器、TIM6和TIM7基本定时器。
一、定时器中断
1.1、时基单元包括:
- 计数器寄存器 (TIMx_CNT)
- 预分频器寄存器 (TIMx_PSC)
- 自动重载寄存器 (TIMx_ARR)
1.2、通用定时器功能
- 16 位/32 位(仅 TIM2 和 TIM5)向上、向下、向上/向下自动装载计数器(TIMx_CNT),注意:TIM9~TIM14 只支持向上(递增)计数方式。
- 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。
- 4 个独立通道(TIMx_CH1~4,TIM9~TIM14 最多 2 个通道),这些通道可以用来作为:
- 4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
- 5)如下事件发生时产生中断/DMA(TIM9~TIM14 不支持 DMA):
1.3、计数器模式
- 递增计数模式
- 预分频器的缓冲区中将重新装载预装载值(TIMx_PSC 寄存器的内容)
- 自动重载影子寄存器将以预装载值进行更新
- 递减计数模式
- 预分频器的缓冲区中将重新装载预装载值(TIMx_PSC 寄存器的内容)。
-
自动重载活动寄存器将以预装载值( TIMx_ARR 寄存器的内容)进行更新。注意,自动重载寄存器会在计数器重载之前得到更新,因此,下一个计数周期就是我们所希望的新的周期长度。
-
中心对齐模式(递增 / 递减计数)
- 预分频器的缓冲区中将重新装载预装载值(TIMx_PSC 寄存器的内容)。
-
自动重载活动寄存器将以预装载值 ( TIMx_ARR 寄存器的内容)进行更新。注意,如果更新操作是由计数器上溢触发的,则自动重载寄存器在重载计数器之前更新,因此,下一个计数周期就是我们所希望的新的周期长度(计数器被重载新的值)。
1.3 相关寄存器
-
TIMx_CR1控制寄存器
-
TIMx_DIER中断/DMA使能寄存器
- TIMx_PSC预分频器
-
TIMx_CNT 计数器(存储计数值)
-
TIMx_ARR自动重装载寄存器
二、定时器PWM输出
2.1、PWM输入模式
- 两个 ICx 信号被映射至同一个 TIx 输入。
- 这两个 ICx 信号在边沿处有效,但极性相反。
- 选择两个 TIxFP 信号之一作为触发输入,并将从模式控制器配置为复位模式。
2.2、定时器PWM输出
STM32F4 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出!这里我们仅使用 TIM14 的 CH1 产生一路 PWM 输出。
关于PWM在FPGA上的产生更加有助于理解
二、15【FPGA】呼吸灯实现_追逐者-桥的博客-CSDN博客_fpga 呼吸灯
- 比较结果发生改变,或 输出比较模式(TIMx_CCMRx 寄存器中的 OCxM 位)从“冻结”配置(不进行比较, OCxM=“000”)切换为任一 PWM 模式(OCxM=“110”或“111”)。
2.3、PWM输出相关寄存器
除了使用到了定时器中断的那几个寄存器外还用到了以下寄存器
-
TIMx_CCMR1/2捕获 / 比较模式寄存器
-
TIMx_CCER捕获 / 比较使能寄存器
-
TIMx_CCR1~4捕获 / 比较寄存器
三、定时器输入捕获
3.1、简介
在 t1~t2 之间,可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高电平太长,导致数据不准确。如图15.1.1所示,t1~t2之间,CNT计数的次数等于:N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。
STM32F4 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。
3.2、寄存器配置
-
选择有效输入: TIMx_CCR1 必须连接到 TI1 输入,因此向 TIMx_CCMR1 寄存器中的CC1S 位写入 01 。只要 CC1S 不等于 00 ,就会将通道配置为输入模式,并且 TIMx_CCR1寄存器将处于只读状态。
-
根据连接到定时器的信号,对所需的输入滤波时间进行编程(如果输入为 TIx 输入之一,则对 TIMx_CCMRx 寄存器中的 ICxF 位进行编程)。假设信号变化时,输入信号最多在 5 个内部时钟周期内发生抖动。因此, 我们必须将滤波时间设置为大于 5 个内部时钟周期 。在检测到 8 个具有新电平的连续采样(以 f DTS 频率采样)后,可以确认 TI1上的跳变沿。然后向 TIMx_CCMR1 寄存器中的 IC1F 位写入 0011。
-
通过向 TIMx_CCER 寄存器中的 CC1P 位和 CC1NP 位写入 0 ,选择 TI1 通道的有效转换边沿(本例中为上升沿)。
-
对输入预分频器进行编程。在本例中,我们希望每次有效转换时都执行捕获操作,因此需要禁止预分频器(向 TIMx_CCMR1 寄存器中的 IC1PS 位写入 00 )。
-
通过将 TIMx_CCER 寄存器中的 CC1E 位置 1 ,允许将计数器的值捕获到捕获寄存器中。
-
如果需要,可通过将 TIMx_DIER 寄存器中的 CC1IE 位置 1 来使能相关中断请求,并且 /或者通过将该寄存器中的 CC1DE 位置 1 来使能 DMA 请求。
3.3、发生输入捕获时:
-
发生有效跳变沿时, TIMx_CCR1 寄存器会获取计数器的值。
-
将 CC1IF 标志置 1 (中断标志)。如果至少发生了两次连续捕获,但 CC1IF 标志未被清零,这样 CC1OF 捕获溢出标志会被置 1 。
-
根据 CC1IE 位生成中断。
-
根据 CC1DE 位生成 DMA 请求。
实战演练
一、定时器中断配置步骤
1.1、TIM3 时钟设置与使能
//设置时钟频率:HSE=8M M=8 N=336 P=2 Q=7
//PLL=HSE/M=1M PLLCLK=PLL*N/P=168M USB=PLL*N/Q=48M
Stm32_Clock_Init(336,8,2,7);
- RCC_CFGR RCC时钟配置寄存器
想CFGR[12:10]位写5,即4分频,因此APB1=PLLCLK/4=42M
RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);
- TIM3 时钟使能
TIM3挂载在APB1时钟总线上,
TIM3由于在配置APB1的时候使用了4分频,所以定时器进行了2倍频,为84MHz。
RCC->APB1ENR |= 1<<1;
1.2、设置 TIM3_ARR 和 TIM3_PSC的值
时间计算公式:Tout = ( (arr+1) * (psc+1) ) / Tclk;
TIM3->ARR=arr; //设置TIM3的自动装载值
TIM3->PSC=psc; //预分频器设置
1.3、设置 TIM3_DIER 允许更新中断
TIM3->DIER|=1<<0;
1.4、允许 TIM3 工作(TIM3_CR1)
TIM3->CR1|=0x01;
使能定时计数器器,且为递增计数
1.5、TIM3 中断分组与中断服务函数设置
- 中断分组,TIM3优先级配置
MY_NVIC_Init(1,3,TIM3_IRQn,2);
- 编写中断服务函数
TIMX-SR状态寄存器:
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001) //中断发生,最低位由硬件置1
{
LED1=!LED1;
}
TIM3->SR&=~(1<<0); //必须将最低位软件置0,等待下次中断的到来
}
1.6、主函数的编写
TIM(定时器)=84MHz psc(分频系数)=8400 arr(重装在值)=5000
定时器计数频率 = 84MHz / 8400 =10KHz
Tout = 5000 / 10KHz = 0.5s
int main(void)
{
Stm32_Clock_Init(336,8,2,7); //pll=1M pllclk=168M
delay_init(168);
LED_Init();
TIM3_Int_Init(5000-1,8400-1); //arr psc
while(1)
{
LED0=!LED0;
delay_ms(200);
};
}
二、定时器PWM输出配置步骤
2.1、配置TIM14的输出端口
RCC->APB1ENR|=1<<8; //使能TIM13定时器的时钟
RCC->AHB1ENR|=1<<5; //使能端口FA9的端口时钟
GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);
GPIO_AF_Set(GPIOF,9,9); //PF9,AF9
2.2、设置 TIM14 的 ARR 和 PSC
TIM14->ARR=arr; //重装载值
TIM14->PSC=psc; //分频系数
2.3、TIM14-CH1设置为PWM输出 模式
TIM14-CCMR1捕获比较寄存器1相关位描述
TIMx_CCER捕获/比较使能寄存器相关位描述
TIM14->CCMR1|=6<<4; //打开TIM14的CH1
TIM14->CCMR1|=1<<3; //使能与 TIM14_CCR1 相关的预装载寄存器
TIM14->CCER|=1<<0; //下降沿触发,低电平有效
TIM14->CCER|=1<<1; //使能CH1输出
2.4、TIMx_CR1控制寄存器 1,使能 TIM14
TIM14->CR1|=1<<7; //自动装载进行缓存
TIM14->CR1|=1<<0; //定时计数器使能
2.5、主函数的编写和占空比控制
#define LED0_PWM_VAL TIM14->CCR1
int main(void)
{
u16 led0pwmval=0; //TIM14-CCR1的值,及占空比的计数值
u8 dir=1;
Stm32_Clock_Init(336,8,2,7);
delay_init(168);
TIM14_PWM_Init(500-1,84-1); //1MHz的计数频率,PWM频率2KHz, T=0.5ms
while(1)
{
delay_ms(10); //10ms重载一次占空比,20次PWM的输出
if(dir)led0pwmval++; //占空比增加
else led0pwmval--; //占空比减小
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
LED0_PWM_VAL=led0pwmval; //重新装载占空比值
}
}
三、定时器输入捕获配置步骤
3.1、配置TIM5输入端口
RCC->APB1ENR|=1<<3;
RCC->AHB1ENR|=1<<0;
GPIO_Set(GPIOA,PIN0,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PD);
GPIO_AF_Set(GPIOA,0,2); //PA0,AF2 (TIM5-CH1)
3.2、设置 TIM5 的 ARR 和 PSC
计数分频系数与计数器数设置,在定时器中断有相关描述
TIM5->ARR=arr;
TIM5->PSC=psc;
3.3、TIM5-CH1(TI1)设置为输入捕获模式
TIM5-CCMR1捕获比较寄存器1相关位描述
TIM5_CCER捕获/比较使能寄存器相关位描述
TIM5->CCMR1|=1<<0; //CCIS位,将CC1设置成输入且映射到TI1
TIM5->CCMR1|=0<<4; //IC1PSC,无分频
TIM5->CCMR1|=0<<10; //IC1F,无滤波器
TIM5->CCER|=0<<1; //CC1P,无反向,上升沿触发
TIM5->CCER|=1<<0; //CC1E,使能输入捕获
3.4、设置 TIM5->DIER,使能捕获和更新中断。
TIM5->DIER|=1<<1;
TIM5->DIER|=1<<0;
//设置软件控制产生更新事件,是载入PSC值立刻生效,否则要等到溢出后才生效
TIM5->EGR=1<<0;
3.5、TIMx_CR1控制寄存器 1,使能 TIM5
TIM5->CR1|=0x01;
3.6、设置中断分组及编写中断函数
MY_NVIC_Init(2,0,TIM5_IRQn,2)
u8 TIM5CH1_CAPTURE_STA=0; //捕获标志0x40上升沿捕获,0x80下降沿捕获
u32 TIM5CH1_CAPTURE_VAL; //捕获计数值存储空间
void TIM5_IRQHandler(void)
{
u16 tsr;
tsr=TIM5->SR; //定时器状态寄存器
if((TIM5CH1_CAPTURE_STA&0X80)==0) //捕获未完成
{
if(tsr&0X01) //定时器溢出标志
{
if(TIM5CH1_CAPTURE_STA&0X40) //上升沿被捕获到后
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F) //达到计数器溢出值
{
TIM5CH1_CAPTURE_STA|=0X80; //标记捕获到了一个定时计数器周期
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF; //保存一个完成的计数周期
}else TIM5CH1_CAPTURE_STA++; //计数器未溢出
}
}
if(tsr&0x02) //发生上升沿捕获事件进入中断
{
if(TIM5CH1_CAPTURE_STA&0X40) //上升沿捕获已完成,下降沿捕获中断进入
{
TIM5CH1_CAPTURE_STA|=0X80; //已经捕获到下降沿标志
TIM5CH1_CAPTURE_VAL=TIM5->CCR1; //储存捕获计数器值
TIM5->CCER&=~(1<<1); //设置为上升沿捕获进入中断
}else //将计数器清零,重新计数
{
TIM5CH1_CAPTURE_STA=0;
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //已经捕获到上升沿标志
TIM5->CR1&=~(1<<0); //使能定时器
TIM5->CNT=0; //清空计数器
TIM5->CCER|=1<<1; //设置为下降沿捕获进入中断
TIM5->CR1|=0x01; //开启定时器
}
}
}
TIM5->SR=0;
}
3.7、输入捕获主函数
extern u8 TIM5CH1_CAPTURE_STA;
extern u32 TIM5CH1_CAPTURE_VAL;
int main(void)
{
long long temp=0;
Stm32_Clock_Init(336,8,2,7);
delay_init(168);
uart_init(84,115200);
TIM14_PWM_Init(500-1,84-1);
TIM5_CH1_Cap_Init(0XFFFFFFFF,84-1);
while(1)
{
delay_ms(10);
LED0_PWM_VAL++;
if(LED0_PWM_VAL==300)LED0_PWM_VAL=0;
if(TIM5CH1_CAPTURE_STA&0X80) //捕获到了一个定时器计数周期或者下降沿
{
temp=TIM5CH1_CAPTURE_STA&0X3F; //提取捕获值
temp*=0XFFFFFFFF; //计算捕获时间
temp+=TIM5CH1_CAPTURE_VAL; //加入定时器周期时间和下降沿捕获时间
printf("HIGH:%lld usrn",temp); //输出捕获总时间
TIM5CH1_CAPTURE_STA=0; //将捕获标志位清0,进入下一次捕获
}
}
}
输入捕获功能应用
电容触摸按键:电容按键是接触式的,点一下就松开(与微动开关类似),因此需要消除抖。在之前的微动开关使用时间延迟判断两次,状态相同时才认为是按下。这里同理,这里使用的是输入捕获即手指接触一定时间后到达B认为电容按键按下。
正点原子中的文档内容描述:
对于电容按键用的不是特别多,这里不进行实验,有需要的可以自己去看一下正点原子的相关实验。
最后
以上就是迅速季节为你收集整理的8、【STM32】定时器(TIM)——中断、PWM、输入捕获实验(一文精通定时器)前言理论学习实战演练 输入捕获功能应用的全部内容,希望文章能够帮你解决8、【STM32】定时器(TIM)——中断、PWM、输入捕获实验(一文精通定时器)前言理论学习实战演练 输入捕获功能应用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复