【精选】嵌入式STM32

您所在的位置:网站首页 嵌入式中断程序例子 【精选】嵌入式STM32

【精选】嵌入式STM32

2023-10-27 08:34| 来源: 网络整理| 查看: 265

在这里插入图片描述

嵌入式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

2、时基单元

这个部分与普通单片机的定时器相似,由3个小单元组成,分别是:

计数器(CNT) ;

预分频器(PSC)

自动装载寄存器 (ARR)。

三者的关系是如下图:

img

计数器可以向上计数、向下计数或者向上向下双向计数,计数器时钟由预分频器分频输出提供。

预分频器是基于一个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

#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

//基本定时器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

//定时器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

//基本定时器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

//定时器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

//外部中断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

/************************************************** * 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

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


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3