用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;

}


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

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>