在之前我们讲到过滴答时钟,也就是滴答定时器,这节我们讲TIM定时器。
一、定时器分类
STM32有很多定时器,大家可能会搞混,这里简单讲解一下,定时器有两大类,第一类就是内核中的滴答定时器(SysTick),也叫系统滴答定时器,另一类就是STM32中的定时器,TIM定时器。
STM32定时器:1、滴答定时器(内核中)
2、TIM定时器(内核外)
(1):高级控制定时器(TIM1和TIM8)
(2):通用定时器(TIM2——TIM5)
(3):基本定时器(TIM6和TIM7)
二、定时器位置
可以看到,TIM1和TIM8在APB2高速总线上,而剩下的定时器都在APB1低速总线上。
三、定时器计数模式
1、向下计数模式
向下计数模式,就是计数器中填入一个自动加载值,这个值向下计数(倒数)至0,然后产生一个下溢事件,计数器重新装填。滴答定时器只有向下计数模式。
2、向上计数模式
同理,向上计数模式,就是计数器中填入一个自动加载值,计数器从0开始向上计数(正数)至填入的自动加载值,然后产生一个上溢事件,计数器重新装填。
3、向上向下双向计数模式(中央对齐模式)
理解了向下计数和向上计数,那么向上向下双向计数模式顾名思义,就是计数器从0计数到 自动装载值-1,产生一个上溢事件,然后向下计数到01,并且产生一个计数器下溢事件,然后再从0开始重新计数。
四、TIM定时器
1、高级控制定时器
高级控制定时器(TIM1和TIM8)由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动。 它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、PWM、嵌入死区时间的互补PWM等)。 使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。 高级控制定时器(TIM1和TIM8)和通用定时器(TIMx)是完全独立的,它们不共享任何资源。它们可以同步操作。
2、通用定时器
通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。 它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作,参
3、基本定时器
基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。 它们可以作为通用定时器提供时间基准,特别地可以为数模转换器(DAC)提供时钟。实际上,它们在芯片内部直接连接到DAC并通过触发输出直接驱动DAC。 这2个定时器是互相独立的,不共享任何资源。
五、定时器寄存器
定时器的寄存器比较复杂,在这里只介绍有关任务的寄存器。剩下的寄存器,可以查阅数据手册进行更多的了解
1、TIMx_CR1(控制寄存器 1)
这个寄存器我们经常只使用这两位
2、TIMx_DIER(DMA/中断使能寄存器)
这个寄存器只使用第0位
3、TIMx_SR(状态寄存器)
状态寄存器我们使用第0位
4、 TIMx_PSC(预分频器)
该寄存器用设置对时钟进行分频,然后提供给计数器,作为计数器的时钟,定时器的时钟源有4个:
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用A定时器作为B定时器的预分频器
如上四个时钟源由TIMx_SMCR寄存器设置。
4、 TIMx_ARR(自动重装载寄存器)
六、代码
一、TIM.c
#include "stm32f10x.h"
#include "led.h"
#include "stdio.h"
#include "tim.h"
int count=0;
void TIM4_NVIC(void)
{
SCB->AIRCR|=0x05FA0500;
NVIC->ISER[0]|=0x40000000;
NVIC->IP[30]|=15<<4;
}
void TIM4_Init(unsigned int ARR,unsigned int PSC)
{
RCC->APB1ENR|=1<<2;
TIM4->ARR=ARR;
TIM4->PSC=PSC;
TIM4->DIER|=1<<0;
TIM4->CR1|=0x01;
TIM4_NVIC();
}
void TIM4_IRQHandler(void)
{
if(TIM4->SR&(1<<0))
{
LED1=~(LED1);
}
TIM4->SR&=~(1<<0);
}
二、usart.c
#include "stm32f10x.h"
#include "tim.h"
#include "stdio.h"
#include "usart.h"
#include "delay.h"
void menu(void)
{
printf("\r\n请选择你要设置的定时,开启或改变LED:\r\n");
printf("\r\n1、200ms闪烁一次\r\n");
printf("\r\n2、500ms闪烁一次\r\n");
printf("\r\n3、1000ms闪烁一次\r\n");
}
void USART1_Init_NVIC(void)
{
RCC->APB2ENR|=1<<0;
SCB->AIRCR|=0X05FA5000;
NVIC->ISER[1]|=0x0000020;
NVIC->IP[1]|=0x0000F000;
}
void USART1_PUT(unsigned char C)
{
while((!(USART1->SR&(1<<6)))&&(!(USART1->SR&(1<<7))));
USART1->DR=C;
}
int fputc(int ch, FILE *f)
{
USART1_PUT(ch);
return ch;
}
void USART1_Init(void)
{
RCC->APB2ENR|=1<<2;
RCC->APB2ENR|=1<<14;
RCC->APB2RSTR|=1<<14;
RCC->APB2RSTR&=~(1<<14);
GPIOA->CRH&=0xFFFFF00F;
GPIOA->CRH|=0x00000490;
USART1->BRR|=468<<4|12;
USART1->CR1&=~(1<<12);
USART1->CR1&=~(1<<10);
USART1->CR1|=1<<3;
USART1->CR1|=1<<2;
USART1->CR1|=1<<5;
USART1->CR1|=1<<13;
USART1_Init_NVIC();
}
void USART1_IRQHandler()
{
unsigned char num;
num=USART1->DR;
switch(num)
{
case 49:
printf("\r\n当前为200ms闪烁一次\r\n");
count=0;
TIM4_Init(1999,7199);
break;
case 50:
printf("\r\n当前为500ms闪烁一次\r\n");
count=0;
TIM4_Init(4999,7199);
break;
case 51:
printf("\r\n当前为1000ms闪烁一次\r\n");
count=0;
TIM4_Init(9999,7199);
break;
}
}
三、main.c
#include "delay.h"
#include "led.h"
#include "usart.h"
#include "tim.h"
#include "pwm.h"
int main()
{
System_clock(9);
LED_Init();
USART1_Init();
menu();
while(1)
{
LED0=!LED0;
SysTick_ms(1000);
}
}
七、代码效果
下载程序后串口助手里会显示如图的菜单,我们通过发送对应序号来改变定时器的定时事件,红灯为参考灯,一秒闪烁一次,我们发送了2,也就是定时器为500ms,则绿灯会00ms闪烁一次。
|