一、定时器控制LED亮灭
一、定时器
简单理解定时器就是对输入的时钟进行计数,当计数到设定的值时产生中断
1、定时器类型
本次实验使用通用定时器TIM2
2、通用定时器框图
3、基本定时器框图
下面红圈部分称时基单元
预分频器(16位):
对内部(计数)时钟进行预分频。若预分频器写0,就是不分频(1分频),这时输出频率=输入频率=72Mhz。若写1,就是2分频,输出频率等于=输入频率/2=36Mhz
计数器(16位):
对预分频后的时钟进行计数,计数时钟每来一个上升沿,计数器就加一。当计数器达到目标值就产生中断
自动重装寄存器(16位):
存固定(计数目标)目标值,当计数器到达目标值时,产生中断信号,并且清零计数器
更新中断
最后的向上折线箭头,代表会产生中断信号。像计数值=自动重装值产生的中断,我们一般称为”更新中断“。更新中断后,就会通往NVIC。
更新事件
下面一个箭头就代表会产生”更新事件“,该事件不会触发中断,但会触发内部其他电路的工作。
DAC(数字模拟转换器)
主模式触发DAC,让内部硬件在不受程序的控制下实现自动运行
对比以上两个框图,可以发现通用定时器框图就是在基本定时器框图的基础上加上了其他的寄存器,但本次实验还用不到其他的寄存器,只需控制中断便是。
二、代码
主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "Timer.h"
#include "OLED.h"
//操作IO口的三个步骤
//1、使用RCC开启GPIO时钟
//2、使用GPIO_Init函数初始化GPIO
//3、使用输出或输入函数控制GPIO口
uint8_t signal;
int main()
{
LED_Init();
while(1)
{
}
}
void TIM2_IRQHandler (void) //中断函数
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//获取中断标志位,第二个是想查看的中断标志位
{
if(signal==0)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);
}
else{
GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);
}
signal=~signal;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清楚中断标志位
}
}
TIM初始化
#include "stm32f10x.h" // Device header
extern uint16_t Num;//告诉编译器有Num这个变量,但不在目前文件中,在其他代码页
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启APB1时钟,因为TIMER2是在APB1总线上的
TIM_InternalClockConfig(TIM2); //选择时基单元驱动TIM2,这里选择内部时钟,也可以不写,因为定时器上电后默认调用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//一分频,这里主要是对于滤波采样分频,跟时基单元无关
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInitStructure.TIM_Period= 20000-1 ; //自动重装器
TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1; //PSC预分频器的值,分频系数
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //重复计数器的值,在高级计数器里面才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//时基单元初始化完成
/*TIM_Prescaler:
定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定 TIMx_PSC寄存器的值。可设置范围为 0 至 65535,实现 1 至 65536 分频。
TIM_Period:
定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范围为 0 至 65535。
根据定时器时钟的频率,比如时钟的频率是72MHZ,可以理解为一秒钟STM32会自己数72M次,预分频系数就是将频率分割,比如分频系数是72,则该时钟的频率会变成72MHZ/72=1MHZ,但是在设置的时候要注意,数值应该是72-1。
假定分频系数是72-1,那么频率变成1MHZ,也就意味着STM32在一秒钟会数1M次,即1us数一次。
好了,接下来就是确定预装载值,比如需要定时1ms,由于1ms=1us*1000,那么预装载值就是1000-1;如此类推,在预分频系数确定的情况下,定时的时长就由预装载值确定了。至于要把值减一的原因,估计是计数是从0开始,所以要减一。
*/
//NVIC模块
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//避免刚初始化完就进入中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//更新中断到NVIC的通路
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2,ENABLE);//开启定时器
}
计时设置原理
系统时钟为什么默认为72MHz
如果需要其他频率的时钟,也可以进行更改
二、呼吸灯
想要实现呼吸灯,就需要用到PWM
1、PWM简介
2、PWM基本结构
3、PWM参数计算
4、代码
PWM初始化
#include "stm32f10x.h" // Device header
//PWM初始化
/*1、开启定时器时钟与GPIO时钟
2、配饰时基单元
3、配置输出比较
4、配置GPIO复用功能
5、启启启。。。动
*/
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启APB1时钟,因为TIMER2是在APB1总线上的
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*使用重映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA ,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM2); //选择时基单元驱动时钟,这里选择内部时钟,也可以不写,因为定时器上电后默认调用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//一分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInitStructure.TIM_Period= 100-1 ; //自动重装器
TIM_TimeBaseInitStructure.TIM_Prescaler=720-1; //PSC预分频器的值,分频系数
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //重复计数器的值,在高级计数器里面才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//时基单元初始化完成
//输出比较初始化
TIM_OCInitTypeDef TIM_OCInitstructure;
TIM_OCStructInit(&TIM_OCInitstructure);
TIM_OCInitstructure.TIM_OCMode=TIM_OCMode_PWM1; //输出比较模式
TIM_OCInitstructure.TIM_OCPolarity=TIM_OCPolarity_High; //输出比较极性,为高,ref就不翻转,为低则翻转
TIM_OCInitstructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitstructure.TIM_Pulse=0; //设置CCR
TIM_OC1Init(TIM2,&TIM_OCInitstructure);//启动对应CH,可以自己查看GPIO复用功能
TIM_Cmd(TIM2,ENABLE);//开启定时器
}
//占空比修改
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "PWM.h"
//操作IO口的三个步骤
//1、使用RCC开启GPIO时钟
//2、使用GPIO_Init函数初始化GPIO
//3、使用输出或输入函数控制GPIO口
int main()
{
PWM_Init();
uint8_t i=0;
while(1)
{
for(i=0;i<=100;i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i=0;i<=100;i++)
{
PWM_SetCompare1(100-i);
Delay_ms(10);
}
}
}
logic仿真图
参考:https://www.bilibili.com/video/BV1th411z7sn?p=16&vd_source=18b235d50bb08f33a29fda5f713ad48c