![在这里插入图片描述](https://img-blog.csdnimg.cn/ec53f8de22054e7c879947b1c4f4d04e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6KGM6LWw55qE55qu5Y2h5LiY,size_17,color_FFFFFF,t_70,g_se,x_16#pic_center)
嵌入式STM32--实验三 、通用定时器实验
一、实验目的二、实验原理1、STM32通用定时器概述2、时基单元3、工作模式4、时钟选择三、实验内容3.1、结合按键中断和定时器编程实现以下内容:(定时器可采用多个)(代码:实验3-1)3.1.1、按键和LED初始化3.1.2、基本定时器6的初始化3.1.3、基本定时器6的中断函数 (定时1S)3.1.4、基本定时器7的初始化3.1.5、基本定时器7的中断函数 (定2S)3.1.6、外部中断初始化3.1.7、外部中断函数3.1.7、主函数,其中实现LED1、LED2、LED3正向流水灯功能(即依次顺序点亮,从上到下),LED间隔时间0.2S。
3.2、编程利用PWM实现LED1呼吸灯功能:LED1逐步增亮,然后逐步变暗,如此循环。(代码:实验3-2)3.2.1、通用定时器3的初始化(用于PWM-呼吸灯)3.2.2、呼吸灯实验的主函数
四、实验总结实验三代码下载链接
自律 学习 坚强 ,拒绝迷茫。
作者:行走的皮卡丘
时间:2021/4/4
喜欢就去追,这个红灯等不到,说不定下一个红灯等到了,嘻嘻!!!!!!!
一、实验目的
1、掌握STM32定时器设计流程。 2、熟悉STM32定时器中断设计流程。
二、实验原理
1、STM32通用定时器概述
STM32的通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。 STM32有4个通用定时器(TIM2、TIM3、TIM4、TIM5),它们适用于多种场合,除了基本的定时,它主要用在测量输入脉冲的频率、脉冲宽与输出PWM脉冲的场 合,还具有编码器的接口。每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。
![img](https://img-blog.csdnimg.cn/img_convert/8322da8775453641d6d14d29d3249068.png#pic_center)
2、时基单元
这个部分与普通单片机的定时器相似,由3个小单元组成,分别是:
计数器(CNT) ; 预分频器(PSC) 自动装载寄存器 (ARR)。 三者的关系是如下图:
![img](https://img-blog.csdnimg.cn/img_convert/0d85d1d3b68a6ab107608cd83758f05d.png#pic_center)
计数器可以向上计数、向下计数或者向上向下双向计数,计数器时钟由预分频器分频输出提供。 预分频器是基于一个16位寄存器(TIMx_PSC)控制的16位计数器。它可以将计数器的时钟频率按1到65536之间的任意值分频**(分频值为TIMx_PSC寄存器的值+1)**。 则定时器的工作频率计算公式为CK_CNT=定时器时钟/ (TIMx_PSC +1) 由此我们可得到STM32单片机1个时钟周期为: T=1/ CK_CNT 例如普通定时器模块的时钟为72MHz,分频比位7199,那么我们想要得到一个1秒钟的定时,定时计数器的值需要设定为
TIMx_ARR = 10 000
因为72 000 000 / 7200 = 10KHz *// 7199=7200-1* 时钟周期T=1/10KHz=100us 100us × 10 000 = 1S
结论 :分频比7199 定时计数器的值 10 000
3、工作模式
通用定时器的计数器功能的3种工作模式:
向上计数模式 向下计数模式 中央对齐模式(向上/向下计数)
在向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。
在向下模式中,计数器从自动装入的值(TIMx_ARR计数器的值)开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出事件。
在中央对齐模式,计数器从0开始计数到自动加载的值(TIMx_ARR寄存器)−1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。中央对齐模式又可分为3种不同模式,由控制寄存器 1(TIMx_CR1)的CMS[1:0]设置选择。
4、时钟选择
在时基单元之前,需要选择定时器的时钟源,以提供CK_PSC信号。由通用定时器框图可知道,每个通用定时器的时钟有多种选择:
内部时钟(CK_INT) 外部时钟模式1:外部输入脚(TIx) 外部时钟模式2:外部触发输入(ETR) 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。 具体如何选择,可参考《STM32数据手册》第14章的时钟选择和从模式控制寄存器(TIMx_SMCR)的描述相关内容。
以下通过例程来说明定时器中断步骤:
使用定时器产生中断,然后在中断服务函数里面翻转LED1上的电压信号,来指示定时器中断的产生。以通用定时器TIM3为实例,来说明要经过哪些步骤,
1)TIM3时钟使能。
通过APB1ENR的第1位来设置TIM3的时钟,因为Stm32_Clock_Init函数里面把APB1的分频设置为2,TIM3时钟就是APB1时钟的2倍,等于系统时钟。
2)设置TIM3_ARR和TIM3_PSC的值。
通过这两个寄存器,来设置自动重装的值,以及分频系数。这两个参数加上时钟频率就决定了定时器的溢出时间。
3)设置TIM3_DIER允许更新中断。
要使用TIM3的更新中断,所以设置DIER的UIE位,并使能触发中断。
4)允许TIM3工作。
配置好定时器后,需要开启定时器。通过TIM3_CR1的CEN位来设置。
5)TIM3中断分组设置。
定时器配置完,因为要产生中断,要设置NVIC相关寄存器,以使能TIM3中断。
6)编写中断服务函数。
通过该函数来处理定时器产生的相关中断。在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。然后执行相关的操作,可使用更新(溢出)中断,所以在状态寄存器SR的最低位。在处理完中断之后应该向TIM3_SR的最低位写0,来清除该中断标志。
三、实验内容
1、结合按键中断和定时器编程实现以下内容:(定时器可采用多个)
1)主程序状态如下:实现LED1、LED2、LED3正向流水灯功能(即依次顺序点亮,从上到下),LED间隔时间0.2S。 2)若键盘K2按下,控制LED1以1S为间隔,闪烁3次后退出中断。 3)若键盘K3按下,控制LED2以2S为间隔,闪烁3次后退出中断。 4) 若有同时按下2个按键情况,按照K3 、 K2优先级别从高到低执行。
2、编程利用PWM实现LED1呼吸灯功能:LED1逐步增亮,然后逐步变暗,如此循环。
3.1、结合按键中断和定时器编程实现以下内容:(定时器可采用多个)(代码:实验3-1)
3.1.1、按键和LED初始化
![img](https://img-blog.csdnimg.cn/img_convert/fc82a7bc1aa75424a6f576ec907f6944.png#pic_center)
#include "stm32f10x.h"
#include "delay.h"
/**************************************************
* Function main
* @author 行走的皮卡丘
* @date 2021/4/4
* @brief
* @param[in]
* @param[out] void
* @retval void
* @par History 无
* LED1 ---> PB.0
* LED2 ---> PC.4
* LED3 ---> PC.3
* KEY1 ---> PA0
* KEY2 ---> PC13
*************************************************/
#define LED1 PBout(0)
#define LED2 PCout(4)
#define LED3 PCout(3)
#define KEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键
#define KEY3 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)//读取按键
//LED初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStr;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);//
GPIO_InitStr.GPIO_Mode=GPIO_Mode_Out_PP;//推挽
GPIO_InitStr.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStr.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStr); // PB0 LED1
GPIO_InitStr.GPIO_Pin=GPIO_Pin_4;
GPIO_Init(GPIOC,&GPIO_InitStr); // PC4 LED2
GPIO_InitStr.GPIO_Pin=GPIO_Pin_3;
GPIO_Init(GPIOC,&GPIO_InitStr); // PC3 LED3
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_SetBits(GPIOC,GPIO_Pin_4);
GPIO_SetBits(GPIOC,GPIO_Pin_3);
}
//按键初始化
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStr;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE); //开启时钟
GPIO_InitStr.GPIO_Mode=GPIO_Mode_IPD;//下拉输入
GPIO_InitStr.GPIO_Pin=GPIO_Pin_0;
GPIO_Init(GPIOA,&GPIO_InitStr); // PA0 KEY2
GPIO_InitStr.GPIO_Pin=GPIO_Pin_13;
GPIO_Init(GPIOC,&GPIO_InitStr); // PC13 KEY3
}
3.1.2、基本定时器6的初始化
![img](https://img-blog.csdnimg.cn/img_convert/8404263e94f33d0a12eef4f49bf623a5.png#pic_center)
//基本定时器6中断初始化,这里时钟选择为APB1的2倍
//arr:自动重装值 psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//通用定时器中断初始化
void TIM6_Int_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);//TIM7时钟使能
//定时器TIM7初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
TIM_Cmd(TIM6,ENABLE);//开启定时器6
}
3.1.3、基本定时器6的中断函数 (定时1S)
![img](https://img-blog.csdnimg.cn/img_convert/29e5befd7f49d15b1bc0688a14f5d575.png#pic_center)
//定时器6中断服务程序
void TIM6_IRQHandler(void)
{
if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//是更新中断
{
TIM_ClearITPendingBit(TIM6, TIM_IT_Update ); //清除TIM7更新中断标志
delay_1000ms_flay = 1 ;
TIM_Cmd(TIM6, DISABLE); //关闭TIM7
}
}
3.1.4、基本定时器7的初始化
![img](https://img-blog.csdnimg.cn/img_convert/3440b93d6435cff434677be2b6964136.png#pic_center)
//基本定时器7中断初始化,这里时钟选择为APB1的2倍
//arr:自动重装值 psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//通用定时器中断初始化
void TIM7_Int_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能
//定时器TIM7初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
TIM_Cmd(TIM7,ENABLE);//开启定时器7
}
3.1.5、基本定时器7的中断函数 (定2S)
![img](https://img-blog.csdnimg.cn/img_convert/f7cdaa284709a9a67e51050e5e53ba44.png#pic_center)
//定时器7中断服务程序
void TIM7_IRQHandler(void)
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断
{
TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志
delay_2000ms_flay = 1 ;
TIM_Cmd(TIM7, DISABLE); //关闭TIM7
}
}
3.1.6、外部中断初始化
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init(); // 按键端口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
//GPIOA—0 中断线以及中断初始化配置 上升沿触发 //KEY2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOC.13 中断线以及中断初始化配置 下降沿触发 //KEY3
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource13);
EXTI_InitStructure.EXTI_Line=EXTI_Line13;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键KEY2所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能按键KEY3所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
3.1.7、外部中断函数
![img](https://img-blog.csdnimg.cn/img_convert/4e3bf6c45a345188b538b7ee726d7b34.png#pic_center)
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
short n;
if( KEY2 == 1) //KEY2按键 硬件上以消抖
{
LED1 = 1; LED2 = 1; LED3 = 1;
for(n = 0; n
short n;
if( KEY3 == 0) //KEY3按键 硬件上以消抖
{
LED1 = 1; LED2 = 1; LED3 = 1;
for(n = 0; n
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
delay_init();
LED_Init();
KEY_Init();
EXTIX_Init();
TIM6_Int_Init(9999,7199); //1s
TIM7_Int_Init(9999,7199*2); //2s
while(1)
{
LED1 = 0; LED2 = 1; LED3 = 1; delay_ms(200);
LED1 = 1; LED2 = 1; LED3 = 1; delay_ms(200);
LED1 = 1; LED2 = 0; LED3 = 1; delay_ms(200);
LED1 = 1; LED2 = 1; LED3 = 1; delay_ms(200);
LED1 = 1; LED2 = 1; LED3 = 0; delay_ms(200);
}
}
3.2、编程利用PWM实现LED1呼吸灯功能:LED1逐步增亮,然后逐步变暗,如此循环。(代码:实验3-2)
3.2.1、通用定时器3的初始化(用于PWM-呼吸灯)
![img](https://img-blog.csdnimg.cn/img_convert/5c3551874563e7a9dc108edabc97bb78.png#pic_center)
/**************************************************
* Function main
* @author 行走的皮卡丘
* @date 2021/3/24
* @brief
* @param[in]
* @param[out] void
* @retval void
* @par History 无
* LED1 ---> PB.0
*************************************************/
//PB0 >> TIM3_CH3
//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC3
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR3上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
3.2.2、呼吸灯实验的主函数
![img](https://img-blog.csdnimg.cn/img_convert/6c2e951f24ea156e8621f6285bd74351.png#pic_center)
int main()
{
unsigned int i;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
delay_init();
TIM3_PWM_Init(899,0) ;
while(1) //低电平才亮
{
for( i = 899; i > 300 ; i--) //逐渐变亮
{
TIM_SetCompare3(TIM3,i);
if(i>600) delay_ms(8);
else delay_ms(2);
}
for( i = 300; i |