目录
第一部分、STM32定时器的基本知识
1、STM32定时器的分类(图片来自野火文档)
2、开发过程中定时器的常用基础功能
3、定时器挂在的总线
第二部分、能用的代码
1、定时器计时代码
2、定时器输出PWM代码
第三部分、总结
输入捕获通道应用:在测量方波信号的脉宽或者频率。输出比较应用在:PWM波形输出,控制电机转速或者呼吸灯等。
在STM32的使用过程中,我常用的两个功能如下:(1)间隔多少时间来一次定时器中断,可以用做计算时间、退出某一程序或者用来做LCD显示。(2)产生PWM波,通过控制PWM波的占空比,然后实现对电机转速的控制。
注意:定时器定时时间的计算公式 T =((arr+1)*(psc+1))/72M (单位为 us微秒)
F = 72M /{(arr+1)*(psc+1) (单位为 Hz赫兹)
其实在开发过程中,如果经常使用正点原子或者野火例程中的代码,那么这一点常常不会被关注到,因为这些基本的配置代码别人都给你配置好了,所以你在使用的过程中不会出现错误,但是了解一下,对开发是有帮助的。
STM32的AHB 总线延伸出来的两条 APB2 和 APB1 总线,上面挂载着 STM32 各种各样的特色外设。我们经常说的 GPIO、串口、 I2C、 SPI 这些外设就挂载在这两条总线上。
记忆方法: APB1 总线上一般挂载的都是比较高级,速度比较快的外设, 而APB2总线上挂载的都是一些比较low的外设。(细节详情请参考:《零死角玩转 STM32—基于野火 F103[MINI]开发板》这本书,上传不了,直接去野火论坛下载野火电子论坛 - Powered by Discuz! (firebbs.cn))
代码写法可能不是最好的,但是用起来没问题。我记录下来,方便自己下次直接Copy,可能对你没帮助哦。
time.c配置代码
#include "timer.h"
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u32 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
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_ITConfig( //使能或者失能指定的TIM中断
TIM3, //TIM2
TIM_IT_Update ,
ENABLE //使能
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
extern int hour_return;
extern int min_return;
extern int sec_return;
u16 overtime; //用作语音模块自动退出
extern u8 h_flag;
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
sec_return++;
if(h_flag==1)
{
overtime++; //用作语音模块自动退出
if(overtime >= 20)
{
overtime = 0;
h_flag=0;
}
}
if(sec_return == 60)
{
sec_return = 0;
min_return++;
if(min_return == 60)
{
min_return = 0;
hour_return ++;
if(hour_return == 24) hour_return = 0;
}
}
}
}
main.c初始化
注意:这里时间的变量要到前面定义,
//主函数
int main()
{
/*定时器*/
TIM3_Int_Init(10000,7199); //设置延时1s
while(1)
{
}
}
pwm.c文件
#include "pwm.h"
//电机初始化
void Motor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PC端口时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
}
//定时器复用功能引脚初始化
static void ADVANCE_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道 GPIO 初始化
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);
// 输出比较通道互补通道 GPIO 初始化
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1N_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADVANCE_TIM_CH1N_PORT, &GPIO_InitStructure);
// 输出比较通道刹车通道 GPIO 初始化
RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_BKIN_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);
// BKIN 引脚默认先输出低电平
GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);
}
/*电机占空比*/
extern unsigned char ADVANCE_TIM_PULSE;
//定时器模式配置
void ADVANCE_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟 CK_INT=72M
ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计 TIM_Period+1 个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;
// 驱动 CNT 计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置为 PWM 模式 1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 互补输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
/* 设置占空比大小 */
TIM_OCInitStructure.TIM_Pulse = ADVANCE_TIM_PULSE;
// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 互补输出通道电平极性配置
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
// 输出通道空闲电平极性配置
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
// 互补输出通道空闲电平极性配置
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);
/*-------------------刹车和死区结构体初始化-------------------*/
// 有关刹车和死区结构体的成员具体可参考 BDTR 寄存器的描述
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
// 输出比较信号死区时间配置,具体如何计算可参考 BDTR:UTG[7:0]的描述
// 这里配置的死区时间为 152ns
TIM_BDTRInitStructure.TIM_DeadTime = 11;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
// 当 BKIN 引脚检测到高电平的时候,输出比较信号被禁止,就好像是刹车一样
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);
// 使能计数器
TIM_Cmd(ADVANCE_TIM, ENABLE);
// 主输出使能,当使用的是通用定时器时,这句不需要
TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);
}
void ADVANCE_TIM_Init(void)
{
ADVANCE_TIM_GPIO_Config();
ADVANCE_TIM_Mode_Config();
}
pwm.h文件
#ifndef __PWM_H
#define __PWM_H
#include "stdio.h"
#include "sys.h"
//电机
#define MOTOR PAout(5) // PA5
#define PWM_ShaChe PBout(12) // PB12
void Motor_Init(void);
//宏定义
/************高级定时器 TIM 参数定义,只限 TIM1 和 TIM8************/
// 当使用不同的定时器的时候,对应的 GPIO 是不一样的,这点要注意
// 这里我们使用高级控制定时器 TIM1
#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1
/********PWM 的周期和占空比******************/
/*可供修改公式 F = TIM_CLK/{(ARR+1)*(PSC+1)*/
/* TIM_CLK 等于 72MHZ*/
#define ADVANCE_TIM_PERIOD (80-1) //ARR 即自动重装载寄存器的值
#define ADVANCE_TIM_PSC (900-1) //PSC 即计数器时钟的分频因子
//#define ADVANCE_TIM_PULSE1 10 //和ARR的数值有关,ARR的一半表示PWM高低各占一半(计数到40反转)
#define ADVANCE_TIM_IRQ TIM1_UP_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_UP_IRQHandler
// TIM1 输出比较通道
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_CH1_PORT GPIOA
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_8
// TIM1 输出比较通道的互补通道
#define ADVANCE_TIM_CH1N_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_CH1N_PORT GPIOB
#define ADVANCE_TIM_CH1N_PIN GPIO_Pin_13
// TIM1 输出比较通道的刹车通道
#define ADVANCE_TIM_BKIN_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_BKIN_PORT GPIOB
#define ADVANCE_TIM_BKIN_PIN GPIO_Pin_12
static作用
静态函数不能被其它文件所用;
其它文件中可以定义相同名字的函数,不会发生冲突;
//static void ADVANCE_TIM_GPIO_Config(void);
void ADVANCE_TIM_Mode_Config(void);
void ADVANCE_TIM_Init(void);
#endif
main.c文件的简易使用代码,需要更改。
/*设置占空比*/
unsigned char ADVANCE_TIM_PULSE = 0;
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断控制器分组设置
/*高级定时器PWM初始化 */
ADVANCE_TIM_Init(); //PWM PA8
Motor_Init(); //电机初始化PA5
while(1)
{
ADVANCE_TIM_PULSE = 40;
ADVANCE_TIM_Mode_Config(); //PWM 可以更换
}
}
这上面的代码如果你想使用,需要你有一定的32开发基础,我这里做笔记,只是为了方便自己下次使用。