0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:Vulcan Matrix 作者:Vulcan 2023-07-24 14:36 次阅读

这是之前写平衡小车时自己用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;
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5336

    浏览量

    120244
  • STM32
    +关注

    关注

    2270

    文章

    10896

    浏览量

    355767
  • 步进电机
    +关注

    关注

    150

    文章

    3109

    浏览量

    147445
  • PWM波
    +关注

    关注

    0

    文章

    99

    浏览量

    16855
  • 预分频器
    +关注

    关注

    0

    文章

    18

    浏览量

    8131
收藏 人收藏

    评论

    相关推荐

    stm32的官方库函数步进电机的那个脉冲函数吗?

    stm32的官方库函数步进电机的那个脉冲函数吗,还是说要自己编写
    发表于 07-23 06:37

    stm32比较输出模式及用于步进电机控制

    驱动器,PWM输出模式只适用于电机直转,不适合精准控制脉冲个数和精准角度以及做步进
    发表于 10-26 07:00

    Stepper电机驱动库有哪些问题?

    Stepper库,但是,结果是电机未转动,以至于我认为购买的电机出了问题,自己还测试了下,最后发现电机是正确的,是
    发表于 07-08 08:01

    Stepper库函数控制步进电机

    问题来源最近自学Arduino,在使用步进电机时开始没能使步进电机转起来,转起来后感觉没法调速,遂完成此篇笔记供自己后续查阅以及方便遇到相同问题的诸君寻找灵感。对于如何使
    发表于 09-07 09:15

    STM32F429如何使用TIM4和TIM14 的PWM波驱动步进电机

    STM32F429如何使用TIM4和TIM14 的PWM波驱动步进电机
    发表于 12-21 07:24

    PWM的两比较模式是什么

    (TIMx_CCMR1)的作用PWM输出极性是什么?PWM配置注意事项占空比怎么设置?库函数配置步骤第步:使能GPIO/AFIO/
    发表于 02-16 07:14

    STM8的PWM输出模式PWM1和PWM2的区别是什么

    STM8自学笔记:TIMPWM输出模式中关于PWM1 和 PWM2的区别STM8的
    发表于 03-02 06:07

    TIM—高级定时器输出PWM

    :TIM1->CH1CVR=30; CH1CVR表示通道1,不同通道数字不同,如CH2CVR表示通道23参数都不能超过16位,最大65535暂停PWM输出,强制拉高拉低修改
    发表于 03-08 11:35

    步进电机只抖动不转是什么问题?

    (\"ni\"); stepper.step(-1024); //4步模式下旋转2048 步。 delay(500);}使用上述代码 步进
    发表于 11-10 08:15

    开关手动控制步进电机的实现方法

    介绍了开关代替单片机对步进电机进行手动控制的原理电路,给出了步进
    发表于 04-29 13:52 23次下载

    MSP430 Stepper Motor Controller电机控制:步进电机

    MSP430 Stepper Motor Controller电机控制:步进电机
    发表于 10-12 14:50 12次下载
    MSP430 <b class='flag-5'>Stepper</b> Motor Controller<b class='flag-5'>电机</b>控制:<b class='flag-5'>步进</b><b class='flag-5'>电机</b>

    STM8TIM1配置

    基于STM8-TIM1的单通道PWM输出,互补PWM输出谨以本文记录最近使用STM8进行电机驱动
    发表于 12-03 13:51 6次下载
    STM8<b class='flag-5'>TIM</b>1配置

    PWM输出实验详细示例

    (TIMx_CCMR1)的作用PWM输出极性是什么?PWM配置注意事项占空比怎么设置?库函数配置步骤第步:使能GPIO/AFIO/
    发表于 12-20 18:58 2次下载
    <b class='flag-5'>PWM</b><b class='flag-5'>输出</b>实验详细示例

    STM32驱动步进电机梯形算法库函数

    关于梯形算法的原理查看:AVR446: Linear speed control of stepper motor 里面有原理和代码(库函数版F4)废话不多说直接上链接: 梯形算法驱动步进电机
    发表于 03-23 10:20 0次下载
    STM32驱动<b class='flag-5'>步进</b><b class='flag-5'>电机</b>梯形算法<b class='flag-5'>库函数</b>版

    PWM输出控制电机

    PWM 输出控制电机电机系列 PWM 输出控制电机
    发表于 05-06 11:03 5次下载
    <b class='flag-5'>PWM</b><b class='flag-5'>输出</b>控制<b class='flag-5'>电机</b>