STM32hal库学习(F1)-TIMER

定时器概述

软件定时原理

void delay_us(uint32_t us)
{
    us *= 72;
    while(us--);
}
//有明显的缺点:
//延时不精准,
//CPU死等
//例如函数调用压栈出栈需要时间,ARM流水线导致时间不确定,只是大概的延时

定时器定时原理

定时器的核心就是计数器

STM32hal库学习(F1)-TIMER_第1张图片

STM32定时器分类

STM32hal库学习(F1)-TIMER_第2张图片

实时定时器指的是RTC

定时器特性表(F1)

STM32hal库学习(F1)-TIMER_第3张图片

计数器位数0-65535,也就是65536 2^16

常规定时器功能区别

定时器类型

主要功能

基本定时器

没有输入输出通道,常用作时基,即定时功能

通用定时器

具有多路独立通道,可用于输入捕获/输出比较,也可用作时基

高级定时器

除具备通用定时器所有功能外,

还具备带死区控制的互补信号输出、刹车输入等功能

(可用于电机控制、数字电源设计等) 

基本定时器

基本定时器简介

基本定时器:

TIM6/TIM7

主要特性:

16位递增计数器(计数值:0~65535

16位预分频器(分频系数:1~65536

可用于触发DAC

在更新事件(计数器溢出)时,会产生中断/DMA请求

基本定时器框图

STM32hal库学习(F1)-TIMER_第4张图片

ARR和PSC具有缓冲,转移到影子寄存器才能生效改变后的ARR和PSC.

同时ARR还有一位ARPE专门决定是否有缓冲

缓冲的意思是在更新事件或者中断/DMA请求后生效

简单来说ARR有立即重装载和更新后重装载的区别

这里举个例子充分理解一下缓冲

如果我们让灯亮1s,灭2s,这时我们在1s后改变ARR需要时间执行,这时会导致不精准

那如果我让灯亮1s,灭1s,我们不需要改变ARR,所以也就可以不需要缓冲

STM32定时器计数模式及溢出条件

计数器模式

溢出条件

递增计数模式

CNT==ARR  

递减计数模式

CNT==0

中心对齐模式

CNT==ARR-1CNT==1

STM32hal库学习(F1)-TIMER_第5张图片

递增计数模式实例

STM32hal库学习(F1)-TIMER_第6张图片

递减计数模式实例

STM32hal库学习(F1)-TIMER_第7张图片

中心对齐计数模式实例

STM32hal库学习(F1)-TIMER_第8张图片

定时器相关寄存器

TIMx_CR1 (TIM6和TIM7控制寄存器)

STM32hal库学习(F1)-TIMER_第9张图片

该寄存器用于设置ARR寄存器是否具有缓冲,使能/关闭计数器

TIMx_DIER (TIM6和TIM7 DMA/中断使能寄存器)

STM32hal库学习(F1)-TIMER_第10张图片该寄存器用于使能更新中断

TIMx_SR (TIM6和TIM7状态寄存器)

STM32hal库学习(F1)-TIMER_第11张图片该寄存器用于判断是否发生了更新中断,由硬件置1,软件清0

TIMx_CNT(TIM6和TIM7计数器)

STM32hal库学习(F1)-TIMER_第12张图片

该寄存器用于设置计时器初始值,范围:0~65535

TIMx_PSC(TIM6和TIM7预分频器)

STM32hal库学习(F1)-TIMER_第13张图片

该寄存器用于设置预分频系数,范围:0~65535;

实际预分频系数等于PSC+1

TIMx_ARR(TIM6和TIM7自动重装载寄存器)

STM32hal库学习(F1)-TIMER_第14张图片

该寄存器用于设置自动重装载值,范围:0~65535

定时器溢出时间计算方法

STM32hal库学习(F1)-TIMER_第15张图片

定时器中断配置步骤

STM32hal库学习(F1)-TIMER_第16张图片

相关函数介绍

函数 

主要寄存器

主要功能

HAL_TIM_Base_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_Base_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_Base_Start_IT()

DIERCR1

使能更新中断并启动计数器

HAL_TIM_IRQHandler()

SR

定时器中断处理公用函数,处理各种中断

HAL_TIM_PeriodElapsedCallback()

定时器更新中断回调函数,由用户重定义

//TIM_HandleTypedef* htim  先打开句柄

//相关结构体
typedef struct 
{ 
    TIM_TypeDef *Instance;            /* 外设寄存器基地址 */  //stm32f103xe.h查找及地址 
    TIM_Base_InitTypeDef Init;        /* 定时器初始化结构体*/
     ...
}TIM_HandleTypeDef;

typedef struct 
{ 
    uint32_t Prescaler;               /* 预分频系数PSC */ 
    uint32_t CounterMode;             /* 计数模式:基本定时器只有一种*/ 
    uint32_t Period;                  /* 自动重载值 ARR */ 
    uint32_t ClockDivision;           /* 时钟分频因子:通用和高级才有 */ 
    uint32_t RepetitionCounter;       /* 重复计数器寄存器的值:高级才有*/ 
    uint32_t AutoReloadPreload;       /* 自动重载预装载使能:设置缓冲ARR*/
} TIM_Base_InitTypeDef;
//Timer.c

#include "Timer.h"
#include "led.h"

TIM_HandleTypeDef Timer6_handler; // 定时器句柄

/* 定时器中断初始化函数 */
void TIM_Init(uint16_t psc, uint16_t arr)
{
    Timer6_handler.Instance = TIM6;      // 基地址:TIM6
    Timer6_handler.Init.Prescaler = psc; // 预分频系数:psc,实际分频系数psc+1;
    Timer6_handler.Init.Period = arr;    // 自动重装载值:arr,计数值为arr+1
    HAL_TIM_Base_Init(&Timer6_handler);  // 初始化实际单元,调用HAL_TIM_Base_MspInit()

    HAL_TIM_Base_Start_IT(&Timer6_handler); // 使能计数器,开启定时器中断
}

/* 定时器基础MSP初始化函数 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM6) // 判断是不是定时器6回调
    {
        __HAL_RCC_TIM6_CLK_ENABLE();           // 使能定时器6时钟
        HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3); // 设置TIM6定时器中断,抢占优先级1,响应优先级3
        HAL_NVIC_EnableIRQ(TIM6_IRQn);         // 使能定时器6中断,调用void TIM6_IRQHandler()
    }
}

/* 定时器6中断服务函数 */
void TIM6_IRQHandler()
{
    HAL_TIM_IRQHandler(&Timer6_handler); // 定时器公共处理函数,调用void HAL_TIM_PeriodElapsedCallback(),同时自动清除标志位
}

/* 定时器溢出中断中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM6) // 判断是不是定时器6回调
    {
        LED0 = !LED0; // 中断内容,可以自己写其他的
    }
}
#ifndef __TIMER_H
#define __TIMER_H

#include "sys.h"
void TIM_Init(uint16_t psc, uint16_t arr);

#endif 
//main.c
//本例程是每0.5秒进一次中断然后翻转LED电平,使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//	HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	TIM_Init(7199, 4999);
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "Timer.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16,"YMZ ^_^");
	TIM_Init(7199, 4999);
	while (1)
	{
	}
}

通用定时器

通用定时器简介

通用定时器:

TIM2/TIM3 /TIM4 /TIM5

主要特性:

16位递增、递减中心对齐计数器(计数值:0~65535

16位预分频器(分频系数:1~65536

可用于触发DACADC

在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求

4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式

使用外部信号控制定时器且可实现多个定时器互连的同步电路

支持编码器和霍尔传感器电路等

通用定时器框图

STM32hal库学习(F1)-TIMER_第17张图片

时钟源选择

STM32hal库学习(F1)-TIMER_第18张图片

时钟源寄存器设置

计数器时钟选择类型

设置方法

内部时钟(CK_INT)

 设置TIMx_SMCRSMS=000 ,(ECE=0但ECE默认是0,所以不需要配置)

外部时钟模式1

外部输入引脚(TIx)

设置TIMx_SMCR的SMS=111,(ECE=0,但ECE默认是0,所以不需要配置)

外部时钟模式2

外部触发输入(ETR)

设置TIMx_SMCRECE=1

(或者SMS=111和TS=111,不过这里选择的线路是ETRF)

IO口复用

内部触发输入(ITRx)

使用一个定时器作为另一个定时器的预分频器,

如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

设置可参考STM32F10xxx参考手册_V10(中文版).pdf14.3.15

外部时钟模式1
STM32hal库学习(F1)-TIMER_第19张图片外部时钟模式2

STM32hal库学习(F1)-TIMER_第20张图片

内部触发输入(ITRx)

使用一个定时器作为另一个定时器的预分频器

STM32hal库学习(F1)-TIMER_第21张图片

通用定时器PWM输出

框图

STM32hal库学习(F1)-TIMER_第22张图片

捕获/比较通道1的主电路---输出部分

STM32hal库学习(F1)-TIMER_第23张图片

捕获/比较通道的输出部分

STM32hal库学习(F1)-TIMER_第24张图片

PWM输出原理

STM32hal库学习(F1)-TIMER_第25张图片

PWM配置步骤

STM32hal库学习(F1)-TIMER_第26张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_PWM_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_PWM_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_PWM_ConfigChannel()

CCMRxCCRxCCER

配置PWM模式、比较值、输出极性等

HAL_TIM_PWM_Start()

CCERCR1

使能输出比较并启动计数器

__HAL_TIM_SET_COMPARE()

CCRx

修改比较值

__HAL_TIM_ENABLE_OCxPRELOAD()

CCER

使能通道预装载

//相关结构体
typedef struct 
{ 
   uint32_t OCMode; 	  /* 输出比较模式选择 */
   uint32_t Pulse; 	      /* 设置比较值 */
   uint32_t OCPolarity;   /* 设置输出比较极性 */
   uint32_t OCNPolarity;  /* 设置互补输出比较极性 */
   uint32_t OCFastMode;   /* 使能或失能输出比较快速模式 */
   uint32_t OCIdleState;  /* 空闲状态下OC1输出 */
   uint32_t OCNIdleState; /* 空闲状态下OC1N输出 */ 
} TIM_OC_InitTypeDef;
//PWM.c
#include "PWM.h"

uint16_t ledrpwmval = 0;
uint8_t dir = 1;

TIM_HandleTypeDef Tim_PWM_Handler; // 定时器时基单元句柄
TIM_OC_InitTypeDef TIM_OC_Handler; // 定时器输出句柄
void PWM_Init(uint16_t psc, uint16_t arr)
{
    Tim_PWM_Handler.Instance = TIM3;                       // 定时器基地址:PWM3
    Tim_PWM_Handler.Init.Prescaler = psc;                  // 定时器预分频系数:psc,实际分频系数:psc+1
    Tim_PWM_Handler.Init.Period = arr;                     // 自动重装载值:arr,实际计数个数:arr+1
    Tim_PWM_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式(通用定时器有三种模式,所以需要选择)
    HAL_TIM_PWM_Init(&Tim_PWM_Handler);                    // 定时器时基单元初始化,调用HAL_TIM_PWM_MspInit

    TIM_OC_Handler.OCMode = TIM_OCMODE_PWM1;                                    // 输出模式:PWM模式1,CNTCCR,无效电平
    TIM_OC_Handler.Pulse = 0;                                             // 比较值:CCR,设置为arr/2,则占空比为50%
    TIM_OC_Handler.OCPolarity = TIM_OCPOLARITY_LOW;                             // 有效电平为低电平
    HAL_TIM_PWM_ConfigChannel(&Tim_PWM_Handler, &TIM_OC_Handler, TIM_CHANNEL_2); // 定时器PWM通道配置函数

    HAL_TIM_PWM_Start(&Tim_PWM_Handler, TIM_CHANNEL_2); // 使能输出并启动计数器
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3) // 判断基地址是不是TIM3
    {
        GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化句柄

        __HAL_RCC_TIM3_CLK_ENABLE();  // 使能定时器3时钟
        __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB的时钟

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;       // PB5复用定时器3通道2
        GPIO_InitStructure.Pin = GPIO_PIN_5;             // PB5
        GPIO_InitStructure.Pull = GPIO_PULLUP;           // 上拉,这里随便配置不影响
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
        HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);              // GPIO初始化

        __HAL_RCC_AFIO_CLK_ENABLE();    // 使用复用功能IO
        __HAL_AFIO_REMAP_TIM3_PARTIAL(); // 使能AFIO到TIM3的重映射
    }
}

void TIM_PWM_LED_Breath()
{
    delay_ms(5);
    if (dir)
        ledrpwmval++; /* dir==1 ledrpwmval递增 */
    else
        ledrpwmval--; /* dir==0 ledrpwmval递减 */

    if (ledrpwmval ==500)
        dir = 0; /* ledrpwmval到达300后,方向为递减 */
    if (ledrpwmval == 0)
        dir = 1; /* ledrpwmval递减到0后,方向改为递增 */
    /* 修改比较值控制占空比 */
    __HAL_TIM_SET_COMPARE(&Tim_PWM_Handler, TIM_CHANNEL_2, ledrpwmval);
}
//PWM.h
#ifndef __PWM_H
#define __PWM_H

#include "sys.h"
#include "delay.h"
void PWM_Init(uint16_t psc, uint16_t arr);
void TIM_PWM_LED_Breath();

#endif 
//main.c
//本例程是2KHZ PWM输出PB5,使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//	HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	PWM_Init(72 - 1, 500 - 1);		// psc:71,实际分频系数72,arr:499,实际计数个数500;
									// 计数频率72M/72=1M
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "PWM.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	PWM_Init(72 - 1, 500 - 1);		// psc:71,实际分频系数72,arr:499,实际计数个数500;
									// 计数频率72M/72=1M
									// PWM波频率:1M/500=2000HZ
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
	while (1)
	{
		TIM_PWM_LED_Breath();//调用呼吸灯函数
	}
}

输入捕获

框图

STM32hal库学习(F1)-TIMER_第27张图片

捕获/比较通道的输入部分

STM32hal库学习(F1)-TIMER_第28张图片

捕获/比较通道的主电路

STM32hal库学习(F1)-TIMER_第29张图片

输入捕获脉宽测量原理

STM32hal库学习(F1)-TIMER_第30张图片

输入捕获配置步骤

STM32hal库学习(F1)-TIMER_第31张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_IC_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_IC_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_IC_ConfigChannel()

CCMRxCCER

配置通道映射、捕获边沿、分频、滤波等

__HAL_TIM_ENABLE_IT()

DIER

使能更新中断等

HAL_TIM_IC_Start_IT()

CCERDIERCR1

使能输入捕获、捕获中断并启动计数器

HAL_TIM_IRQHandler()

SR

定时器中断处理公用函数,处理各种中断

HAL_TIM_PeriodElapsedCallback()

定时器更新中断回调函数,由用户重定义

HAL_TIM_IC_CaptureCallback()

定时器输入捕获回调函数,由用户重定义

//相关结构体
typedef struct
{ 
    uint32_t ICPolarity;       /* 输入捕获触发方式选择,比如上升、下降沿捕获 */ 
    uint32_t ICSelection;      /* 输入捕获选择,用于设置映射关系 */ 
    uint32_t ICPrescaler;      /* 输入捕获分频系数 */ 
    uint32_t ICFilter;         /* 输入捕获滤波器设置 */ 
} TIM_IC_InitTypeDef;
//IC.c
#include "IC.h"

TIM_HandleTypeDef TIM_IC_Handler;        // 定时器实际单元句柄
TIM_IC_InitTypeDef TIM_IC_InitStructure; // 定时器输入捕获句柄

void IC_Init(uint16_t psc, uint16_t arr)
{
    TIM_IC_Handler.Instance = TIM5;                       // 定时器基地址:TIM5
    TIM_IC_Handler.Init.Prescaler = psc;                  // 预分频系数:psc;分频系数psc+1
    TIM_IC_Handler.Init.Period = arr;                     // 自动重装载值:arr,实际计数值arr+1
    TIM_IC_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    HAL_TIM_IC_Init(&TIM_IC_Handler);                     // 定时器时基单元初始化,调用HAL_TIM_IC_MspInit()

    TIM_IC_InitStructure.ICPolarity = TIM_ICPOLARITY_RISING;                         // 上升沿捕获
    TIM_IC_InitStructure.ICPrescaler = TIM_ICPSC_DIV1;                               // 捕获信号不分频
    TIM_IC_InitStructure.ICSelection = TIM_ICSELECTION_DIRECTTI;                     // 通道映射TI1FP1
    TIM_IC_InitStructure.ICFilter = 0;                                               // 不滤波
    HAL_TIM_IC_ConfigChannel(&TIM_IC_Handler, &TIM_IC_InitStructure, TIM_CHANNEL_1); // 定时器输入捕获通道配置

    __HAL_TIM_ENABLE_IT(&TIM_IC_Handler, TIM_IT_UPDATE);
    HAL_TIM_IC_Start_IT(&TIM_IC_Handler, TIM_CHANNEL_1);
}

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM5) // 判断基地址是不是TIM5
    {
        __HAL_RCC_TIM5_CLK_ENABLE();  // 使能TIM5
        __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA

        GPIO_InitTypeDef GPIO_InitStructure;             // GPIO初始化句柄
        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;       // 复用推挽输出模式,这里注意只要是复用模式都行
        GPIO_InitStructure.Pin = GPIO_PIN_0;             // PA0
        GPIO_InitStructure.Pull = GPIO_PULLDOWN;         // 下拉,由于PA0按键一端接3.3,所以另一端接下拉电阻
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
        HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);       // GPIO初始化

        HAL_NVIC_SetPriority(TIM5_IRQn, 1, 3); // 设置TIM5中断,抢占1,响应3
        HAL_NVIC_EnableIRQ(TIM5_IRQn);         // 使能TIM5中断
    }
}

void TIM5_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM_IC_Handler); // 调用中断公共处理函数,自动清除标志位,
    // 且会调用HAL_TIM_IC_CaptureCallback()捕获中断
    //  HAL_TIM_PeriodElapsedCallback()溢出中断
}

/* 输入捕获状态(IC_sta)
 * [7]  :0,没有成功的捕获;1,成功捕获到一次.
 * [6]  :0,还没捕获到高电平;1,已经捕获到高电平了.
 * [5:0]:捕获高电平后溢出的次数,最多溢出63次,所以最长捕获值 = 63*65536 + 65535 = 4194303
 *       注意:为了通用,我们默认ARR和CCRy都是16位寄存器,对于32位的定时器(如:TIM5),也只按16位使用
 *       按1us的计数频率,最长溢出时间为:4194303 us, 约4.19秒
 *
 *      (说明一下:正常32位定时器来说,1us计数器加1,溢出时间:4294秒)
 */
uint8_t IC_sta = 0;  /* 输入捕获状态 */
uint16_t IC_val = 0; /* 输入捕获值 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM5) // 判断基地址是不是TIM5
    {
        if ((IC_sta & 0X80) == 0) /* 还没有成功捕获 */
        {
            if (IC_sta & 0X40) /*[6]=1捕获到一个下降沿 */
            {
                IC_sta |= 0X80;                                                                 /* 标记成功捕获到一次高电平脉宽 */
                IC_val = HAL_TIM_ReadCapturedValue(&TIM_IC_Handler, TIM_CHANNEL_1);             /* 获取当前的捕获值 */
                TIM_RESET_CAPTUREPOLARITY(&TIM_IC_Handler, TIM_CHANNEL_1);                      /* 一定要先清除原来的设置 */
                TIM_SET_CAPTUREPOLARITY(&TIM_IC_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); /* 配置TIM5通道1上升沿捕获 */
            }
            else /* 还未开始,第一次捕获上升沿 */
            {
                IC_sta = 0; /* 清空 */
                IC_val = 0;
                IC_sta |= 0X40;                                                                  /* 标记捕获到了上升沿 */
                __HAL_TIM_DISABLE(&TIM_IC_Handler);                                              /* 关闭定时器5 */
                __HAL_TIM_SET_COUNTER(&TIM_IC_Handler, 0);                                       /* 定时器5计数器清零 */
                TIM_RESET_CAPTUREPOLARITY(&TIM_IC_Handler, TIM_CHANNEL_1);                       /* 一定要先清除原来的设置!! */
                TIM_SET_CAPTUREPOLARITY(&TIM_IC_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 定时器5通道1设置为下降沿捕获 */
                __HAL_TIM_ENABLE(&TIM_IC_Handler);                                               /* 使能定时器5 */
            }
        }
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM5)
    {
        if ((IC_sta & 0X80) == 0) /* 还未成功捕获 */
        {
            if (IC_sta & 0X40) /* 已经捕获到高电平了 */
            {
                if ((IC_sta & 0X3F) == 0X3F) /* 高电平太长了 */
                {
                    TIM_RESET_CAPTUREPOLARITY(&TIM_IC_Handler, TIM_CHANNEL_1);                      /* 一定要先清除原来的设置 */
                    TIM_SET_CAPTUREPOLARITY(&TIM_IC_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); /* 配置TIM5通道1上升沿捕获 */
                    IC_sta |= 0X80;                                                                 /* 标记成功捕获了一次 */
                    IC_val = 0XFFFF;
                }
                else /* 累计定时器溢出次数 */
                {
                    IC_sta++;
                }
            }
        }
    }
}

void IC_GetTime()
{
    uint32_t temp = 0;
    if (IC_sta & 0X80) /* 成功捕获到了一次高电平 */
    {
        temp = IC_sta & 0X3F;
        temp *= 65536;                  /* 溢出时间总和 */
        temp += IC_val;                 /* 得到总的高电平时间 */
        printf("HIGH:%d us\r\n", temp); /* 打印总的高点平时间 */
        IC_sta = 0;                     /* 开启下一次捕获*/
    }
}
//IC.h
#ifndef __IC_H
#define __IC_H

#include "sys.h"
#include 
void IC_Init(uint16_t psc, uint16_t arr);
void IC_GetTime();

#endif // __IC_H
//main.c
//本例程是1MHZ PA0捕获按键高电平时长,并在串口上打印高电平时长,使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//  HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	delay_init(72);					// 初始化延时函数
//	uart_init(115200);				// 初始化串口
//	LED_Init();						// 初始化LED
//	IC_Init(72 - 1, 65536 - 1);     // psc:72-1,实际分频系数72
								    // arr:65535,实际计数个数65536
								    //以1M频率计数


#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "IC.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
	IC_Init(72 - 1, 65536 - 1); // psc:72-1,实际分频系数72
								// arr:65535,实际计数个数65536
								//以1M频率计数
	while (1)
	{
		//这里可以LED0每10ms闪烁用来提示程序运行
		IC_GetTime();
		LED0 = !LED0;
		delay_ms(10);
	}
}

脉冲计数

框图

STM32hal库学习(F1)-TIMER_第32张图片

应选择外部时钟模式1

STM32hal库学习(F1)-TIMER_第33张图片

配置步骤

STM32hal库学习(F1)-TIMER_第34张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_IC_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_IC_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_SlaveConfigSynchro()

SMCRCCMRxCCER

配置定时器从模式、触发选择、分频、滤波等

HAL_TIM_IC_Start()

CCERCR1

使能输入捕获、启动计数器

__HAL_TIM_GET_COUNTER()

CNT

获取计数器当前值

__HAL_TIM_SET_COUNTER()

CNT

设置计数器的值

//相关结构体
typedef struct 
{ 
    uint32_t SlaveMode;         /* 从模式选择 */ 
    uint32_t InputTrigger;      /* 输入触发源选择 */ 
    uint32_t TriggerPolarity;   /* 输入触发极性 */ 
    uint32_t TriggerPrescaler;  /* 输入触发预分频 */ 
    uint32_t TriggerFilter;     /* 输入滤波器设置 */ 
} TIM_SlaveConfigTypeDef;
//IC.c
#include "IC.h"

TIM_HandleTypeDef TIM_IC_Handler;                 // 定时器时基单元句柄
TIM_SlaveConfigTypeDef TIM_SLAVE_ConfigStructure; // 定时器从模式配置句柄

void IC_Init(uint16_t psc, uint16_t arr)
{
    TIM_IC_Handler.Instance = TIM2;                       // 定时器基地址:TIM2
    TIM_IC_Handler.Init.Prescaler = psc;                  // 预分频系数:psc;实际分频系数psc+1
    TIM_IC_Handler.Init.Period = arr;                     // 自动重装载值:arr;实际计数值arr+1
    TIM_IC_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    HAL_TIM_IC_Init(&TIM_IC_Handler);                     // 定时器时基单元初始化,回调HAL_TIM_IC_MspInit()

    TIM_SLAVE_ConfigStructure.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;           // 外部时钟模式1
    TIM_SLAVE_ConfigStructure.InputTrigger = TIM_TS_TI1FP1;                  // TI1FP1
    TIM_SLAVE_ConfigStructure.TriggerFilter = 0;                             // 不滤波
    TIM_SLAVE_ConfigStructure.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;  // 上升沿触发
    HAL_TIM_SlaveConfigSynchro(&TIM_IC_Handler, &TIM_SLAVE_ConfigStructure); // 配置TIM从模式

    HAL_TIM_IC_Start(&TIM_IC_Handler, TIM_CHANNEL_1); // 使能输入捕获,计数器
}

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2) // 判断是不是TIM2
    {
        GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

        __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
        __HAL_RCC_TIM2_CLK_ENABLE();  // 使能TIM2时钟

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;       // 复用推挽输出,这里只要是复用都行
        GPIO_InitStructure.Pin = GPIO_PIN_0;             // PA0
        GPIO_InitStructure.Pull = GPIO_PULLDOWN;         // 由于PA0一端接3.3V,所以我们这里接下拉电阻
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
        HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);       // GPIO结构体初始化
    }
}

void IC_Pulse_Get_Counter()
{
    static uint16_t Pulse_cnt = 0;    // 每次获得的脉冲数
    static uint16_t Pulse_oldcnt = 0; // 上一次获得的脉冲数
    static uint8_t key = 0;

    key = KEY_Scan(0);
    if (key == KEY0_PRES) // 如果按下KEY0
    {
        __HAL_TIM_SET_COUNTER(&TIM_IC_Handler, 0); // 清除所有脉冲数,重新计数
    }

    Pulse_cnt = __HAL_TIM_GET_COUNTER(&TIM_IC_Handler); // 获得脉冲数
    if (Pulse_oldcnt != Pulse_cnt)                      // 如果这次与上次的脉冲数不同时
    {
        Pulse_oldcnt = Pulse_cnt;           // 将每次获得的脉冲数赋给上一次获得的脉冲数,及时进行下一次的获取
        printf("CNT:%d\r\n", Pulse_oldcnt); //  打印脉冲数
    }
}
//IC.h
#ifndef __IC_H
#define __IC_H

#include "sys.h"
#include "KEY.h"
#include 
void IC_Init(uint16_t psc, uint16_t arr);
void IC_Pulse_Get_Counter();

#endif
//main.c
//本例程是用外部模式1捕获按键KEY_UP的脉冲个数,并在串口上打印脉冲个数,KEY0清除脉冲个数
//使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//  HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	uart_init(115200);				// 初始化串口
//	KEY_Init();						// 按键初始化
//	IC_Init(0, 65535);				// 预分频系数psc,实际分频系数psc+1 1
									// 自动重装载值arr,实际可以记得的脉冲数65536

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "IC.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	KEY_Init();						// 按键初始化
	LCD_Init();						// 初始化LCD FSMC接口
	IC_Init(0, 65535);				// 预分频系数psc,实际分频系数psc+1 1
									// 自动重装载值arr,实际可以记得的脉冲数65536
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");

	while (1)
	{
		IC_Pulse_Get_Counter();
	}
}

高级定时器

高级定时器简介

高级定时器:

TIM1/TIM8

主要特性:

16位递增、递减、中心对齐计数器(计数值:0~65535

16位预分频器(分频系数:1~65536

可用于触发DACADC

在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求

4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式

使用外部信号控制定时器且可实现多个定时器互连的同步电路

支持编码器和霍尔传感器电路等

重复计数器

死区时间带可编程的互补输出

断路输入,用于将定时器的输出信号置于用户可选的安全配置中

高级定时器框图:

STM32hal库学习(F1)-TIMER_第35张图片

重复计数器的特性

STM32hal库学习(F1)-TIMER_第36张图片

输出指定个数PWM

框图

STM32hal库学习(F1)-TIMER_第37张图片

配置步骤

STM32hal库学习(F1)-TIMER_第38张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_PWM_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_PWM_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_PWM_ConfigChannel()

CCMRxCCRxCCER

配置PWM模式、比较值、输出极性等

__HAL_TIM_ENABLE_IT()

CCER

使能更新中断等

HAL_TIM_PWM_Start()

CCERCR1

使能输出、主输出、启动计数器

HAL_TIM_IRQHandler()

SR

定时器中断处理公用函数,处理各种中断

HAL_TIM_PeriodElapsedCallback()

定时器更新中断回调函数,由用户重定义

HAL_TIM_GenerateEvent()

EGR

通过软件产生事件

__HAL_TIM_ENABLE()

CR1

启动计数器

//相关结构体
typedef struct 
{ 
   uint32_t OCMode; 	  /* 输出比较模式选择 */
   uint32_t Pulse; 	      /* 设置比较值 */
   uint32_t OCPolarity;   /* 设置输出比较极性 */
   uint32_t OCNPolarity;  /* 设置互补输出比较极性 */
   uint32_t OCFastMode;   /* 使能或失能输出比较快速模式 */
   uint32_t OCIdleState;  /* 空闲状态下OC1输出 */
   uint32_t OCNIdleState; /* 空闲状态下OC1N输出 */ 
} TIM_OC_InitTypeDef;
//PWM.c
#include "PWM.h"

TIM_HandleTypeDef TIM_PWM_Handler;       // 定时器时基单元初始化句柄
TIM_OC_InitTypeDef TIM_OC_InitStructure; // 定时器输出比较结构体

void PWM_Init(uint16_t psc, uint16_t arr)
{
    TIM_PWM_Handler.Instance = TIM8;                       // 定时器基地址:TIM8
    TIM_PWM_Handler.Init.Prescaler = psc;                  // 预分频系数psc,实际分频系数psc+1
    TIM_PWM_Handler.Init.Period = arr;                     // 自动重装载值arr,实际计数值arr+1
    TIM_PWM_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    TIM_PWM_Handler.Init.RepetitionCounter = 0;            // 重复计数器值先初始化为0
    HAL_TIM_PWM_Init(&TIM_PWM_Handler);                    // 定时器时基单元初始化,回调HAL_TIM_PWM_MspInit()

    TIM_OC_InitStructure.OCMode = TIM_OCMODE_PWM1;                                     // PWM1模式
    TIM_OC_InitStructure.Pulse = arr / 2;                                              // 占空比50%
    TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;                             // 有效电平:高电平
    HAL_TIM_PWM_ConfigChannel(&TIM_PWM_Handler, &TIM_OC_InitStructure, TIM_CHANNEL_1); // 定时器输出PWM通道配置

    __HAL_TIM_ENABLE_IT(&TIM_PWM_Handler, TIM_IT_UPDATE); // 使能定时器更新中断
    HAL_TIM_PWM_Start(&TIM_PWM_Handler, TIM_CHANNEL_1);   // 使能PWM输出,主输出,启动计数器
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM8) // 判断是不是TIM8
    {
        GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

        __HAL_RCC_TIM8_CLK_ENABLE();  // 使能TIM8时钟
        __HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC时钟

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;       // 复用推挽输出模式
        GPIO_InitStructure.Pin = GPIO_PIN_6;             // PC6-TIM8CH1
        GPIO_InitStructure.Pull = GPIO_PULLUP;           // 上拉,这里PWM输出无影响
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
        HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);       // GPIO初始化

        HAL_NVIC_SetPriority(TIM8_UP_IRQn, 1, 3); // 设置TIM8更新中断,抢占优先级1,响应优先级3
        HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);         // 使能TIM8,NVIC中断
    }
}

void TIM8_UP_IRQHandler()
{
    HAL_TIM_IRQHandler(&TIM_PWM_Handler); // 定时器公共处理函数,自动清除标志位,回调HAL_TIM_PeriodElapsedCallback()
}

static uint8_t PWMn_remain = 0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM8) // 判断是不是TIM8
    {
        if (PWMn_remain) // 判断PWM执行标志
        {
            TIM8->RCR = PWMn_remain - 1;                                     // 往RCR寄存器写n-1,则会输出n个pwm波
            HAL_TIM_GenerateEvent(&TIM_PWM_Handler, TIM_EVENTSOURCE_UPDATE); // 软件触发更新事件
            __HAL_TIM_ENABLE(&TIM_PWM_Handler);                              // 启动计数器,这里其实可以不需要,因为PWMn_SET()设置时就已经启动了计数器
            PWMn_remain = 0;                                                 // 执行n个PWM波
        }
        else
        {
            TIM8->CR1 &= ~(1 << 0); // 关闭计数器
        }
    }
}

/* 高级定时器TIMX pwmn设置PWM个数函数 */
void PWMn_SET(uint8_t pwmn)
{
    if (pwmn == 0)
        return;
    PWMn_remain = pwmn; // 将pwm个数存到另一个变量
    HAL_TIM_GenerateEvent(&TIM_PWM_Handler, TIM_EVENTSOURCE_UPDATE); // 软件触发更新事件
    __HAL_TIM_ENABLE(&TIM_PWM_Handler); // 启动计数器
}

void PWM_LED_Input()
{
    GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

    __HAL_RCC_GPIOE_CLK_ENABLE(); // 使能GPIO时钟

    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;       // 输入模式
    GPIO_InitStructure.Pin = GPIO_PIN_5;             // PE5,LED1
    GPIO_InitStructure.Pull = GPIO_PULLUP;           // 上拉,
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);       // GPIO初始化
}

//PWM.h
#ifndef __PWM_H
#define __PWM_H

#include "sys.h"
void PWM_Init(uint16_t psc, uint16_t arr);
void PWMn_SET(uint8_t pwmn);
void PWM_LED_Input();

#endif
//main.c
//本例程是用TIM8 CH1,即PC6,输出指定个数PWM到PE5(LED1),用杜邦线连接,这里注意PE5需要改成输入模式
//使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//  HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	LED_Init();						// 初始化LED
//	KEY_Init();						// 按键初始化
//	PWM_LED_Input();			  // LED1设置输入模式
//	PWM_Init(7200 - 1, 5000 - 1); // 计数频率10000HZ,溢出频率2HZ

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "PWM.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	KEY_Init();						// 按键初始化
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
	PWM_LED_Input();			  // LED1设置输入模式
	PWM_Init(7200 - 1, 5000 - 1); // 计数频率10000HZ,溢出频率2HZ
	PWMn_SET(5);				  // 设置5个pwm
	uint8_t key;

	while (1)
	{
		key = KEY_Scan(0);
		if (key == KEY0_PRES)//按下KEY0
		{
			PWMn_SET(6);//输出6个PWM
		}

		LED0 = 1;
	}
}

输出比较

框图

这里其实跟PWM差不多,重点是死区控制,互补通道输出

STM32hal库学习(F1)-TIMER_第39张图片

配置步骤

STM32hal库学习(F1)-TIMER_第40张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_OC_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_OC_MspInit

存放NVICCLOCKGPIO初始化代码

HAL_TIM_OC_ConfigChannel()

CCMRxCCRxCCER

设置输出比较模式、比较值、输出极性等

__HAL_TIM_ENABLE_OCxPRELOAD()

CCMRx

使能通道预装载

HAL_TIM_OC_Start()

CR1CCERBDTR

使能输出比较、主输出、启动计数器

__HAL_TIM_SET_COMPARE()

CCRx

修改捕获/比较寄存器的值

typedef struct 
{ 
   uint32_t OCMode; 	  /* 输出比较模式选择 */
   uint32_t Pulse; 	      /* 设置比较值 */
   uint32_t OCPolarity;   /* 设置输出比较极性 */
   uint32_t OCNPolarity;  /* 设置互补输出比较极性 */
   uint32_t OCFastMode;   /* 使能或失能输出比较快速模式 */
   uint32_t OCIdleState;  /* 空闲状态下OC1输出 */
   uint32_t OCNIdleState; /* 空闲状态下OC1N输出 */ 
} TIM_OC_InitTypeDef;
//OC.c
#include "OC.h"

TIM_HandleTypeDef TIM_OC_Handler;        // 定时器时基单元初始化句柄
TIM_OC_InitTypeDef TIM_OC_InitStructure; // 定时器输出比较结构体

void OC_Init(uint16_t psc, uint16_t arr)
{
    TIM_OC_Handler.Instance = TIM8;                       // 定时器:TIM8
    TIM_OC_Handler.Init.Prescaler = psc;                  // 预分频系数:psc,实际分频系数:psc+1
    TIM_OC_Handler.Init.Period = arr;                     // 自动重装载值:arr,实际计数个数:arr+1
    TIM_OC_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    HAL_TIM_OC_Init(&TIM_OC_Handler);                     // 定时器时基单元初始化,回调HAL_TIM_OC_MspInit()

    TIM_OC_InitStructure.OCMode = TIM_OCMODE_TOGGLE;//输出模式:翻转
    TIM_OC_InitStructure.Pulse = arr;//CCR:arr
    TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;//有效电平:高电平
    HAL_TIM_OC_ConfigChannel(&TIM_OC_Handler, &TIM_OC_InitStructure, TIM_CHANNEL_ALL);//定时器输出通道配置

    __HAL_TIM_ENABLE_OCxPRELOAD(&TIM_OC_Handler, TIM_CHANNEL_ALL);//使能通道预装载

    HAL_TIM_OC_Start(&TIM_OC_Handler, TIM_CHANNEL_ALL);//使能输出,启动计数器
}

void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM8)//判断是不是TIM8
    {
        GPIO_InitTypeDef GPIO_InitStructure;//GPIO初始化结构体

        __HAL_RCC_GPIOC_CLK_ENABLE();//使能GPIOC时钟
        __HAL_RCC_TIM8_CLK_ENABLE();//使能TIM8时钟

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//复用推挽输出
        GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;//PC6,PC7,PC8,PC9-TIM8CHALL
        GPIO_InitStructure.Pull = GPIO_PULLUP;//上拉,这里随便配置无影响
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;//高速
        HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);//GPIO初始化
    }
}


/*将TIM8四个通道配置成相位25%,50%,75%,100%*/
void OC_SET()
{
    __HAL_TIM_SET_COMPARE(&TIM_OC_Handler, TIM_CHANNEL_1, 250 - 1);
    __HAL_TIM_SET_COMPARE(&TIM_OC_Handler, TIM_CHANNEL_2, 500 - 1);
    __HAL_TIM_SET_COMPARE(&TIM_OC_Handler, TIM_CHANNEL_3, 750 - 1);
    __HAL_TIM_SET_COMPARE(&TIM_OC_Handler, TIM_CHANNEL_4, 1000 - 1);
}
//OC.h
#ifndef __OC_H
#define __OC_H

#include "sys.h"
void OC_Init(uint16_t psc, uint16_t arr);
void OC_SET();

#endif 
//main.c
//本例程是用TIM8 CH1,CH2,CH3,CH4输出频率500HZ,占空比为50%的PWM波,采用翻转模式
//相位分别是25%,50%,75%,100%
//使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//  HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//  OC_Init(72 - 1, 1000 - 1);      // 计数频率1M,溢出频率1000HZ,PWM频率500HZ
//	OC_SET();

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "OC.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");

	OC_Init(72 - 1, 1000 - 1); // 计数频率1M,溢出频率1000HZ,PWM频率500HZ
	OC_SET();
	while (1)
	{
		
	}
}

死区控制

STM32hal库学习(F1)-TIMER_第41张图片

框图

STM32hal库学习(F1)-TIMER_第42张图片

死区时间计算

STM32hal库学习(F1)-TIMER_第43张图片

刹车功能(时钟安全系统)

STM32hal库学习(F1)-TIMER_第44张图片

刹车后

STM32hal库学习(F1)-TIMER_第45张图片

死区应用互补输出H桥

STM32hal库学习(F1)-TIMER_第46张图片

配置步骤

STM32hal库学习(F1)-TIMER_第47张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_PWM_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_PWM_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_PWM_ConfigChannel()

CCMRxCCRxCCER

配置PWM模式、比较值、输出极性等

HAL_TIMEx_ConfigBreakDeadTime()

BDTR

配置刹车功能、死区时间等

HAL_TIM_PWM_Start()

CCERCR1

使能输出、主输出、启动计数器

HAL_TIMEx_PWMN_Start()

CCERCR1

使能互补输出、主输出、启动计数器

//相关结构体
typedef struct 
{ 
   uint32_t OCMode; 	  /* 输出比较模式选择 */
   uint32_t Pulse; 	      /* 设置比较值 */
   uint32_t OCPolarity;   /* 设置输出比较极性 */
   uint32_t OCNPolarity;  /* 设置互补输出比较极性 */
   uint32_t OCFastMode;   /* 使能或失能输出比较快速模式 */
   uint32_t OCIdleState;  /* 空闲状态下OCx输出 */
   uint32_t OCNIdleState; /* 空闲状态下OCxN输出 */ 
} TIM_OC_InitTypeDef;
//OC.c
#include "OC.h"

TIM_HandleTypeDef TIM_OC_Handler;              // 定时器时基单元初始化句柄
TIM_OC_InitTypeDef TIM_OC_InitStructure;       // 定时器输出比较结构体
TIM_BreakDeadTimeConfigTypeDef TIM_BDT_Config; // 定时器死区配置句柄

void OC_Init(uint16_t psc, uint16_t arr)
{
    TIM_OC_Handler.Instance = TIM1;                             // 定时器:TIM1
    TIM_OC_Handler.Init.Prescaler = psc;                        // 预分频系数:psc,实际分频系数psc+1
    TIM_OC_Handler.Init.Period = arr;                           // 自动重装载值arr,实际计数个数arr+1
    TIM_OC_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;       // 向上计数模式
    TIM_OC_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 时钟分频因子4分频
    HAL_TIM_PWM_Init(&TIM_OC_Handler);                          // 定时器时基单元初始化,回调HAL_TIM_PWM_MspInit()

    TIM_OC_InitStructure.OCMode = TIM_OCMODE_PWM1;                                   // 输出模式:PWM1
    TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;                           // OC有效电平:高电平
    TIM_OC_InitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;                         // OCN有效电平:高电平
    TIM_OC_InitStructure.OCIdleState = TIM_OCIDLESTATE_RESET;                        // 空闲状态:OC低电平
    TIM_OC_InitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;                      // 空闲状态:OCN低电平
    HAL_TIM_OC_ConfigChannel(&TIM_OC_Handler, &TIM_OC_InitStructure, TIM_CHANNEL_1); // 定时器输出配置

    TIM_BDT_Config.OffStateIDLEMode = TIM_OSSI_DISABLE;              // 空闲状态下关闭输出不使能
    TIM_BDT_Config.OffStateRunMode = TIM_OSSR_DISABLE;               // 运行状态下关闭输出不使能
    TIM_BDT_Config.LockLevel = TIM_LOCKLEVEL_OFF;                    // 不使用寄存器锁存功能
    TIM_BDT_Config.BreakPolarity = TIM_BREAKPOLARITY_HIGH;           // 刹车信号高电平
    TIM_BDT_Config.BreakState = TIM_BREAK_ENABLE;                    // 刹车使能
    TIM_BDT_Config.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;     // AOE位,允许刹车结束后自动恢复输出
    HAL_TIMEx_ConfigBreakDeadTime(&TIM_OC_Handler, &TIM_BDT_Config); // 死区配置
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1) // 判断是不是TIM1
    {
        GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

        __HAL_RCC_GPIOE_CLK_ENABLE(); // 使能GPIOE时钟
        __HAL_RCC_TIM1_CLK_ENABLE();  // 使能TIM1时钟

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;                      // 复用推挽模式
        GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_15; // PE8:TIM1_CH1N PE9:TIM1_CH1 PE15:TIM1_BKIN
        GPIO_InitStructure.Pull = GPIO_PULLDOWN;                        // 下拉,我配置了刹车信号高电平,所以选取下拉
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;                // 高速
        HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);                      // GPIO结构体初始化
    }
}

/**
 * @brief       定时器TIMX 设置输出比较值 & 死区时间
 * @param       ccr: 输出比较值
 * @param       dtg: 死区时间
 *   @arg       dtg[7:5]=0xx时, 死区时间 = dtg[7:0] * tDTS
 *   @arg       dtg[7:5]=10x时, 死区时间 = (64 + dtg[6:0]) * 2  * tDTS
 *   @arg       dtg[7:5]=110时, 死区时间 = (32 + dtg[5:0]) * 8  * tDTS
 *   @arg       dtg[7:5]=111时, 死区时间 = (32 + dtg[5:0]) * 16 * tDTS
 *   @note      tDTS = 1 / (Ft /  CKD[1:0]) = 1 / 18M = 55.56ns
 * @retval      无
 */
void TIM_SET(uint16_t ccr, uint8_t dtg)
{
    __HAL_TIM_SET_COMPARE(&TIM_OC_Handler, TIM_CHANNEL_1, ccr);      // 设置CCR
    TIM_BDT_Config.DeadTime = dtg;                                   // 设置死区时间
    HAL_TIMEx_ConfigBreakDeadTime(&TIM_OC_Handler, &TIM_BDT_Config); // 死区配置
}
//OC.h
#ifndef __OC_H
#define __OC_H

#include "sys.h"
void OC_Init(uint16_t psc, uint16_t arr);
void TIM_SET(uint16_t ccr, uint8_t dtg);

#endif 
//main.c
//本例程是用TIM1,CH1,CH1N死区控制输出5.56us死区时间,占空比70%
//使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//  HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	OC_Init(72 - 1, 1000 - 1);      //计数频率1M,溢出频率1000HZ

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "OC.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");

	OC_Init(72 - 1, 1000 - 1);//计数频率1M,溢出频率1000HZ
	TIM_SET(700 - 1, 100);//CCR:700:占空比:70%,死区时间5.56us

	while (1)
	{
		LED0 = 1;
	}
}

PWM输入模式

框图

STM32hal库学习(F1)-TIMER_第48张图片

输入模式时序

STM32hal库学习(F1)-TIMER_第49张图片

配置步骤

STM32hal库学习(F1)-TIMER_第50张图片

相关库函数

函数

主要寄存器

主要功能

HAL_TIM_IC_Init()

CR1ARRPSC

初始化定时器基础参数

HAL_TIM_IC_MspInit()

存放NVICCLOCKGPIO初始化代码

HAL_TIM_IC_ConfigChannel()

CCMRxCCER

配置通道映射、捕获边沿、分频、滤波等

HAL_TIM_SlaveConfigSynchro()

SMCRCCER

配置从模式、触发源、触发边沿等

HAL_TIM_IC_Start_IT()

CCERDIERCR1

使能输入捕获、捕获中断并启动计数器

HAL_TIM_IRQHandler()

SR

定时器中断处理公用函数,处理各种中断

HAL_TIM_IC_CaptureCallback()

定时器输入捕获回调函数,由用户重定义

//相关结构体
typedef struct
{ 
    uint32_t ICPolarity;    /* 输入捕获触发方式选择,比如上升、下降沿捕获 */ 
    uint32_t ICSelection;   /* 输入捕获选择,用于设置映射关系 */ 
    uint32_t ICPrescaler;   /* 输入捕获分频系数 */ 
    uint32_t ICFilter;      /* 输入捕获滤波器设置 */ 
} TIM_IC_InitTypeDef;


typedef struct 
{ 
    uint32_t SlaveMode;         /* 从模式选择 */ 
    uint32_t InputTrigger;      /* 输入触发源选择 */ 
    uint32_t TriggerPolarity;   /* 输入触发极性 */ 
    uint32_t TriggerPrescaler;  /* 输入触发预分频 */ 
    uint32_t TriggerFilter;     /* 输入滤波器设置 */ 
} TIM_SlaveConfigTypeDef;
//PWM.c
#include "PWM.h"

TIM_HandleTypeDef TIM_PWM_Handler;       // 定时器时基单元初始化句柄
TIM_OC_InitTypeDef TIM_OC_InitStructure; // 定时器输出初始化结构体

void PWM_Init(uint16_t psc, uint16_t arr)
{
    TIM_PWM_Handler.Instance = TIM3;                       // 定时器:TIM3
    TIM_PWM_Handler.Init.Prescaler = psc;                  // 预分频系数:psc,实际分频系数psc+1
    TIM_PWM_Handler.Init.Period = arr;                     // 自动重装载值:arr,实际计数个数:arr+1
    TIM_PWM_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    HAL_TIM_PWM_Init(&TIM_PWM_Handler);                    // 定时器时基单元初始化,回调HAL_TIM_PWM_MspInit()

    TIM_OC_InitStructure.Pulse = arr / 2;                                              // CCR:arr/2 占空比50%PWM
    TIM_OC_InitStructure.OCMode = TIM_OCMODE_PWM1;                                     // PWM1模式
    TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_LOW;                              // 有效电平:低电平
    HAL_TIM_PWM_ConfigChannel(&TIM_PWM_Handler, &TIM_OC_InitStructure, TIM_CHANNEL_2); // 定时器PWM通道配置

    HAL_TIM_PWM_Start(&TIM_PWM_Handler, TIM_CHANNEL_2); // 使能PWM输出,开启计数器
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3) // 判断是不是TIM3
    {
        GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

        __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
        __HAL_RCC_TIM3_CLK_ENABLE();  // 使能TIM3时钟

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;       // 复用推挽输出
        GPIO_InitStructure.Pin = GPIO_PIN_5;             // PB5——TIM3 CH2
        GPIO_InitStructure.Pull = GPIO_PULLUP;           // 上拉,这里随便配置
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
        HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);       // GPIO初始化

        __HAL_RCC_AFIO_CLK_ENABLE();     // 使能AFIO
        __HAL_AFIO_REMAP_TIM3_PARTIAL(); // 将TIM3CH2重映射到PB5
    }
}
//PWM.h
#ifndef __PWM_H
#define __PWM_H

#include "sys.h"
void PWM_Init(uint16_t psc, uint16_t arr);

#endif 
//IC.c
#include "IC.h"

TIM_HandleTypeDef TIM_IC_Handler;        // 定时器时基单元初始化句柄
TIM_SlaveConfigTypeDef TIM_Slaveconfig;  // 定时器从模式配置句柄
TIM_IC_InitTypeDef TIM_IC_InitStructure; // 定时器输入捕获结构体

/* PWM输入状态(PWM_sta)
 * 0,没有成功捕获.
 * 1,已经成功捕获了
 */
uint8_t PWM_sta = 0;   /* PWM输入状态 */
uint16_t PWM_psc = 0;  /* PWM输入分频系数 */
uint32_t PWM_hval = 0; /* PWM的高电平脉宽 */
uint32_t PWM_cval = 0; /* PWM的周期宽度 */
double ht, ct, f, tpsc;

/* PWM输入模式 初始化函数,采样时钟频率为72Mhz,精度约13.8ns,采样最长PWM周期910.2us*/
void IC_Init()
{
    TIM_IC_Handler.Instance = TIM8;                       // 定时器:TIM8
    TIM_IC_Handler.Init.Prescaler = 0;                    // 不分频,频率越高,采样精度越高
    TIM_IC_Handler.Init.Period = 65535;                   // 自动重装载值设置最大,可采样周期保证尽可能大
    TIM_IC_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    HAL_TIM_IC_Init(&TIM_IC_Handler);                     // 定时器时基单元初始化,回调HAL_TIM_IC_MspInit()

    TIM_Slaveconfig.SlaveMode = TIM_SLAVEMODE_RESET;               // 从模式:复位模式
    TIM_Slaveconfig.InputTrigger = TIM_TS_TI1FP1;                  // TI1FP1
    TIM_Slaveconfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;  // 上升沿触发
    TIM_Slaveconfig.TriggerFilter = 0;                             // 不滤波
    HAL_TIM_SlaveConfigSynchro(&TIM_IC_Handler, &TIM_Slaveconfig); // 定时器从模式配置初始化

    TIM_IC_InitStructure.ICPolarity = TIM_ICPOLARITY_RISING;                         // TI1FP1捕获上升沿
    TIM_IC_InitStructure.ICSelection = TIM_ICSELECTION_DIRECTTI;                     // TI1FP1
    TIM_IC_InitStructure.ICPrescaler = TIM_ICPSC_DIV1;                               // 不分频
    TIM_IC_InitStructure.ICFilter = 0;                                               // 不滤波
    HAL_TIM_IC_ConfigChannel(&TIM_IC_Handler, &TIM_IC_InitStructure, TIM_CHANNEL_1); // 定时器输入捕获通道1配置

    TIM_IC_InitStructure.ICPolarity = TIM_ICPOLARITY_FALLING;                        // TI1FP2捕获下降沿
    TIM_IC_InitStructure.ICSelection = TIM_ICSELECTION_INDIRECTTI;                   // TI1FP2
    HAL_TIM_IC_ConfigChannel(&TIM_IC_Handler, &TIM_IC_InitStructure, TIM_CHANNEL_2); // 定时器输入捕获通道2配置

    HAL_TIM_IC_Start_IT(&TIM_IC_Handler, TIM_CHANNEL_1); // 定时器通道1使能计数器,启动中断
    HAL_TIM_IC_Start(&TIM_IC_Handler, TIM_CHANNEL_2);    // 定时器通道2使能计数器
}

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM8) // 判断是不是TIM8
    {
        GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

        __HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC时钟
        __HAL_RCC_TIM8_CLK_ENABLE();  // 使能TIM8

        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;       // 复用推挽输出,可以输入
        GPIO_InitStructure.Pin = GPIO_PIN_6;             // PC6,TIM8 CH1
        GPIO_InitStructure.Pull = GPIO_PULLDOWN;         // 下拉
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
        HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);       // GPIO初始化

        HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3); // 设置TIM8输入捕获中断优先级,抢占优先级1,响应优先级3
        HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);         // 使能TIM8输入捕获中断
    }
}

void TIM8_CC_IRQHandler()
{
    HAL_TIM_IRQHandler(&TIM_IC_Handler); // 定时器公共处理函数,自动清除标志位,回调HAL_TIM_IC_CaptureCallback()
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM8) // 判断是不是TIM8
    {
        if (PWM_sta == 0) // 如果还没有捕获
        {
            if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // TIM8CH1
            {
                PWM_hval = HAL_TIM_ReadCapturedValue(&TIM_IC_Handler, TIM_CHANNEL_2) + 1 + 1; // 高电平读取通道2
                PWM_cval = HAL_TIM_ReadCapturedValue(&TIM_IC_Handler, TIM_CHANNEL_1) + 1 + 1; // 周期读取通道1
                PWM_sta = 1;                                                                  // 输入捕获标志
            }
        }
    }
}

/* PWM输入模式 重新启动捕获 */
void IC_PWM_REST(void)
{
    INTX_DISABLE(); /* 关闭中断 */

    PWM_sta = 0; /* 清零状态,重新开始检测 */
    PWM_hval = 0;
    PWM_cval = 0;

    INTX_ENABLE(); /* 打开中断 */
}

void IC_Show()
{
    if (PWM_sta) /* 捕获了一次数据 */
    {
        printf("\r\n");                                   /* 输出空,另起一行 */
        printf("PWM PSC  :%d\r\n", PWM_psc);              /* 打印分频系数 */
        printf("PWM Hight:%d\r\n", PWM_hval);             /* 打印高电平脉宽 */
        printf("PWM Cycle:%d\r\n", PWM_cval);             /* 打印周期 */
        tpsc = ((double)PWM_psc + 1) / 72;                /* 得到PWM采样时钟周期时间 */
        ht = PWM_hval * tpsc;                             /* 计算高电平时间 */
        ct = PWM_cval * tpsc;                             /* 计算周期长度 */
        f = (1 / ct) * 1000000;                           /* 计算频率 */
        printf("PWM Hight time:%.3fus\r\n", ht);          /* 打印高电平脉宽长度 */
        printf("PWM Cycle time:%.3fus\r\n", ct);          /* 打印周期时间长度 */
        printf("PWM Frequency :%.3fHz\r\n", f);           /* 打印频率 */
        IC_PWM_REST();                                    /* 重启PWM输入检测 */
    }
}
//IC.h
#ifndef __IC_H
#define __IC_H

#include "sys.h"
#include 
void IC_Init();
void IC_PWM_REST(void);
void IC_Show();

#endif 
//main.c
//本例程是用TIM3 CH2 PB5输入PWM ,TIM8 CH1 PC6捕获占空比,频率等,并将信息打印出来
//使用的是正点原子F1精英开发板
//我这里调用了很多初始化,但其实如果不考虑其他的
//这里必要的只有
//  HAL_Init();						// 初始化HAL库
//	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
//	uart_init(115200);				// 初始化串口
//	PWM_Init(72 - 1, 10 - 1);       // 计数频率1MHZ.溢出频率10^5
//	TIM3->CCR2 = 3;//占空比70%
//	IC_Init();

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usmart.h"
#include "KEY.h"
#include "led.h"
#include "lcd.h"
#include "PWM.h"
#include "IC.h"

int main(void)
{
	HAL_Init();						// 初始化HAL库
	Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
	delay_init(72);					// 初始化延时函数
	uart_init(115200);				// 初始化串口
	LED_Init();						// 初始化LED
	LCD_Init();						// 初始化LCD FSMC接口
	usmart_dev.init(84);			// 初始化USMART
	POINT_COLOR = RED;				// 画笔颜色:红色
	LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");

	PWM_Init(72 - 1, 10 - 1); // 计数频率1MHZ.溢出频率10^5
	TIM3->CCR2 = 3;//占空比70%
	IC_Init();
	
	while (1)
	{
		IC_Show();
		LED0 = 1;
	}
}

你可能感兴趣的:(STM32,stm32,学习,嵌入式硬件)