文章目录
1 前言
一说到定时器,大家应该会比较熟悉了。比如我们煮鸡蛋需要5分钟,我们就会习惯性的打开手机上的计时功能。那stm32单片机的定时器是怎么样的呢。
2 提前了解的知识
2.0 单片机通用定时器框图
2.1 几个重要的结构体
参考博客:stm32f103c8t6使用定时器实现1s翻转LED+cubemx生成
变量TIM_Prescaler
是指定时器的预分频值(Prescaler value)。
变量TIM_CounterMode
是指定时器的计数模式。
变量TIM_Period
是指定时器的自动重装载值(Auto-reload value)。
变量TIM_ClockDivision
是指定时器时钟分割。
变量TIM_RepetitionCounter
是指定时器重复计数(我们一般不适用)。
参考博客:stm32定时器–通道pwm输出参数TIM_OCInitStructure如何配置
变量TIM_OCMode
是指模式选择。
变量TIM_OutputState
是指输出状态。
变量TIM_OutputNState
是指互补通道的输出状态。
变量TIM_Pulse
是指占空比。
变量TIM_OCPolarity
是指输出极性。
变量TIM_OCNPolarity
是指互补通道的输出极性。
变量TIM_OCIdleState
是指空闲状态。
变量TIM_OCNIdleState
是指互补通道的空闲状态。
变量NVIC_IRQChannel
是指中断通道。
变量NVIC_IRQChannelPreemptionPriority
是指抢占优先级。
变量NVIC_IRQChannelSubPriority
是指子优先级。
变量NVIC_IRQChannelCmd
是通道使能。
关于中断通道,在stm32f10x.h
中有定义的。因为我们使用的是TIM3定时器,这里我们使用的是TIM3_IRQn
。
关于抢占优先级和子优先级的取值见下图。下图可以看出,当我们采用不同的优先组的时候,两个变量的取值也是不同的。
参考博客:STM32】NVIC 中断优先级管理,抢占优先级,响应优先级,中断寄存器
3 库函数解析
3.1 TIM_TimeBaseInit函数介绍
3.1.1 源码展示
3.1.2 TIMx_CR1寄存器内容修改
这里我们初始化的是TIM3。
首先介绍下图所示的控制寄存器TIMx_CR1
。
这里采用向上计数的模式,所以需要修改寄存器的4、5、6位。
TIM3->CR1 &= (uint16_t)0xFF8F; //CMS[1:0]和DIR位清零
TIM3->CR1 |= (uint16_t)0x0000; //设置计数为向上计数
这里采用一分频,所以需要修改寄存器的8、9位。
TIM3->CR1 &= (uint16_t)0xFFCF; //CKD[1:0]位清零
TIM3->CR1 |= (uint16_t)0x0000; //设置1分频
3.1.3 TIMx_ARR寄存器内容修改
接着需要介绍自动重装载寄存器TIMx_ARR
。可以看出这个寄存器可以存放的数据为0-65535。
TIM3->ARR = 9999; //设置自动装载值
3.1.4 TIMx_PSC寄存器内容修改
接着需要介绍预分频寄存器TIMx_PSC
。可以看出这个寄存器可以存放的数据为0-65535。
TIM3->PSC = 7199; //设置预分频值
3.1.5 TIMx_EGR寄存器内容修改
接着需要介绍事件产生寄存器TIMx_EGR
。
TIM3->EGR = (uint16_t)0x0001;
3.2 NVIC_PriorityGroupConfig函数介绍
3.2.1 源码展示
3.2.2 AIRCR寄存器内容修改
其中AIRCR_VECTKEY_MASK的值如下图所示。
分组可以看出有5种选择,这里我们选择NVIC_PriorityGroup_2
。
那么最终传入的AIRCR寄存器的的内容为0x05FA0500
下图是AIRCR寄存器的具体图。如果想要对AIRCR寄存器做修改,高16位一定要写入0x05FA
手册查看地址:编程手册
3.3 NVIC_Init函数介绍
3.3.1 源码展示
参考博客:NVIC_Init(&NVIC_InitStructure)学习
首先咱们先看第一句,tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
其中有一个SCB->AIRCR
很重要。下图可SCB_BASE为0xE000ED00
再看((SCB->AIRCR) & (uint32_t)0x700)
这个语句首先读取AIRCR寄存器中的值,
然后对8、9、10位进行保留,其他位进行清零操作。(注意:这里只是读取AIRCR寄存器,而不是对寄存器进行操作)。这里得到的值应该是0x500
。
再看(0x700 - ((SCB->AIRCR) & (uint32_t)0x700))
,这里得到的值为0x200
。
再看tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08
,这里得到的值为tmppriority =2
。
接着我们看第二句话,tmppre = (0x4 - tmppriority)
,这里的tmppre = 2
。
接着我们看第三句话,tmpsub = tmpsub >> tmppriority;
,初始的tmpsub为0x0F(0b00001111),然后右移动2位,最后得到的tmpsub=0b00000011
。
接着我们看第四句话,tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre
,这里假设NVIC_IRQChannelPreemptionPriority
的值为1,然后向左移动2位,最后得到tmppriority=4
。
接着我们看第五句话,tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub
,这里假设NVIC_IRQChannelSubPriority
的值为2(0b00000010),经过运算后得到tmppriority=0b00000110
。
接着我们看第六句话,tmppriority = tmppriority << 0x04;
,经过计算后得到tmppriority = 0b01100000
。
接着我们看第七句话,NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
因为我们使用的是TIM3定时器,这里我们使用的是TIM3_IRQn
。TIM3_IRQn
下图可以看出为29。也就是说NVIC->IP[29] = tmppriority
参考博客:玩转STM32寄存器(关于NVIC的寄存器配置)
接着我们看第八句话,NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
比如现在选择的通道是TIM3_IRQn
,TIM3_IRQn
表示数字为29。 29>>5为0。
即NVIC->ISER[0] = 1<< 29
3 代码分享
main.c文件。
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "tim.h"
#include "stdio.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组配置
RCC->APB2ENR |= (uint32_t)0x00000010;//打开GPIOC时钟
GPIOC->CRH &= (uint32_t)0xFF0FFFFF;//使用前清零
GPIOC->CRH |= (uint32_t)0x00300000;//配置PC13为推挽输出,最大速度50MHz
Delay_Init();//延时初始化
Usart_Init(115200);//串口初始化
Timer_Init(9999,7199); //(9999+1)*(7199+1)/72mhz = 1s
printf("hello world!\r\n");
while(1)
{
//GPIOC->BSRR = (uint32_t)0x00002000;//PC13引脚输出高电平
//Delay_ms(500); //延时500ms
//GPIOC->BRR = (uint16_t)0x2000; //PC13引脚输出低电平
//Delay_ms(500); //延时500ms
}
}
tim.c文件。
#include "stm32f10x.h"
#include "tim.h"
//uint32_t my_tmppriority = 0x00,my_tmppre = 0x00,my_tmpsub = 0x0F;
void Timer_Init(uint16_t per,uint16_t psc)
{
//库函数版本
//----------------------------------------------------------------//
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = per;
TIM_TimeBaseInitStruct.TIM_Prescaler = psc;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM3,ENABLE);
//----------------------------------------------------------------//
//寄存器版本
//----------------------------------------------------------------//
// RCC->APB1ENR |= (uint32_t)0x00000002; //打开定时器3的时钟
// TIM3->CR1 &= (uint16_t)0xFF8F; //CMS[1:0]和DIR位清零
// TIM3->CR1 |= (uint16_t)0x0000; //设置计数为向上计数
//
// TIM3->CR1 &= (uint16_t)0xFCFF; //CKD[1:0]位清零
// TIM3->CR1 |= (uint16_t)0x0000; //设置1分频
//
// TIM3->ARR = 9999; //设置自动装载值
// TIM3->PSC = 7199; //设置预分频值
//
// TIM3->EGR = (uint16_t)0x0001;
//
// TIM3->DIER |= (uint16_t)0x0001;
//
// my_tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
// my_tmppre = (0x4 - my_tmppriority);
// my_tmpsub = my_tmpsub >> my_tmppriority;
// my_tmppriority = 1 << my_tmppre; //抢占优先级为1
// my_tmppriority |= 2 & my_tmpsub;//子优先级为2
// my_tmppriority = my_tmppriority << 0x04;
//
// NVIC->IP[TIM3_IRQn] = my_tmppriority;
// NVIC->ISER[TIM3_IRQn >> 0x05] =(uint32_t)0x01 << (TIM3_IRQn & (uint8_t)0x1F);//开启通道
// //NVIC->ISER[0] =1<<29;
// TIM3->CR1 |= (uint16_t)0x0001;
//----------------------------------------------------------------//
}
tim.h文件。
#ifndef __TIM_H
#define __TIM_H
#include "stm32f10x.h"
void Timer_Init(uint16_t per,uint16_t psc);
#endif
stm32f10x_it.c文件
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)
{
GPIOC->ODR ^= (uint16_t)0x2000;
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3上的更新中断标志位
}
4 逻辑分析仪查看定时时间
main.c文件。
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "tim.h"
#include "stdio.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组配置
RCC->APB2ENR |= (uint32_t)0x00000010;//打开GPIOC时钟
RCC->APB2ENR |= (uint32_t)0x00000008;//打开GPIOB时钟
GPIOC->CRH &= (uint32_t)0xFF0FFFFF;//使用前清零
GPIOC->CRH |= (uint32_t)0x00300000;//配置PC13为推挽输出,最大速度50MHz
GPIOB->CRL &= (uint32_t)0xFFFFFFF0;//使用前清零
GPIOB->CRL |= (uint32_t)0x00000003;//配置PB0为推挽输出,最大速度50MHz
Delay_Init();//延时初始化
Usart_Init(115200);//串口初始化
Timer_Init(9999,7199); //(9999+1)*(7199+1)/72mhz = 1s
printf("hello world!\r\n");
while(1)
{
//GPIOC->BSRR = (uint32_t)0x00002000;//PC13引脚输出高电平
//Delay_ms(500); //延时500ms
//GPIOC->BRR = (uint16_t)0x2000; //PC13引脚输出低电平
//Delay_ms(500); //延时500ms
}
}
tim.c文件。
#include "stm32f10x.h"
#include "tim.h"
//uint32_t my_tmppriority = 0x00,my_tmppre = 0x00,my_tmpsub = 0x0F;
void Timer_Init(uint16_t per,uint16_t psc)
{
//库函数版本
//----------------------------------------------------------------//
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = per;
TIM_TimeBaseInitStruct.TIM_Prescaler = psc;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM3,ENABLE);
//----------------------------------------------------------------//
//寄存器版本
//----------------------------------------------------------------//
// RCC->APB1ENR |= (uint32_t)0x00000002; //打开定时器3的时钟
// TIM3->CR1 &= (uint16_t)0xFF8F; //CMS[1:0]和DIR位清零
// TIM3->CR1 |= (uint16_t)0x0000; //设置计数为向上计数
//
// TIM3->CR1 &= (uint16_t)0xFCFF; //CKD[1:0]位清零
// TIM3->CR1 |= (uint16_t)0x0000; //设置1分频
//
// TIM3->ARR = 9999; //设置自动装载值
// TIM3->PSC = 7199; //设置预分频值
//
// TIM3->EGR = (uint16_t)0x0001;
//
// TIM3->DIER |= (uint16_t)0x0001;
//
// my_tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
// my_tmppre = (0x4 - my_tmppriority);
// my_tmpsub = my_tmpsub >> my_tmppriority;
// my_tmppriority = 1 << my_tmppre; //抢占优先级为1
// my_tmppriority |= 2 & my_tmpsub;//子优先级为2
// my_tmppriority = my_tmppriority << 0x04;
//
// NVIC->IP[TIM3_IRQn] = my_tmppriority;
// NVIC->ISER[TIM3_IRQn >> 0x05] =(uint32_t)0x01 << (TIM3_IRQn & (uint8_t)0x1F);//开启通道
// //NVIC->ISER[0] =1<<29;
// TIM3->CR1 |= (uint16_t)0x0001;
//----------------------------------------------------------------//
}
tim.h文件。
#ifndef __TIM_H
#define __TIM_H
#include "stm32f10x.h"
void Timer_Init(uint16_t per,uint16_t psc);
#endif
stm32f10x_it.c文件。
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)
{
GPIOC->ODR ^= (uint16_t)0x2000;
GPIOB->ODR ^= (uint16_t)0x0001;
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3上的更新中断标志位
}
通过逻辑分析仪抓取波形如下。可以看出单片机的PB0引脚高与低电平各占1s。满足我们的要求。
文章评论