通用定时器的输入捕获与输出比较
1.输入捕获(测量两个上升沿之间的时间或者两个下降沿之间的时间)
用cubeMX来配置生成如下:先配置系统时钟为168Mhz.
接着配置TIM2把通道1作为输入捕获通道。
以上所有完成TIM2的配置,要在TIM2的CH1通道捕获正弦波。并且在上升沿捕获(cubeMX有配置)。这里用一个引脚来模拟一个源源不断的正弦波。通过用滴答定时器下溢事件中断不断的翻转这个引脚的电平。
因为系统时钟是168Mhz。所以滴答定时器每向下递减1(滴答定时器是向下计数)花的时间是1/168Mhz。当我们设置滴答定时器的重装载值。并且每次计数到0都会自动重新设置这个重装载值(并且是由硬件来实现)。所以就可以算出溢出时间:重装载值 * 1/168Mhz*。但要注意这个重装值不能超过2的24次方,因为滴答定时器是24位的。
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/100000); // HAL_RCC_GetHCLKFreq() HCLK是168Mhz 看上面的第一张图RCC配置
HAL_SYSTICK_Config(168000000/100000);
HAL_SYSTICK_Config(1680); // 这个是滴答定时器的初始化函数,而形参值就是重装载值
1680 * 1/168Mhz = 10us(溢出时间为10us) 所以10us进一次滴答定时器的中断。
void SysTick_Handler(void) // 这个是滴答定时器的中断函数
{
HAL_IncTick();
HAL_SYSTICK_IRQHandler();
}
// HAL_IncTick函数的定义
__weak void HAL_IncTick(void)
{
uwTick++;//每10us自增一次
}
void HAL_SYSTICK_IRQHandler(void)
{
HAL_SYSTICK_Callback(); // 滴答定时器回调函数
}
// HAL_SYSTICK_Callback函数的定义
void HAL_SYSTICK_Callback()
{
if( (uwTick%Toggle_Pulse) == 0 )//每4000的倍数翻转一次IO的状态 Toggle_Pulse = 4000 4000*10us = 40ms(相当于40翻转一次)
HAL_GPIO_TogglePin(GPIOI,GPIO_PIN_5); // 引脚翻转
}
总结:40ms翻转一次电平 , 那么周期就是80ms。f= 1/80ms (输出12.5Hz的脉冲)。这个引脚源源不断的脉冲。
那么下面就看定时器2的配置代码。分为.h与.c。
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* 类型定义 ------------------------------------------------------------------*/
typedef struct //测量两个上升沿之间的时间
{
uint8_t ucFinishFlag; /* 第二个上升沿的标志位 */
uint8_t ucStartFlag; /* 第一个上升沿的标志位 */
int32_t sCtr; /*第二个上升沿前捕获的值 */
int32_t sLastCtr; /* 第一个上升沿捕获的值 */
int32_t sPeriod; /* 保存两个上升沿值之差(周期) */
}STRUCT_CAPTURE;
/* 宏定义 --------------------------------------------------------------------*/
/********************通用定时器TIM参数定义,TIM2~TIM5************/
#define GENERAL_TIMx TIM2
#define GENERAL_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
#define GENERAL_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM2_CLK_DISABLE()
#define GENERAL_TIM_IRQ TIM2_IRQn
#define GENERAL_TIM_IRQHANDLER TIM2_IRQHandler
// 定义定时器预分频,定时器实际时钟频率为:84MHz/(GENERAL_TIM_PRESCALER+1)
#define GENERAL_TIM_PRESCALER 0 /* 定时器预分频值 */
// 定义定时器周期,当定时器开始计数到BASIC_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define GENERAL_TIM_PERIOD 0xFFFF // 定时器产生中断频率为:定时器频率/0xFFFF Hz
#define GENERAL_TIM_FREQ() (HAL_RCC_GetPCLK1Freq()<<1)//42000000*2=84000000 // PCLK1时钟是42Mhz,虽然TIM2挂载在PCL1上但时钟并不是42Mhz。而是要*2=84Mhz.(看第一张图的配置)
#define GENERAL_TIMX_GPIO_RCC_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE();
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_PIN_15
#define GENERAL_TIMx_GPIO_AF GPIO_AF1_TIM2
#define GENERAL_TIM_CHANNEL1 TIM_CHANNEL_1
#define GENERAL_TIM_IT_CC1 TIM_IT_CC1
#define GENERAL_TIM_STRAT_ICPolarity TIM_INPUTCHANNELPOLARITY_RISING //测量的起始边沿
/* 使用I/O模拟翻转输出脉冲 */
#define GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() // 输出控制脉冲给电机驱动器
#define PUL_PORT GPIOI // 对应驱动器的PUL-(驱动器使用共阳接法)
#define PUL_PIN GPIO_PIN_5 // 而PLU+直接接开发板的VCC
/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx_GeneralTIM;
/* 函数声明 ------------------------------------------------------------------*/
void GENERAL_TIMx_Init(void);
以上都是BSP(板级支持包)方便代码移植做此封装。
#include "GeneralTIM/bsp_GeneralTIM.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
TIM_HandleTypeDef htimx_GeneralTIM; /* 定时器句柄结构体 */
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能: 定时器通道1引脚初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(htim_base == &htimx_GeneralTIM)
{
/* 基本定时器外设时钟使能 */
GENERAL_TIM_RCC_CLK_ENABLE();
GENERAL_TIMX_GPIO_RCC_CLK_ENABLE();
GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽模式
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Alternate = GENERAL_TIMx_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStruct);
/* 脉冲输出引脚配置 */
GPIO_InitStruct.Pin = PUL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = 0; // GPIO引脚用做TIM复用功能,不做复用功能
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(PUL_PORT, &GPIO_InitStruct);
/* 配置定时器中断优先级并使能 */
HAL_NVIC_SetPriority(GENERAL_TIM_IRQ, 0, 0);
HAL_NVIC_EnableIRQ(GENERAL_TIM_IRQ) ;
}
}
/**
* 函数功能: 定时器初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void GENERAL_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig; /* 时钟配置结构体(基本定时器只能是内部时钟) */
TIM_MasterConfigTypeDef sMasterConfig; /* 主模式结构体 */
TIM_IC_InitTypeDef sConfigIC; /* 输入捕获结构体 */
GENERAL_TIM_RCC_CLK_ENABLE();
/* 定时器外设时钟使能 */
htimx_GeneralTIM.Instance = GENERAL_TIMx;
htimx_GeneralTIM.Init.Prescaler = GENERAL_TIM_PRESCALER;
htimx_GeneralTIM.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx_GeneralTIM.Init.Period = GENERAL_TIM_PERIOD;
htimx_GeneralTIM.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htimx_GeneralTIM);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htimx_GeneralTIM, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htimx_GeneralTIM, &sMasterConfig);
sConfigIC.ICPolarity = GENERAL_TIM_STRAT_ICPolarity; /* 极性上升沿 */
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; /* 直连模式 */
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htimx_GeneralTIM, &sConfigIC, GENERAL_TIM_CHANNEL1); /* 输入捕获的配置 */
}
下面main.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "led/bsp_led.h"
#include "GeneralTIM/bsp_GeneralTIM.h"
#include "usart/bsp_debug_usart.h"
#include "key/bsp_key.h"
/* 私有变量 ------------------------------------------------------------------*/
__IO STRUCT_CAPTURE strCapture = { 0, 1, 0, 0, 0}; // 初始化定时器.h自定义的结构体
int main(void)
{
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 通用定时器初始化 */
GENERAL_TIMx_Init();
printf ( "STM32 输入捕获实验\n" );
Toggle_Pulse = 4000; //40ms翻转一次电平 , 输出12.5Hz的脉冲
strCapture.ucFinishFlag = 0;
strCapture.ucStartFlag = 0;
/* 获取定时器时钟频率 */
ulTimClk = GENERAL_TIM_FREQ()/(GENERAL_TIM_PRESCALER+1);//84MHz一分频之后还是84MHz (不分频)
/* 启动定时器 */
HAL_TIM_Base_Start_IT(&htimx_GeneralTIM);//使能定时器更新中断
/* 启动定时器通道1输入捕获并开启中断 */
HAL_TIM_IC_Start_IT(&htimx_GeneralTIM,GENERAL_TIM_CHANNEL1);
while (1)
{
if(strCapture.ucFinishFlag == 1) // 两次上升沿捕获完成
{
if(ulCcr != 0) // 捕获值不为0的判断
{
/* 脉冲频率 Freq = 1/T ,T是周期; T = Δc*t = Δc/f , f是定时器频率,Δc是捕获值,Freq = f / Δc */
fFreq = (double)ulTimClk / (double)ulCcr ; // Δc就是 ulCcr Freq = f / Δc
}
/* 打印频率 */
printf ( ">>Freq:%.4lf kHz\n", fFreq/1000) ;
/* 调整定时器分频值 */
strCapture .ucFinishFlag = 0; // 清零方便下一次捕获
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 更新中断的回调函数
{
if(strCapture.ucStartFlag == 1)//计数超过65535后产生更新中断,判断是否捕获到第一次上升沿(必须是捕获第一次上升沿)
strCapture .sPeriod ++;
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) // 上升沿捕获中断标志的回调函数
{
if(strCapture.ucStartFlag == 0 ) //捕获第一次上升沿
{
// 获取定时器计数值
strCapture .sLastCtr = HAL_TIM_ReadCapturedValue(htim,GENERAL_TIM_CHANNEL1); // 记录捕获第一次上升沿值
/* 周期数清零 */
strCapture .sPeriod = 0;
strCapture.ucStartFlag = 1;
}
else if(strCapture.ucStartFlag == 1 ) // 捕获第二次上升沿
{
strCapture.sCtr = HAL_TIM_ReadCapturedValue(htim,GENERAL_TIM_CHANNEL1); // / 记录捕获第二次上升沿值
/* 获得前后两次上升沿的捕获值差值 */
ulCcr = strCapture .sPeriod * (int32_t)GENERAL_TIM_PERIOD + (strCapture .sCtr - strCapture .sLastCtr); /* u1Ccr = 周期 */
/* GENERAL_TIM_PERIOD = 0xffff在定时器的.h定义 */
/* 这里为什么要 strCapture .sPeriod * (int32_t)GENERAL_TIM_PERIOD 而不是直接两个捕获相减*/
/* 因为在两次上升沿之间的时间里,定时器的计数值会溢出(当记到0xffff时),可能会有多次。所以要记录溢出的次数要加上这段时间(多理解这段) */
strCapture.ucFinishFlag = 1; // 标志两个上升沿捕获完成
strCapture.ucStartFlag = 0;
}
}
总结:当计算的频率为引脚模拟正弦波12.5h时就是捕获正确。
2.定时器捕获高低电平的脉宽(这里是捕获高电平的时间)。
其实与第一个实验配置一样
这里的预分频是83 而定时器2是84Mhz 分频后84/83+1=1Mhz
一样的有定时器.h与.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* 类型定义 ------------------------------------------------------------------*/
typedef struct //测量高电平脉宽
{
uint8_t ucFinishFlag; /* 下降沿结束 */
uint8_t ucStartFlag; /* 上升沿开始 */
uint16_t usCtr; /* 高电平的计数时间 */
uint16_t usPeriod; /* 高电平的计数值可能会溢出 此值计算记录溢出次数 */
}STRUCT_CAPTURE;
/* 宏定义 --------------------------------------------------------------------*/
/********************通用定时器TIM参数定义,TIM2~TIM5************/
#define GENERAL_TIMx TIM2
#define GENERAL_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
#define GENERAL_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM2_CLK_DISABLE()
#define GENERAL_TIM_IRQ TIM2_IRQn
#define GENERAL_TIM_IRQHANDLER TIM2_IRQHandler
// 定义定时器预分频,定时器实际时钟频率为:84MHz/(GENERAL_TIM_PRESCALER+1)
#define GENERAL_TIM_PRESCALER 83
// 定义定时器周期,当定时器开始计数到BASIC_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define GENERAL_TIM_PERIOD 0xFFFF // 定时器产生中断频率为:定时器频率/0xFFFF Hz
#define GENERAL_TIM_FREQ() (HAL_RCC_GetPCLK1Freq()<<1)//84000000MHz
#define GENERAL_TIMX_GPIO_RCC_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE();
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_PIN_15
#define GENERAL_TIMx_GPIO_AF GPIO_AF1_TIM2
#define GENERAL_TIM_CHANNEL1 TIM_CHANNEL_1
#define GENERAL_TIM_IT_CC1 TIM_IT_CC1
#define GENERAL_TIM_STRAT_ICPolarity TIM_INPUTCHANNELPOLARITY_FALLING //测量的起始边沿
#define GENERAL_TIM_END_ICPolarity TIM_INPUTCHANNELPOLARITY_RISING //测量的结束边沿
/* 使用I/O翻转输出脉冲 */
#define GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() // 输出控制脉冲给电机驱动器
#define PUL_PORT GPIOI // 对应驱动器的PUL-(驱动器使用共阳接法)
#define PUL_PIN GPIO_PIN_5 // 而PLU+直接接开发板的VCC
/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx_GeneralTIM;
/* 函数声明 ------------------------------------------------------------------*/
void GENERAL_TIMx_Init(void);
/* 包含头文件 ----------------------------------------------------------------*/
#include "GeneralTIM/bsp_GeneralTIM.h"
TIM_HandleTypeDef htimx_GeneralTIM;
/**
* 函数功能: 定时器通道1引脚初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(htim_base == &htimx_GeneralTIM)
{
/* 基本定时器外设时钟使能 */
GENERAL_TIM_RCC_CLK_ENABLE();
GENERAL_TIMX_GPIO_RCC_CLK_ENABLE();
GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GENERAL_TIMx_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStruct);
/* 脉冲输出引脚配置 */
GPIO_InitStruct.Pin = PUL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = 0; // GPIO引脚用做TIM复用功能
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(PUL_PORT, &GPIO_InitStruct);
/* 配置定时器中断优先级并使能 */
HAL_NVIC_SetPriority(GENERAL_TIM_IRQ, 0, 0);
HAL_NVIC_EnableIRQ(GENERAL_TIM_IRQ);
}
}
/**
* 函数功能: 定时器初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void GENERAL_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
GENERAL_TIM_RCC_CLK_ENABLE();
/* 定时器外设时钟使能 */
htimx_GeneralTIM.Instance = GENERAL_TIMx;
htimx_GeneralTIM.Init.Prescaler = GENERAL_TIM_PRESCALER;
htimx_GeneralTIM.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx_GeneralTIM.Init.Period = GENERAL_TIM_PERIOD;
htimx_GeneralTIM.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htimx_GeneralTIM);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htimx_GeneralTIM, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htimx_GeneralTIM, &sMasterConfig);
sConfigIC.ICPolarity = GENERAL_TIM_END_ICPolarity;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htimx_GeneralTIM, &sConfigIC, GENERAL_TIM_CHANNEL1);
}
下面main函数
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "led/bsp_led.h"
#include "GeneralTIM/bsp_GeneralTIM.h"
#include "usart/bsp_debug_usart.h"
#include "key/bsp_key.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
STRUCT_CAPTURE strCapture = { 0, 0, 0 };
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能: 系统时钟配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR时钟
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); //设置调压器输出电压级别1
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_OscInitStruct.HSEState = RCC_HSE_ON; //打开HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; //打开PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; //PLL时钟源选择HSE
RCC_OscInitStruct.PLL.PLLM = 8; //8分频MHz
RCC_OscInitStruct.PLL.PLLN = 336; //336倍频
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; //2分频,得到168MHz主时钟
RCC_OscInitStruct.PLL.PLLQ = 7; //USB/SDIO/随机数产生器等的主PLL分频系数
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用
// HAL_RCC_GetHCLKFreq()/1000 1ms中断一次
// HAL_RCC_GetHCLKFreq()/100000 10us中断一次
// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器
/* 系统滴答定时器时钟源 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 系统滴答定时器中断优先级配置 */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
__IO uint32_t ulTmrClk, ulTime;
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 通用定时器初始化 */
GENERAL_TIMx_Init();
printf ( "按下KEY1,测试KEY1按下的时间\n" );
/* 获取定时器时钟频率 */
ulTmrClk = GENERAL_TIM_FREQ()/(GENERAL_TIM_PRESCALER+1); // 1Mhz 84分频
/* 启动定时器 */
HAL_TIM_Base_Start_IT(&htimx_GeneralTIM);
/* 启动定时器通道输入捕获并开启中断 */
HAL_TIM_IC_Start_IT(&htimx_GeneralTIM,GENERAL_TIM_CHANNEL1);
/* 无限循环 */
while (1)
{
/* 完成测量低电平脉宽 */
if(strCapture.ucFinishFlag == 1 )
{
/* 计算低电平计数值 */
ulTime = strCapture .usPeriod * GENERAL_TIM_PERIOD + strCapture .usCtr;
/* strCapture .usPeriod * GENERAL_TIM_PERIOD 是因为低电平的脉宽会超过溢出时间 */
/* 打印低电平脉宽时间 */
printf ( ">>测得低电平脉宽时间:%f s\n", ((float)ulTime / ulTmrClk ) ); // 一个计数值是时间 1/ulTmrClk
/* ulTmrClk = 定时器时钟频率也就是1Mhz */
strCapture .ucFinishFlag = 0;
}
/* 当按下按键去控制引脚电平。当按下时有一个下降沿。按5s 松开会有一个上升沿。 这时5s就是低电平的脉宽时间 */
if(KEY1_StateRead() == KEY_DOWN)
{
HAL_GPIO_WritePin(PUL_PORT,PUL_PIN,GPIO_PIN_RESET); // 按下为低电平
}
else
HAL_GPIO_WritePin(PUL_PORT,PUL_PIN,GPIO_PIN_SET); // 松开为高电平
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
strCapture .usPeriod ++; // 当持续高电平溢出值的计数次数
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
TIM_IC_InitTypeDef sConfigIC;
if ( strCapture .ucStartFlag == 0 ) // 因为引脚一开始为高电平,所以第一次为下降沿捕获清除计数值
{
__HAL_TIM_SET_COUNTER(htim,0); // 清零定时器计数
strCapture .usPeriod = 0;
strCapture .usCtr = 0;
// 配置输入捕获参数,主要是修改触发电平
sConfigIC.ICPolarity = GENERAL_TIM_STRAT_ICPolarity; // 第一次下降沿捕获
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(htim, &sConfigIC, GENERAL_TIM_CHANNEL1);
// 启动输入捕获并开启中断
HAL_TIM_IC_Start_IT(htim,GENERAL_TIM_CHANNEL1);
strCapture .ucStartFlag = 1;
}
else
{
// 获取定时器计数值
strCapture .usCtr = HAL_TIM_ReadCapturedValue(htim,GENERAL_TIM_CHANNEL1); //所以第二次为上升沿捕获(就捕获到低电平的脉宽)
// 配置输入捕获参数,主要是修改触发电平
sConfigIC.ICPolarity = GENERAL_TIM_END_ICPolarity; // 第二次上升沿捕获
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(htim, &sConfigIC, GENERAL_TIM_CHANNEL1);
// 启动输入捕获并开启中断
HAL_TIM_IC_Start_IT(htim,GENERAL_TIM_CHANNEL1);
strCapture .ucStartFlag = 0;
strCapture .ucFinishFlag = 1; /* 捕获上升沿结束 */
}
}
3.定时器捕获输入PWM(也是周期与占空比)
也是配置TIM2,但与前面不同配置从模式为复位模式,触发源为TI1FP1。定时器时钟还是内部时钟。这里会用到两个捕获/比较定时器的寄存器(第一个保存周期,第二个保存占空比)。但通道只有CH1,因为CH1会有输出有两路TI1FP1与TI1FP2。
TI1FP1 配置直通到IC1,而TI1FP2配置交叉到IC2。IC1与IC2捕获的边沿是互补的。当IC1设置上升沿捕获,那么IC2自动配置为下降沿。
下面TIM2的.h与.c
#include "stm32f4xx_hal.h"
/* 类型定义 ------------------------------------------------------------------*/
typedef struct //测量高电平脉宽
{
uint8_t ucFinishFlag;//完成标志
uint8_t ucStartFlag;//开始标志
uint32_t usLowPulse;// 低电平
uint32_t usHighPulse;// 高电平
uint32_t usCtr;//占空比
uint32_t usPeriod;//周期
}STRUCT_CAPTURE;
/* 宏定义 --------------------------------------------------------------------*/
/********************通用定时器TIM参数定义,TIM2~TIM5************/
#define GENERAL_TIMx TIM2
#define GENERAL_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
#define GENERAL_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM2_CLK_DISABLE()
#define GENERAL_TIM_IRQ TIM2_IRQn
#define GENERAL_TIM_IRQHANDLER TIM2_IRQHandler
// 定义定时器预分频,定时器实际时钟频率为:84MHz/(GENERAL_TIM_PRESCALER+1)
#define GENERAL_TIM_PRESCALER 1
// 定义定时器周期,当定时器开始计数到BASIC_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define GENERAL_TIM_PERIOD 0xFFFFFFFF // 定时器产生中断频率为:定时器频率/0xFFFF Hz
#define GENERAL_TIM_FREQ() (HAL_RCC_GetPCLK1Freq()<<1)//获取当前频率为84MHz
#define GENERAL_TIMX_GPIO_RCC_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE();
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_PIN_15
#define GENERAL_TIMx_GPIO_AF GPIO_AF1_TIM2
#define GENERAL_TIM_CHANNEL1 TIM_CHANNEL_1
#define GENERAL_TIM_IT_CC1 TIM_IT_CC1
#define GENERAL_TIM_CHANNEL2 TIM_CHANNEL_2
#define GENERAL_TIM_IT_CC2 TIM_IT_CC2
#define GENERAL_TIM_STRAT_ICPolarity TIM_INPUTCHANNELPOLARITY_RISING //测量的起始边沿
#define GENERAL_TIM_END_ICPolarity TIM_INPUTCHANNELPOLARITY_FALLING //测量的结束边沿
/* 使用I/O翻转输出脉冲 */
#define GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() // 输出控制脉冲给电机驱动器
#define PUL_PORT GPIOI // 对应驱动器的PUL-(驱动器使用共阳接法)
#define PUL_PIN GPIO_PIN_5 // 而PLU+直接接开发板的VCC
/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx_GeneralTIM;
/* 函数声明 ------------------------------------------------------------------*/
void GENERAL_TIMx_Init(void);
/* 包含头文件 ----------------------------------------------------------------*/
#include "GeneralTIM/bsp_GeneralTIM.h"
TIM_HandleTypeDef htimx_GeneralTIM;
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(htim_base == &htimx_GeneralTIM)
{
/* 基本定时器外设时钟使能 */
GENERAL_TIM_RCC_CLK_ENABLE();
GENERAL_TIMX_GPIO_RCC_CLK_ENABLE();
GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GENERAL_TIMx_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStruct);
/* 脉冲输出引脚配置 */
GPIO_InitStruct.Pin = PUL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = 0; // GPIO引脚用做TIM复用功能
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(PUL_PORT, &GPIO_InitStruct);
/* 配置定时器中断优先级并使能 */
HAL_NVIC_SetPriority(GENERAL_TIM_IRQ, 0, 0);
HAL_NVIC_EnableIRQ(GENERAL_TIM_IRQ);
}
}
void GENERAL_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_SlaveConfigTypeDef sslaveConfig;
TIM_IC_InitTypeDef sConfigIC;
GENERAL_TIM_RCC_CLK_ENABLE();
/* 定时器外设时钟使能 */
htimx_GeneralTIM.Instance = GENERAL_TIMx;
htimx_GeneralTIM.Init.Prescaler = GENERAL_TIM_PRESCALER;
htimx_GeneralTIM.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx_GeneralTIM.Init.Period = GENERAL_TIM_PERIOD;
htimx_GeneralTIM.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htimx_GeneralTIM);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;//选择内部时钟
HAL_TIM_ConfigClockSource(&htimx_GeneralTIM, &sClockSourceConfig);
/* 从模式配置,IT1触发更新 */
sslaveConfig.SlaveMode = TIM_SLAVEMODE_RESET; // 复位模式
sslaveConfig.InputTrigger = TIM_TS_TI1FP1;//选择触发源
sslaveConfig.TriggerFilter = 0;
sslaveConfig.TriggerPolarity = 0;
sslaveConfig.TriggerPrescaler = 0;
HAL_TIM_SlaveConfigSynchronization(&htimx_GeneralTIM,&sslaveConfig);
/*通道一模式配置*/
sConfigIC.ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htimx_GeneralTIM, &sConfigIC, GENERAL_TIM_CHANNEL1);
/*通道二模式配置*/
sConfigIC.ICPolarity = GENERAL_TIM_END_ICPolarity;
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htimx_GeneralTIM, &sConfigIC, GENERAL_TIM_CHANNEL2);
}
这里的PWM信号还是通过引脚去模拟并用滴答定时器计时去控制模拟正弦波的周期。
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器 1ms进一次中断(具体看上面的第一个列子)
void SysTick_Handler(void) // 滴答定时器中断服务函数
{
HAL_IncTick();
HAL_SYSTICK_IRQHandler();
// HAL_IncTick的定义
__weak void HAL_IncTick(void)
{
uwTick++;
}
void HAL_SYSTICK_IRQHandler(void)
{
HAL_SYSTICK_Callback();
}
// HAL_SYSTICK_Callback的实现在main.c
void HAL_SYSTICK_Callback(void) //滴答定时器的回调函数
{
if(uwTick % Toggle_Pulse == 0) Toggle_Pulse = 100 // 100ms翻转一次 那么周期=200ms q= 100ms(高电平的时间)
{
HAL_GPIO_TogglePin(PUL_PORT,PUL_PIN);
}
}
}
main.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "led/bsp_led.h"
#include "GeneralTIM/bsp_GeneralTIM.h"
#include "usart/bsp_debug_usart.h"
#include "key/bsp_key.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
STRUCT_CAPTURE strCapture = { 0, 0, 0, 0 };
/* 扩展变量 ------------------------------------------------------------------*/
extern __IO uint32_t uwTick;
__IO uint16_t Toggle_Pulse = 0; // 脉宽控制
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
1. 函数功能: 系统时钟配置
2. 输入参数: 无
3. 返 回 值: 无
4. 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR时钟
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); //设置调压器输出电压级别1
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_OscInitStruct.HSEState = RCC_HSE_ON; //打开HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; //打开PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; //PLL时钟源选择HSE
RCC_OscInitStruct.PLL.PLLM = 8; //8分频MHz
RCC_OscInitStruct.PLL.PLLN = 336; //336倍频
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; //2分频,得到168MHz主时钟
RCC_OscInitStruct.PLL.PLLQ = 7; //USB/SDIO/随机数产生器等的主PLL分频系数
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用
// HAL_RCC_GetHCLKFreq()/1000 1ms中断一次
// HAL_RCC_GetHCLKFreq()/100000 10us中断一次
// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器
/* 系统滴答定时器时钟源 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 系统滴答定时器中断优先级配置 */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
int main(void)
{
__IO uint32_t ulTmrClk, ulTime, ulDuty;
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 板载LED初始化 */
LED_GPIO_Init();
/* 初始化串口并配置串口中断优先级 */
MX_DEBUG_USART_Init();
/* 通用定时器初始化 */
GENERAL_TIMx_Init();
// printf ( "STM32 PWM输入捕获实验\n" );
HAL_Delay(1000); // 1s
/* 获取定时器时钟频率 */
ulTmrClk = GENERAL_TIM_FREQ()/(GENERAL_TIM_PRESCALER+1);
/* 启动定时器 */
HAL_TIM_Base_Start_IT(&htimx_GeneralTIM);
/* 启动定时器通道使能输入捕获并开启中断 */
HAL_TIM_IC_Start_IT(&htimx_GeneralTIM,GENERAL_TIM_CHANNEL1); // 通道1使能输入捕获并开启中断
HAL_TIM_IC_Start_IT(&htimx_GeneralTIM,GENERAL_TIM_CHANNEL2); // 通道2使能输入捕获并开启中断
Toggle_Pulse = 100; // 100 *1ms =100ms翻转
/* 无限循环 */
while (1)
{
/* 完成测量高电平脉宽 */
if(strCapture.ucFinishFlag == 1 )
{
ulTime = strCapture.usPeriod ;
/* 打印周期时间 */
printf ( ">>测得脉冲周期:%0.3f s\n", ((float)ulTime / ulTmrClk ) ); // 一个计数值要花费 1/ulTmrClk的时间 所以ulTime / ulTmrClk
ulTime = strCapture .usCtr;
/* 打印低电平脉宽时间 */
printf ( ">>测得脉宽时间:%0.3f s\n", ((float)ulTime / ulTmrClk ) );
strCapture .ucFinishFlag = 0;
}
}
}
void HAL_SYSTICK_Callback(void)
{
if(uwTick % Toggle_Pulse == 0)
{
HAL_GPIO_TogglePin(PUL_PORT,PUL_PIN);
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
strCapture.usPeriod = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1; // 捕获周期
}
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
strCapture.usCtr = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1; // 捕获占空比
strCapture.ucFinishFlag = 1;
}
}
总结:在第一次上升沿周期以占空比的计数值都要清零,然后下降沿到来捕获到占空比的值(占空比下降沿捕获),当第二次上升沿到来就捕获周期(周期上升沿捕获)。
4.定时器输出比较翻转模式
1.什么是翻转模式:就是计数器的值(CNT)与捕获/比较寄存器的值(预先设定的)相等。如果之前为高电平那么现在翻转为低电平。如果之前为低电平那么现在翻转为高电平。
这里通道的比较值在代码里在设置。
高级定时器有多个中断服务函数,而基本与通用只有一个中断服务函数(通过标志位来判断是哪种中断).
高级定时器的多个中断:
TIM8的.h与.c
#define ADVANCED_TIMx TIM8
#define ADVANCED_TIMx_GPIO_AF GPIO_AF3_TIM8
#define ADVANCED_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM8_CLK_ENABLE()
#define ADVANCED_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM8_CLK_DISABLE()
#define ADVANCED_TIM_GPIO_RCC_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE()
#define ADVANCED_TIM_OC_IRQn TIM8_CC_IRQn
#define ADVANCED_TIM_OC_IRQHANDLER TIM8_CC_IRQHandler
#define ADVANCED_TIM_CH1_PORT GPIOI
#define ADVANCED_TIM_CH1_PIN GPIO_PIN_5
#define ADVANCED_TIM_CH2_PORT GPIOI
#define ADVANCED_TIM_CH2_PIN GPIO_PIN_6
#define ADVANCED_TIM_CH3_PORT GPIOI
#define ADVANCED_TIM_CH3_PIN GPIO_PIN_7
#define ADVANCED_TIM_CH4_PORT GPIOI
#define ADVANCED_TIM_CH4_PIN GPIO_PIN_2
#endif
// 定义定时器预分频,定时器实际时钟频率为:168MHz/(ADVANCED_TIMx_PRESCALER+1)
#define ADVANCED_TIM_PRESCALER 83 // 实际时钟频率为:2MHz
// 定义定时器周期,当定时器开始计数到ADVANCED_TIMx_PERIOD值并且重复计数寄存器为0时更新定时器并生成对应事件和中断
#define ADVANCED_TIM_PERIOD 0xFFFF // 定时器产生中断频率为:1MHz/(35536)=1KHz,即1ms定时周期
// 定义高级定时器重复计数寄存器值,
#define ADVANCED_TIM_REPETITIONCOUNTER 0
// 最终定时器频率计算为: 168MHz/(ADVANCED_TIMx_PRESCALER+1)/(ADVANCED_TIM_REPETITIONCOUNTER+1)/(ADVANCED_TIMx_PERIOD+1)
// 比如需要产生1ms周期定时,可以设置为: 168MHz/(167+1)/(0+1)/(999+1)=1KHz,即1ms周期
// 这里设置 ADVANCED_TIMx_PRESCALER=167;ADVANCED_TIM_REPETITIONCOUNTER=0;ADVANCED_TIMx_PERIOD=999;
/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx;
/* 函数声明 ------------------------------------------------------------------*/
void ADVANCED_TIMx_Init(void);
TIM8.c
#include "AdvancedTIM/bsp_AdvancedTIM.h"
TIM_HandleTypeDef htimx; /* 句柄结构体 */
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(htim->Instance==ADVANCED_TIMx)
{
/* 定时器通道功能引脚端口时钟使能 */
ADVANCED_TIM_GPIO_RCC_CLK_ENABLE();
/* 定时器通道1功能引脚IO初始化 */
GPIO_InitStruct.Pin = ADVANCED_TIM_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = ADVANCED_TIMx_GPIO_AF;
HAL_GPIO_Init(ADVANCED_TIM_CH1_PORT, &GPIO_InitStruct);
/* 定时器通道2功能引脚IO初始化 */
GPIO_InitStruct.Pin = ADVANCED_TIM_CH2_PIN;
HAL_GPIO_Init(ADVANCED_TIM_CH2_PORT, &GPIO_InitStruct);
/* 定时器通道3功能引脚IO初始化 */
GPIO_InitStruct.Pin = ADVANCED_TIM_CH3_PIN;
HAL_GPIO_Init(ADVANCED_TIM_CH3_PORT, &GPIO_InitStruct);
/* 定时器通道4功能引脚IO初始化 */
GPIO_InitStruct.Pin = ADVANCED_TIM_CH4_PIN;
HAL_GPIO_Init(ADVANCED_TIM_CH4_PORT, &GPIO_InitStruct);
HAL_NVIC_SetPriority(ADVANCED_TIM_OC_IRQn,1,0); /* 输出比较中断 */
HAL_NVIC_EnableIRQ(ADVANCED_TIM_OC_IRQn); /* 输出比较中断使能 */
}
}
/**
* 函数功能: 基本定时器初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void ADVANCED_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig; /* 定时器时钟结构体 */
TIM_MasterConfigTypeDef sMasterConfig; /* 主模式配置结构体 */
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig; /* 刹车死区时间结构体 */
TIM_OC_InitTypeDef sConfigOC; /* 输出比较结构体 */
/* 定时器外设时钟使能 */
ADVANCED_TIM_RCC_CLK_ENABLE();
htimx.Instance = ADVANCED_TIMx;
htimx.Init.Prescaler = ADVANCED_TIM_PRESCALER;
htimx.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx.Init.Period = ADVANCED_TIM_PERIOD;
htimx.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
htimx.Init.RepetitionCounter = ADVANCED_TIM_REPETITIONCOUNTER;//重复计数为0+1
HAL_TIM_OC_Init(&htimx);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;//选择内部时钟
HAL_TIM_ConfigClockSource(&htimx, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htimx, &sMasterConfig);
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0; /* 死区时间为0 */
sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
HAL_TIMEx_ConfigBreakDeadTime(&htimx, &sBreakDeadTimeConfig);
sConfigOC.OCMode = TIM_OCMODE_TOGGLE; // 比较输出翻转模式
sConfigOC.Pulse = 100; // 比较模式下这是无意义的值
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出极性的选择 */
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; /* 互补输出极性的选择 */
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 快速模式
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; // 空闲状态
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; // 互补的空闲状态
/* 配置CH1为比较输出模式 */
HAL_TIM_OC_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_1);
/* 配置CH2为比较输出模式 */
HAL_TIM_OC_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_2);
/* 配置CH3为比较输出模式 */
HAL_TIM_OC_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_3);
/* 配置CH4为比较输出模式 */
HAL_TIM_OC_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_4 );
}
main.c
#include "stm32f4xx_hal.h"
#include "AdvancedTIM/bsp_AdvancedTIM.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
__IO uint16_t sizeo;
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
void SystemClock_Config(void);
/* 函数体 --------------------------------------------------------------------*/
/**
1. 函数功能: 主函数.
2. 输入参数: 无
3. 返 回 值: 无
4. 说 明: 无
*/
int main(void)
{
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 高级定时器初始化:*/
ADVANCED_TIMx_Init();
/* 启动CH1比较输出以及中断 */
HAL_TIM_OC_Start_IT(&htimx,TIM_CHANNEL_1);
/* 无限循环 */
while (1)
{
}
}
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) /* 比较中断的回调函数 */
{
__IO uint16_t count = 0;
count = __HAL_TIM_GetCounter(htim); /* 获取捕获寄存器的值 */
__HAL_TIM_SET_COMPARE(htim,TIM_CHANNEL_1,count+499); /* 设置捕获寄存器的值 */
}
总结:虽然配置TIM8的四各通道但只用到CH1,也只开启了CH1.
在定时器初始化时CH1的比较值为100,所以当CNT对于100时就翻转。然后重新设置比较值(在原有的比较值上+500)
所以当到了600时又翻转。所以源源不断输出脉冲。但是比较值一直加会超过65536。这时寄存器会减去65536(当超过65536才减)。
所以即使超了也不影响脉冲输出。
5.定时器输出比较PWM输出
由于是84分频/83+1 = 1Mhz T = 1Mhz /999+1 = 1ms (1Khz) 所以CH1的占空比=20% CH2的占空比=40% CH3的占空比=60%
CH4的占空比=80% 。可以用示波器抓取这四个通道。
TIM2的.h与.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* 类型定义 ------------------------------------------------------------------*/
/* 宏定义 --------------------------------------------------------------------*/
#define GENERAL_TIMx TIM2
#define GENERAL_TIM_GPIO_AF GPIO_AF1_TIM2
#define GENERAL_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
#define GENERAL_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM2_CLK_DISABLE()
#define GENERAL_TIM_GPIO_RCC_CLK_ENABLE() {__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();}
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_PIN_15
#define GENERAL_TIM_CH2_PORT GPIOB
#define GENERAL_TIM_CH2_PIN GPIO_PIN_3
#define GENERAL_TIM_CH3_PORT GPIOB
#define GENERAL_TIM_CH3_PIN GPIO_PIN_10
#define GENERAL_TIM_CH4_PORT GPIOB
#define GENERAL_TIM_CH4_PIN GPIO_PIN_11
// 定义定时器预分频,定时器实际时钟频率为:84MHz/(GENERAL_TIMx_PRESCALER+1)
#define GENERAL_TIM_PRESCALER 83 // 实际时钟频率为:1MHz
// 定义定时器周期,当定时器开始计数到GENERAL_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define GENERAL_TIM_PERIOD 999 // 定时器产生中断频率为:1MHz/(999+1)=1KHz,即1ms定时周期
#define GENERAL_TIM_CH1_PULSE 200 // 定时器通道1占空比为:GENERAL_TIM_CH1_PULSE/GENERAL_TIM_PERIOD*100%=200/1000*100%=20%
#define GENERAL_TIM_CH2_PULSE 400 // 定时器通道2占空比为:GENERAL_TIM_CH2_PULSE/GENERAL_TIM_PERIOD*100%=400/1000*100%=40%
#define GENERAL_TIM_CH3_PULSE 600 // 定时器通道3占空比为:GENERAL_TIM_CH3_PULSE/GENERAL_TIM_PERIOD*100%=600/1000*100%=60%
#define GENERAL_TIM_CH4_PULSE 800 // 定时器通道4占空比为:GENERAL_TIM_CH4_PULSE/GENERAL_TIM_PERIOD*100%=800/1000*100%=80%
/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx;
void GENERAL_TIMx_Init(void);
TIM2的.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "GeneralTIM/bsp_GeneralTIM.h"
TIM_HandleTypeDef htimx;
void HAL_GeneralTIM_MspPostInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 基本定时器外设时钟使能 */
GENERAL_TIM_RCC_CLK_ENABLE();
/* 定时器通道功能引脚端口时钟使能 */
GENERAL_TIM_GPIO_RCC_CLK_ENABLE();
/* 定时器通道1功能引脚IO初始化 */
GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GENERAL_TIM_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStruct);
/* 定时器通道2功能引脚IO初始化 */
GPIO_InitStruct.Pin = GENERAL_TIM_CH2_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GENERAL_TIM_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStruct);
/* 定时器通道3功能引脚IO初始化 */
GPIO_InitStruct.Pin = GENERAL_TIM_CH3_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GENERAL_TIM_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStruct);
/* 定时器通道4功能引脚IO初始化 */
GPIO_InitStruct.Pin = GENERAL_TIM_CH4_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GENERAL_TIM_GPIO_AF;
HAL_GPIO_Init(GENERAL_TIM_CH4_PORT, &GPIO_InitStruct);
}
void GENERAL_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
HAL_GeneralTIM_MspPostInit();
htimx.Instance = GENERAL_TIMx;
htimx.Init.Prescaler = GENERAL_TIM_PRESCALER;
htimx.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx.Init.Period = GENERAL_TIM_PERIOD;
htimx.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htimx);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;//选择内部时钟
HAL_TIM_ConfigClockSource(&htimx, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htimx, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;//选择PWM模式1
sConfigOC.Pulse = GENERAL_TIM_CH1_PULSE; // 捕获比较寄存器的值
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 输出极性
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 快速模式
HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_1); // 通道1 PWM1模式初始化
sConfigOC.Pulse = GENERAL_TIM_CH2_PULSE;
HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_2); // 通道2 PWM1模式初始化
sConfigOC.Pulse = GENERAL_TIM_CH3_PULSE;
HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_3); // 通道3 PWM1模式初始化
sConfigOC.Pulse = GENERAL_TIM_CH4_PULSE;
HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_4); // 通道4 PWM1模式初始化
}
main.c
#include "stm32f4xx_hal.h"
#include "GeneralTIM/bsp_GeneralTIM.h"
int main(void)
{
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 通用定时器初始化并配置PWM输出功能 */
GENERAL_TIMx_Init();
/* 启动四个通道PWM输出 捕获比较寄存器的值是可以改变的*/
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_4);
/* 无限循环 */
while (1)
{}
}
总结:用示波器去测量这四个通道。
6.总结
这就是定时器的输入捕获与输出比较的用法,当然定时器的用法还很多。