用TIM的PWM输出模式写一个步进电机的Stepper库函数

发布时间: 2024-07-18
来源: 电子工程世界

这是之前写平衡小车时自己用TIM的PWM输出模式写了一个步进电机的Stepper库函数。

1

调用顺序

图片

1.1

init函数

图片

图片

1.2

begin函数

图片

1.3

setSpeed函数

图片

图片

2

Stepper类结构

图片

3

TIM结构框图

Stm32手册中的结构框图很重要,只要理解了外设的运行逻辑,按照逻辑一步一步给寄存器设值就可以让外设按我们的要求运行。

图片

图片

#ifndef __STEPPER_H

#define __STEPPER_H



#include "peripheral.h"

#include "math.h"



#ifdef __cplusplus

extern "C"

{

#endif



    enum DIRCTION

    {

        POS,

        INV

    };



    class Stepper

    {

    private:

        /* data */

        uint16_t TIMx_prescaler = 0;

        uint32_t TIMx_freq = 0;

        TIM_TypeDef *TIMx;

        uint32_t Channel;

        float speed;



    public:

        Stepper(TIM_TypeDef *TIMx, uint32_t Channel);

        ~Stepper();

        void init();

        void gpio_init();

        void begin();

        void stop();



        void setDirection(DIRCTION dir);

        void setFreq(uint16_t freq);

        void setSpeed(float speed);



        float getSpeed();

    };



    extern Stepper Stepper_left;

    extern Stepper Stepper_right;



#ifdef __cplusplus

}

#endif



#endif

复制

#include "Stepper.h"



Stepper Stepper_left(TIM1, LL_TIM_CHANNEL_CH1);

Stepper Stepper_right(TIM2, LL_TIM_CHANNEL_CH2);



Stepper::Stepper(TIM_TypeDef *TIMx, uint32_t Channel)

{

    this- >TIMx = TIMx;

    this- >Channel = Channel;

}



Stepper::~Stepper()

{

}



void Stepper::init()

{

    //开定时器外设时钟

    if (TIMx == TIM1)

    {

        LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

        //设置预分频器

        LL_TIM_SetPrescaler(TIMx, 90);

        TIMx_freq = 90000000;

        TIMx_prescaler = 90;

    }



    if (TIMx == TIM2)

    {

        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

        //设置预分频器

        LL_TIM_SetPrescaler(TIMx, 45);

        TIMx_freq = 45000000;

        TIMx_prescaler = 45;

    }



    //定时器选择时钟源

    LL_TIM_SetClockSource(TIMx, LL_TIM_CLOCKSOURCE_INTERNAL);



    //设置自动重载寄存器

    LL_TIM_SetAutoReload(TIMx, 2000 - 1);

    //设置计数方向

    LL_TIM_SetCounterMode(TIMx, LL_TIM_COUNTERMODE_CENTER_UP);

    //使能自动重载预装载

    LL_TIM_EnableARRPreload(TIMx);



    if (Channel == LL_TIM_CHANNEL_CH1 || Channel == LL_TIM_CHANNEL_CH1N)

    {

        //设置比较值

        LL_TIM_OC_SetCompareCH1(TIMx, 1000 - 1);

        //设置成PWM模式

        LL_TIM_OC_SetMode(TIMx, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);



        //设置捕获/比较寄存器值

        LL_TIM_OC_EnablePreload(TIMx, LL_TIM_CHANNEL_CH1);

    }



    if (Channel == LL_TIM_CHANNEL_CH2 || Channel == LL_TIM_CHANNEL_CH2N)

    {

        //设置比较值

        LL_TIM_OC_SetCompareCH2(TIMx, 1000 - 1);

        //设置成PWM模式

        LL_TIM_OC_SetMode(TIMx, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_PWM1);



        //设置捕获/比较寄存器值

        LL_TIM_OC_EnablePreload(TIMx, LL_TIM_CHANNEL_CH2);

    }


    //设置输出极性

    LL_TIM_OC_SetPolarity(TIMx, Channel, LL_TIM_OCPOLARITY_HIGH);

    //使能输出

    LL_TIM_EnableAllOutputs(TIMx);

    LL_TIM_CC_EnableChannel(TIMx, Channel);



    //GPIO初始化

    gpio_init();

}



void Stepper::gpio_init()

{

    if (TIMx == TIM1)

    {

        //开启GPIO时钟

        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);



        //GPIO选为AF

        //M1-DIR

        LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

        GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;

        GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

        GPIO_InitStruct.Pin = LL_GPIO_PIN_9;

        GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

        GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;

        LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        //M1-STEP

        GPIO_InitStruct.Alternate = LL_GPIO_AF_1;

        GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

        GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

        GPIO_InitStruct.Pin = LL_GPIO_PIN_8;

        GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

        GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;

        LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    }



    if (TIMx == TIM2)

    {

        //开启GPIO时钟

        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);

        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);



        //GPIO选为AF

        //M2-DIR

        LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

        GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;

        GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

        GPIO_InitStruct.Pin = LL_GPIO_PIN_7;

        GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

        GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;

        LL_GPIO_Init(GPIOC, &GPIO_InitStruct);   

        //M2-STEP

        GPIO_InitStruct.Alternate = LL_GPIO_AF_1;

        GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

        GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

        GPIO_InitStruct.Pin = LL_GPIO_PIN_3;

        GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

        GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;

        LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    }

}



void Stepper::begin()

{

    LL_TIM_EnableCounter(TIMx);

}



void Stepper::stop()

{

    LL_TIM_DisableCounter(TIMx);

}



void Stepper::setDirection(DIRCTION dir)

{

    if (dir == INV)

    {

        if (TIMx == TIM1)

        {

            LL_GPIO_WriteOutputPort(GPIOB, LL_GPIO_ReadOutputPort(GPIOB) & (~LL_GPIO_PIN_4));

        }

        if (TIMx == TIM2)

        {

            LL_GPIO_WriteOutputPort(GPIOB, LL_GPIO_ReadOutputPort(GPIOB) & (~LL_GPIO_PIN_10));

        }

    }

    if (dir == POS)

    {

        if (TIMx == TIM1)

        {

            LL_GPIO_WriteOutputPort(GPIOB, LL_GPIO_ReadOutputPort(GPIOB) | (LL_GPIO_PIN_4));

        }

        if (TIMx == TIM2)

        {

            LL_GPIO_WriteOutputPort(GPIOB, LL_GPIO_ReadOutputPort(GPIOB) | (LL_GPIO_PIN_10));

        }

    }

}



void Stepper::setFreq(uint16_t freq)

{

    if (freq == 0)

    {

        LL_TIM_DisableCounter(TIMx);

        return;

    }

    else if (!LL_TIM_IsEnabledCounter(TIMx))

    {

        LL_TIM_EnableCounter(TIMx);

    }



    uint32_t ARR_t = TIMx_freq / TIMx_prescaler / freq;



    if (ARR_t > 65535) //触碰上限——频率过低

    {

        TIMx_prescaler *= 10; //提高分频比

    }



    if (ARR_t < 2) //触碰下限——频率过高

    {

        TIMx_prescaler /= 10; //降低分频比

    }



    LL_TIM_SetPrescaler(TIMx, TIMx_prescaler);



    ARR_t = TIMx_freq / TIMx_prescaler / freq;



    LL_TIM_SetAutoReload(TIMx, ARR_t);



    uint16_t CCR_t = ARR_t * 0.5;



    if (Channel == LL_TIM_CHANNEL_CH1)

    {

        LL_TIM_OC_SetCompareCH1(TIMx, CCR_t);

    }

    if (Channel == LL_TIM_CHANNEL_CH2)

    {

        LL_TIM_OC_SetCompareCH2(TIMx, CCR_t);

    }

}


/**

 * @brief 设置转速

 * 

 * @param speed 转速-单位(度/秒)

 */

void Stepper::setSpeed(float speed)

{

    //判断速度方向

    DIRCTION _dir = POS;

    if (speed != abs(speed))

    {

        _dir = INV;

        speed = abs(speed);

    }

    setDirection(_dir);



    //速度限幅

    if (speed > 5000)

        speed = 5000;

    if (speed < 10)

        speed = 10;



    this- >speed = speed;



    //将速度转化为定时器频率

    uint16_t _freq = speed / 1.8;

    setFreq(_freq);

}



float Stepper::getSpeed()

{

    return speed;

}


文章来源于: 电子工程世界 原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。