赞
踩
一说到定时器,大家应该会比较熟悉了。比如我们煮鸡蛋需要5分钟,我们就会习惯性的打开手机上的计时功能。那stm32单片机的定时器是怎么样的呢。
参考博客: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 中断优先级管理,抢占优先级,响应优先级,中断寄存器
这里我们初始化的是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分频
接着需要介绍自动重装载寄存器TIMx_ARR
。可以看出这个寄存器可以存放的数据为0-65535。
TIM3->ARR = 9999; //设置自动装载值
接着需要介绍预分频寄存器TIMx_PSC
。可以看出这个寄存器可以存放的数据为0-65535。
TIM3->PSC = 7199; //设置预分频值
接着需要介绍事件产生寄存器TIMx_EGR
。
TIM3->EGR = (uint16_t)0x0001;
其中AIRCR_VECTKEY_MASK的值如下图所示。
分组可以看出有5种选择,这里我们选择NVIC_PriorityGroup_2
。
那么最终传入的AIRCR寄存器的的内容为0x05FA0500
下图是AIRCR寄存器的具体图。如果想要对AIRCR寄存器做修改,高16位一定要写入0x05FA
手册查看地址:编程手册
参考博客: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
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上的更新中断标志位
}
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。满足我们的要求。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。