目录
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。