void delay_us(uint32_t us)
{
us *= 72;
while(us--);
}
//有明显的缺点:
//延时不精准,
//CPU死等
//例如函数调用压栈出栈需要时间,ARM流水线导致时间不确定,只是大概的延时
定时器的核心就是计数器
实时定时器指的是RTC
计数器位数0-65535,也就是65536 2^16
定时器类型 |
主要功能 |
基本定时器 |
没有输入输出通道,常用作时基,即定时功能 |
通用定时器 |
具有多路独立通道,可用于输入捕获/输出比较,也可用作时基 |
高级定时器 |
除具备通用定时器所有功能外, 还具备带死区控制的互补信号输出、刹车输入等功能 (可用于电机控制、数字电源设计等) |
TIM6/TIM7
16位递增计数器(计数值:0~65535)
16位预分频器(分频系数:1~65536)
可用于触发DAC
在更新事件(计数器溢出)时,会产生中断/DMA请求
ARR和PSC具有缓冲,转移到影子寄存器才能生效改变后的ARR和PSC.
同时ARR还有一位ARPE专门决定是否有缓冲
缓冲的意思是在更新事件或者中断/DMA请求后生效
简单来说ARR有立即重装载和更新后重装载的区别
这里举个例子充分理解一下缓冲
如果我们让灯亮1s,灭2s,这时我们在1s后改变ARR需要时间执行,这时会导致不精准
那如果我让灯亮1s,灭1s,我们不需要改变ARR,所以也就可以不需要缓冲
计数器模式 |
溢出条件 |
递增计数模式 |
CNT==ARR |
递减计数模式 |
CNT==0 |
中心对齐模式 |
CNT==ARR-1、CNT==1 |
该寄存器用于设置ARR寄存器是否具有缓冲,使能/关闭计数器
该寄存器用于设置计时器初始值,范围:0~65535
该寄存器用于设置预分频系数,范围:0~65535;
实际预分频系数等于PSC+1
该寄存器用于设置自动重装载值,范围:0~65535
相关函数介绍
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_Base_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_Base_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_Base_Start_IT() |
DIER、CR1 |
使能更新中断并启动计数器 |
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)
可用于触发DAC、ADC
在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求
4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式
使用外部信号控制定时器且可实现多个定时器互连的同步电路
支持编码器和霍尔传感器电路等
计数器时钟选择类型 |
设置方法 |
内部时钟(CK_INT) |
设置TIMx_SMCR的SMS=000 ,(ECE=0但ECE默认是0,所以不需要配置) |
外部时钟模式1: 外部输入引脚(TIx) |
设置TIMx_SMCR的SMS=111,(ECE=0,但ECE默认是0,所以不需要配置) |
外部时钟模式2: 外部触发输入(ETR) |
设置TIMx_SMCR的ECE=1 (或者SMS=111和TS=111,不过这里选择的线路是ETRF) IO口复用 |
内部触发输入(ITRx) |
使用一个定时器作为另一个定时器的预分频器, 如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。 设置可参考STM32F10xxx参考手册_V10(中文版).pdf,14.3.15节 |
使用一个定时器作为另一个定时器的预分频器
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_PWM_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_PWM_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_PWM_ConfigChannel() |
CCMRx、CCRx、CCER |
配置PWM模式、比较值、输出极性等 |
HAL_TIM_PWM_Start() |
CCER、CR1 |
使能输出比较并启动计数器 |
__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();//调用呼吸灯函数
}
}
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_IC_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_IC_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_IC_ConfigChannel() |
CCMRx、CCER |
配置通道映射、捕获边沿、分频、滤波等 |
__HAL_TIM_ENABLE_IT() |
DIER |
使能更新中断等 |
HAL_TIM_IC_Start_IT() |
CCER、DIER、CR1 |
使能输入捕获、捕获中断并启动计数器 |
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);
}
}
应选择外部时钟模式1
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_IC_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_IC_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_SlaveConfigSynchro() |
SMCR、CCMRx、CCER |
配置定时器从模式、触发选择、分频、滤波等 |
HAL_TIM_IC_Start() |
CCER、CR1 |
使能输入捕获、启动计数器 |
__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)
可用于触发DAC、ADC
在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求
4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式
使用外部信号控制定时器且可实现多个定时器互连的同步电路
支持编码器和霍尔传感器电路等
重复计数器
死区时间带可编程的互补输出
断路输入,用于将定时器的输出信号置于用户可选的安全配置中
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_PWM_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_PWM_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_PWM_ConfigChannel() |
CCMRx、CCRx、CCER |
配置PWM模式、比较值、输出极性等 |
__HAL_TIM_ENABLE_IT() |
CCER |
使能更新中断等 |
HAL_TIM_PWM_Start() |
CCER、CR1 |
使能输出、主输出、启动计数器 |
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差不多,重点是死区控制,互补通道输出
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_OC_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_OC_MspInit |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_OC_ConfigChannel() |
CCMRx、CCRx、CCER |
设置输出比较模式、比较值、输出极性等 |
__HAL_TIM_ENABLE_OCxPRELOAD() |
CCMRx |
使能通道预装载 |
HAL_TIM_OC_Start() |
CR1、CCER、BDTR |
使能输出比较、主输出、启动计数器 |
__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)
{
}
}
刹车后
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_PWM_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_PWM_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_PWM_ConfigChannel() |
CCMRx、CCRx、CCER |
配置PWM模式、比较值、输出极性等 |
HAL_TIMEx_ConfigBreakDeadTime() |
BDTR |
配置刹车功能、死区时间等 |
HAL_TIM_PWM_Start() |
CCER、CR1 |
使能输出、主输出、启动计数器 |
HAL_TIMEx_PWMN_Start() |
CCER、CR1 |
使能互补输出、主输出、启动计数器 |
//相关结构体
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;
}
}
函数 |
主要寄存器 |
主要功能 |
HAL_TIM_IC_Init() |
CR1、ARR、PSC |
初始化定时器基础参数 |
HAL_TIM_IC_MspInit() |
无 |
存放NVIC、CLOCK、GPIO初始化代码 |
HAL_TIM_IC_ConfigChannel() |
CCMRx、CCER |
配置通道映射、捕获边沿、分频、滤波等 |
HAL_TIM_SlaveConfigSynchro() |
SMCR、CCER |
配置从模式、触发源、触发边沿等 |
HAL_TIM_IC_Start_IT() |
CCER、DIER、CR1 |
使能输入捕获、捕获中断并启动计数器 |
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;
}
}