第十五章 TIM高级定时器(下)

目录

14.6 使用高级定时器实现精确计时

14.6.1 实验要求

14.6.2 软件设计

14.6.3 下载验证

14.7 TIM高级定时器PWM波互补输出

14.7.1 PWM波是什么

14.7.2 实验要求

14.7.3 软件设计

14.7.4 下载验证

14.8 TIM高级定时器输入捕获

14.6.1 实验要求

14.6.2 软件设计

14.6.3 下载验证


14.6 使用高级定时器实现精确计时

14.6.1 实验要求

        使用高级定时器实现LED灯和蜂鸣器状态1s翻转一次。

14.6.2 软件设计

14.6.2.1 设计思路

        首先对高级定时器的时钟,周期,中断等信息进行宏定义即bsp_AdvanceTim.h文件便于之后对代码的移植,完成后需要对定时器的模式和中断优先级进行配置即bsp_AdvaceTim.c文件,配置完成过后还需要在stm32f10x_it.c文件中编写中断服务函数,以上都完成后就能够在main.c文件中编写我们所需要的功能代码了。

14.6.2.2 代码分析

bsp_AdvanceTim.h

#ifndef _BSP_ADVANCETIM_H
#define _BSP_ADVANCETIM_H

#include "stm32f10x.h"

#define ADVANCE_TIM1	

#ifdef 	ADVANCE_TIM1 	//使用高级定时器1
#define ADVANCE_TIM						TIM1
#define ADVANCE_TIM_APBxClock_FUN		RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK					RCC_APB2Periph_TIM1
#define ADVANCE_TIM_Period				(1000-1)
#define ADVANCE_TIM_Prescaler			71
#define ADVANCE_TIM_IRQ					TIM1_UP_IRQn
#define ADVANCE_TIM_IRQHandler			TIM1_UP_IRQHandler

#else				//使用高级定时器8
#define ADVANCE_TIM						TIM8 
#define ADVANCE_TIM_APBxClock_FUN		RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK					RCC_APB2Periph_TIM8
#define ADVANCE_TIM_Period				(1000-1)
#define ADVANCE_TIM_Prescaler			71
#define ADVANCE_TIM_IRQ					TIM8_UP_IRQn
#define ADVANCE_TIM_IRQHandler			TIM8_UP_IRQHandler

#endif

void ADVANCE_TIM_Init(void);

#endif /*_BSP_ADVANCETIM_H*/

bsp_AdvanceTim.c

#include "bsp_AdvanceTim.h"

static void ADVANCE_TIM_Mode_Config(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	
	//开启定时器时钟,即内部CK_INT = 72M
	ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);
	
	//自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period = ADVANCE_TIM_Period;
	
	//时钟分频系数
	TIM_TimeBaseStructure.TIM_Prescaler = ADVANCE_TIM_Prescaler;
	
	//时钟分频因子,基本定时器没有不用管
	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_ClearFlag(ADVANCE_TIM,TIM_FLAG_Update);
	
	//开启计数器中断
	TIM_ITConfig(ADVANCE_TIM,TIM_IT_Update,ENABLE);
	
	//使能计数器
	TIM_Cmd(ADVANCE_TIM,ENABLE);
}

static void ADVANCE_TIM_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//设置中断优先级分组位0
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	
	//设置中断源
	NVIC_InitStruct.NVIC_IRQChannel = ADVANCE_TIM_IRQ;
	
	//设置主优先级为0
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	 
	//设置抢占优先级为3
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
	
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStruct);
}

void ADVANCE_TIM_Init(void)
{
	ADVANCE_TIM_NVIC_Config();
	ADVANCE_TIM_Mode_Config();
}

中断服务函数

#include "stm32f10x_it.h"
#include "bsp_AdvanceTim.h"

extern volatile uint32_t time;
void ADVANCE_TIM_IRQHandler(void)
{
	if ( TIM_GetITStatus(ADVANCE_TIM, TIM_IT_Update) != RESET ) 
	{	
		time++;
		TIM_ClearITPendingBit(ADVANCE_TIM, TIM_FLAG_Update);  		 
	}
}

main.c

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_buzzer.h"
#include "bsp_AdvanceTim.h"

volatile uint32_t time = 0;		//ms计时变量

int main(void)
{
	LED_GPIO_Config();
	
	ADVANCE_TIM_Init();
	
	BUZZER_GPIO_Config();
	
	while(1)
	{
		//1000 * 1ms = 1s
		if(time == 1000)
		{
			time = 0;
			
			LED2_Toggle;
			BUZZER_Toggle;
		}
	}
}

14.6.3 下载验证

        编译下载到开发板中能够观察到板载的LED灯和蜂鸣器亮,响一秒,反复循环此状态。

14.7 TIM高级定时器PWM波互补输出

14.7.1 PWM波是什么

        PWM波(Pulse Width Modulation),即脉宽调制,是一种用于控制电子元件(如开关、电机、LED等)的技术。它通过改变脉冲的宽度和周期来控制电流或电压的大小,从而实现对目标设备的精确控制。

        PWM波可以通过快速切换电源开关来产生,其中一段时间为高电平,另一段时间为低电平。这样产生的信号被称为矩形波形。改变矩形波形的频率,可以改变PWM波信号所控制器件的响应速度。当频率高于人耳能听到的范围时,人们无法感知。

        通常情况下,PWM波信号被用于控制DC电机、步进电机、LED灯等。以LED为例,当PWM波信号中高电平占空比较大时,即高电平时间长且低电平时间短时,LED会发出较亮的光芒。反之,则发出弱光。因此,通过控制PWM波信号的占空比来调节LED灯的亮度。

        总之,PWM波作为一种常见的控制技术,在工业生产中有着广泛应用,并得到了很好的实践效果。

14.7.2 实验要求

        实现频率为1MHz占空比50%的PWM波并使用KingstLA1010逻辑分析仪或者使用Keil5自带的仿真器观察输出的波形。

14.7.3 软件设计

14.7.3.1 设计思路

        首先对定时器用到的 GPIO 初始化,定时器时基结构体 TIM_TimeBaseInitTypeDef 初始化,定时器输出比较结构体 TIM_OCInitTypeDef 初始化,定时器刹车和死区结构体 TIM_BDTRInitTypeDef 初始化。

14.7.3.2 代码分析

bsp_AdvanceTim.h

#ifndef _BSP_ADVANCETIM_H
#define _BSP_ADVANCETIM_H

#include "stm32f10x.h"

#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)}
#define ADVANCE_TIM_PERIOD            (8-1)
#define ADVANCE_TIM_PSC               (100-1)
#define ADVANCE_TIM_PULSE             4

#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

void ADVANCE_TIM_Init(void);

#endif /*_BSP_ADVANCETIM_H*/

bsp_AdvanceTim.c

#include "bsp_AdvanceTim.h"

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;		//速度为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;		//速度为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;		//速度为50MHz
	GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);
	
	//BKIN引脚默认先输出低电平
	GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);	
}

static 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);
	
/*-------------------刹车和死区结构体初始化-------------------*/
	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
	
	// 有关刹车和死区结构体的成员具体可参考BDTR寄存器的描述
	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();
}

main.c

#include "stm32f10x.h"
#include "bsp_AdvanceTim.h"

int main(void)
{
	//高级定时器初始化
	ADVANCE_TIM_Init();
	
	while(1)
	{   
		
	}
}

14.7.4 下载验证

        代码经编译后没错误后使用KingstVIS逻辑分析仪观察波形,逻辑分析仪的GND引脚接到开发板的GND引脚,开发板的PA8,PB13分别接到逻辑分析仪的CH0,CH1上,也可以使用Keil5软件自带的逻辑分析仪来观察波形,能够观察到两条互补的PWM波,频率为1MHz,占空比为50%。

14.8 TIM高级定时器输入捕获

14.6.1 实验要求

        使用通用定时器来产生不同占空比的PWM波然后用高级定时器来捕获通用定时器输出的PWM波通过串口显示频率和占空比。

14.6.2 软件设计

14.6.2.1 设计思路

        (1) 通用定时器产生 PWM 配置

        (2) 高级定时器 PWM 输入配置

        (3) 编写中断服务程序,计算测量的频率和占空比,并打印出来比较编程的要点主要分成两部分,一个是通用定时器的 PWM 信号输出,另一个是 PWM 信号输入捕获。

14.6.2.2 代码分析

bsp_GeneralTim.h

#ifndef __BSP_GENERALTIME_H
#define __BSP_GENERALTIME_H

#include "stm32f10x.h"

/************通用定时器TIM参数定义,只限TIM2、3、4、5************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意

#define GENERAL_TIM                   TIM3
#define GENERAL_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd 
#define GENERAL_TIM_CLK               RCC_APB1Periph_TIM3

// 输出PWM的频率为 72M/{ (ARR+1)*(PSC+1) }
#define GENERAL_TIM_PERIOD            (100-1)
#define GENERAL_TIM_PSC               (72-1)

#define GENERAL_TIM_CCR1              50
#define GENERAL_TIM_CCR2              40
#define GENERAL_TIM_CCR3              30
#define GENERAL_TIM_CCR4              20

// TIM3 输出比较通道1
#define GENERAL_TIM_CH1_GPIO_CLK      RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT          GPIOA
#define GENERAL_TIM_CH1_PIN           GPIO_Pin_6

// TIM3 输出比较通道2
#define GENERAL_TIM_CH2_GPIO_CLK      RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH2_PORT          GPIOA
#define GENERAL_TIM_CH2_PIN           GPIO_Pin_7

// TIM3 输出比较通道3
#define GENERAL_TIM_CH3_GPIO_CLK      RCC_APB2Periph_GPIOB
#define GENERAL_TIM_CH3_PORT          GPIOB
#define GENERAL_TIM_CH3_PIN           GPIO_Pin_0

// TIM3 输出比较通道4
#define GENERAL_TIM_CH4_GPIO_CLK      RCC_APB2Periph_GPIOB
#define GENERAL_TIM_CH4_PORT          GPIOB
#define GENERAL_TIM_CH4_PIN           GPIO_Pin_1
 
/**************************函数声明********************************/

void GENERAL_TIM_Init(void);

#endif	/* __BSP_GENERALTIME_H */

bsp_GeneralTim.c

#include "bsp_GeneralTim.h"

static void GENERAL_TIM_GPIO_Config(void) 
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// 输出比较通道1 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
	
	// 输出比较通道2 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH2_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
	
	// 输出比较通道3 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure);
	
	// 输出比较通道4 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH4_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH4_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GENERAL_TIM_CH4_PORT, &GPIO_InitStructure);
}

///*
// * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
// * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
// * 另外三个成员是通用定时器和高级定时器才有.
// *-----------------------------------------------------------------------------
// *typedef struct
// *{ TIM_Prescaler            都有
// *	TIM_CounterMode			     TIMx,x[6,7]没有,其他都有
// *  TIM_Period               都有
// *  TIM_ClockDivision        TIMx,x[6,7]没有,其他都有
// *  TIM_RepetitionCounter    TIMx,x[1,8,15,16,17]才有
// *}TIM_TimeBaseInitTypeDef; 
// *-----------------------------------------------------------------------------
// */

/* ----------------   PWM信号 周期和占空比的计算--------------- */
// ARR :自动重装载寄存器的值
// CLK_cnt:计数器的时钟,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM 信号的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M
// 占空比P=CCR/(ARR+1)
 
static void GENERAL_TIM_Mode_Config(void)
{
	// 开启定时器时钟,即内部时钟CK_INT=72M
	GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);

/*--------------------时基结构体初始化-------------------------*/
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	
	// 配置周期,这里配置为100K
	// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD;
	
	// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC;
	
	// 时钟分频因子 ,配置死区时间时需要用到
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	
	// 计数器计数模式,设置为向上计数
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	
	// 重复计数器的值,没用到不用管
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	
	// 初始化定时器
	TIM_TimeBaseInit(GENERAL_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_OCPolarity = TIM_OCPolarity_High;
	
	// 输出比较通道 1
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR1;
	TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 输出比较通道 2
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR2;
	TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 输出比较通道 3
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR3;
	TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 输出比较通道 4
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR4;
	TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 使能计数器
	TIM_Cmd(GENERAL_TIM, ENABLE);
}

void GENERAL_TIM_Init(void) 
{
	GENERAL_TIM_GPIO_Config();
	GENERAL_TIM_Mode_Config();		
}

bsp_AdvanceTim.h

#ifndef __BSP_ADVANCETIME_H
#define __BSP_ADVANCETIME_H


#include "stm32f10x.h"


/************高级定时器TIM参数定义,只限TIM1和TIM8************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里我们使用高级控制定时器TIM1

#define ADVANCE_TIM                   TIM1
#define ADVANCE_TIM_APBxClock_FUN     RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK               RCC_APB2Periph_TIM1

// 输入捕获能捕获到的最小的频率为 72M/{ (ARR+1)*(PSC+1) }
#define ADVANCE_TIM_PERIOD            (1000-1)
#define ADVANCE_TIM_PSC               (72-1)

// 中断相关宏定义
#define ADVANCE_TIM_IRQ               TIM1_CC_IRQn
#define ADVANCE_TIM_IRQHandler        TIM1_CC_IRQHandler

// TIM1 输入捕获通道1
#define ADVANCE_TIM_CH1_GPIO_CLK      RCC_APB2Periph_GPIOA 
#define ADVANCE_TIM_CH1_PORT          GPIOA
#define ADVANCE_TIM_CH1_PIN           GPIO_Pin_8

#define ADVANCE_TIM_IC1PWM_CHANNEL    TIM_Channel_1
#define ADVANCE_TIM_IC2PWM_CHANNEL    TIM_Channel_2

/**************************函数声明********************************/

void ADVANCE_TIM_Init(void);

#endif	/* __BSP_ADVANCETIME_H */

bsp_AdvanceTim.c

#include "bsp_AdvanceTim.h"

static void ADVANCE_TIM_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		    	// 设置中断组为0
	
    NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQ; 		// 设置中断来源
	
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	// 设置抢占优先级 
	 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;			// 设置子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void ADVANCE_TIM_GPIO_Config(void)  
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);	//开启PA8时钟
	
	GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_CH1_PIN;			//选择PA8端口
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//模式配置为浮空输入
	GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);	
}

static 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);	// 初始化定时器

/*--------------------输入捕获结构体初始化-------------------*/	
// 使用PWM输入模式时,需要占用两个捕获寄存器,一个测周期,另外一个测占空比
	
	TIM_ICInitTypeDef  TIM_ICInitStructure;
	
	// 捕获通道IC1配置
	TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;	// 选择捕获通道
	
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		// 设置捕获的边沿
	
	// 设置捕获通道的信号来自于哪个输入通道,有直连和非直连两种
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
	
	// 1分频,即捕获信号的每个有效边沿都捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			
	
	TIM_ICInitStructure.TIM_ICFilter = 0x0;							// 不滤波
	
	// 初始化PWM输入模式
	TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);			
	
	// 当工作做PWM输入模式时,只需要设置触发信号的那一路即可(用于测量周期)
	// 另外一路(用于测量占空比)会由硬件自带设置,不需要再配置
	
	// 捕获通道IC2配置	
	//TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;
 
	//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;

	//TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
	//TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	//TIM_ICInitStructure.TIM_ICFilter = 0x0;
	//TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
	
	
	TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1);		// 选择输入捕获的触发信号

	// 选择从模式: 复位模式
	// PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT会被复位
	TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset);
	TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable); 

	// 使能捕获中断,这个中断针对的是主捕获通道(测量周期那个)
	TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE);	
	
	// 清除中断标志位
	TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
	
	// 使能高级控制定时器,计数器开始计数
	TIM_Cmd(ADVANCE_TIM, ENABLE);
}

void ADVANCE_TIM_Init(void)
{
	ADVANCE_TIM_NVIC_Config();
	ADVANCE_TIM_GPIO_Config();
	ADVANCE_TIM_Mode_Config();
}

bsp_usart.h

#ifndef _BSP_USART_H
#define _BSP_USART_H

#include "stm32f10x.h"
#include <stdio.h>

#define DEBUG_USART1	1
#define DEBUG_USART2	0
#define DEBUG_USART3	0
#define DEBUG_USART4	0
#define DEBUG_USART5	0

#if DEBUG_USART1
//串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

//USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler 
/**************************************************************/
#elif DEBUG_USART2
//串口2-USART2
#define  DEBUG_USARTx                   USART2
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

//USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

#define  DEBUG_USART_IRQ                USART2_IRQn
#define  DEBUG_USART_IRQHandler         USART2_IRQHandler
/**************************************************************/
#elif DEBUG_USART3
//串口3-USART3
#define  DEBUG_USARTx                   USART3
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

//USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA    
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

#define  DEBUG_USART_IRQ                USART3_IRQn
#define  DEBUG_USART_IRQHandler         USART3_IRQHandler
/**************************************************************/
#elif DEBUG_USART4
//串口4-USART4
#define  DEBUG_USARTx                   USART4
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART4
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

//USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

#define  DEBUG_USART_IRQ                USART4_IRQn
#define  DEBUG_USART_IRQHandler         USART4_IRQHandler
/**************************************************************/
#elif DEBUG_USART5
//串口5-USART5
#define  DEBUG_USARTx                   USART5
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART5
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200 

//USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2

#define  DEBUG_USART_IRQ                USART5_IRQn
#define  DEBUG_USART_IRQHandler         USART5_IRQHandler
/**************************************************************/
#endif

void USART_Config(void);
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str);
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
void Usart_SendHalfWorld(USART_TypeDef* pUSARTx, uint16_t data);
void Usart_Sendarray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num);

#endif /*_BSP_USART_H*/

bsp_usart.c

#include "bsp_usart.h"

static void USART_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//开启GPIO时钟 
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE);
	
	//将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
	
	//将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
}

	NVIC_InitTypeDef NVIC_InitStruct;
	
	//配置NVIC的优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//配置中断源:按键1
	NVIC_InitStruct.NVIC_IRQChannel = DEBUG_USART_IRQ;
	static void USART_NVIC_Config(void)
{

	//配置抢占优先级:1
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	
	//配置子优先级:1
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	
	//使能中断通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; 
	NVIC_Init(&NVIC_InitStruct);	
}

void USART_Config(void)
{
	USART_GPIO_Config();
	USART_NVIC_Config();
	
	USART_InitTypeDef USART_InitStructure;
	
	//开启串口外设时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
	
	//配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	
	//设置数据帧字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	
	//配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	
	//奇偶校验控制选择
	USART_InitStructure.USART_Parity = USART_Parity_No;
	
	//串口模式选择
	//使用或操作将串口模式配置为收发模式
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	
	//硬件流控制选择
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	 
	//串口初始化
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	//使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	
	//使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);
}

void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
	//发送一个字节数据到USART
	USART_SendData(pUSARTx, data);
	
	//等待发送数据寄存器为空
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

void Usart_SendHalfWorld(USART_TypeDef* pUSARTx, uint16_t data)
{
	uint8_t temp_h,temp_l;
	
	temp_h = (data & 0xff00) >> 8;
	temp_l =  data & 0x00ff;
	
	//发送一个字节数据到USART
	USART_SendData(pUSARTx, temp_h);
	
	//等待发送数据寄存器为空
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	 
	//发送一个字节数据到USART
	USART_SendData(pUSARTx, temp_l);
	
	//等待发送数据寄存器为空
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

void Usart_Sendarray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)
{
	uint8_t i;
	
	for(i=0;i<num;i++)
	{
		Usart_SendByte( pUSARTx, array[i]);
	}
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
{
	uint8_t i=0;
	do
	{
		Usart_SendByte(pUSARTx, *(str+i));
		i++;
	}while(*(str+i) != '\0');
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

int fputc(int ch, FILE *f)
{
	//发送一个字节数据到USART 
	USART_SendData(DEBUG_USARTx, (uint8_t) ch);
	
	//等待发送数据寄存器为空
	while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
	
	return (ch);
}

int fgetc(FILE *f)
{
	//等待发送数据寄存器为空
	while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
	
	return (int)USART_ReceiveData(DEBUG_USARTx);
}

中断服务函数

#include "stm32f10x_it.h"
#include "bsp_usart.h"
#include "bsp_AdvanceTim.h" 

uint16_t IC2Value = 0;
uint16_t IC1Value = 0;
float DutyCycle = 0;
float Frequency = 0;

/*
 * 如果是第一个上升沿中断,计数器会被复位,锁存到CCR1寄存器的值是0,CCR2寄存器的值也是0
 * 无法计算频率和占空比。当第二次上升沿到来的时候,CCR1和CCR2捕获到的才是有效的值。其中 
 * CCR1对应的是周期,CCR2对应的是占空比。
 */
void ADVANCE_TIM_IRQHandler(void)
{
	//清除中断标志位
	TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
	
	//获取输入捕获值
	IC1Value = TIM_GetCapture1(ADVANCE_TIM);
	IC2Value = TIM_GetCapture2(ADVANCE_TIM);
	
	// 注意:捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须加1
	if (IC1Value != 0)
	{
		// 占空比计算
		DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);

		// 频率计算 
		Frequency = (72000000/(ADVANCE_TIM_PSC+1))/(float)(IC1Value+1);
		printf("占空比:%0.2f%%   频率:%0.2fHz\n",DutyCycle,Frequency);
	}
	else
	{
		DutyCycle = 0;
		Frequency = 0;
	}
}

main.c

#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_GeneralTim.h"
#include "bsp_AdvanceTim.h"

int main(void)
{
	//串口初始化
	USART_Config();
	
	//通用定时器初始化
	GENERAL_TIM_Init();
	
	//高级定时器初始化
	ADVANCE_TIM_Init();
	
	while(1)
	{
		
	}
}

14.6.3 下载验证

        PA6和PA8连接时串口调试助手显示50%占空比,频率1KHz,PA7和PA8连接时串口调试助手显示40%占空比,频率1KHz,PB0和PA8连接时串口调试助手显示30%占空比,频率1KHz,PB1和PA8连接时串口调试助手显示20%占空比,频率1KHz。

猜你喜欢

转载自blog.csdn.net/weixin_68288412/article/details/129965432