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

发布者:快乐旋律最新更新时间:2024-07-18 来源: elecfans关键字:TIM  PWM输出模式  步进电机 手机看文章 扫描二维码
随时随地手机看文章

这是之前写平衡小车时自己用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;

}


关键字:TIM  PWM输出模式  步进电机 引用地址:用TIM的PWM输出模式写一个步进电机的Stepper库函数

上一篇:SIMATIC S7-1500数据块介绍其应用(1)
下一篇:防爆伺服电机和防爆步进电机的区别

推荐阅读最新更新时间:2024-11-12 19:34

步进驱动器+步进电机+西门子PLC控制案例
步进电机常用来做定位控制,它可以由PLC输出的脉冲数量控制旋转的角度(相对来说可以是距离),脉冲的频率控制步进电机旋转的速度。但用于控制精度不是很高的场合,简单、经济、控制方便;对于控制精度要求很高的场合,就得使用伺服控制系统了。 步进系统=步进驱动器+步进电机。步进电机由步进驱动器来驱动,相当于驱动电源,且它受外部的脉冲信号和方向信号控制(这里举例是西门子PLC输出脉冲),进而控制步进电机的旋转角度和速度。 步进驱动器+步进电机+西门子PLC(CPU 222 CN) 相关的定义 1、驱动器:用于PLC控制步进电机的媒介,负责把PLC给的脉冲信号经过放大后,输给步进电机,使电机按照PLC和驱动器给定的参数运行。 控制过
[嵌入式]
步进驱动器+<font color='red'>步进电机</font>+西门子PLC控制案例
N76E003控制双路步进电机(开环)
0、引言 实验材料 1、程序逻辑 1.1、程序采用PWM中断,实现对PWM脉冲计数,由此实现开环控制步进电机,设定为下降沿触发。 1.2、步进电机驱动器提供EN、Pulse、DIR、COM四个接口,其中COM接单片机的VCC,EN用于控制电机使能,DIR用于电机换向,Pulse为输入脉冲。 1.3、根据上述说明,单片机PWM持续产生脉冲,通过EN控制步进电机是否使能 1.4、流程图如下 2、程序代码 #include N76E003.h #include Common.h #include Delay.h #include SFR_Macro.h #include Function_define.h #def
[单片机]
N76E003控制双路<font color='red'>步进电机</font>(开环)
STM32定时TIM2触发ADC采样,使用DMA保存结果
1.adc.h文件 //ADC-------------------------------------------------------------------------// #ifndef __EVAL_ADC_H #define __EVAL_ADC_H // Includes ------------------------------------------------------------------// #include stm32f10x.h #include eval.h // Exported types --------------------------------
[单片机]
步进电机一顿一顿是为什么_步进电机堵转检测方法
  步进电机一顿一顿是为什么   步进电机一顿一顿的情况通常是由于驱动信号的不稳定或者电机负载过大引起的。具体来说,步进电机的转动需要通过驱动器向电机提供一系列脉冲信号来控制电机转动的步长和速度。如果驱动信号不稳定,就会导致电机转动不平稳,出现一顿一顿的情况。此外,如果电机负载过大,电机就需要消耗更多的能量来转动,也会导致转动不稳定。   解决步进电机一顿一顿的问题需要从多个方面入手:   提高电机驱动信号的稳定性。可以通过检查电路连接是否牢固、电缆是否损坏、信号传输线路是否受干扰等方式,排除信号不稳定的因素。   优化电机驱动参数。电机驱动参数包括起始速度、加速度、脉冲频率等,需要根据具体情况进行优化和调整,以提高电机转速和
[嵌入式]
STM32学习笔记(3) TIM基本定时器
1.基本定时器 功能:定时,无PWM ● 计数器寄存器(TIMx_CNT) ● 预分频寄存器(TIMx_PSC) ● 自动重装载寄存器(TIMx_ARR) 2.基本定时器TIM的工作原理 来自内部时钟源的CK_PSC(频率=72MHz,72*10^6)进入到预分频器,预分频器PSC再对内部时钟CK_PSC分频,得到计数器时钟 CK_CNT = CK_PSC/(PSC+1) 当CNT_EN使能为1后,计数器CNT在CK_CNT时钟信号下,从0开始计数,电压周期性(计数一次的时间 1/CK_CNT) 的变为1/0,电压每变为1就记作是一次,当CNT的值与ARR的值相等时,就自动生成事件,且CNT自动清0,再重新
[单片机]
STM32学习笔记(3) <font color='red'>TIM</font>基本定时器
步进电机的噪音从何而来 如何使步进电机完全静音
步进电机广泛用于自动化、数字制造、医疗和光学设备等几乎所有类型的移动应用中。 步进电机的优点是成本相对较低,在不使用变速箱的情况下在静止和低速时具有高扭矩,以及对定位任务的固有适用性。与三相无刷电机和伺服驱动器相比,步进电机不一定需要复杂的控制算法或位置反馈来进行换向。 步进器的缺点是噪音很大,即使在低速或静止时也是如此。步进电机有两个主要的振动源:步进分辨率,以及斩波器和脉冲宽度调制 (PWM) 模式导致的副作用。 步进分辨率和微步 典型的步进电机有 50 个磁极,可实现 200 个完整步长,每个步距角为 1.8°,可实现 360° 的完整机械旋转。但也有步数较少的步进电机,甚至高达 800 个全步。最初,这些电机用
[嵌入式]
<font color='red'>步进电机</font>的噪音从何而来 如何使<font color='red'>步进电机</font>完全静音
单片机串口控制步进电机的源程序
#include reg52.h #include intrins.h #define uchar unsigned char #define uint unsigned int #define LED P1 //因为步进电机是减速步进电机,减速比的1/64 , //所以N=64时,步进电机主轴转一圈 //使用前请短接J2跳线帽,串口助手设置为比特率9600,无校验位,停止位1,发送数据为16进制形式 uchar code CCW ={0x08,0x0c,0x04,0x06,0x02,0x03,0x01,0x09}; //逆时钟旋转相序表 uchar code CW ={0x09,0x01,0
[单片机]
编译STM32错误之一:Error: L6218E: Undefined symbol TIM_Cmd
注意要包含头文件:#include stm32f10x_tim.h 还有要把stm32f10x_tim.c加进工程。
[单片机]
小广播
最新嵌入式文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved