一、STM32定时功能介绍
STM32的定时器功能是一种强大的工具,可用于在嵌入式系统中实现定时操作和控制。STM32微控制器通常具有多个定时器,每个定时器具有多种不同的配置和功能。下面是对STM32定时器功能的一些基本介绍:
- 定时器类型:STM32微控制器通常具有几种不同类型的定时器,包括高级定时器(TIM2-TIM5)、基本定时器(TIM6-TIM7)和看门狗定时器(TIM1-TIM3和TIM15-TIM17)。每种类型的定时器都有其特定的用途和特性。
- 定时器模式:每个定时器都可以配置为多种模式之一,例如向上计数模式、向下计数模式、中央对齐模式等。根据所需的定时行为,可以选择适当的模式。
- 计数范围:定时器的计数范围可以根据需要进行配置。计数周期的范围可以从0到2^32-1,这使得定时器能够用于各种不同的应用场景。
- 触发输入:一些定时器类型还具有触发输入,这使得它们能够从外部信号触发计时。这对于需要与外部事件同步的应用非常有用。
- 捕获比较功能:定时器可以配置为在达到特定计数值时触发中断或更新输出。这使得定时器可以用于生成毫秒级别的定时或用于PWM控制等任务。
- 同步:STM32的定时器之间可以相互同步,这使得它们可以协同工作以实现更复杂的功能。例如,可以使用一个定时器的输出信号来触发另一个定时器的开始或停止。
- 定时器链:STM32还支持定时器链,这使得多个定时器可以连接在一起并同步工作。这对于需要精确时间同步的应用非常有用。
- 倒计时功能:STM32的定时器还支持倒计时功能,可以在一定的时间间隔后触发一个事件。这对于需要在一段时间后触发特定操作的场景非常有用。
- 中断控制:STM32的定时器支持中断控制,这意味着可以在定时器达到特定值时触发中断,然后在中断处理程序中执行特定的任务。
- 校准:STM32的定时器还具有校准功能,可以调整计时器的频率以实现更精确的计时。这对于需要高精度计时的应用非常有用。
总的来说,STM32的定时器功能非常强大,可以根据不同的应用需求进行灵活配置。通过使用这些功能,可以轻松地实现各种定时操作和控制任务。
二、PWM功能介绍
PWM(Pulse Width Modulation)即脉冲宽度调制,简称脉宽调制。PWM是一种模拟控制方式,根据相应载荷的变化来调制晶体管基极或 MOS 管栅极的偏置,来实现晶体管或 MOS 管导通时间的改变,从而实现开关稳压电源输出的改变。PWM 是一种对模拟信号电平进行数字编码的方法。 通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。 PWM 信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有( ON ),要么完全无( OFF )。 只要带宽足够,任何模拟值都可以使用 PWM 进行编码。在STM32中,PWM功能常用于控制电机速度、调节LED亮度、产生音频信号等应用。通过调整PWM的占空比(高电平时间占总周期的比例),可以控制输出信号的平均电压或功率。
三、创建工程
在原有基础工程上,我们在左边文件栏“Hardware"添加一个c文件,和一个头文件。
在“PWM.h”中添加基本的函数语句
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h" // Device header
#endif
四、配置函数解析
4.1 在STM32中,控制TIM2使能的是APB1,且需要控制TIM2为内部时钟模式,配置的相关函数为:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//APB1使能。
TIM_InternalClockConfig(TIM2);//TIM2认定内部时钟模式
4.2 配置时基单元需要定义一个结构体变量,再将其结构体成员引出来分别进行配置。声明结构体的语句为
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//配置结构体
根据公式,时钟频率为72MHZ/(PSC+1)/(ARR+1),经过计算,当PSC设置为719,ARR设置为99,时钟频率为1000HZ,也就是0.001s,也就是说,每个电平存在时间为0.001s
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//配置结构体
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,选择不分频
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式,向上计数
TIM_TimeBaseInitStruct.TIM_Period=100-1;//周期,ARR自动重装的值
TIM_TimeBaseInitStruct.TIM_Prescaler= 720-1;//PSC预分频器的值
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才会使用
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
4.3 需要配置使能中断,设置为输出比较模式,这样才能将已激活的时钟信号进行输出比较从而得到PWM波形。输出比较结构体定义为
TIM_OCInitTypeDef TIM_OCInitStruct;//输出比较结构体定义
能中断的代码如下:
//使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启更新中断到NVIC
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);//结构体赋初始值,避免没有出现的成员未编译而报错
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式,选择PWM1模式
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//输出极性,高电平有效
TIM_OCInitStruct.TIM_OutputState= TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStruct.TIM_Pulse=0 ;//设置CCR
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
4.4 需要使用LED灯,所以还需要加上GPIO的配置函数。此外,TIM对应GPIO引脚需要查找定义表,TIM2_CH1_ETR引脚复用在PA0上,故我们配置PA0。
//配置GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
4.5 PWM的占空比的公式为CCR/ARR+1。前面我们设定为ARR为99,故我们可以设定CCR在0-100波动,这样算出的占空比就是0-100%了,具体结果会在主函数里展示。我们现在只是定义了一个输出比较函数,在STM32标准库里,系统给出了设置CCR函数为
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
我们可以通过这个函数来设定一个不断变化的CCR值。
在这里我们写入以下代码
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);//设置CCR寄存器的值,可以随时改变CCR的值
}
4.6 头文件以及C文件最终函数
头文件:
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h" // Device header
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
#endif
C文件
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//APB1使能。
TIM_InternalClockConfig(TIM2);//TIM2认定内部时钟模式
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//配置结构体
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,选择不分频
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式,向上计数
TIM_TimeBaseInitStruct.TIM_Period=100-1;//周期,ARR自动重装的值
TIM_TimeBaseInitStruct.TIM_Prescaler= 720-1;//PSC预分频器的值
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才会使用
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启更新中断到NVIC
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);//结构体赋初始值,避免没有出现的成员未编译而报错
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式,选择PWM1模式
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//输出极性,高电平有效
TIM_OCInitStruct.TIM_OutputState= TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStruct.TIM_Pulse=0 ;//设置CCR
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
//TIM对应GPIO引脚需要查找定义表,TIM2_CH1_ETR引脚复用在PA0上
//配置GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);//设置CCR寄存器的值,可以随时改变CCR的值
}
五、主函数编写解析
5.1 主函数代码
在for循环中,不断调用设置CCR的函数,不断改变CCR从而实现呼吸灯,最终代码如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
//#include "OLED.h"
#include "PWM.h"
#include "LED.h"
#include "Delay.h"
uint16_t Num;
int main(void)
{
// OLED_Init();
PWM_Init();//配置函数
// OLED_ShowString(1,1,"a:");
// OLED_ShowString(2,1,"b:");
while (1)
{//在for循环中,不断调用设置CCR的函数实现呼吸灯
for(Num=0;Num<=100;Num++)//幅值不断增大
{
PWM_SetCompare1(Num);
Delay_ms(10);//通过延时函数可改变闪动的周期。
// OLED_ShowNum(1,6,Num,3);
}
for(Num=0;Num<=100;Num++)//幅值不断减小
{
PWM_SetCompare1(100-Num);
Delay_ms(10);
// OLED_ShowNum(2,6,100-Num,3);
}
}
}
六、烧录结果以及波形观察
在PA0端口插上一个LED灯,其他跟之前的实验一样。
打开魔术棒,设置如下:
打开keil的调试界面,点击逻辑分析仪:
在界面点击”SET UP“
在弹出界面中设置以下(上面栏里填入”PORTA.0)。
得到结果如下:
七、总结
通过本次实验使我明白了不同基本定时中断模式的运作原理,也探索了通用定时器的输出比较功能。