第32章 STM32H7的TIM定时器基础知识和HAL库API
本章节为大家讲解TIM1 – TIM17(STM32H7没有TIM9,TIM10和TIM11)共计14个定时器的基础知识和对应的HAL库API。
32.1 初学者重要提示
32.2 定时器基础知识
32.3 定时器的HAL库用户
32.4 源文件stm32h7xx_hal_tim.c
32.5 总结
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
TIM5_IRQHandler
TIM6_DAC_IRQHandler <------------------要注意
TIM7_IRQHandler
TIM8_BRK_TIM12_IRQHandler <------------------要注意,定时器12也是用的这个
TIM8_UP_TIM13_IRQHandler <------------------要注意,定时器13也是用的这个
TIM8_TRG_COM_TIM14_IRQHandler <------------------要注意,定时器14也是用的这个
TIM8_CC_IRQHandler
TIM15_IRQHandler
TIM16_IRQHandler
TIM17_IRQHandler
注,不同定时支持的功能略有区别,基础定时器功能较少,TIM1和TIM8高级定时器功能多些。
– 输入捕获。
– 输出比较。
– PWM 生成(边沿和中心对齐模式)。
– 单脉冲模式输出。
– 更新:计数器上溢/下溢、计数器初始化(通过软件或内部/外部触发)
– 触发事件(计数器启动、停止、初始化或通过内部/外部触发计数)
– 输入捕获
– 输出比较
STM32H7支持的定时器有点多,要简单的区分下。STM32H7支持TIM1-TIM8,TIM12-TIM17共14个定时器,而中间的TIM9,TIM10,TIM11是不存在的,这点要注意。
粗略的比较如下:
通过上面的表格,至少要了解到以下两点:
认识一个外设,最好的方式就是看他的框图,方便我们快速的了解定时器的基本功能,然后再看手册了解细节。
下面我们直接看最复杂的高级定时器TIM1&TIM8框图:
通过这个框图,我们可以得到如下信息:
外部触发输入接口。ETR支持多种输入源:输入引脚(默认配置)、比较器输出和模拟看门狗。
这四个通道主要用于输入捕获,可以计算波形频率和脉宽。
断路功能,主要用于保护由 TIM1 和 TIM8 定时器产生的 PWM 信号所驱动的功率开关
主要用于定时器级联,ADC和DAC的定时器触发。
OC1到OC4有对应的输出引脚,而OC5和OC6没有对应的输出引脚,主要用于内部控制。
主要用于PWM输出,注意CH1到CH3有互补输出,而CH4没有互补输出。
定时器TIM1&TIM8还支持的其它功能在用到的时候再做说明。
定时器要工作就需要一个基本时基单元,而基本的时基单元是由下面几个寄存器组成的:
用于设置定时器的分频,比如定时器的主频是200MHz,通过此寄存器可以将其设置为100MHz,50MHz,25MHz等分频值。
注:预分频器有个缓冲功能,可以让用户实时更改,新的预分频值将在下一个更新事件发生时被采用(以递增计数模式为例,就是CNT计数值达到ARR自动重装寄存器的数值时会产生更新事件)。
计数器是最基本的计数单元,计数值是建立在分频的基础上面,比如通过TIMx_PSC设置分频后的频率为100MHz,那么计数寄存器计一次数就是10ns。
自动重装寄存器是CNT计数寄存器能达到的最大计数值,以递增计数模式为例,就是CNT计数器达到ARR寄存器数值时,重新从0开始计数。
注,自动重载寄存器是预装载的。对自动重载寄存器执行写入或读取操作时会访问预装载寄存器。预装载寄存器的内容既可以立即传送到影子寄存器(让设置立即起到效果的寄存器),也可以在每次发生更新事件时传送到影子寄存器。简单的说就是让ARR寄存器的数值立即更新还是更新事件发送的时候更新。
以递增计数模式为例,当CNT计数器数值达到ARR自动重载数值时,重复计数器的数值加1,重复次数达到TIMx_RCR+ 1后就,将生成更新事件。
注,只有TIM1,TIM8,TIM15,TIM16,TIM17有此寄存器。
比如我们要配置定时器实现周期性的中断,主要使用这几个寄存器即可。
使用定时器时基单元的那几个寄存器仅仅能设置周期,还不能设置占空比。针对这个问题,还需要比较捕获寄存CCR的参与,这样就可以设置占空比了。
为了方便大家理解,以PWM 边沿对齐模式,递增计数配置为例:
下面是TIMx_ARR=8的波形效果:
与PWM一样,使用定时器实现输入捕获,仅靠时基单元的那几个寄存器是不行的,我们需要一个寄存器来记录发生捕获时的具体时间,这个寄存器依然由比较捕获寄存器TIMx_CCRx来实现。
比如我们要测量一路方波的周期:
不过这里要特别注意一点,如果CNT发生溢出(比如16位定时器,计数到65535就溢出了)就需要特别处理下,将CNT计数溢出考虑进来。
定时器的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC、中断和DMA。下面我们逐一展开为大家做个说明。
定时器相关的寄存器是通过HAL库中的结构体TIM_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:
typedef struct
{
__IO uint16_t CR1; /*!< TIM control register 1, Address offset: 0x00 */
uint16_t RESERVED0; /*!< Reserved, 0x02 */
__IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */
__IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */
__IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */
__IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */
__IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */
__IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */
__IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */
__IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */
__IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */
__IO uint16_t PSC; /*!< TIM prescaler, Address offset: 0x28 */
uint16_t RESERVED9; /*!< Reserved, 0x2A */
__IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */
__IO uint16_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */
uint16_t RESERVED10; /*!< Reserved, 0x32 */
__IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */
__IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */
__IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */
__IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */
__IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */
__IO uint16_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */
uint16_t RESERVED12; /*!< Reserved, 0x4A */
__IO uint16_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */
uint16_t RESERVED13; /*!< Reserved, 0x4E */
uint16_t RESERVED14; /*!< Reserved, 0x50 */
__IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x54 */
__IO uint32_t CCR5; /*!< TIM capture/compare register5, Address offset: 0x58 */
__IO uint32_t CCR6; /*!< TIM capture/compare register6, Address offset: 0x5C */
__IO uint32_t AF1; /*!< TIM alternate function option register 1, Address offset: 0x60 */
__IO uint32_t AF2; /*!< TIM alternate function option register 2, Address offset: 0x64 */
__IO uint32_t TISEL; /*!< TIM Input Selection register, Address offset: 0x68 */
} TIM_TypeDef;
这个结构体的成员名称和排列次序和CPU的定时器寄存器是一 一对应的。
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
下面我们看下定时器的定义,在stm32h743xx.h文件。
#define PERIPH_BASE ((uint32_t)0x40000000)
#define D2_APB1PERIPH_BASE PERIPH_BASE
#define D2_APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
/*!< D2_APB1PERIPH 外设 */
#define TIM2_BASE (D2_APB1PERIPH_BASE + 0x0000) <----- 展开这个宏,(TIM_TypeDef *) 0x40000000
#define TIM3_BASE (D2_APB1PERIPH_BASE + 0x0400)
#define TIM4_BASE (D2_APB1PERIPH_BASE + 0x0800)
#define TIM5_BASE (D2_APB1PERIPH_BASE + 0x0C00)
#define TIM6_BASE (D2_APB1PERIPH_BASE + 0x1000)
#define TIM7_BASE (D2_APB1PERIPH_BASE + 0x1400)
#define TIM12_BASE (D2_APB1PERIPH_BASE + 0x1800)
#define TIM13_BASE (D2_APB1PERIPH_BASE + 0x1C00)
#define TIM14_BASE (D2_APB1PERIPH_BASE + 0x2000)
/*!< D2_APB1PERIPH 外设 */
#define TIM1_BASE (D2_APB2PERIPH_BASE + 0x0000)
#define TIM8_BASE (D2_APB2PERIPH_BASE + 0x0400)
#define TIM15_BASE (D2_APB2PERIPH_BASE + 0x4000)
#define TIM16_BASE (D2_APB2PERIPH_BASE + 0x4400)
#define TIM17_BASE (D2_APB2PERIPH_BASE + 0x4800)
#define TIM1 ((TIM_TypeDef *) TIM1_BASE)
#define TIM2 ((TIM_TypeDef *) TIM2_BASE)
#define TIM3 ((TIM_TypeDef *) TIM3_BASE)
#define TIM4 ((TIM_TypeDef *) TIM4_BASE)
#define TIM5 ((TIM_TypeDef *) TIM5_BASE)
#define TIM6 ((TIM_TypeDef *) TIM6_BASE)
#define TIM7 ((TIM_TypeDef *) TIM7_BASE)
#define TIM8 ((TIM_TypeDef *) TIM8_BASE)
#define TIM12 ((TIM_TypeDef *) TIM12_BASE)
#define TIM13 ((TIM_TypeDef *) TIM13_BASE)
#define TIM14 ((TIM_TypeDef *) TIM14_BASE)
#define TIM15 ((TIM_TypeDef *) TIM15_BASE)
#define TIM16 ((TIM_TypeDef *) TIM16_BASE)
#define TIM17 ((TIM_TypeDef *) TIM17_BASE)
我们访问TIM2的CR1寄存器可以采用这种形式:TIM2->CR1 = 0;
HAL库在TIM_TypeDef的基础上封装了一个结构体TIM_HandleTypeDef,定义如下:
typedef struct
{
TIM_TypeDef *Instance; /*!< Register base address */
TIM_Base_InitTypeDef Init; /*!< TIM Time Base required parameters */
HAL_TIM_ActiveChannel Channel; /*!< Active channel */
/*!< DMA Handlers array This array is accessed by a @ref DMA_Handle_index */
DMA_HandleTypeDef *hdma[7];
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_TIM_StateTypeDef State; /*!< TIM operation state */
}TIM_HandleTypeDef;
这里重点介绍前四个参数,其它参数主要是HAL库内部使用的。
TIM_TypeDef *Instance
这个参数是寄存器的例化,方便操作寄存器,比如使能定时器的计数器。
SET_BIT(huart->Instance->CR1, TIM_CR1_CEN)。
TIM_Base_InitTypeDef Init
这个参数是用户接触最多的,用于配置定时器的基本参数。
TIM_Base_InitTypeDef结构体的定义如下:
typedef struct
{
uint32_t Prescaler;
uint32_t CounterMode;
uint32_t Period;
uint32_t ClockDivision;
uint32_t RepetitionCounter;
uint32_t AutoReloadPreload;
} TIM_Base_InitTypeDef;
用于设置定时器分频,对于32位的TIM2和TIM5范围是0到0xFFFFFFFF,其它定时器是0到0xFFFF。
用于设置计数模式,向上计数模式、向下计数模式和中心对齐模式。
#define TIM_COUNTERMODE_UP ((uint32_t)0x0000U) /*!< Up counting mode */
#define TIM_COUNTERMODE_DOWN TIM_CR1_DIR /*!< Down counting mode */
#define TIM_COUNTERMODE_CENTERALIGNED1 TIM_CR1_CMS_0 /*!< Center-aligned counting mode 1 */
#define TIM_COUNTERMODE_CENTERALIGNED2 TIM_CR1_CMS_1 /*!< Center-aligned counting mode 2 */
#define TIM_COUNTERMODE_CENTERALIGNED3 TIM_CR1_CMS /*!< Center-aligned counting mode 3 */
用于设置定时器周期,对于32位的TIM2和TIM5范围是0到0xFFFFFFFF,其它定时器是0到0xFFFF。
用于指示定时器时钟 (CK_INT) 频率与死区发生器以及数字滤波器(ETR、TIx)所使用的死区及采样时钟 (tDTS) 之间的分频比。
#define TIM_CLOCKDIVISION_DIV1 ((uint32_t)0x0000U) /*!< Clock Division DIV1 */
#define TIM_CLOCKDIVISION_DIV2 (TIM_CR1_CKD_0) /*!< Clock Division DIV2 */
#define TIM_CLOCKDIVISION_DIV4 (TIM_CR1_CKD_1) /*!< Clock Division DIV4 */
用于设置重复计数器,仅TIM1和TIM8有,其它定时器没有。作用是每当计数器上溢/下溢时,重复计数器减1,当减到零时,才会生成更新事件,这个在生成PWM时比较有用。
用于设置定时器的ARR自动重装寄存器是更新事件产生时写入有效还是立即写入有效。如果使能了表示更新事件产生时写入有效,否则反之。
#define TIM_AUTORELOAD_PRELOAD_DISABLE ((uint32_t)0x0000U) /*!< TIMx_ARR register is not buffered */
#define TIM_AUTORELOAD_PRELOAD_ENABLE (TIM_CR1_ARPE) /*!< TIMx_ARR register is buffered */
HAL_TIM_ActiveChannel Channel;
用于设置定时器通道,比如TIM1和TIM8都是6个通道。
typedef enum
{
HAL_TIM_ACTIVE_CHANNEL_1 = 0x01U, /*!< The active channel is 1 */
HAL_TIM_ACTIVE_CHANNEL_2 = 0x02U, /*!< The active channel is 2 */
HAL_TIM_ACTIVE_CHANNEL_3 = 0x04U, /*!< The active channel is 3 */
HAL_TIM_ACTIVE_CHANNEL_4 = 0x08U, /*!< The active channel is 4 */
HAL_TIM_ACTIVE_CHANNEL_5 = 0x10U, /*!< The active channel is 5 */
HAL_TIM_ACTIVE_CHANNEL_6 = 0x20U, /*!< The active channel is 6 */
HAL_TIM_ACTIVE_CHANNEL_CLEARED = 0x00U /*!< All active channels cleared */
}HAL_TIM_ActiveChannel;
DMA_HandleTypeDef *hdma[7];
用于关联DMA。
配置定时器参数,其实就是配置结构体TIM_HandleTypeDef的成员。
TIM_HandleTypeDef TimHandle = {0};
/*
定时器中断更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
此结构体主要用于定时器的输出比较,定义如下:
typedef struct
{
uint32_t OCMode;
uint32_t Pulse;
uint32_t OCPolarity;
uint32_t OCNPolarity;
uint32_t OCFastMode;
uint32_t OCIdleState;
uint32_t OCNIdleState;
} TIM_OC_InitTypeDef;
下面将这几个参数一 一做个说明。
用于配置输出比较模式,支持的模式较多:
/*!< TIM Output timing mode */
#define TIM_OCMODE_TIMING ((uint32_t)0x0000U)
/*!< TIM Output Active mode */
#define TIM_OCMODE_ACTIVE ((uint32_t)TIM_CCMR1_OC1M_0)
/*!< TIM Output Inactive mode */
#define TIM_OCMODE_INACTIVE ((uint32_t)TIM_CCMR1_OC1M_1)
/*!< TIM Output Toggle mode */
#define TIM_OCMODE_TOGGLE ((uint32_t)TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)
/*!< TIM PWM mode 1 */
#define TIM_OCMODE_PWM1 ((uint32_t)TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1)
/*!< TIM PWM mode 2 */
#define TIM_OCMODE_PWM2 ((uint32_t)TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)
/*!< TIM Forced Active mode */
#define TIM_OCMODE_FORCED_ACTIVE ((uint32_t)TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0)
/*!< TIM Forced Inactive mode */
#define TIM_OCMODE_FORCED_INACTIVE ((uint32_t)TIM_CCMR1_OC1M_2)
/*!< TIM Rettrigerrable OPM mode 1 */
#define TIM_OCMODE_RETRIGERRABLE_OPM1 ((uint32_t)TIM_CCMR1_OC1M_3)
/*!< TIM Rettrigerrable OPM mode 2 */
#define TIM_OCMODE_RETRIGERRABLE_OPM2 ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_0)
/*!< TIM Combined PWM mode 1 */
#define TIM_OCMODE_COMBINED_PWM1 ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_2)
/*!< TIM Combined PWM mode 2 */
#define TIM_OCMODE_COMBINED_PWM2 ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_2)
/*!< TIM Asymetruc PWM mode 1 */
#define TIM_OCMODE_ASSYMETRIC_PWM1 ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)
/*!< TIM Asymetruc PWM mode 2 */
#define TIM_OCMODE_ASSYMETRIC_PWM2 ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M)
可用于设置占空比,对应定时器的CCR寄存器,32位的TIM2和TIM5范围是0到0xFFFFFFFF。
设置输出极性,可选高电平或低电平有效。
#define TIM_OCPOLARITY_HIGH ((uint32_t)0x0000U)
#define TIM_OCPOLARITY_LOW (TIM_CCER_CC1P)
互补输出极性设置,可选高电平或者低电平有效。
#define TIM_OCNPOLARITY_HIGH ((uint32_t)0x0000U)
#define TIM_OCNPOLARITY_LOW (TIM_CCER_CC1NP)
快速输出模式使能,仅OCMode配置为PWM1或者PWM2模式时才有意义。
#define TIM_OCFAST_DISABLE ((uint32_t)0x0000U)
#define TIM_OCFAST_ENABLE (TIM_CCMR1_OC1FE)
空闲状态时,设置输出比较引脚的电平状态。
#define TIM_OCIDLESTATE_SET (TIM_CR2_OIS1)
#define TIM_OCIDLESTATE_RESET ((uint32_t)0x0000U)
空闲状态时,设置互补输出引脚的电平状态。
#define TIM_OCNIDLESTATE_SET (TIM_CR2_OIS1N)
#define TIM_OCNIDLESTATE_RESET ((uint32_t)0x0000U)
此结构体主要用于定时器的输入捕获,定义如下:
typedef struct
{
uint32_t ICPolarity;
uint32_t ICSelection;
uint32_t ICPrescaler;
uint32_t ICFilter;
} TIM_IC_InitTypeDef;
下面将这几个参数一 一做个说明。
输入触发极性,可以选择上升沿,下降沿或者双沿触发。
#define TIM_ICPOLARITY_RISING TIM_INPUTCHANNELPOLARITY_RISING
#define TIM_ICPOLARITY_FALLING TIM_INPUTCHANNELPOLARITY_FALLING
#define TIM_ICPOLARITY_BOTHEDGE TIM_INPUTCHANNELPOLARITY_BOTHEDGE
输入捕获通道选择,可以选择直接输入(即CC1选择TI1,CC2选择TI2等),间接输入(CC1选择TI2,CC3选择TI4等)或者TRC。
#define TIM_ICSELECTION_DIRECTTI (TIM_CCMR1_CC1S_0)
#define TIM_ICSELECTION_INDIRECTTI (TIM_CCMR1_CC1S_1)
#define TIM_ICSELECTION_TRC (TIM_CCMR1_CC1S)
输入捕获分频,表示每捕获1,2,4或8个事件后表示一次捕获。
#define TIM_ICPSC_DIV1 ((uint32_t)0x0000U)
#define TIM_ICPSC_DIV2 (TIM_CCMR1_IC1PSC_0)
#define TIM_ICPSC_DIV4 (TIM_CCMR1_IC1PSC_1)
#define TIM_ICPSC_DIV8 (TIM_CCMR1_IC1PSC)
输入捕获滤波器,可以定义采样频率和多少个连续事件才视为有效的触发,参数范围0到15。具体定义如下,其中fCK_INT表示定时器时钟,fDTS表示死区时间采样率,N表示这么多个事件代表一次有效边沿。
0000:无滤波器,按 fDTS 频率进行采样
0001: fSAMPLING=fCK_INT, N=2
0010: fSAMPLING=fCK_INT, N=4
0011: fSAMPLING=fCK_INT, N=8
0100: fSAMPLING=fDTS/2, N=6
0101: fSAMPLING=fDTS/2, N=8
0110: fSAMPLING=fDTS/4, N=6
0111: fSAMPLING=fDTS/4, N=8
1000: fSAMPLING=fDTS/8, N=6
1001: fSAMPLING=fDTS/8, N=8
1010: fSAMPLING=fDTS/16, N=5
1011: fSAMPLING=fDTS/16, N=6
1100: fSAMPLING=fDTS/16, N=8
1101: fSAMPLING=fDTS/32, N=5
1110: fSAMPLING=fDTS/32, N=6
HAL库有个自己的底层初始化回调函数,比如调用函数HAL_TIM_Base_Init就会调用HAL_TIM_Base_MspInit,此函数是弱定义的。
__weak void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(htim);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_TIM_Base_MspDeInit could be implemented in the user file
*/
}
用户可以在其它的C文件重定向,并将相对的底层初始化在里面实现。对应的底层复位函数HAL_TIM_Base_DeInit是在函数HAL_TIM_Base_MspDeInit里面被调用的,也是弱定义的。
当然,用户也可以自己初始化,不限制必须在两个函数里面实现。
定时器外设的基本参数配置完毕后还不能使用,还需要配置GPIO、时钟、中断等参数,比如下面配置TIM1使用PA8做PWM输出。
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 使能TIM1时钟 */
__HAL_RCC_TIM1_CLK_ENABLE ();
/* 使能GPIOA时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE ();
/* 设置TIM1使用PA8做PWM输出引脚,将其配置为输出,推挽,复用模式 */
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
GPIO_InitStruct.Pin = GPIO_PIN_8;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
总结下来就是以下几点:
关于这个底层配置有以下几点要着重说明下:
#define GPIO_AF1_TIM1 ((uint8_t)0x01) /* TIM1 Alternate Function mapping */
但是却有4个输出通道,每个通道都有几个支持的输出引脚:
TIM1_CH1, PA8 PE9 PK1
TIM1_CH2, PA9 PE11
TIM1_CH3, PA10 PE13 PJ9
TIM1_CH4, PA11 PE14 PJ11
具体使用哪个,配置对应引脚的复用即可:
下面我们介绍__HAL_TIM_GET_FLAG函数。这个函数用来检查定时器标志位是否被设置。
/** @brief Check whether the specified TIM interrupt flag is set or not.
* @param __HANDLE__: specifies the TIM Handle.
* @param __FLAG__: specifies the TIM interrupt flag to check.
* This parameter can be one of the following values:
* @arg TIM_FLAG_UPDATE: Update interrupt flag
* @arg TIM_FLAG_CC1: Capture/Compare 1 interrupt flag
* @arg TIM_FLAG_CC2: Capture/Compare 2 interrupt flag
* @arg TIM_FLAG_CC3: Capture/Compare 3 interrupt flag
* @arg TIM_FLAG_CC4: Capture/Compare 4 interrupt flag
* @arg TIM_FLAG_CC5: Compare 5 interrupt flag
* @arg TIM_FLAG_CC6: Compare 6 interrupt flag
* @arg TIM_FLAG_COM: Commutation interrupt flag
* @arg TIM_FLAG_TRIGGER: Trigger interrupt flag
* @arg TIM_FLAG_BREAK: Break interrupt flag
* @arg TIM_FLAG_BREAK2: Break 2 interrupt flag
* @arg TIM_FLAG_SYSTEM_BREAK: System Break interrupt flag
* @arg TIM_FLAG_CC1OF: Capture/Compare 1 overcapture flag
* @arg TIM_FLAG_CC2OF: Capture/Compare 2 overcapture flag
* @arg TIM_FLAG_CC3OF: Capture/Compare 3 overcapture flag
* @arg TIM_FLAG_CC4OF: Capture/Compare 4 overcapture flag
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_TIM_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &(__FLAG__)) == (__FLAG__))
前5个是比较常用的中断标志。
定时器更新标准,配置一个周期性的定时器中断要用到。
TIM_FLAG_CC2
TIM_FLAG_CC3
TIM_FLAG_CC4
捕获/比较标志,配置了捕获/比较中断要用到。
与标志获取函数__HAL_TIM_GET_FLAG对应的清除函数是__HAL_TIM_CLEAR_FLAG:
/** @brief Clear the specified TIM interrupt flag.
* @param __HANDLE__: specifies the TIM Handle.
* @param __FLAG__: specifies the TIM interrupt flag to clear.
* This parameter can be one of the following values:
* @arg TIM_FLAG_UPDATE: Update interrupt flag
* @arg TIM_FLAG_CC1: Capture/Compare 1 interrupt flag
* @arg TIM_FLAG_CC2: Capture/Compare 2 interrupt flag
* @arg TIM_FLAG_CC3: Capture/Compare 3 interrupt flag
* @arg TIM_FLAG_CC4: Capture/Compare 4 interrupt flag
* @arg TIM_FLAG_CC5: Compare 5 interrupt flag
* @arg TIM_FLAG_CC6: Compare 6 interrupt flag
* @arg TIM_FLAG_COM: Commutation interrupt flag
* @arg TIM_FLAG_TRIGGER: Trigger interrupt flag
* @arg TIM_FLAG_BREAK: Break interrupt flag
* @arg TIM_FLAG_BREAK2: Break 2 interrupt flag
* @arg TIM_FLAG_SYSTEM_BREAK: System Break interrupt flag
* @arg TIM_FLAG_CC1OF: Capture/Compare 1 overcapture flag
* @arg TIM_FLAG_CC2OF: Capture/Compare 2 overcapture flag
* @arg TIM_FLAG_CC3OF: Capture/Compare 3 overcapture flag
* @arg TIM_FLAG_CC4OF: Capture/Compare 4 overcapture flag
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_TIM_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR = ~(__FLAG__))
清除标志函数所支持的参数跟获取函数是一 一对应的。除了这两个函数,还是定时器的中断开启和中断关闭函数用的也比较多。
/** @brief Enable the specified TIM interrupt.
* @param __HANDLE__: specifies the TIM Handle.
* @param __INTERRUPT__: specifies the TIM interrupt source to enable.
* This parameter can be one of the following values:
* @arg TIM_IT_UPDATE: Update interrupt
* @arg TIM_IT_CC1: Capture/Compare 1 interrupt
* @arg TIM_IT_CC2: Capture/Compare 2 interrupt
* @arg TIM_IT_CC3: Capture/Compare 3 interrupt
* @arg TIM_IT_CC4: Capture/Compare 4 interrupt
* @arg TIM_IT_COM: Commutation interrupt
* @arg TIM_IT_TRIGGER: Trigger interrupt
* @arg TIM_IT_BREAK: Break interrupt
* @retval None
*/
#define __HAL_TIM_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->DIER |= (__INTERRUPT__))
/** @brief Disable the specified TIM interrupt.
* @param __HANDLE__: specifies the TIM Handle.
* @param __INTERRUPT__: specifies the TIM interrupt source to disable.
* This parameter can be one of the following values:
* @arg TIM_IT_UPDATE: Update interrupt
* @arg TIM_IT_CC1: Capture/Compare 1 interrupt
* @arg TIM_IT_CC2: Capture/Compare 2 interrupt
* @arg TIM_IT_CC3: Capture/Compare 3 interrupt
* @arg TIM_IT_CC4: Capture/Compare 4 interrupt
* @arg TIM_IT_COM: Commutation interrupt
* @arg TIM_IT_TRIGGER: Trigger interrupt
* @arg TIM_IT_BREAK: Break interrupt
* @retval None
*/
#define __HAL_TIM_DISABLE_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->DIER &= ~(__INTERRUPT__))
常用的也是前五个参数,1个定时器更新中断以及4个CC中断 。
注意:操作定时器的寄存器不限制必须要用HAL库提供的API,比如要操作寄存器CR1,直接调用TIM1->CR1操作即可。
使用方法由HAL库提供:
第1步:通过下面几个函数配置定时器工作在相应的模式
简单的定时器时基础功能
配置定时器产生输出比较信号
配置定时器产生PWM信号
配置定时器测量外部信号
配置定时器工作在单脉冲模式
配置定时器使用编码器接口
第2步:定时器几个常用功能的底层初始化API,这个里面需要用户自己填
第1步里面的几个函数会调用下面的API。
第3步:底层初始化具体实现
第2步中函数的具体实现。
第4步:启动定时器外设
HAL_TIM_Base_Start()
HAL_TIM_Base_Start_DMA()
HAL_TIM_Base_Start_IT()
HAL_TIM_IC_Start()
HAL_TIM_IC_Start_DMA()
HAL_TIM_IC_Start_IT()
HAL_TIM_OC_Start()
HAL_TIM_OC_Start_DMA()
HAL_TIM_OC_Start_IT()
HAL_TIM_PWM_Start()
HAL_TIM_PWM_Start_DMA()
HAL_TIM_PWM_Start_IT()
HAL_TIM_OnePulse_Start()
HAL_TIM_OnePulse_Start_IT().
HAL_TIM_Encoder_Start()
HAL_TIM_Encoder_Start_DMA()
HAL_TIM_Encoder_Start_IT().
第5步:定时器的DMA突发使用下面两个函数
定时器常用的功能,通过上面这几步即可实现。
此文件涉及到的函数非常多,这里把几个常用的函数做个说明:
函数原型:
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
{
/* 检测是否是有效句柄 */
if(htim == NULL)
{
return HAL_ERROR;
}
/* 检测参数 */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
if(htim->State == HAL_TIM_STATE_RESET)
{
/* 默认取消锁 */
htim->Lock = HAL_UNLOCKED;
/* 初始化底层硬件 : GPIO, CLOCK, NVIC */
HAL_TIM_Base_MspInit(htim);
}
/* 设置TIM状态 */
htim->State= HAL_TIM_STATE_BUSY;
/* 基本参数配置 */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* 设置TIM就绪 */
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}
函数描述:
此函数用于初始化定时器用于PWM。
函数参数:
注意事项:
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_TIM_STATE_RESET = 0x00U。
解决办法有三:
方法1:用户自己初始定时器和涉及到的GPIO等。
方法2:定义TIM_HandleTypeDef TimHandle为全局变量。
方法3:下面的方法
if(HAL_TIM_Base_DeInit(&TimHandle) != HAL_OK)
{
Error_Handler();
}
if(HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
Error_Handler();
}
使用举例:
TIM_HandleTypeDef TimHandle = {0};
/*
定时器中断更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
函数原型:
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
{
/* 检测参数状态 */
assert_param(IS_TIM_INSTANCE(htim->Instance));
/* 设置定时器状态 */
htim->State= HAL_TIM_STATE_BUSY;
/* 使能定时器 */
__HAL_TIM_ENABLE(htim);
/* 设置定时器状态 */
htim->State= HAL_TIM_STATE_READY;
/* 返回HAL_OK */
return HAL_OK;
}
函数描述:
此函数比较简单,调用函数HAL_TIM_Base_Init配置了基础功能后,启动定时器。
函数参数:
使用举例:
TIM_HandleTypeDef TimHandle = {0};
/*
定时器中断更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
/* 启动定时器 */
if (HAL_TIM_Base_Start(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
函数原型:
HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
{
/* 检查句柄是否有效 */
if(htim == NULL)
{
return HAL_ERROR;
}
/* 检测参数 */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
if(htim->State == HAL_TIM_STATE_RESET)
{
/* 默认取消锁 */
htim->Lock = HAL_UNLOCKED;
/* 初始底层 : GPIO, CLOCK, NVIC 和 DMA */
HAL_TIM_PWM_MspInit(htim);
}
/* 设置定时状态 */
htim->State= HAL_TIM_STATE_BUSY;
/* 配置定时器用于PWM */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* 设置定时器状态 */
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}
函数描述:
此函数用于初始化定时为PWM方式。
函数参数:
注意事项:
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_TIM_STATE_RESET = 0x00U。
解决办法有三:
方法1:用户自己初始定时器和涉及到的GPIO等。
方法2:定义TIM_HandleTypeDef TimHandle为全局变量。
方法3:下面的方法
if(HAL_TIM_PWM_DeInit(&TimHandle) != HAL_OK)
{
Error_Handler();
}
if(HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
Error_Handler();
}
使用举例:
TIM_HandleTypeDef TimHandle = {0};
/* PWM频率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIM1;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
函数原型:
HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
TIM_OC_InitTypeDef* sConfig,
uint32_t Channel)
{
/* 省略 */
/* 开锁 */
__HAL_LOCK(htim);
htim->State = HAL_TIM_STATE_BUSY;
switch (Channel)
{
case TIM_CHANNEL_1:
{
/* 检查参数 */
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
/* 配置通道1的PWM模式 */
TIM_OC1_SetConfig(htim->Instance, sConfig);
/*预装载使能,更新事件产生时写入有效 */
htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE;
/* 配置是快速输出模式 */
htim->Instance->CCMR1 &= ~TIM_CCMR1_OC1FE;
htim->Instance->CCMR1 |= sConfig->OCFastMode;
}
break;
case TIM_CHANNEL_2:
{
/* 省略 */
}
break;
case TIM_CHANNEL_3:
{
/* 省略 */
}
break;
case TIM_CHANNEL_4:
{
/* 省略 */
}
break;
case TIM_CHANNEL_5:
{
/* 省略 */
}
break;
case TIM_CHANNEL_6:
{
/* 省略 */
}
break;
default:
break;
}
htim->State = HAL_TIM_STATE_READY;
/* 关锁 */
__HAL_UNLOCK(htim);
return HAL_OK;
}
函数描述:
此函数用于配置定时器的PWM通道。
函数参数:
TIM_CHANNEL_1
TIM_CHANNEL_2
TIM_CHANNEL_3
TIM_CHANNEL_4
TIM_CHANNEL_5
TIM_CHANNEL_6
使用举例:
TIM_HandleTypeDef TimHandle = {0};
/* PWM频率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIM1;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
/* 配置定时器PWM输出通道 */
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
/* 占空比 */
sConfig.Pulse = pulse;
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TimChannel[_ucChannel]) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
函数原型:
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
/* 检测参数 */
assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
/* 使能捕获比较通道 */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
if(IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
{
/* 使能主输出 */
__HAL_TIM_MOE_ENABLE(htim);
}
/* 使能定时器 */
__HAL_TIM_ENABLE(htim);
/* 返回状态*/
return HAL_OK;
}
函数描述:
此函数用于启动PWM。
函数参数:
TIM_CHANNEL_1
TIM_CHANNEL_2
TIM_CHANNEL_3
TIM_CHANNEL_4
使用举例:
TIM_HandleTypeDef TimHandle = {0};
/* PWM频率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIM1;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
/* 配置定时器PWM输出通道 */
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
/* 占空比 */
sConfig.Pulse = pulse;
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TimChannel[_ucChannel]) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
/* 启动PWM输出 */
if (HAL_TIM_PWM_Start(&TimHandle, TimChannel[_ucChannel]) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
函数原型:
HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)
{
/* 检测形参是否有效 */
if(htim == NULL)
{
return HAL_ERROR;
}
/* 检测参数 */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
if(htim->State == HAL_TIM_STATE_RESET)
{
/* 默认取消锁 */
htim->Lock = HAL_UNLOCKED;
/* 初始底层 : GPIO, CLOCK, NVIC 和 DMA */
HAL_TIM_IC_MspInit(htim);
}
/* 设置定时器状态 */
htim->State= HAL_TIM_STATE_BUSY;
/* 配置定时器为输入捕获模式 */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* 设置定时器状态 */
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}
函数描述:
此函数用于定时器输入捕获初始化。
函数参数:
注意事项:
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个HAL_TIM_STATE_RESET = 0x00U。
解决办法有三:
方法1:用户自己初始定时器和涉及到的GPIO等。
方法2:定义TIM_HandleTypeDef TimHandle为全局变量。
方法3;下面的方法
if(HAL_TIM_IC_DeInit(&UartHandle) != HAL_OK)
{
Error_Handler();
}
if(HAL_TIM_IC_Init(&UartHandle) != HAL_OK)
{
Error_Handler();
}
函数原型:
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef* sConfig, uint32_t Channel)
{
/* 检查参数 */
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
assert_param(IS_TIM_IC_POLARITY(sConfig->ICPolarity));
assert_param(IS_TIM_IC_SELECTION(sConfig->ICSelection));
assert_param(IS_TIM_IC_PRESCALER(sConfig->ICPrescaler));
assert_param(IS_TIM_IC_FILTER(sConfig->ICFilter));
/* 开锁 */
__HAL_LOCK(htim);
htim->State = HAL_TIM_STATE_BUSY;
if (Channel == TIM_CHANNEL_1)
{
/* 配置输入通道1 */
TIM_TI1_SetConfig(htim->Instance,
sConfig->ICPolarity,
sConfig->ICSelection,
sConfig->ICFilter);
/* 清零IC1PSC位 */
htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;
/* 根据用户配置,设置分频 */
htim->Instance->CCMR1 |= sConfig->ICPrescaler;
}
else if (Channel == TIM_CHANNEL_2)
{
/* 省略 */
}
else if (Channel == TIM_CHANNEL_3)
{
/* 省略 */
}
else
{
/* 省略 */
}
htim->State = HAL_TIM_STATE_READY;
__HAL_UNLOCK(htim);
return HAL_OK;
}
函数描述:
此函数用于配置定时器的输入捕获通道。
函数参数:
TIM_CHANNEL_1
TIM_CHANNEL_2
TIM_CHANNEL_3
TIM_CHANNEL_4
函数原型:
HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef *htim, uint32_t Channel)
{
/* 检查参数 */
assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
switch (Channel)
{
case TIM_CHANNEL_1:
{
/* 使能CC1(Capture/Compare 1)中断 */
__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
}
break;
case TIM_CHANNEL_2:
{
/* 省略 */
}
break;
case TIM_CHANNEL_3:
{
/* 省略 */
}
break;
case TIM_CHANNEL_4:
{
/* 省略 */
}
break;
default:
break;
}
/* 使能输入捕获通道 */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
/* 使能定时器 */
__HAL_TIM_ENABLE(htim);
/* 返回状态 */
return HAL_OK;
}
函数描述:
此函数用于启动定时器输入捕获模式,采用定时器方式。
函数参数:
TIM_CHANNEL_1
TIM_CHANNEL_2
TIM_CHANNEL_3
TIM_CHANNEL_4
函数原型:
HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef* htim)
{
/* 检测形参是否有效 Check */
if(htim == NULL)
{
return HAL_ERROR;
}
/* 检查参数 */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
if(htim->State == HAL_TIM_STATE_RESET)
{
/* 默认取消锁 */
htim->Lock = HAL_UNLOCKED;
/* 初始底层 : GPIO, CLOCK, NVIC 和 DMA */
HAL_TIM_OC_MspInit(htim);
}
/* 设置定时器状态Set the TIM state */
htim->State= HAL_TIM_STATE_BUSY;
/* 配置定时器为输出比较模式Init the base time for the Output Compare */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* 设置定时器状态 */
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}
函数描述:
此函数用于定时器输入捕获初始化。
函数参数:
注意事项:
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个HAL_TIM_STATE_RESET = 0x00U。
解决办法有三:
方法1:用户自己初始定时器和涉及到的GPIO等。
方法2:定义TIM_HandleTypeDef TimHandle为全局变量。
方法3;下面的方法
if(HAL_TIM_OC_DeInit(&UartHandle) != HAL_OK)
{
Error_Handler();
}
if(HAL_TIM_OC_Init(&UartHandle) != HAL_OK)
{
Error_Handler();
}
函数原型:
HAL_StatusTypeDef HAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef *htim,
TIM_OC_InitTypeDef* sConfig,
uint32_t Channel)
{
/* Check the parameters */
assert_param(IS_TIM_CHANNELS(Channel));
assert_param(IS_TIM_OC_MODE(sConfig->OCMode));
assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));
/* Process Locked */
__HAL_LOCK(htim);
htim->State = HAL_TIM_STATE_BUSY;
switch (Channel)
{
case TIM_CHANNEL_1:
{
/* 检测参数Check the parameters */
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
/* 配置定时器输出比较通道1 */
TIM_OC1_SetConfig(htim->Instance, sConfig);
}
break;
case TIM_CHANNEL_2:
{
/* 省略 */
}
break;
case TIM_CHANNEL_3:
{
/* 省略 */
}
break;
case TIM_CHANNEL_4:
{
/* 省略 */
}
break;
case TIM_CHANNEL_5:
{
/* 省略 */
}
break;
case TIM_CHANNEL_6:
{
/* 省略 */
}
break;
default:
break;
}
htim->State = HAL_TIM_STATE_READY;
__HAL_UNLOCK(htim);
return HAL_OK;
}
函数描述:
此函数用于初始化串口的基础特性和高级特性。
函数参数:
TIM_CHANNEL_1
TIM_CHANNEL_2
TIM_CHANNEL_3
TIM_CHANNEL_4
TIM_CHANNEL_5
TIM_CHANNEL_6
函数原型:
HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
/* 检查参数 */
assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
/* 使能输出比较通道 */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
if(IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
{
/* 使能主输出 */
__HAL_TIM_MOE_ENABLE(htim);
}
/* 使能定时器 */
__HAL_TIM_ENABLE(htim);
/* 返回状态 */
return HAL_OK;
}
函数描述:
此函数用于启动定时器输出比较模式。
函数参数:
TIM_CHANNEL_1
TIM_CHANNEL_2
TIM_CHANNEL_3
TIM_CHANNEL_4
本章节就为大家讲解这么多,建议大家将GPIO的驱动源码结合参考手册中的寄存器通读一遍,对于我们后面章节的学习大有裨益。