打印
[STM32F4]

STM32F4 PWM输出驱动

[复制链接]
465|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
观海|  楼主 | 2021-8-2 12:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32F4的定时器时钟有点复杂,当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM11的时钟为APB2的时钟,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟;
而如果APB1和APB2分频数不为1,那么TIM1、TIM8~TIM11的时钟为APB2的时钟的两倍,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍

因此专门写了一个函数,用于获取定时器时钟频率 u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh)



/*************************************************************************************************************
* 文件名                :        stm32f4_timer.c
* 功能                        :        STM32F4 timer驱动
* 作者                        :        cp1300@139.com
* 创建时间                :        2017-08-24
* 最后修改时间        :        2017-08-24
* 详细:                        高级定时器:tim1,time8(16位)
                                        通用定时器:tim3,tim4(16位),tim2,tim5(32位)
                                        通用定时器:tim9-tim14(16位)
                                        基本定时器:tim6-tim7(16位)
                                        2019-10-24:增加 u32 Timer_GetTimeClockSpeed(TTIMER_CH timerCh) 支持
                                       
                                        因为系统初始化SystemInit函数里初始化APB1总线时钟为4分频即42M,APB2总线时钟为2分频即84M,
                                        所以TIM1、TIM8~TIM11的时钟为APB2时钟的两倍即168M,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍即84M。
*************************************************************************************************************/       
#include "stm32f4_timer.h"
#include "system.h"



//定时器结构体指针
static const TIM_TypeDef *TimerX[14]        = {TIM1,TIM2,TIM3,TIM4,TIM5,TIM6,TIM7,TIM8,TIM9,TIM10,TIM11,TIM12,TIM13,TIM14};


/*************************************************************************************************************************
* 函数                        :        u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh)
* 功能                        :        STM32F4获取定时器时钟频率
* 参数                        :        timerCh:定时器选择,见TIMER_CH;
* 返回                        :        定时器时钟频率,单位Hz
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2019-10-24
* 最后修改时间         :         2019-10-24
* 说明                        :         当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM11的时钟为APB2的时钟,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟;
                                        而如果APB1和APB2分频数不为1,那么TIM1、TIM8~TIM11的时钟为APB2的时钟的两倍,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍
*************************************************************************************************************************/
u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh)
{
        u32 clock = 0;
        u8 PPRE;
       
        switch(timerCh)
        {
                case TIMER1:       
                case TIMER8:
                case TIMER9:
                case TIMER10:
                case TIMER11:        //来自APB2高速时钟总线
                {
                        PPRE = (RCC->CFGR>>13)&0X7;        //获取PPRE2 分频值
                        if(PPRE == 0)        //分频数位0,直接等于APB2时钟
                        {
                                clock = SYS_GetAPB2ClockSpeed();
                        }
                        else //分频数不为1,时钟为APB2的2倍
                        {
                                clock = SYS_GetAPB2ClockSpeed()*2;
                        }
                }break;       
                case TIMER2:       
                case TIMER3:
                case TIMER4:
                case TIMER5:
                case TIMER6:
                case TIMER7:       
                case TIMER12:
                case TIMER13:
                case TIMER14:        //来自APB1低速时钟       
                {
                        PPRE = (RCC->CFGR>>10)&0X7;        //获取PPRE1 分频值
                        if(PPRE == 0)        //分频数位0,直接等于APB1时钟
                        {
                                clock = SYS_GetAPB1ClockSpeed();
                        }
                        else //分频数不为1,时钟为APB1的2倍
                        {
                                clock = SYS_GetAPB1ClockSpeed()*2;
                        }
                }break;
                default:
                {
                        clock = 0;
                }break;
        }
       
        return clock;
}



使用特权

评论回复
沙发
观海|  楼主 | 2021-8-2 12:10 | 只看该作者
/*************************************************************************************************************************
* 函数        :        void TimerPWM_Init(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 psc, u16 arr)
* 功能        :        初始化定时器PWM输出
* 参数        :        timerCh:定时器选择,见TIMER_CH       
                        pwmCh :PWM输出通道选择,见TIMER_PWM_CH
                        psc: 预分频器
                        arr: 重装值
* 返回        :        无
* 依赖        :        底层读写函数
* 作者        :        cp1300@139.com
* 时间        :        20130331
* 最后修改时间 : 20130331
* 说明        :         初始为边沿对齐模式,向上计数,更新时计数器不停止
                        此函数不初始化外部引脚,需要初始化对应的PWM输出脚为复用推挽输出即可
                        同一定时器产生的不同通道的PWM频率一样
*************************************************************************************************************************/
void TimerPWM_Init(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 psc, u16 arr)
{
        TIM_TypeDef *TIMx;
        const SYS_DEV_CLOCK DEV_TimeBuff[] = {DEV_TIM1,DEV_TIM2,DEV_TIM3,DEV_TIM4,DEV_TIM5,DEV_TIM6,DEV_TIM7,DEV_TIM8,DEV_TIM9,DEV_TIM10,
        DEV_TIM11,DEV_TIM12,DEV_TIM13,DEV_TIM14};

        if(timerCh == TIMER6 || timerCh == TIMER7)        //定时器6和定时器7为基本定时器,不带PWM输出
                return;

        TIMx = (TIM_TypeDef *)TimerX[timerCh];                                        //获取指定定时器寄存器结构指针       
        SYS_DeviceClockEnable(DEV_TimeBuff[timerCh],TRUE);                //使能定时器时钟
       
        //初始化配置
        TIMx->CR1 = 0;                                                        //关闭定时器,并复位相关设置
        TIMx->CR2 = 0;                                                        //复位相关设置
        TIMx->PSC = psc;                                                //预分频器
        TIMx->ARR = arr;                                                //自动重装值
        //配置PWM通道
        switch (pwmCh)
        {
                 case PWM_CH1:
                {       
                        TIMx->CCMR1 &= ~0xff;                        //清除之前设置
                        TIMx->CCMR1 |= 7 << 4;                        //PWM2模式
                        TIMx->CCMR1 |= 1 << 3;                  //CH1预装载使能
                        TIMx->CCER &= ~(0xf<<0);                   //清除之前设置
                        TIMx->CCER |= 1<<1;                                //OC1 低电平有效
                        TIMx->CCER |= 1<<0;                           //OC1 输出使能
                }break;
                case PWM_CH2:
                {
                        TIMx->CCMR1 &= ~(0xff << 8);        //清除之前设置
                        TIMx->CCMR1 |= (7 << 12);                //PWM2模式
                        TIMx->CCMR1 |= 1 << 11;                  //CH2预装载使能       
                        TIMx->CCER &= ~(0xf<<4);                   //清除之前设置
                        TIMx->CCER |= 1<<5;                                //OC2 低电平有效
                        TIMx->CCER |= 1<<4;                           //OC2 输出使能
                }break;
                case PWM_CH3:
                {
                        TIMx->CCMR2 &= ~0xff;                        //清除之前设置
                        TIMx->CCMR2 |= 7 << 4;                        //PWM2模式
                        TIMx->CCMR2 |= 1 << 3;                  //CH3预装载使能
                        TIMx->CCER &= ~(0xf<<8);                   //清除之前设置
                        TIMx->CCER |= 1<<9;                                //OC3 低电平有效
                        TIMx->CCER |= 1<<8;                           //OC3 输出使能
                }break;
                case PWM_CH4:
                {
                        TIMx->CCMR2 &= ~(0xff << 8);        //清除之前设置
                        TIMx->CCMR2 |= 7 << 12;                        //PWM2模式
                        TIMx->CCMR2 |= 1 << 11;                  //CH4预装载使能
                        TIMx->CCER &= ~(0xf<<12);           //清除之前设置
                        TIMx->CCER |= 1<<13;                        //OC4 低电平有效
                        TIMx->CCER |= 1<<12;                           //OC4 输出使能
                }break;
                default : break;
        }
        TIMx->CR1 |= BIT7;                                                //定时器自动重装使能
        TIMx->EGR |= BIT0;                                                //更新
        TIMx->BDTR |= BIT15;                                        //主输出使能,只针对TIM1,TIM8
        TIMx->CR1 |= BIT0;                                            //使能定时器
}



使用特权

评论回复
板凳
观海|  楼主 | 2021-8-2 12:11 | 只看该作者
/*************************************************************************************************************************
* 函数        :        void TimerPWM_Set(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 pwm)
* 功能        :        设置PWM占空比
* 参数        :        timerCh:定时器选择,见TIMER_CH       
                        pwmCh :PWM输出通道选择,见TIMER_PWM_CH
                        pwm:PWM为高的时间
* 返回        :        无
* 依赖        :        底层读写函数
* 作者        :        cp1300@139.com
* 时间        :        20130331
* 最后修改时间 : 20130331
* 说明        :         需要先初始化定时器
                        值越大,占空比越大
*************************************************************************************************************************/
void TimerPWM_Set(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 pwm)
{
        TIM_TypeDef *TIMx;

        if(timerCh == TIMER6 || timerCh == TIMER7)                                //定时器6和定时器7为基本定时器,不带PWM输出
                return;       

        TIMx = (TIM_TypeDef *)TimerX[timerCh];                                        //获取指定定时器寄存器结构指针
        //设置捕获/比较寄存器的值,调节占空比
        switch (pwmCh)
        {
                 case PWM_CH1 : TIMx->CCR1 = pwm; break;
                case PWM_CH2 : TIMx->CCR2 = pwm; break;
                case PWM_CH3 : TIMx->CCR3 = pwm; break;
                case PWM_CH4 : TIMx->CCR4 = pwm; break;
                default : break;
        }
}


使用特权

评论回复
地板
观海|  楼主 | 2021-8-2 12:12 | 只看该作者
/*************************************************************************************************************
* 文件名                :        stm32f4_timer.h
* 功能                        :        STM32F4 timer驱动
* 作者                        :        cp1300@139.com
* 创建时间                :        2017-08-24
* 最后修改时间        :        2017-08-24
* 详细:                        高级定时器:tim1,time8(16位)
                                        通用定时器:tim3,tim4(16位),tim2,tim5(32位)
                                        通用定时器:tim9-tim14(16位)
                                        基本定时器:tim6-tim7(16位)
*************************************************************************************************************/       
#ifndef __STM32F4_TIMER_H_
#define __STM32F4_TIMER_H_
#include "system.h"
//定时器选择,定时器1,8高级定时器,2,3,4,5 9-14为通用定时器,6,7为基本定时器
typedef enum
{
        TIMER1        =        0,
        TIMER2        =        1,
        TIMER3        =        2,
        TIMER4        =        3,
        TIMER5        =        4,
        TIMER6        =        5,
        TIMER7        =        6,
        TIMER8        =        7,
        TIMER9        =        8,
        TIMER10        =        9,
        TIMER11        =        10,
        TIMER12        =        11,
        TIMER13        =        12,
        TIMER14        =        13,
} TIMER_CH;
//定时器PWM通道,每个定时器有4路PWM输出通道
typedef enum
{
        PWM_CH1        =        0,
        PWM_CH2        =        1,
        PWM_CH3        =        2,
        PWM_CH4        =        3,
} TIMER_PWM_CH;
//API
void TimerPWM_Init(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 psc, u16 arr);        //初始化定时器PWM输出
void TimerPWM_Set(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 pwm);                        //设置PWM占空比
u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh);                                                                //STM32F4获取定时器时钟频率
#endif //__STM32F4_TIMER_H_


使用特权

评论回复
5
观海|  楼主 | 2021-8-2 12:13 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        u32 SYS_GetSystemClockSpeed(void)               
* 功能                        :        STM32F4 获取系统时钟频率
* 参数                        :        无
* 返回                        :        系统时钟频率HZ
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2016-03-13
* 最后修改时间         :         2016-03-13
* 说明                        :        用于获取系统时钟,SYSCLK时钟
                                        PLL使能后:SYSCLK=PLLOUT=VCO/PLLP
                                        f(VCO 时钟) = f(PLL 时钟输入) × (PLLN / PLLM)
                                        f(PLL 常规时钟输出) = f(VCO 时钟) / PLLP
*************************************************************************************************************************/
u32 SYS_GetSystemClockSpeed(void)
{
        static const u8 PLLP[] = {2,4,6,8};        //PLL 分频系数。
        u32 temp;
       
        switch(SYS_GetSystemClockSource())        //获取时钟来源
        {
                case SYS_CLOCK_HSI:        //HSI作为系统时钟
                {
                        return HSI_CLOCK*1000000;
                }
                case SYS_CLOCK_HSE: //HSE作为系统时钟
                {
                        return HSE_CLOCK*1000000;
                }
                default:        //PLL
                {
                        temp = (RCC->PLLCFGR>>6)&0x1ff;                        //获取PLLN,(PLL输入时钟/PLLM = 1MHZ)
                        temp /= PLLP[(RCC->PLLCFGR>>16)&0x3];        //除以PLLP
                        temp *= 1000000;
                        return temp;
                }
        }
}

/*************************************************************************************************************************
* 函数                        :        u32 SYS_GetAHBClockSpeed(void)               
* 功能                        :        STM32F4 获取AHB时钟频率
* 参数                        :        无
* 返回                        :        时钟频率HZ
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2016-03-13
* 最后修改时间         :         2016-03-13
* 说明                        :        AHB=SYSCLK/PRESC
*************************************************************************************************************************/
u32 SYS_GetAHBClockSpeed(void)
{
        static const u16 HPRE[] = {1,1,1,1,1,1,1,1,2,4,8,16,64,128,256,512};        //AHB 时钟分频系数。
       
        return SYS_GetSystemClockSpeed()/HPRE[((RCC->CFGR>>4)&0XF)];
}


/*************************************************************************************************************************
* 函数                        :        u32 SYS_GetAPB1ClockSpeed(void)       
* 功能                        :        STM32F4 获取APB1时钟频率
* 参数                        :        无
* 返回                        :        时钟频率HZ
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2016-03-13
* 最后修改时间         :         2016-03-13
* 说明                        :        APB1=AHB/PPRE1
*************************************************************************************************************************/
u32 SYS_GetAPB1ClockSpeed(void)
{
        static const u8 PPRE1[] = {1,1,1,1,2,4,8,16};
       
        return SYS_GetAHBClockSpeed()/PPRE1[((RCC->CFGR>>10)&0X7)];
}

/*************************************************************************************************************************
* 函数                        :        u32 SYS_GetAPB2ClockSpeed(void)       
* 功能                        :        STM32F4 获取APB2时钟频率
* 参数                        :        无
* 返回                        :        时钟频率HZ
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2016-03-13
* 最后修改时间         :         2016-03-13
* 说明                        :        APB1=AHB/PPRE2
*************************************************************************************************************************/
u32 SYS_GetAPB2ClockSpeed(void)
{
        static const u8 PPRE2[] = {1,1,1,1,2,4,8,16};
       
        return SYS_GetAHBClockSpeed()/PPRE2[((RCC->CFGR>>13)&0X7)];
}


这样就可以动态的去调节系统时钟频率而保障PWM频率不变。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

3882

帖子

1

粉丝