定时器
目录
一、时基单元
1.1.定时器简介

单片机内部的片上外设
参考手册13:高级定时器(TIM1、TIM8功能最全)
参考手册14:通用定时器(TIM2~5、TIM9~14)
参考手册15:基本定时器(TIM6、TIM7)
定时器结构框图:

1.2.时基单元的基本结构

**时钟来源:**产生时基单元的时钟信号
- 来源1:时钟树
- 来源2:从模式控制器
**预分频器:**对时钟信号进行降频
- 分频系数:/ (PSC + 1)
**计数器(CNT):**对分频后的脉冲信号计数
- 每来一个脉冲,计数器加一
**自动重装寄存器(ARR):**设置定时周期
- 当计数器增长到与ARR相等时溢出
- 计数器从0开始重新进行计数
- 周期等于ARR+1
**重复计数器(RCR):**设置重复计算的次数
- 计数器需要溢出RCR+1次才会产生update事件

1.3.上计数、下计数和中心对齐
1.3.1.上计数

1.3.2.下计数

1.3.3.中心对齐

1.4.时钟的来源
**分辨率:**计时的最小间隔(比如手表:1s)
周期: 循环一次的总时间(比如手表:60s)
时钟来源:
TIM1:
如果APB2分频器的分频系数为1
倍频器的倍频系数为1,TIM_CLK = PCLK2
如果APB2分频器的分频系数不为1
倍频器的倍频系数为2,TIM_CLK = PCLK2 * 2

TIM2~4:
如果APB1分频器的分频系数为1
倍频器的倍频系数为1,TIM_CLK = PCLK1
如果APB2分频器的分频系数不为1
倍频器的倍频系数为2,TIM_CLK = PCLK1 * 2

**示例:**将TIM3的时基单元设置为分辨率1us、周期1ms

1us = 1 * 10^(-6) s = 1MHz
1MHz = 72MHz / (71 + 1)
1ms = (999 + 1) * 1us
输入信号的频率为72MHz
预分频器PSC设置为71
自动重装载寄存器ARR设置为999
重复计数器RCR设置为0

1.5.寄存器预加载
**预加载:**将修改值存入影子寄存器,等到上一个周期结束后再生效

示例:
假设不使用寄存器预加载,在定时器运行过程中突然改变ARR

写入值立即生效,如果当前值大于改变值,则会一直增加到65535
再次发生溢出,此时的周期就变成了65536
二、自制延迟函数
**延迟函数:**系统滴答定时器(内核定时器)

2.1.获取单片机的当前时间
配置时基单元,让单片机每毫秒产生一个中断



创建记录时间的变量
cpp
//记录当前时间(单位:ms)
volatile uint32_t currentTick = 0;
**注:**volatile关键字:防止编译器对变量进行优化,保证每次访问变量时都直接从其内存地址读取或写入,而不是使用可能过期的缓存值
2.2.实现延迟函数
cpp
//
//@简介:延迟一段时间
//@参数:ms:要延迟的时间(单位:ms)
//
void App_Delay(uint32_t ms)
{
/*设置延迟结束时间*/
uint32_t expireTime = currentTick + ms;
/*等待延迟结束*/
while(currentTick < expireTime);
}
2.3.初始化时基单元

2.3.1.使能时钟
cpp
//#1:开启定时器3的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
2.3.2.配置时基单元的参数
cpp
//#2:配置时基单元的参数
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
2.3.3.使能总开关
cpp
//#3:闭合时基单元的开关
TIM_Cmd(TIM3,ENABLE);
2.4.4.总代码
cpp
//创建TIM3初始化函数
void App_TIM3_TimeBaseInit(void)
{
//#1:开启定时器3的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//#2:配置时基单元的参数
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
//#3:闭合时基单元的开关
TIM_Cmd(TIM3,ENABLE);
}
2.4.配置中断
2.4.1.开启中断

cpp
void TIM_ITConfig(TIM_TypeDef* TIMx,uint16_t TIM_IT,FunctionalState NewState);
解析:
**参数1:**定时器名字
**参数2:**中断的名字
- TIM_IT_Update
- TIM_IT_Trigger
- TIM_IT_CC1~TIM_IT_CC4
**参数3:**ENALE(使能)DISABLE(禁止)
**作用:**使能/禁止定时器的中断
cpp
//#4:使能Update中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
2.4.2.配置NVIC
cpp
int main(void)
{
/*配置中断优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
App_TIM3_TimeBaseInit();
while(1)
{
}
}
//创建TIM3初始化函数
void App_TIM3_TimeBaseInit(void)
{
//#1:开启定时器3的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//#2:配置时基单元的参数
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
//#3:闭合时基单元的开关
TIM_Cmd(TIM3,ENABLE);
//#4:使能Update中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
//#5:配置NVIC模块
/*声明NVIC结构变量*/
NVIC_InitTypeDef NVIC_InitStruct;
/*设置TIM3_IRQn中断名称*/
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
/*设置抢占优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
/*子优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
/*闭合中断开关*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/*初始化NVIC*/
NVIC_Init(&NVIC_InitStruct);
}
2.5.编写中断响应函数
cpp
void TIM3_IRQHandler(void)
{
/*如果是Update触发中断*/
if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == SET)
{
/*清除中断标志位*/
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
/*时间变量加一*/
currentTick++;
}
}
2.6.总代码
cpp
#include "stm32f10x.h"
//记录当前时间(单位:ms)
volatile uint32_t currentTick = 0;
//声明延迟函数
void App_Delay(uint32_t ms);
//声明TIM3初始化函数
void App_TIM3_TimeBaseInit(void);
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
int main(void)
{
/*配置中断优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
App_TIM3_TimeBaseInit();
App_OnBoardLED_Init();
while(1)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
App_Delay(50);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
App_Delay(500);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
App_Delay(50);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
App_Delay(500);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
App_Delay(500);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
App_Delay(500);
}
}
void TIM3_IRQHandler(void)
{
/*如果是Update触发中断*/
if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == SET)
{
/*清除中断标志位*/
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
/*时间变量加一*/
currentTick++;
}
}
//
//@简介:延迟一段时间
//@参数:ms:要延迟的时间(单位:ms)
//
void App_Delay(uint32_t ms)
{
/*设置延迟结束时间*/
uint32_t expireTime = currentTick + ms;
/*等待延迟结束*/
while(currentTick < expireTime);
}
//创建TIM3初始化函数
void App_TIM3_TimeBaseInit(void)
{
//#1:开启定时器3的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//#2:配置时基单元的参数
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
//#3:闭合时基单元的开关
TIM_Cmd(TIM3,ENABLE);
//#4:使能Update中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
//#5:配置NVIC模块
/*声明NVIC结构变量*/
NVIC_InitTypeDef NVIC_InitStruct;
/*设置TIM3_IRQn中断名称*/
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
/*设置抢占优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
/*子优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
/*闭合中断开关*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/*初始化NVIC*/
NVIC_Init(&NVIC_InitStruct);
}
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*通用输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PC13引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
/*熄灭板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
三、输出比较
3.1.输出比较简介

中间是CCRx寄存器:
捕获比较寄存器
左边为输入捕获:
外部信号通过CHx引脚输入到定时器内部,测量输入信号的周期、占空比等参数
右边为输出比较:
通过定时器产生稳定性方波信号,再由CHx引脚将信号输出
3.2.基本工作原理(PWM)
**PWM(Pulse Width Modulation):**脉冲宽度调制
一种周期固定、占空比可调的信号,通过调节占空比,等效地调节信号的输出幅度
**占空比:**一个周期中,高电平占整个周期的比例


**示例:**使用定时器生成30%占空比的方波信号

3.3.8种工作模式

**OCxRef(Output Compare x Reference):**输出比较通道x参考

**冻结:**OCxRef保持原有的电平不变
**相等有效(高电平):**当CNT与CCR相等时,OCxRef输出高电平,否则为低电平
**相等无效(低电平):**当CNT与CCR相等时,OCxRef输出低电平,否则为高电平
**翻转:**当CNT与CCR相等时,电平高低切换
**强制无效:**OCxRef始终为低电平
**强制有效:**OCxRef始终为高电平
**PWM模式1:**当CNT < CCR时,OCxRef为高电平,否则为低电平
**PWM模式2:**当CNT < CCR时,OCxRef为低电平,否则为高电平
3.4.互补输出
**互补输出:**参考信号经过反相器与CHxN引脚相连,与正常输出的电平波形相反

**示例:**通过同步buck电路将12V的电压降为3.3V

3.5.极性选择

**目的:**灵活翻转输出电平的有效极性
四、呼吸灯实验
4.1.呼吸灯原理
蓝色Led:连接在定时器1通道1的正常输出(CH1)
红色Led:连接在定时器1通道1的互补输出(CH1N)
**现象本质:**让Led的亮度连续变化

y = 0.5 * (sin(2πt) + 1)
波形配置:

占空比 = 0.5 * (sin(2πt) + 1)
周期 = 1ms
4.2.初始化IO引脚



Led长脚为正极

cpp
void App_PWM_Init(void)
{
//#1:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA8*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB13*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
4.3.配置时基单元


时钟:
TIM1挂在APB2总线(分频系数为1),从PCLK2(72MHz)获取时钟信号,此时倍频系数为1
所以时钟信号为72MHz
预分频器:
分辨率为1us = 1MHz,所以PSC = 71,72 / (71 + 1) = 1MHz
自动重装寄存器:
定时周期为1ms = 1us * 1000,所以ARR = 999,1us * (999 + 1)= 1ms
重复计数器:
RCR = 0,每溢出1次,产生一次update事件
cpp
void App_PWM_Init(void)
{
//#1:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA8*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB13*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
//#2:配置时基单元的参数
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
/*使能Update中断*/
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
}
4.4.配置输出比较的参数

cpp
//@简介:初始化输出比较通道1的参数
//@参数:初始化参数列表
void TIM_OC1Init(TIM_TypeDef* TIM,TIM_OCInitTypeDef* TIM_OCInitStruct);
cpp
//@简介:闭合/断开MOE开关
//@参数:ENABLE(闭合) DISABLE(断开)
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx,FunctionalState NewState);
**注:**只有使用高级定时器时才需要闭合MOE开关
cpp
//创建初始化PWM函数
void App_PWM_Init(void)
{
//#1:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA8*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB13*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
//#2:配置时基单元的参数
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
//#3:初始化输出比较
/*声明输出比较结构体变量*/
TIM_OCInitTypeDef TIM_InitStruct;
/*设置为PWM1模式*/
TIM_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
/*设置互补通道为高极性*/
TIM_InitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
/*设置正常通道为高极性*/
TIM_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
/*开启互补通道开关*/
TIM_InitStruct.TIM_OutputNState = TIM_OutputNState_Enable;
/*开启正常通道开关*/
TIM_InitStruct.TIM_OutputState = TIM_OutputState_Enable;
/*设置CCR初值*/
TIM_InitStruct.TIM_Pulse = 0;
/*初始化通道1*/
TIM_OC1Init(TIM1,&TIM_InitStruct);
/*闭合MOE总开关*/
TIM_CtrlPWMOutputs(TIM1,ENABLE);
/*手动开启CCR预加载*/
TIM_CCPreloadControl(TIM1,ENABLE);
}
4.5.改变PWM占空比
cpp
//@简介:设置CCR1的值
void TIM_SetCompare1(TIM_TypeDef* TIMx,uint16_t Compare1);
cpp
#include "stm32f10x.h"
#include "delay.h"
#include "math.h"
//声明初始化PWM函数
void App_PWM_Init(void);
int main(void)
{
App_PWM_Init();
while(1)
{
/*获取当前时间*/
float t = GetTick() * 1.0e-3f;
/*计算占空比*/
float duty = 0.5* (sin(2*3.14*t) + 1);
/*计算CCR1*/
uint16_t ccr1 = duty * 1000;
/*设置CCR1*/
TIM_SetCompare1(TIM1,ccr1);
}
}
4.6.总代码
cpp
#include "stm32f10x.h"
#include "delay.h"
#include "math.h"
//声明初始化PWM函数
void App_PWM_Init(void);
int main(void)
{
App_PWM_Init();
while(1)
{
/*获取当前时间*/
float t = GetTick() * 1.0e-3f;
/*计算占空比*/
float duty = 0.5* (sin(2*3.14*t) + 1);
/*计算CCR1*/
uint16_t ccr1 = duty * 1000;
/*设置CCR1*/
TIM_SetCompare1(TIM1,ccr1);
}
}
//创建初始化PWM函数
void App_PWM_Init(void)
{
//#1:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA8*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB13*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
//#2:配置时基单元的参数
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
//#3:初始化输出比较
/*声明输出比较结构体变量*/
TIM_OCInitTypeDef TIM_InitStruct;
/*设置为PWM1模式*/
TIM_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
/*设置互补通道为高极性*/
TIM_InitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
/*设置正常通道为高极性*/
TIM_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
/*开启互补通道开关*/
TIM_InitStruct.TIM_OutputNState = TIM_OutputNState_Enable;
/*开启正常通道开关*/
TIM_InitStruct.TIM_OutputState = TIM_OutputState_Enable;
/*设置CCR初值*/
TIM_InitStruct.TIM_Pulse = 0;
/*初始化通道1*/
TIM_OC1Init(TIM1,&TIM_InitStruct);
/*闭合MOE总开关*/
TIM_CtrlPWMOutputs(TIM1,ENABLE);
/*手动开启CCR预加载*/
TIM_CCPreloadControl(TIM1,ENABLE);
}
五、输入捕获

外部信号通过CHx引脚输入到定时器内部,测量输入信号的周期、占空比等参数
5.1.输入捕获的基本原理
输入信号由一个一个的上升沿与下降沿组成
输入捕获电路可以捕捉到这些电平信号变化
当事件发生时,会发生拍照动作
计数器将当前数值存放在CCR中

脉宽 = (8 - 2)/ 1us = 6us
5.2.输入捕获的内部结构

5.3.输入滤波

由输入滤波电路组成
**作用:**对输入信号进行滤波
输入信号由于噪声的影响,会在波形中产生毛刺,干扰后续测量
5.4.边沿检测

由边沿检测电路与复用器组成
**作用:**检测输入信号的变化边沿
上升沿脉冲检测上升沿,下降沿脉冲检测下降沿
5.5.信号选择

由复位器组成
**TRC:**来自于从模式控制器
**直接:**通道信号来自于本身
**间接:**通道信号来自于另一个通道
交叉引用可以节省引脚:

5.6.分频器

由分频器组成
**作用:**出现分配系数次的上升沿才能产生事件,发生拍照动作
六、超声波测距实验
6.1.HCSR04模块的使用方法

距离 = 声速(340m/s)* 时间 / 2

**VCC:**电源正
**GND:**地
**Trig:**触发引脚,用于启动测量
**Echo:**回声引脚,用于返回脉冲

工作流程:
- 向Trig引脚输入大于10us的脉冲信号
- 发送器连续发送8个49KHz的超声波后
- Echo引脚立即输出高电平
- 接收器连续接收8个周期的回声后
- Echo引脚立即输出低电平
- Echo引脚上的脉冲宽度等于超声波收发时间间隔
- 通过定时器的输入捕获来测量脉冲宽度
6.2.搭建电路



6.3.梳理思路
初始化串口:
cpp
#include "stm32f10x.h"
#include "usart.h"
//声明USART1初始化函数
void App_USART1_Init(void);
int main(void)
{
App_USART1_Init();
//测试代码
My_USART_SendString(USART1,"Hello world. \r\n");
while(1)
{
}
}
//创建USART1初始化函数
void App_USART1_Init(void)
{
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*声明GPIO结构*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA10引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构的前置声明*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
}


- 对计数器CNT清零(防止CNT溢出)
- 对CC1和CC2清零(标志位)
- 开启定时器,闭合时基单元开关
- 发送脉冲启动传感器
- 等待CC1和CC2(从0变成1)
- 关闭定时器,断开时基单元开关

6.4.初始化时基单元

**分辨率:**足够精细(不能取无限小)
**周期:**长度最大脉宽,越长越好
由于HCSR04芯片的最大测距精度最高为3mm
3mm / 340 m/s ≈ 8us
所以分辨率要小于8us,可以取1us
由于PSC、ARR、CNT、RCR都为16位寄存器
它们的值域都为0~65535
所以周期可以取65535

cpp
//创建HCSR04初始化函数
void App_HCSR04_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
}
6.5.初始化输入捕获



注:
由于Echo引脚空闲时两边都处于低电压,所以最好配置成输入下拉模式,保证电平稳定
cpp
//创建HCSR04初始化函数
void App_HCSR04_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
//#2:初始化输入捕获引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*输入下拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
/*初始化PA8引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
}

cpp
//创建HCSR04初始化函数
void App_HCSR04_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
//#2:初始化输入捕获引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*输入下拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
/*初始化PA8引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输入捕获通道1
/*声明IC结构变量*/
TIM_ICInitTypeDef TIM_ICInitStruct;
/*选择通道1*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获上升沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为直接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
/*初始化通道1*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#3:初始化输入捕获通道2
/*选择通道2*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获下降沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为间接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
/*初始化通道2*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
}
6.6.编程
cpp
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
//声明USART1初始化函数
void App_USART1_Init(void);
//声明HCSR04初始化函数
void App_HCSR04_Init(void);
int main(void)
{
App_USART1_Init();
App_HCSR04_Init();
while(1)
{
//#1:向CNT写0
TIM_SetCounter(TIM1,0);
//#2:向CC1和CC2写0
TIM_ClearFlag(TIM1,TIM_FLAG_CC1);
TIM_ClearFlag(TIM1,TIM_FLAG_CC2);
//#3:开启定时器
TIM_Cmd(TIM1,ENABLE);
//#4:向Trig引脚发送10us脉冲
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
/*延迟10us*/
DelayUs(10);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
//#5:等待CC1和CC2标志位
while(TIM_GetFlagStatus(TIM1,TIM_FLAG_CC1) == RESET);
while(TIM_GetFlagStatus(TIM1,TIM_FLAG_CC2) == RESET);
//#6:关闭定时器
TIM_Cmd(TIM1,DISABLE);
/*读取CCR1的值*/
uint16_t ccr1 = TIM_GetCapture1(TIM1);
/*读取CCR2的值*/
uint16_t ccr2 = TIM_GetCapture2(TIM1);
/*计算距离:(CCR1 - CCR2)* 分辨率 * 声速 / 2*/
float distance = (ccr2 - ccr1) * 1.0e-6f * 340.0f / 2;
/*打印结果*/
My_USART_Printf(USART1,"distance = %.4f\r\n",distance);
/*延迟100ms*/
Delay(100);
}
}
//创建USART1初始化函数
void App_USART1_Init(void)
{
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*声明GPIO结构*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA10引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构的前置声明*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
}
//创建HCSR04初始化函数
void App_HCSR04_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM1*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
//#2:初始化输入捕获引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*输入下拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
/*初始化PA8引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输入捕获通道1
/*声明IC结构变量*/
TIM_ICInitTypeDef TIM_ICInitStruct;
/*选择通道1*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获上升沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为直接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
/*初始化通道1*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#3:初始化输入捕获通道2
/*选择通道2*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获下降沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为间接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
/*初始化通道2*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#4:初始化Trig引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA0引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*设置通用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
/*最大输出速度为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA0引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
七、从模式控制器
7.1.什么是从模式控制器

**TRGI(Trigger Input):**触发输入
相当于红外接收头,接收命令
从而控制时基单元,做出指定动作
控制定时器开关、控制计数器清零、增减
**TRGO(Trigger Output):**触发输出
相当于遥控器,发送命令,将定时器内部事件输出
从而控制另外一个定时器(定时器的级联)
或者控制其他模块的采样时机(ADC模块)
7.2.8种从模式

**从模式:**通过TRGI接收控制信号,用来控制时基单元,做出相应动作
(定时器为被控对象)
8种从模式:
**从模式禁止:**不通过TRGI接收控制信号
**编码器模式1:**与编码器有关
**编码器模式2:**与编码器有关
**编码器模式3:**与编码器有关
**复位模式:**TRGI上升沿对CNT清零
**门模式:**TRGI高、总开关闭合;TRGI低、总开关断开
**触发模式:**TRGI上升沿闭合总开关
**外部时钟模式1:**TRGI作为时基单元的时钟来源
7.3.8种主模式

8种主模式:
**复位:**手动产生Update事件时,TRGO输出脉冲
**使能:**总开关闭合,TRGO为0;总开关打开,TRGO为1
**Update:**每个update事件,TRGO输出脉冲
**比较脉冲:**与输出比较通道1有关
**比较OC1REF:**OC1REF作为TRGO
**比较OC2REF:**OC2REF作为TRGO
**比较OC3REF:**OC3REF作为TRGO
**比较OC4REF:**OC4REF作为TRGO
7.4.定时器级联的例子
让TIM1、TIM2、TIM3同时启停
当TIM1开关闭合时,TIM2、TIM3也跟着闭合
当TIM1开关断开时,TIM2、TIM3也跟着断开

将TIM1的TRGO设置为使能模式
将TIM2、TIM3的TRGI设置为门模式
将TIM1的TRGO与TIM2、TIM3的TRGI相连
7.5.定时器同时启停的例子

八、PWM参数测量原理

捕获PWM信号的周期与占空比
8.1.参考手册的定时器结构框图
参考手册13:高级定时器(TIM1、TIM8功能最全)

8.2.输入捕获节点的名字

**TI(Timer Input):**定时器输入
**F(Filtered):**滤波后的
**P(Polarized):**选择了极性的
示例:
**TI1FP1:**输入信号从定时器通道1输入进来,经过了滤波与极性选择后,给通道1使用
**TI2FP1:**输入信号从定时器通道2输入进来,经过了滤波与极性选择后,给通道1使用
8.3.TRGI的来源


**TI1FP1:**通道1直接信号
**TI2FP2:**通道2直接信号
**TIMx_ETR:**外部参考信号(实际使用较少)


**TRC:**ITR0~ITR3(其他定时器的TRGO信号)、TI1F_ED(实际使用较少)



TIM2和TIM3都为通用定时器:
TIM2的ITR0表示TIM1的TRGO
TIM2的ITR1表示TIM8的TRGO
TIM2的ITR2表示TIM3的TRGO
TIM2的ITR3表示TIM4的TRGO
所以TIM2选择ITR0
TIM3的ITR0表示TIM1的TRGO
TIM3的ITR0表示TIM2的TRGO
TIM3的ITR0表示TIM5的TRGO
TIM3的ITR0表示TIM4的TRGO
所以TIM3选择ITR0
8.4.PWM参数测量原理

通道1捕获上升沿,通道2捕获下降沿
TRGI选择复位模式,TRGI的信号来源为TI1FP1
检测到上升沿,CCR1存放当前数值,随后计数清零
检测到下降沿,CCR2存放当前数值,继续计数
再次检测到上升沿,CCR1存放当前数值,计数清零
其中CCR2是一个周期中高电平时间
CCR1 - CCR2 是一个周期中低电平时间

九、PWM参数测量实验
9.1.实验原理
TIM3通道1产生PWM信号
TIM1通道1接收PWM信号
将参数值输出到USART1



初始化串口:
cpp
#include "stm32f10x.h"
#include "usart.h"
//声明串口初始化函数
void App_USART1_Init(void);
int main(void)
{
App_USART1_Init();
My_USART_SendString(USART1,"你好世界");
while(1)
{
}
}
//创建串口初始化函数
void App_USART1_Init(void)
{
//#1:初始化IO引脚 PA9 AF_PP
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:配置USART1参数
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构的前置声明*/
USART_InitTypeDef USART_InitStruct = {0};
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*无硬件流控*/
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为发送方向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
}
9.2.产生PWM信号

cpp
#include "stm32f10x.h"
#include "usart.h"
//声明串口初始化函数
void App_USART1_Init(void);
//声明TIM3初始化函数
void App_TIM3_Init(void);
int main(void)
{
App_USART1_Init();
App_TIM3_Init();
TIM_SetCompare1(TIM3,200);
while(1)
{
}
}
//创建TIM3初始化函数
void App_TIM3_Init(void)
{
//#1:初始化时基单元
/*开启定时器3的时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM3,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM3,ENABLE);
//#2:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输出比较
/*声明输出比较结构体变量*/
TIM_OCInitTypeDef TIM_InitStruct;
/*设置为PWM1模式*/
TIM_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
/*设置正常通道为高极性*/
TIM_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
/*开启正常通道开关*/
TIM_InitStruct.TIM_OutputState = TIM_OutputState_Enable;
/*设置CCR初值*/
TIM_InitStruct.TIM_Pulse = 0;
/*初始化通道1*/
TIM_OC1Init(TIM3,&TIM_InitStruct);
/*闭合MOE总开关*/
TIM_CtrlPWMOutputs(TIM3,ENABLE);
/*手动开启CCR预加载*/
TIM_CCPreloadControl(TIM3,ENABLE);
}
9.3.初始化时基单元

cpp
//创建TIM1初始化函数
void App_TIM1_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535 ;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
}
9.4.初始化输入捕获

cpp
//创建TIM1初始化函数
void App_TIM1_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535 ;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
//#2:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*输入下拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
/*初始化PA8引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输入捕获通道1
/*声明IC结构变量*/
TIM_ICInitTypeDef TIM_ICInitStruct;
/*选择通道1*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获上升沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为直接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
/*初始化通道1*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#3:初始化输入捕获通道2
/*选择通道2*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获下降沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为间接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
/*初始化通道2*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
}
9.5.初始化模式控制器
cpp
//创建TIM1初始化函数
void App_TIM1_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535 ;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
//#2:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*输入下拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
/*初始化PA8引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输入捕获通道1
/*声明IC结构变量*/
TIM_ICInitTypeDef TIM_ICInitStruct;
/*选择通道1*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获上升沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为直接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
/*初始化通道1*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#3:初始化输入捕获通道2
/*选择通道2*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获下降沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为间接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
/*初始化通道2*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#4:初始化从模式控制器
/*选择TI1FP1作为TRGI信号源*/
TIM_SelectInputTrigger(TIM1,TIM_TS_TI1FP1);
/*TRGI设为复位模式*/
TIM_SelectSlaveMode(TIM1,TIM_SlaveMode_Reset);
}
9.6.测量PWM参数

cpp
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
//声明串口初始化函数
void App_USART1_Init(void);
//声明TIM3初始化函数
void App_TIM3_Init(void);
//声明TIM1初始化函数
void App_TIM1_Init(void);
int main(void)
{
App_USART1_Init();
App_TIM3_Init();
App_TIM1_Init();
TIM_SetCompare1(TIM3,200);
while(1)
{
//#1:清除Trigger标志位
TIM_ClearFlag(TIM1,TIM_FLAG_Trigger);
//#2:等待Trigger标志位从0变成1
while(TIM_GetFlagStatus(TIM1,TIM_FLAG_Trigger) == RESET);
//#3:计算
uint16_t ccr1 = TIM_GetCapture1(TIM1);
uint16_t ccr2 = TIM_GetCapture2(TIM1);
float period = ccr1 * 1.0e-6f * 1.0e3f;
float duty = (float)ccr2 / ccr1 * 100.0f;
My_USART_Printf(USART1,"周期 = %.3fms,占空比 = %.2f%%\r\n",period,duty);
Delay(100);
}
}
//创建串口初始化函数
void App_USART1_Init(void)
{
//#1:初始化IO引脚 PA9 AF_PP
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:配置USART1参数
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构的前置声明*/
USART_InitTypeDef USART_InitStruct = {0};
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*无硬件流控*/
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为发送方向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
}
//创建TIM3初始化函数
void App_TIM3_Init(void)
{
//#1:初始化时基单元
/*开启定时器3的时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 999;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM3,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM3,ENABLE);
//#2:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输出比较
/*声明输出比较结构体变量*/
TIM_OCInitTypeDef TIM_InitStruct;
/*设置为PWM1模式*/
TIM_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
/*设置正常通道为高极性*/
TIM_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
/*开启正常通道开关*/
TIM_InitStruct.TIM_OutputState = TIM_OutputState_Enable;
/*设置CCR初值*/
TIM_InitStruct.TIM_Pulse = 0;
/*初始化通道1*/
TIM_OC1Init(TIM3,&TIM_InitStruct);
/*闭合MOE总开关*/
TIM_CtrlPWMOutputs(TIM3,ENABLE);
/*手动开启CCR预加载*/
TIM_CCPreloadControl(TIM3,ENABLE);
}
//创建TIM1初始化函数
void App_TIM1_Init(void)
{
//#1:初始化时基单元
/*开启定时器1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*声明TIM结构体变量*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/*设置预分频系数*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
/*设置自动重装寄存器*/
TIM_TimeBaseInitStruct.TIM_Period = 65535 ;
/*设置计数方向*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*设置重复计数器*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
/*初始化TIM3*/
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
/*手动开启ARR预加载*/
TIM_ARRPreloadConfig(TIM1,ENABLE);
/*闭合时基单元的开关*/
TIM_Cmd(TIM1,ENABLE);
//#2:初始化IO引脚
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA8引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
/*输入下拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
/*初始化PA8引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#3:初始化输入捕获通道1
/*声明IC结构变量*/
TIM_ICInitTypeDef TIM_ICInitStruct;
/*选择通道1*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获上升沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为直接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
/*初始化通道1*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#3:初始化输入捕获通道2
/*选择通道2*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
/*不使用输入滤波器*/
TIM_ICInitStruct.TIM_ICFilter = 0;
/*捕获下降沿*/
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
/*分频系数为1*/
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
/*复用器为间接*/
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
/*初始化通道2*/
TIM_ICInit(TIM1,&TIM_ICInitStruct);
//#4:初始化从模式控制器
/*选择TI1FP1作为TRGI信号源*/
TIM_SelectInputTrigger(TIM1,TIM_TS_TI1FP1);
/*TRGI设为复位模式*/
TIM_SelectSlaveMode(TIM1,TIM_SlaveMode_Reset);
}