高级定时器实例 | 您所在的位置:网站首页 › stm32互补输出pwm › 高级定时器实例 |
前言
仅以此篇文章梳理我编写该实例的过程 概述我手中的正点原子STM32MINI板所用的主控型号为STM32F103C8T6,因此高级定时器只有TIM1和TIM8,本章我所使用的资源为TIM1的CH1,CH1N,BKIN,通过这些资源实现PWM的互补输出以及刹车功能 GPIO的配置在使用TIM1的CH1,CH1N和BKIN前,让我们先来看下,这些引脚涉及到哪些GPIO以及GPIO需要怎样的配置? 翻开STM32中文参考手册,在GPIO章节-外设的GPIO设置中,我们可以看见以下描述: 在了解完GPIO的配置后,我们再看看TIM1_CH1,TIM1_CH1N,TIM1_BKIN在芯片中对应的是哪个GPIO? 翻开数据手册,可以找到如下描述 OK,经过以上我们大致了解到TIM1_CH1、TIM1_CH1N、TIM1_BKIN对应的GPIO以及GPIO的配置 相信大家都有一个疑问,我当时也有这个疑问,那就是在数据手册中,以上功能都是复用的,那到底要不要使能AFIO的时钟? 对于这个问题,我查看了参考手册-GPIO-AFIO章节,找到了如下描述: 到这里,我们已经解决了GPIO、GPIO配置以及是否需要打开AFIO时钟的问题,那接下来还需要确定什么呢? 我们都知道要使用一个外设功能,都需要把对应的时钟打开,因此,打开参考手册-系统架构 OK,到这里,我们需要确认的东西都确认完了,那就可以编写代码了,所有GPIO配置代码如下: static void GPIOConfig(void) { GPIO_InitTypeDef GPIOInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); /*使能GPIOA,GPIOB时钟*/ GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIOInitStruct.GPIO_Pin = GPIO_Pin_8; /*PA8:CH1*/ GPIO_Init(GPIOA, &GPIOInitStruct); GPIOInitStruct.GPIO_Pin = GPIO_Pin_13; /*PB13:CH1N*/ GPIO_Init(GPIOB, &GPIOInitStruct); GPIOInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIOInitStruct.GPIO_Pin = GPIO_Pin_12; /*PB12:BKIN*/ GPIO_Init(GPIOB, &GPIOInitStruct); GPIO_SetBits(GPIOB, GPIO_Pin_12); /*设置PB12*/ } PWM互补输出与刹车功能为方便后面的记录,我先说清TIM_TimeBaseInitTypeDef、TIM_OCInitTypeDef、TIM_BDTRInitTypeDef这3个结构体都是什么 TIM_TimeBaseInitTypeDef这个结构体叫时基结构体,就是一个定时器最基本的结构体,高级/通用/基本定时器都有时基结构体,主要用来配置信号周期、计数器频率,计数模式之类的,在库函数中描述如下: typedef struct { uint16_t TIM_Prescaler; uint16_t TIM_CounterMode; uint16_t TIM_Period; uint16_t TIM_ClockDivision; uint8_t TIM_RepetitionCounter; } TIM_TimeBaseInitTypeDef;TIM_Prescaler:定时器分频系数,用于调整计数器每次计数的时间,计数频率为:fck_psc / (TIM_Prescaler + 1)
TIM_CounterMode:计数模式,有向上、向下、中央对齐1、中央对齐2、中央对齐3这5种模式,在底层是对TIMx_CR1寄存器的bit[6:4]操作 TIM_ClockDivision:原文描述是:定时器时钟CK_INT频率与死区发生器以及数字滤波器采样时钟频率分频化,但实际应该是计数器时钟CK_CNT频率才对,这个只有在配置死区和数字滤波时才用得到,底层是对TIMx_CR1寄存器的bit[9:8]操作,不需要时默认为0x0000就可以了
这个结构体叫做输出比较结构体,在库函数中描述如下: typedef struct { uint16_t TIM_OCMode; uint16_t TIM_OutputState; uint16_t TIM_OutputNState; uint16_t TIM_Pulse; uint16_t TIM_OCPolarity; uint16_t TIM_OCNPolarity; uint16_t TIM_OCIdleState; uint16_t TIM_OCNIdleState; } TIM_OCInitTypeDef;TIM_OCMode:比较输出模式,以CH1为例,在底层是对TIMx_CCMR1寄存器的bit[6:4],共有8种模式,我一般是使用PWM1,具体的模式描述可以看下图或者参考手册 这个结构体主要是配置刹车和死区,我们也叫刹车死区寄存器,只在TIM1和TIM8中用到,该结构体主要是对TIMx_BDTR寄存器操作,在库函数中的描述如下: typedef struct { uint16_t TIM_OSSRState; uint16_t TIM_OSSIState; uint16_t TIM_LOCKLevel; uint16_t TIM_DeadTime; uint16_t TIM_Break; uint16_t TIM_BreakPolarity; uint16_t TIM_AutomaticOutput; } TIM_BDTRInitTypeDef;TIM_OSSRState和TIM_OSSIState:关闭状态选择,具体看以下描述 TIM_Break:刹车功能使能,具体描述如下 在说了那么多后,我们大致都知道该怎么配置了,那么我就直接把代码贴出来吧 static void TIM1Config(void) { TIM_TimeBaseInitTypeDef TIMTimeBaseStruct; TIM_OCInitTypeDef TIMOCInitStruct; TIM_BDTRInitTypeDef TIMBDTRInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); /*使能TIM1时钟*/ /*时基结构体配置*/ TIMTimeBaseStruct.TIM_Period = 1000 - 1; /*从0开始 一个信号周期计数1000次*/ /*定时器时钟:72MHz / 7200 = 10kHz;周期:(1 / 10kHz) * 1000 = 100us*/ TIMTimeBaseStruct.TIM_Prescaler = 7200 - 1; /*计数器频率10kHz,每计数一次花费 100us*/ /*一个信号周期花费时间:1000 * 100us = 100ms*/ TIMTimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; /*时钟分频因子 = 1,tDTS=tCKINT*/ TIMTimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; /*向上计数*/ TIMTimeBaseStruct.TIM_RepetitionCounter = 0; /*禁用重复计数器*/ TIM_TimeBaseInit(TIM1, &TIMTimeBaseStruct); /*输出比较结构体配置*/ TIMOCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; /*PWM1模式*/ TIMOCInitStruct.TIM_OutputState = TIM_OutputState_Enable; /*输出使能*/ TIMOCInitStruct.TIM_OutputNState = TIM_OutputNState_Enable; /*互补输出使能*/ TIMOCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; /*输出有效电平为高电平*/ TIMOCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High; /*互补输出有效电平为高电平*/ TIMOCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set; /*输出空闲时为高电平*/ TIMOCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Reset; /*互补输出空闲时为低电平*/ /*初始化TIM1的通道1*/ TIMOCInitStruct.TIM_Pulse = 250 - 1; /*占空比 = 250 / 1000 = 25%*/ TIM_OC1Init(TIM1,&TIMOCInitStruct); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); /*开启预装载,在更新时间后才会重新装载数值*/ /*刹车和死区结构体配置*/ TIMBDTRInitStruct.TIM_OSSRState = TIM_OSSRState_Enable; /*运行模式下“关闭模式”选择 = 1*/ TIMBDTRInitStruct.TIM_OSSIState = TIM_OSSIState_Enable; /*空闲模式下“关闭模式”选择 = 1*/ TIMBDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_1; /*锁定级别1,见参考手册*/ TIMBDTRInitStruct.TIM_DeadTime = 0x80; /*死区时间:12.8ms*/ TIMBDTRInitStruct.TIM_Break = TIM_Break_Enable; /*开启刹车功能*/ TIMBDTRInitStruct.TIM_BreakPolarity = TIM_BreakPolarity_Low; /*刹车输入低电平有效,如果引脚检测到高电平则会停止PWM的输出,不会产生任何波形*/ TIMBDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; /*开启自动输出*/ TIM_BDTRConfig(TIM1, &TIMBDTRInitStruct); TIM_Cmd(TIM1, ENABLE); /*使能定时器,计数器开始计数*/ TIM_CtrlPWMOutputs(TIM1, ENABLE); /*开启主输出*/ } PWM互补输出与刹车实例演习 void bspTIMInit(void) { GPIOConfig(); TIM1Config(); } /************************************************************************ * 接线: * PA8 CH1 | PB13 CH1N | PB12 BKIN * 功能: * 通过 PA8 和 PB13 输出互补的 PWM 波形;当 PB12 为高时,停止 PWM 输出 * 描述: * PWM 周期 100ms;死区时间 12.8ms */ int main(void) { bspTIMInit(); while (1); }在运行以上代码后,我通过逻辑分析仪抓取的波形如下: 到这里就有人会问,怎么没有死区时间呀,不用着急,我把波形放大给你们看看 好了,到这,PWM互补输出与刹车功能学习过程就记录完了,不知不觉,居然写了5个小时 |
CopyRight 2018-2019 实验室设备网 版权所有 |