步进电机控制_步进电机控制器 - CSDN
精华内容
参与话题
  • stm32步进电机控制教程

    万次阅读 2018-03-13 15:49:35
    原文:点击打开链接本教程介绍步进电机驱动和细分的工作原理,以及stm32103为主控芯片制作的一套自平衡的两轮车系统,附带原理图pcb图和源代码,有兴趣的同学一起来吧.本系统还有一些小问题,不当之处希望得到大家的指正...

    原文:点击打开链接

    本教程介绍步进电机驱动和细分的工作原理,以及stm32103为主控芯片制作的一套自平衡的两轮车系统,附带原理图pcb图和源代码,有兴趣的同学一起来吧.本系统还有一些小问题,不当之处希望得到大家的指正.

    一.混合式步进电机的结构和驱动原理

       电机原理这部分不想讲的太复杂了,拆开一台电机看看就明白了。
       电机的转子是一个永磁体, 它的上面有若干个磁极SN组成,这些磁极固定的摆放成一定角度。电机的定子是几个串联的线圈构成的磁体。 出线一般是四条线标记为A+,A-,B+,B-。A相与B相是不通的,用万用表很容易区分出来,至于各相的+-出线实际是不用考虑的,任意一相正负对调电机将反转。另外一种出线是六条线的只是在A相和B相的中间点做两条引出线别的没什么差别,六出线的电机通过中间出线到A+或A-的电流来模拟正向或负向的电流,可以在没有负相电流控制的电路中实现电机驱动,从而简化驱动电路,但是这种做法任意时刻只有半相有电流,对电机的力矩是有损失的。步进电机的转动也是电磁极与永磁极作用力的结果,只不过电磁极的极性是由驱动电路控制实现的。
       我们做这样的一个实验就可以让步进电机转动起来。1找一节电池正负随意接入到A相两端;然后断开;(记为A正向)2再将电池接入到B相两端; 然后断开;(记为B正向)3电池正负对调再次接入A相; 然后断开;(记为A负向)4保持正负对调接入B相;然后断开;(记为B负向)…如此循环你会看到步进电机在缓慢转动。注意电机的相电阻是很小的接通时近乎短路。我们将相电流的方向记录下来应该为:A+B+A-B-A+…,如果我们更换接线顺序使得相电流顺序为A+B-A-B+A+…这时我们会看到电机向反方向运动。这里每切换一次相电流电机都会转动一个很小的角度,这个角度就是电机的步距角。步距角是步进电机的一个固有参数, 一般两相电机步距角为1.8度即切换200次可以让电机转动一圈。这里我们比较正反转的电流顺序可以看出A+和A-;B+和B-的交换后的顺序和正反顺序是一致的,也就是前面所说的”任意一相正负对调电机将反转”。以上为四排工作方式,为了使相电流更加平滑另外可以使用八排的工作方式即: A+;A+B+;B+;B+A-;A-;A-B-;B-;B-A+;从前往后循环正转,从后往前循环反转。
        为了用单片机实现相电流的正负流向控制必须要有一个H桥的驱动电路,这种带H桥的驱动模块还是很多的,比较便宜的是晶体管H桥比如L298N,晶体管开关速度比较慢,无法驱动电机高速运动。有些模块将细分控制电路也包含在内,我们也不用这种,因为我们的细分由软件控制。实际应用中使用ST的mos管两桥驱动芯片L6205一片即可驱动一台步进电机。有了H桥通过PWM就可以控制相电流大小,改变输入极IN1、IN2的状态(参看手册第8页)可以控制相电流的方向。

    二.细分的原理和输出控制

        从这里开始重点了,别的地方看不到哦。 
        一个理想的步进电机电流曲线应该是相位相差90度的正弦曲线如下图:
     
        图中蓝色线时A相电流,红色线是B相电流。如果把A相正负极值视为A+A-,B相正负极值视为B+B-,比较一下四拍方式正转A+B+A-B-和反转A+B-A-B+不难看出四排方式实际上是用一个脉冲来代替一个正弦半周期,相位点从左到右变化则电机正转,从右到左电机反转。类似的我们把八拍方式A+;A+B+;B+;B+A-;A-;A-B-;B-;B-A+;放到曲线里也可以找到对应点,图中标出了各拍的相位点1,2,3…,不难看出用A+B+代替第2拍点用B+A-代替第四拍点都是近似的做法。那么这种近似和理想情况的电流的差值去哪里了呢?这些电流被无谓的消耗掉了而且多余的电流会引起电机转动的不平稳。为什么要细分呢?实际细分的终极目标就是在正弦的周期中插入若干个点使得相电流接近正弦变化,细分可以提高定位精度和电机运转的平稳性。
        由此我们抛开细分不谈,如果你能调制出两条相差为90度的正弦波形就是理想的步进电机驱动器了,调制出的正弦波形的频率就是步进电机的转速,正弦的幅值就是步进电机的转矩。这个听起来貌似不难啊,但是你不要忘了调制出的正弦是有要求的。第一要有一定的驱动能力步进电机的功率越大驱动能力要求也越大。第二要能够保持90度的相差前提下改变正弦的频率,这样才能够驱动电机按不同的转速运转,步进电机的旋转方向实际上是两条正弦波的相位点顺序。第三最好能够调幅,调整幅值能够实现电机的恒力矩输出,调幅的实际意义还不止这些后面再讲。总之一句话就是通过pwm调制输出可以调频调幅的两路固定相差的正弦波。(如果是三相步进电机应该是相差各位120度的三路正弦波,原理是一样的。)
        上面那个图和两相步进电机驱动的关系可能有些疑惑"真的是这样的吗?",我们在这里再安排一个试验。我们知道电动机和发电机是两个可逆的过程,因此我们可以用步进电机来当发电机。很简单的实验,我们把步进电机的两相引线接到双踪示波器输入上,然后找个电机带着转轴运转(我是用一个手电钻夹住电机的转轴,我的这个手钻是可以正反转的)。保持稳定的转速,你会在示波器上看到上面那个图:即两路相差固定的完美的正弦波,当转速增大时幅值和频率都有变化(线性关系),并且正转和反转时两路正弦相位位置不同,如果你能够确定转速的话你还可以验证以下周期、转速、步距角之间的关系。
        步进电机的驱动要比逆变器、伺服电机驱动复杂的地方在于需要大范围的变频,如果能做好这个步进电机的驱动器其它那两个就不成问题了,至少在波形调制上绝对没问题了,它们的基本原理是通的。
        下面我们展开正弦调制的讨论,这部分是核心的部分将占很大的篇幅,你放心我绝对不会罗列一大堆的数学式在教程里,不然怎么能叫超级无敌呢?教程超级无敌,这个stm32实现的驱动程序也是超级无敌的(吼吼)。但是“载波比、spwm、死区、单双极性”这几个词如果你觉得很陌生的话建议你还是要看看电力电子课程的相关章节基本概念还是要有的。

    三.spwm运算和输出
        Spwm的产生可以分为软件方法和硬件方法,硬件方法通过硬件产生一路三角波一路正弦波,经过一个比较器比较正弦波幅值与三角波幅值的关系即可得到spwm波。这种方法也应用于很多spwm集成芯片http://wenku.baidu.com/view/ac55b849767f5acfa1c7cd4e.html.硬件方法在波形产生上不需要软件参与,并且调频和调幅控制上都是比较简单的。硬件方法的功能和性能取决于芯片本身,对于比较复杂的应用上会受到限制。
        软件方法的思路是使得pwm波以spwm的脉宽数据变化滤波后就可以得到正弦波形,通过计算得到占空比的波形数据,按波形数据调整pwm。其实软方法和硬方法也并不是绝对的,比如ti的dsp芯片内部的spwm发生器,他的做法是在内存中存储一张正弦表,然后用一个和定时器时钟同步的计数器正负计数模拟一个三角波,每个时钟将正弦表的值与三角计数值作比较输出即得到spwm,实际上可以看成是一种半软件半硬件的做法。软件方法的优势在于成本低且更灵活,成本低不用说了,灵活性上举个例子:调制正弦波性的极性是由独立的控制位实现的(双极性),如果输出标准的正弦波形硬方法需要三角波发生器和正弦波发生器的起始点精确对齐,这在硬件电路实现上需要附带锁相环电路才能保证,而软件方法则不需要任何附加操作。现在为了改善步进电机的驱动性能,我们希望极性翻转点落后输出几个微秒,要做到这一点硬件方法改动肯定是难上难,而软件方法上只需要增加个定时滞后输出就行了。
        为了减少运算开销也可以使用查表法,把计算好的spwm数据存储在rom里,按顺序输出表中的值即可。这种方法的数据计算可以在pc机上通过matlab软件进行,将数据算好粘贴到源程序中就可以了。查表法的局限在于参数的变化和存储开销的矛盾,参数越复杂占用存储空间越大。
    (1)三角波向锯齿波的转换
       载波为三角波时输出的是一个左右不对称的pwm波形,只有这种波形能够调制出半周期对称的正弦波,这种方法称为非对称的自然采样法。其它方法(规则采样等效面积)都是为了减小计算量或不得以而采取的近似方法。非对称pwm开点与关闭点没有必然关系,必须由中央对齐的pwm模式通过一个周期的两次更新来输出。三角波可以看成是两个锯齿波的组合,因此我们可以通过锯齿波的数据来简化程序结构。我们比较下面三张图:
     
     
     
          图1是一个锯齿波幅值为1,载波比N=16,正弦幅值0.5,正弦与锯齿波相差为半个锯齿波周期;图2是图1水平翻转的结果;图3是图1和图2的叠加结果。图三中看到三角波形的spwm数据了吗?没错就这么简单,锯齿波正弦幅值比为2:1,相差半个锯齿波周期,计算出来的数据首尾组合成三角波数据。算法上就很简单了,假设数组中存放上述的锯齿波spwm数据,编号0~15共16个,依次取0,1,2,…15为三角波形开点输出数据,则反向取15,14,13,…0为三角波形关点数据即可。特别的如果载波比为奇数时三角波也为奇数,中间的数自然和自己组合的数据仍然是正确的。
          注意这里提及的方法可以把三角波形的计算转换为锯齿波,但并不能减少计算量,因为如果是偶数个三角波只要计算四分之一周期就够了其他的是对称的,而锯齿波形数据需要计算半个周期。至此我们可以使用锯齿波的方法计算按三角波的数据输出。
    (2)spwm迭代运算
          为计算spwm占空比首先要求得锯齿波斜线与正弦交点,即方程KX+B=Y与Sin(X)=Y的解。这个方程是一个超越方程,只能通过迭代的方法计算。我们将直线方程变为X=(Y-B)/K,首先任取一个X值(这个值就是迭代初值),将它带入Sin(X)求一个Y值再将Y值代入(Y-B)/K求一次X值,再将X带入Sin(X)求一个Y值…如此反复若干次后可以得到一个结果就是方程式的解,这个就叫做迭代法。迭代次数越多;迭代初值越接近结果精度越高。每一组数据计算有这样几个参数1:正弦幅值(三角幅值与之成比例)2:载波比N值即半周期中三角波个数。另外pwm的占空比即定时器的通道值是和pwm的周期值有关系的,因此为了计算定时器通道值还需要一个周期值,对于stm32f这个值就是定时器ARR寄存器的值,它决定pwm周期(或频率)。附件中有个matlab_spwm.rar,matlab下计算定时器spwm数值和绘图的小工具上面几个图就是用它画的,开始部分可以置参数
    s_M=32768/65536 %正弦波幅值比0~1
    s_N=16     %半周期三角波个数
    s_Pre=16384     %单片机定时器模数值
    执行分为三部分,计算spwm数据;将数据按周期值换算为定时器设定值;画图;
    计算定时器设定结果在TimerSetting中,复制粘贴替换tab字符成逗号就行了,下面是上述参数的计算结果:
    1780         5246         8444         11221       13461       15088       16063       16384       16075       15182       13764       11893       9645         7102         4346         1463
    (3)spwm实时运算的优化
          如前所述简单的应用查表法就可以解决了但是复杂一点的功能就不能满足要求了,比如步进电机大范围调速、不同转速下恒力矩输出、恒加速运动等等。网上有很多文章介绍自然采样法的数学方法,并给出了各种优化算法,这些算法力图精确求解三角方程与正弦方程的交点,由于运算中带有大量的浮点运算若没有dsp或高速浮点处理芯片的支持必然会造成运算时间过长对实时调控产生影响。实际上我们需要的计算精度和每载波周期可能的开关点数量有关系,此数值用C来表示,称其为控制比(下文同)数值上=载波周期/pwm周期,同步调制方式中此值为整数,可以理解为用多少个pwm周期控制一个载波周期。pwm频率实际上是开关电路的极限频率或最理想工作的频率,假设每载波周期可能的开关点数量为512个则需要二进制的9位计算精度如果再加一位存疑位最多计算10位就够了。如果采用数据类型IEEE32浮点数迭代运算将得到24位(二进制)精度的计算结果,与实际需要相差甚远,也就是说你算了半天大部分是没有意义的计算,这种计算资源浪费发生在每一次运算中,因此累计起来就比较惊人了。从另一个角度看由于pwm频率的限制有高精度的计算结果也无法实施高精度的开关控制,这么说就好理解了。对计算采取一定的优化是必须的它将直接影响系统的实时性能。一个简单的方法就是在计算有初值后确定数据变动方向逐个可能值比较,另一个方法就是去浮点迭代计算,这两个方法在单片机上实现都可行有机会再发文讨论不再详述了。
                
    四.步进电机运行控制   
        至此假设我们可以很快的在单片机上进行实时的迭代运算了。迭代计算一个半周期的spwm其输入的原始参数只和三个数值有关:
        1.M正弦的幅值这个值将决定步进电机的相电流大小,也就是步进电机的输出力矩。步进电机的优点之一是它的低速性能,当步进电机低速运转时转子始终受到磁场力的牵引转动,这个力的大小直接取决于励磁电流的大小,很小的速度下却可以用很大的力牵引转动。而直流电机的低速运动只能靠减小励磁电流实现,实际上就是小力矩实现低速,这样控制就不可能很精确特别是启停阶段尤其麻烦。步进电机在高速时力矩下降很快这个原因也不难理解,因为在步进电机励磁线圈里有多组磁极快速划过产生很大的感生电动势抵消了驱动的电压致使励磁电流变小力矩变小。为了改善高速性能解决办法只有一个提高工作电压。根据电机转速自动调整相电流的大小就可以实现恒定的力矩输出了,即低转速小幅值高转速大幅值。
    2.载波比N和控制比C,这两个参数和调制频率F的关系是:
    F*2C*2N=TF(TF是定时器的时钟频率)
    我们慢慢来解释一下这个式子,调制频率就是我们实际想要的电机转速,从上面式子可以看出要让电机速度增加有两个方法即减小C或减小N(TF也是可以变的暂不考虑);
    C实际上就是定时器的模数值(ARR),他的含义是使用几个定时器时钟周期产生一个pwm周期,前面的2是由于定时器工作在中央对齐模式下,定时器+-计数一轮产生一个完整的三角波周期。ARR的取值范围不可以太小,因为需要定时器中断来更新个通道的值,太小的数值两次更新时间过短而无法实现计算和更新步计数等操作。ARR的值如果太大则输出的pwm频率过低效果不佳。
    3. N是载波比也就是半周期的三角波的数量,他的含义是使用几个pwm周期调制出一个正弦周期,其实也就是我们常说的细分数,它决定一个正弦周期(一个步距角)内可以控制的位置点的数量。在常见的驱动器中这个数值都是由拨码开关事先设定的,工作中是一个固定值,原因是硬件电路无缝的调整细分度几乎是不可能的。软件运算则没有这个问题,N的取值可以是任意的,唯一受影响的就是极性控制,上面算式里N前面的2含义是正弦正半周期和负半周期。N的取值还要考虑内存和计算占用;迭代算法如果有接近结果的初始值将使得运算效率大幅提高,因此对于有初始值的运算每一个计算点都要有存储空间占用,过大的N值要考虑内存资源,如果无初值的计算则要考虑计算资源。特别的当N值变化时初值会与真实值有差距,所以应尽量减少N的变动。
        和步进电机转速有关系的参量在运行时都是已知的,因此任意时刻电机的转速都是可以计算的,如果电机能够平稳运行(没有堵转或丢步的情况)是不需要其它测速码盘装置的,闭环控制就更加没必要了。话又说回来如果丢步或堵转了闭环能解决吗?
         
    五.步值计数产生AB极性逻辑和正反转

        网上看到的步进电机驱动程序千篇一律的都是数组存储io状态查表输出,带细分更少。先来梳理一下目前已经现在做到的内容,内存中有一个数组存放整个正弦半周期的实时运算的spwm数据,这个数据是根据当前的pwm周期折算过,因此每个pwm周期依次将数组内容赋值给定时器通道值就可以在定时器通道管脚输出正弦变化的pwm了。另外使用一个(l6205是两个,也可以用非门)io口来控制极性输出,比如高电平输出正弦负半周,低电平输出正弦正半周。
        接下来需要安排一个合理而简单的数据结构把步进计数、细分和极性控制合为一体。首先我们用一个s32 stepcounter全局量来做步进计数,它的数值与步进电机的实时位置对应,这个变量是一个很关键的变量,因为任意时刻的AB两相spwm数据输出点和极性控制信号都由它产生。假设我们把它的低八位视为细分步计数(256为最大细分),则这个计步值除256对应整步位置。另外安排一个u8 microstep用来控制细分步进,它的取值和当前的细分度有关,如果256细分则microstep=1,128细分microstep=2,以此类推.如果电机正转前进一个微步则stepcounter+=microstep,如果反转一个微步则stepcounter-=microstep(微步进这部分可以放到中断程序里),OK正反转很简单,微步前进自动更新整步。关键点在于如何使用这个计数值产生两个相位的极性信号输出控制和A相B相的spwm数据位置,这里解释一下为什么会希望控制都由这一个变量产生:因为这样的程序最简单,虽然这里讲一大堆但是在编程实现时你就看到了就几行搞定;不容易出错,效率最高,你可以想象的到如果涉及的变量越多操作的代码越多需要考虑的可能性越多也越容易错;便于封装和功能扩展,比如你想做一个AD采样值与电机位置按一个比例同步的程序即转滑阻电机跟着动的小玩意儿,稍微改改把AD采样值赋给计步值其它都不用管了。
        先说第一个数据的输出,spwm数组256个,如果不考虑极性则数据位置只和stepcounter的低8位有关,因此A相数据用stepcounter的低8位作指针从数组取数就可以(A相0值点为计步0值点),B相与A相相差为90度,所以A=0,1,2,。。。255,0 。。。则B=128,129,130,。。。127,128。。。能看出来吗?(A的数据指针+128)%256等同于 A的数据指针^128就是B的数据指针。
    程序上这样写:
    A通道值=spwm数组[(u8)stepcounter];
    B通道值=spwm数组[(u8)stepcounter^128];
    这里数组大小是256,所以一个逻辑异或就解决,如果你非要取大小是100个的话你就得(point+50)%100才能找到B相位点了。
        第二个是极性控制,假定初始时A=0,B=0,C8=0,C7=0,A=0 B=0表示A相B相正半周的极性,C8是stepcounter第8位的值(最低位为0),之所以为8是前面提到过的条件即视低8位为微步,C7第7位,现在stepcounter计数到0和256,A相应该翻转一次,计数到128和128+256,B相翻转一次,由此列出真值表把逻辑关系提取出来:
    C8 C7 A B counter值
    0  0  0 0 0
    0  1  0 1 128
    1  0  1 1 256
    1  1  1 0 256+128
    0  0  0 0 512
    A相的逻辑和C8相同,B相的逻辑能看出来吗?B相逻辑是C8异或C7。程序上很简单,每次stepcounter值变化时执行:
    A相IO控制位值=setpcounter第7位值;
    B相IO控制位值=A相IO控制位值^setpcounter第8位值;
    两句极性就被更新了,不管正转、反转极性输出总是对的,这里结合stm32的位带操作更好。上述内容对细分度为256,128,64...时成立,如果N不为2整数幂则会有余数个错位,N取值(即细分度)很小时影响比较大,但不会产生极性错误。
    展开全文
  • 步进电机控制和步进电机原理

    千次阅读 2017-11-20 20:49:33
    今天调试了一个步进电机,原来调试过一款欧柯达的42步进电机, 本以为两个电机差不多,驱动器给的一样,结果发现死活不能用自己百度了一下,做一个笔记 例如这个电机 42BYGH403AA  42 为机座尺寸, BYG是指混合式...

    今天调试了一个惠斯通的步进电机,原来调试过一款欧柯达的42步进电机, 本以为两个电机差不多,驱动器给的一样,结果发现死活不能动。用自己百度了一下,做一个笔记

    例如这个电机 42BYGH403AA

     42 为机座尺寸, BYGH是指混合式步进电机(混合式步进电机是综合了永磁式和反应式的优点而设计的步进电机。它又分为两相、三相和五相,两相步进角一般为1.8度 ,三相步进角 一 般为 1.2度 ,而五相步进角 一 般为0.72度。
    混合式步进电机的转子本身具有磁性,因此在同样的定子电流下产生的转矩要大于反应式步进电机,且其步距角通常也较小,因此,经济型数控机床一般需用混合式步进电机驱动。但混合转子的结构较复杂、转子惯量大,其快速性要低于反应式步进电机)

    这个图像就能看出电机的具体的尺寸标准 绕组是星型步距角1.8度,H就是混合式,法兰是异性法兰 

      步进电机还有带行星减速的尺寸,在最后我们可以看到一个

    比如这个

    OK42STH47-168AG3.71

    ok:是产品名,欧柯达42是尺寸其他的看不懂,3.71就是减速比


    最后一句话直接买电机对应的驱动器,这个市场根本没有统一的标准,杂乱无章,还是直接买一套直接用好,这一会调好了,一般工频10k左右吧程序发给大家参考一下

    #include "sydj.h"
    #include "gpio.h"
    #include "stmflash.h"
    #include "usart.h"
    
    
     u8 DJ_Buffer_temp[4];
     u8 datatemp[4];
    #define FLASH_SAVE_ADDR  0X08070000 	//设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
    
    int32_t DJ_WEIZHI ;
    u8 End_flage=0;
    u8 Dj_turn=0;
    u32 Dj_step=0;
    u16 Dj_speed=0;
    u16 Dj_set=0;
    
      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
    void TIM1_Init(u16 arr ,u16 psc)
    {
    
    	
    
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //时钟使能
    
    	//定时器TIM3初始化
    	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
    	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
     
    	TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
    	
    	TIM_Cmd(TIM1, ENABLE); 
    	//中断优先级NVIC设置
    	NVIC_InitStructure.NVIC_IRQChannel =TIM1_UP_IRQn;  //TIM7中断
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//先占优先级0级
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级3级
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
    
     //使能TIMx					
    }
    
    void Sydj_move(u8 turn, u16 speed,u32 steep)
    {
    	  
    	  Dj_step=steep;
    	  Dj_speed=speed;
        Dj_set=speed+500;
       	Dj_turn=turn;
    		SYDJ_DR=turn;
    	  TIM1_Init(71,speed);
    		while(End_flage==0);
    
    }
    
    
    void TIM1_UP_IRQHandler()
    {
    	static u8 dj_flag=0;
    	       u8 jiyi_flag=0;
    	if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
    	{
    		dj_flag=~dj_flag;
    	  if(dj_flag!=0)
    		{
    			SYDJ_DR=Dj_turn;
    			SYDJ_PR=1;
    		  
    		}	
        else 
    		{
    			SYDJ_DR=Dj_turn;
    			SYDJ_PR=0;
    		}
    		jiyi_flag++;
    		Dj_step--;
    //		if(jiyi_flag==50)
    //		{
    //			jiyi_flag=0;
    //			 STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,4);
    //			if(Dj_turn)
    //			{
    //			 DJ_WEIZHI=(int32_t)(datatemp[3]<<24|datatemp[2]<<16|datatemp[3]<<8|datatemp[0]);
    //			  printf("%d\n",DJ_WEIZHI);
    //				DJ_WEIZHI+=1;
    //			}
    //			else
    //			{
    //			 DJ_WEIZHI=(int32_t)(datatemp[3]<<24|datatemp[2]<<16|datatemp[3]<<8|datatemp[0]);
    //			 DJ_WEIZHI-=1;
    //			
    //			}
    //			DJ_Buffer_temp[3]=DJ_WEIZHI>>24;
    //			DJ_Buffer_temp[2]=DJ_WEIZHI>>16;
    //			DJ_Buffer_temp[1]=DJ_WEIZHI>>8;
    //			DJ_Buffer_temp[0]=DJ_WEIZHI;			
    //			 STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)DJ_Buffer_temp,4);
    //			 
    //		}
    	if(Dj_step>450)
    	{
        if(Dj_speed>150)
     		{
    	    Dj_speed=Dj_speed-1;
    			printf("%d\n",Dj_speed);
    			TIM1->PSC=Dj_speed;
    			
    		}
    		else 
    			TIM1->PSC=150;	
    	 }
        else if(Dj_step<=450)
    		{  Dj_speed+=1;
    				printf("%d\n",Dj_speed);
    			TIM1->PSC=Dj_speed;
    			if(Dj_step==0)//
    			{
    				TIM_Cmd(TIM1,DISABLE );
    				End_flage=1;
    			}
    		}			
    		
    			TIM_ClearITPendingBit(TIM1, TIM_IT_Update);	
    	}
    	
    }
    
    





    展开全文
  • 利用STM32F103精确控制步进电机

    万次阅读 多人点赞 2020-08-18 20:36:08
    利用STM32F103控制步进电机精确角度转动 ** 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本...

    利用STM32F103精确控制步进电机

    一、用到的元器件

    STM32F103C8T6
    42步进电机(42SHDC4040-17B)
    TB6600电机驱动器升级版

    二、42步进电机

    1.步进电机的基本知识

    (1)拍数——每一次循环所包含的通电状态数(电机转过一个齿距角所需脉冲数)
    (2)单拍制分配方式——状态数=相数
    (3)双拍制分配方式——状态数=相数的两倍
    (4)步距角 ——步进机通过一个电脉冲转子转过的角度
    计算公式

    • N:一个周期的运行拍数
      Zr:转子齿数
      拍数:N=km
      m:相数
      k=1单拍制
      k=2双拍制

    (5)转速
    转速
    (6)角度细分的原理

    • 电磁力的大小与绕组通电电流的大小有关。
    • 当通电相的电流并不马上升到位,而断电相的电流也非立即降为0时,它们所产生的磁场合力,会使转子有一个新的平衡位置,这个新的平衡位置是在原来的步距角范围内。

    2. 42步进电机参数

    无法查找到42SHDC4040-17B型号的详细资料,以通用42步进电机为例:
    步距角 1.8°
    步距角精度 ±5%
    相数 2相
    励磁方式 混合式
    转子齿数 50
    拍制 双拍制
    其他参数:无
    由步距角=1.8°推算出转子齿数为50,拍制为双拍制

    3. 42步进电机速度与角度控制

    电机的转速与脉冲频率成正比,电机转过的角度与脉冲数成正比。所以控制脉冲数和脉冲频率就可以精确调速。
    理论上步进电机转速 = 频率 * 60 /((360/T)*x)
    w=f60(360°T×x) w= \frac{f*60}{(\frac {360°}{T}\times x)}

    • 转速单位: 转/ 分
    • 频率单位:赫兹
    • x 细分倍数
    • T 步距角
      例如,在本实验中,32细分;频率72000 赫兹;步距角1.8°;套用公式7200060((360/1.8)32)=112.5\frac{72000*60}{((360/1.8)*32)}=112.5rad/ min,即1.875 rad/s.
      42步进电机结构图42步进电机结构图

    三、TB6600电机驱动器升级版参数

    TB6600步进电机驱动器升级版是一款专业的两相步进电机驱动,可实现正反转控制。通过S1,S2,S3 3位拨码开关选择7档细分控制(1,2/A,2/B,4,8,16,32,),通过S4,S5,S6 3位拨码开关选择8 档电流控制(0.5A,1A,1.5A,2A,2.5A,2.8A,3.0A,3.5A)。适合驱动57,42 型两相、四相混合式步进电机。

    1.信号输入端

    PUL+:脉冲信号输入正。( CP+ )
    PUL-:脉冲信号输入负。( CP- )
    DIR+:电机正、反转控制正。
    DIR-:电机正、反转控制负。
    EN+:电机脱机控制正。
    EN-:电机脱机控制负。

    • 共阳极接法:分别将PUL+,DIR+,EN+连接到控制系统的电源上, 如果此电源是+5V则可直接接入,如果此电源大于+5V,则须外部另加限流电阻R,保证给驱动器内部光藕提供8—15mA 的驱动电流。
    • 共阴极接法:分别将 PUL-,DIR-,EN-连接到控制系统的地端;脉冲输入信号通过PUL+接入,方向信号通过DIR+接入,使能信号通过EN+接入。若需限流电阻,限流电阻R的接法取值与共阳极接法相同。
    • 注:EN端可不接,EN有效时电机转子处于自由状态(脱机状态),这时可以手动转动电机转轴。

    2.电机绕组连接

    A+:连接电机绕组A+相。
    A-:连接电机绕组A-相。
    B+:连接电机绕组B+相。
    B-:连接电机绕组B-相。

    3.电源电压连接

    VCC:电源正端“+”
    GND:电源负端“-”
    注意:DC直流范围:9-32V。不可以超过此范围,否则会无法正常工作甚至损坏驱动器.

    4.拨码开关

    Micro step 脉冲/转 S1 S2 S3
    NC NC ON ON ON
    1 200 ON ON OFF
    2/A 400 ON OFF ON
    2/B 400 OFF ON ON
    4 800 ON OFF OFF
    8 1600 OFF ON OFF
    16 3200 OFF OFF ON
    32 6400 OFF OFF OFF

    电流大小设定

    Current(A) S4 S5 S6
    0.5 ON ON ON
    1.0 ON OFF ON
    1.5 ON ON OFF
    2.0 ON OFF OFF
    2.5 OFF ON ON
    2.8 OFF OFF ON
    3.0 OFF ON OFF
    3.5 OFF OFF OFF

    三、STM32F103

    ------说明:引脚部分在文章末尾有解释--------

    1.引脚连接

    A 0——PUL+
    A 3——KEY1——V3
    A12——DIR+
    A11——EAN+
    GND——EAN- ——KEY0

    2.引脚功能

    A0控制电机转速
    A3控制按键
    A9
    A11
    A11控制电机是否为锁死状态
    A12控制电机正反转

    3.定时器

    1.本实验利用定时器TIM2和定时器TIM3构造一个主从定时器,TIM2作为主定时器控制电机的转速,TIM3作为从定时器控制电机的转动角度。
    2.电机的转速和转角还与驱动器自身的细分数有关,但是驱动器细分数是通过影响电机的步距角来影响转速和转角,而TIM2和TIM3是控制步进电机的频率和脉冲数来控制转速转角
    3.电机的转速和角度与定时器的关系(在不考虑电机自身的细分数下)

    设TIM2的定时周期(即重装值)为nPDTemp2,预分频值为OCPolarity2
     TIM3的定时周期(即重装值)为nPDTemp3,预分频值为OCPolarity3,
    则单片机产生一个脉冲所需要的时间为:
    T=(nPDTemp2+1)72MHzOCPolarity2+1T= \frac{(nPDTemp2+1)}{ \frac{72MHz}{OCPolarity2+1}}

      本实验中设TIM2的定时周期nPDTemp2=72000/5000-1,预分频值OCPolarity2=999,TIM3的定时周期nPDTemp3=6399,预分频值OCPolarity3为0。即T=72000500072×106999+1=0.0002sT= \frac{\frac{72000}{5000}}{ \frac{72\times 10^6}{999+1}}=0.0002s
      定时器共产生nPDTemp3+1=6400个脉冲,电机转过的角度为6400*1.8°=11520°,即电机转了32圈。
      转动速度
    w=1.8°/360°T=25(rad/s)w = \frac{1.8°/360°}{T}=25 (rad/s)
    4.在32细分的情况下电机1rad/s和转1°需要的重装值为nPDTemp2=11.25,nPDTemp3=17.7778。

    四、程序实现

    1.main.c程序

    #include "main.h"
    #include "sys.h"
    #include "usart1.h"
    #include "delay.h"
    #include "math.h"
    
    u16 t;  
    u16 len;	            //接收到的数据长度
    u16 times=0;
    char receive_data[60];	//接收到的字符 
    char state;				//电机正反转标志位0正转,1反转
    int speed,angle;		//旋转速度,角度
    int a=0;        		//判断是否接收到数据
    int r,data=0;  			//用于数据转换
    int type;				//转换后的数据    
    extern u16 USART_RX_STA;
    
     /**************************
     * 函数名:delay
     * 描述  :延时函数
     * 输入  :无 
     * 输出  :无
     * 返回值:无
    ****************************/
     void delay()//延时
     {
    	 int i,j;
    	 for(i=0;i<2000;i++)
    	 for(j=0;j<1000;j++);
     }
    
     /**************************
     * 函数名:Waiting_receptio
     * 描述  :等待串口接收数据
     * 输入  :无 
     * 输出  :无
     * 返回值:无
    ****************************/
    void Waiting_reception(void)
    {
    	while(a==0)//等待数据发送完成
    	{
    		delay_ms(100);
    		if(a==1)//接收到数据
    		{
    			if(USART_RX_STA&0x8000)//(串口接收用到了正点原子的例程)
    			{
    				len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
    				r=len;				
    				type=0;
    				for(t=0;t<len;t++)//拷贝数据,将字符转换为十进制数
    				{
    					receive_data[t]=USART_RX_BUF[t];
    					data=(int)receive_data[t]-48;
    					r=r-1;
    					type=type+data*(pow(10,r));
    				}
    				USART_RX_STA=0;
    				a=0;
    				delay_ms(500);
    				break;
    			}
    		}
    	}
    }
    /**************************
     * 函数名:KeyStates
     * 描述  :监测按键状态
     * 输入  :无 
     * 输出  :无
     * 返回值:0/1
    ****************************/
    u8 KeyStates()//按键状态
    {
    	static u8 i = 0;
    	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0)
    	{
    		delay();
    		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0)
    			while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0);
    			i = ~i;
    	}
    	return i;
    }
    /****************
     * 函数名:main
     * 描述  :主函数
     * 输入  :无 
     * 输出  :无
    ******************/
    int main(void)
    {
    	NVIC_Configuration();			//中断初始化
    	GPIO_Config();					//IO口初始化
    	USART1_Config();				//串口初始化
        delay_init();	    	   	    //延时函数初始化	
    	GPIO_ResetBits(GPIOA, GPIO_Pin_11);//A11置零  A11——EAN+
    	GPIO_ResetBits(GPIOA, GPIO_Pin_12);//A12置零  A12——DIR+
    	while(1)
    	{
    		delay_ms(100);
    		Initial_state:		printf("\r\n 请选择正反转,正转输入0,反转输入1 (以新行作为结束标志)\r\n");	
    		Waiting_reception();
    		state=type;//将接收到的数据给type
    		if(type==0)//电机正转
    		{
    			GPIO_SetBits(GPIOA, GPIO_Pin_12);//电机正转
    			printf("\r\n 电机为正转模式,请输入旋转速度(rad/s),输入0返回初始模式 \r\n");
    			
    /*********************************************此模块用于配置速度参数********************************************************/
    
    			part1:Waiting_reception();
    			speed =type;//将接收到的数据给speed
    			if(speed==0)goto Initial_state;//如果是0则返回初始模式
    				else{
    					if(speed>=15)
    					{
    						printf("\r\n 旋转速度>15rad/s,请重新选择旋转速度。\r\n");
    						goto part1;
    					}
    						else printf("\r\n 电机旋转速度为%d rad/s,请输入旋转角度,输入0返回初始模式 \r\n",speed);
    				}
    	
    /*********************************************此模块用于配置角度参数********************************************************/		
    
    			Waiting_reception();
    			angle =type;//将接收到的数据给type
    			for(;;)
    			{
    				if(angle>0)//接收到的数据不是0
    				{
    					TIM2_Master__TIM3_Slave_Configuration(speed,angle);	// 配置TIM2和TIM3的重装值 ,改变电机转动速度和角度
    					delay_ms(20000);//电机保护
    					printf("\r\n 电机已旋转%d °,请输入下一次旋转角度,输入0返回初始模式; \r\n",angle);
    					angle=0;
    					Waiting_reception();		
    					angle =type;					
    				}else{
    					if(angle==0)goto Initial_state;//返回初始状态	
    					else {
    						printf("\r\n 角度错误,已返回初始模式 \r\n");		
    						goto Initial_state;
    					}						
    				}
    			}
    		}		
    /*********************************************反转模式********************************************************/		
    		else{
    			if(type==1)
    			{
    				GPIO_ResetBits(GPIOA, GPIO_Pin_12);//电机反转
    				printf("\r\n 电机为正转模式,请输入旋转速度(rad/s),输入0返回初始模式 \r\n");
    				
    /*********************************************此模块用于配置速度参数********************************************************/
    				part2:				Waiting_reception();
    				speed =type;//将接收到的数据给speed
    				if(speed==0)goto Initial_state;//如果是0则返回初始模式
    					else{
    						if(speed>=15)
    						{
    							printf("\r\n旋转速度>15rad/s,请重新选择旋转速度。\r\n");
    							goto part2;
    						}else printf("\r\n 电机旋转速度为%d rad/s,请输入旋转角度,输入0返回初始模式  \r\n",speed);
    					}	
    /*********************************************此模块用于配置角度参数********************************************************/		
    				Waiting_reception();
    				angle =type;//将接收到的数据给type
    				for(;;)
    				{
    					if(angle>0)//接收到的数据不是0
    					{
    						TIM2_Master__TIM3_Slave_Configuration(speed,angle);	// 配置TIM2和TIM3的重装值 ,改变电机转动速度和角度
    						delay_ms(20000);//电机保护
    						printf("\r\n 电机已旋转%d °,请输入下一次旋转角度,输入0返回初始模式;\r\n",angle);
    						angle=0;
    						Waiting_reception();		
    						angle =type;					
    					}else{
    						if(angle==0)goto Initial_state;//返回初始状态	
    						else {
    							printf("\r\n 角度错误,已返回初始模式 \r\n");		
    							goto Initial_state;
    						}								
    					}
    				}
    /****************************************************************************************************************************/					
    		}else{//if(a!=0)&(a!=1)
    			type=NULL;
    			printf("\r\n 输入无效 \r\n");
    			goto Initial_state;//返回初始状态
    			}
    		}
    	}
    }
    

    2.main.h程序

    #ifndef _MAIN_H
    #define	_MAIN_H
    #include <stm32f10x.h>
    #include <usart1.h>
    #include <misc.h>
    #include <nvic.h>
    #include <stdio.h>
    #include "stm32f10x_tim.h"
    #include "timer.h"
    #endif
    

    3.time.c程序

    #include "timer.h"
     /**************************
     * 函数名:GPIO_Config
     * 描述  :无
     * 输入  :无 
     * 输出  :无
     * 调用  :主函数
     * 返回值:无
    ****************************/
    void GPIO_Config(void)
    { 
    		GPIO_InitTypeDef GPIO_InitStructure; 
    		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能IOA
    		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE); //使能TIM2,TIM3
    		
    	    /* Timer2 Channel 1, PA0 */ 
    	    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0; 
    	    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用输出
    	    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    	    GPIO_Init(GPIOA, &GPIO_InitStructure);
    		 
    		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_11|GPIO_Pin_12;
    		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //通用推挽输出模式
    		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //指定GPIO引脚可输出的最高频率为50MHZ
    		GPIO_Init(GPIOA, &GPIO_InitStructure);                   //
    		GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向
    		GPIO_ResetBits(GPIOA, GPIO_Pin_2);//指定引脚输出低电平,此时灯全灭	使能
    		GPIO_SetBits(GPIOA, GPIO_Pin_11);//指定引脚输出低电平,此时灯全灭,方向
    		GPIO_SetBits(GPIOA, GPIO_Pin_12);//指定引脚输出低电平,此时灯全灭	使能
    	
    	 
    		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;         //通用推挽输出模式
    		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //指定GPIO引脚可输出的最高频率为50MHZ
    		GPIO_Init(GPIOA, &GPIO_InitStructure);                   //
    		
    		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 
    		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //通用推挽输出模式
    		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //指定GPIO引脚可输出的最高频率为50MHZ
    		GPIO_Init(GPIOB, &GPIO_InitStructure);                   //
    		GPIO_ResetBits(GPIOB, GPIO_Pin_12);//指定引脚输出低电平,此时灯全灭	使能
    		
    		//GPIO_ResetBits  GPIO_SetBits
    }
    
    //================================================================================
     /**************************
     * 函数名:TIM2_Master__TIM3_Slave_Configuration
     * 描述  :主从定时器配置
     * 输入  :电机转速speed,转角angle 
     * 输出  :无
     * 调用  :主函数
     * 返回值:无
    ****************************/
    void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency, u32 pulse) 
    {
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 
    	TIM_OCInitTypeDef TIM_OCInitStructure; 
    	
    	u16 nPDTemp ; 
    	u16 pulse_number;
    	float p=PulseFrequency;
    	TIM_Cmd(TIM2, DISABLE); 
    	nPDTemp = (11.25/p); 			   //TIM2重装值是11.25时1s转一圈(电机32细分下)
    	pulse_number = (16.7778*pulse);//TIM3重装值是16.7778时转1°(电机32细分下)
    	
    	// 时基配置:配置PWM输出定时器——TIM2 
    	/* Time base configuration */ 
    	TIM_TimeBaseStructure.TIM_Period = nPDTemp; //定时周期为nPDTemp
    	TIM_TimeBaseStructure.TIM_Prescaler = 999; //预分频值1000,即f=72khz
    	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分频因子,会影响滤波器采样频率,与本实验无影响
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //指定重复计数器值
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
    	
    	// 输出配置:配置PWM输出定时器——TIM2 
    	/* PWM1 Mode configuration: Channel1 */    
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM 脉冲宽度调制模式 1
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高电平有效
    	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
    	TIM_OCInitStructure.TIM_Pulse = nPDTemp>>1;//50% //比较tim_ccr的值,输出脉冲发生跳变
    	TIM_OC1Init(TIM2, &TIM_OCInitStructure); //初始化
    	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能 TIMx 在 CCR1 上的预装载寄存器
    	TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能 TIMx 在 ARR 上的预装载寄存器
    	
    	// 时基配置:配置脉冲计数寄存器——TIM3 
    	TIM_TimeBaseStructure.TIM_Period = pulse_number;      //0x1900是360°;//改变给电机的脉冲个数		     
    	TIM_TimeBaseStructure.TIM_Prescaler = 0; 
    	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; 
    	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 
    	// 输出配置:配置输出比较非主动模式定时器——TIM3
    	// Output Compare Active Mode configuration: Channel1 
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; //输出比较非主动模式,(匹配时设置输出引脚为无效	电平,当计数值为比较/捕获寄存器值相同时,强制输出为低电平)      
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
    	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
    	TIM_OCInitStructure.TIM_Pulse = 0xFFFF; // 这里的配置值意义不大   
    	TIM_OC1Init(TIM3, &TIM_OCInitStructure); 
    
    	// 配置TIM2为主定时器 
    	// Select the Master Slave Mode 
    	TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //设置 TIM2 主/从模式并使能
    	// Master Mode selection  
    	TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //使用更新事件作为触发输出
    	
    	// 配置TIM3为从定时器 
    	// Slave Mode selection: TIM3 
    	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated); //选择 TIM3为从模式   TIM_SlaveMode_Gated-当触发信号(TRGI)为高电平时计数器时钟使能
    	TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1); //选择 TIM3 输入触发源    TIM_TS_ITR1-TIM 内部触发 1
    	TIM_ITRxExternalClockConfig(TIM3, TIM_TS_ITR1);//设置 TIM3 内部触发为外部时钟模式   TIM_TS_ITR1-TIM 内部触发 1
    	TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能TIM3     TIM 捕获/比较 1 中断源 	 
    	
    	TIM_Cmd(TIM2, ENABLE); 
    	TIM_Cmd(TIM3, ENABLE); 
    } 
    
     /****************************************************
     * 函数名:Output_Pulse
     * 描述  :无
     * 输入  :无 
     * 输出  :无
     * 返回值:无
    ******************************************************/
    void Output_Pulse(u16 Num)
    {
    		GPIO_ResetBits(GPIOA, GPIO_Pin_2);//指定引脚输出低电平,此时灯全灭	使能
    		TIM3->CCR1 = Num; 
    		TIM3->CNT = 0; 
    		TIM_Cmd(TIM3, ENABLE); 
    		TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); 
    		TIM_Cmd(TIM2, ENABLE); 
    }
    
     /****************************************************
     * 函数名:angle_set
     * 描述  :无
     * 输入  :无 
     * 输出  :无
     * 返回值:无
    ******************************************************/
    void angle_set(u8 dir,u8 angle)
    {
    	if(dir==0)
    				GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向
    	else
    				GPIO_SetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向
    	
    	Output_Pulse(angle*6400);
    }
    
    

    4.time.h程序

    #ifndef __TIMER_H
    #define	__TIMER_H
    #include "main.h"
    extern unsigned char Flag;
    extern unsigned char TIM2_Pulse_TIM3_Counter_OK;
    void GPIO_Config(void);
    void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency,u32 pulse);
    void Frequence_Setting(u32 PulseFrequency);
    void Output_Pulse(u16 Num);
    void angle_set(u8 dir,u8 angle);
    #endif
    

    5.usart1.c程序

    #include <main.h>
    #include <usart1.h>
    #include <string.h>
    #include <math.h>
    
    /******************************************************
     * 函数名:USART1_Config
     * 描述  :USART1 GPIO 配置,工作模式配置
     * 输入  :无
     * 输出  : 无
     * 调用  :外部调用
    ***************************************************** */
    void USART1_Config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    	
    	/* config USART1 clock */
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    	/* USART1 GPIO config */
    	/* Configure USART1 Tx (PA.09) as alternate function push-pull */
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);    
    	/* Configure USART1 Rx (PA.10) as input floating */
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	/* USART1 mode config */
    	USART_InitStructure.USART_BaudRate = 115200;
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;
    	USART_InitStructure.USART_Parity = USART_Parity_No ;
    	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    	USART_Init(USART1, &USART_InitStructure);
    	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    	
    	USART_Cmd(USART1, ENABLE);
    }
    
    /******************************************************
     * 函数名:fputc
     * 描述  :重定向c库函数printf到USART1
     * 输入  :无
     * 输出  :无
     * 调用  :由printf调用
    ***************************************************** */
    int fputc(int ch, FILE *f)
    {
    	/* 将Printf内容发往串口 */
    	USART_SendData(USART1, (unsigned char) ch);
    	while (!(USART1->SR & USART_FLAG_TXE));
    	
    	return (ch);
    }
    
    /*-------------------------------------------------------------------------------*/
    /******************************************************
     * 函数名:USART1_IRQHandler
     * 描述  :USART1中断服务函数
     * 输入  :无
     * 输出  :无
     * 调用  :中断调用
    ***************************************************** */
    u8 Res;
    extern int a;
    u16 USART_RX_STA=0;       //接收状态标记	 
    u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    void USART1_IRQHandler(void)                	//串口1中断服务程序
    	{
    		
    		Res =USART_ReceiveData(USART1);	//读取接收到的数据
    		
    		if((USART_RX_STA&0x8000)==0)//接收未完成
    			{
    			if(USART_RX_STA&0x4000)//接收到了0x0d
    				{
    				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
    				else USART_RX_STA|=0x8000;	//接收完成了 
    				}
    			else //还没收到0X0D
    				{	
    				if(Res==0x0d)USART_RX_STA|=0x4000;
    				else
    					{
    					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
    					USART_RX_STA++;
    					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
    					}		 
    				}
    			}		
    				a=1;	 
    	} 
    #if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    	OSIntExit();  											 
    #endif
    
    /******************* (C) COPYRIGHT 2012 WildFire Team *****END OF FILE************/
    

    6.usart1.h程序

    #ifndef __USART1_H
    #define	__USART1_H
    #include <main.h>
    #include <stdio.h>
    
    #define USART_REC_LEN  			200  	//定义最大接收字节数 200
    #define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
    extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
    extern u16 USART_RX_STA;         		//接收状态标记
    int simple_atoi(char *source);
    void USART1_Config(void);
    #endif /* __USART1_H */
    

    未完,待续…
    ——————————————————————————
    源代码:
    链接:https://pan.baidu.com/s/1PkmxkoeHfSfX06Gnj4Dr3A
    提取码:eas7

    ——————————————————————————
    2020.8.15更新
    1.程序跑飞问题已解决,有时间了整理一下,再传上来
    2.引脚问题
    此次开发板是一个引脚比较少的板子,不同开发板可能拥有的引脚不一样,但是相同名字的芯片引脚功能是一样的,具体可以查一下f103芯片手册。
    (1)KEY0和KEY1本实验中没用到,不用管,是外界的一个按键
    (2)A 0 、 A3 、A11 、A 12 是普通的IO口
    (3)A 3—3.3V这一接法是严重有问题的(好像知道为啥当时外接的按键老是冒火花了)
    (4)PUL- DIR-不接引脚也可以(这里要打个问号,记得是这样,不敢百分百确定)
    (5)串口引脚在USART1.C程序里可以查,两个,RX TX,这里需要大家对着自己的板子检查一下看有没有程序里的这两个引脚,没有换个口,并改一下程序

    所以 可以把引脚修改为
    A 0——PUL+
    A12——DIR+
    A11——EAN+
    GND——EAN-

    引脚功能
    A0控制电机转速
    A3控制按键
    A11控制电机是否为锁死状态
    A12控制电机正反转

    f103手册
    链接:https://pan.baidu.com/s/1VkVxkj2XKzbpsjQd_UibJw
    提取码:kx14

    展开全文
  • 步进电机的细分控制原理

    万次阅读 多人点赞 2017-10-21 11:24:07
    看完第一篇文章应该对步进电机的结构有所了解了,下面将详细介绍一下步进电机的细分控制。  无论什么样的电机,控制方法和其结构是息息相关的。通过上一篇文章之后,我们知道按一定的顺序给步进电机的各相轮流通电...
    步进电机的细分控制。
       无论什么样的电机,控制方法和其结构是息息相关的。通过上一篇文章之后,我们知道按一定的顺序给步进电机的各相轮流通电,这样步进电机才能转动起来。这就是控制电机应该知道的第一个知识点:电机运行时,各相通电需要满足一定的时序。第二个知识点就是力矩的概念,电机力矩的大小和绕组数量、通电电流大小有关。
       那么什么是细分控制呢?细分控制的原理又是什么呢?步进电机之所以能实现步进就是因为在硬件结构上做了拆分(定子上有不同的通电相,转子上有齿),使其一次转动不是一圈,而是一步一步的按固定的角度转动。这一步所转过的角度就是步距角。步距角是步进电机的固有属性,每一个步进电机的步距角在设计完成之后就是固定的。步距角和电机运行的拍数以及转子齿数有关,θ=360/NZ(2相电机的计公式,本文例子全部以两相电机为例),N是拍数(一般可以通过线数来确定),Z是转子的齿数。细分控制是指对步距角再进行详细的分步控制。例如,对一个步距角为1.8°的两相四拍电机进行四细分控制,就是使得电机转动一步是1.8除以4,也就是0.45°来运转。

      对于步进电机来说细分功能完全是由外部驱动电路精确控制电机的相电流产生的,和具体电机无关。下面通过对比图来详细说明一下。



    这个是步进电机在整步运行时的电流图。图里展示了ab相电流的加载时序A->B->A’->B’->A。


    这个是步进电机在4细分时的合成电流图。90°平均分成了四份,但是合成电流的大小一直保持不变。

        用一句话总结就是:通过等角度有规律的插入大小相等的电流合成向量,从而减小合成磁势的角度(步距角),从而达到细分的目的。

        这里保持合成电流的大小相等就是为了保证合成力矩的大小相等,合成力矩相等,电机的运行步数增加,从而实现电机运行稳定性的提高。这里有点类似于用微分发求不规则图形的面积。那是不是说我无限细分就能做到步进电机运行的完美无缺呢?理论上是这样的,实际上细分数和电机本身的硬件条件是有关的,电机转子上的齿数不可能无限多,通电相数也是有限的。只能说细分数越高的电机,控制就能越精确。

        接着我们来具体分析这几个关键词。第一个就是等角度,等角度就是将步距角平均分成几份。4细分就是平均分成4份,我们同样以2相4拍步进电机为例,它的步距角是1.8°,4细分之后它运行一步就是0.45°。第二个是有规律,有规律的意思是每加一步,运行过的角度就增加0.45度。第三个是大小相等的电流合成向量。我们把ia当成x轴ib当成y轴来看,合成电流的大小不变,

    那么ia=i0*cosθ,ib=i0*sinθ(参考图中添加的虚线)。i0是合成电流的大小,这个合成电流的大小和电机的力矩大小有关。这里我们理解的时候可以简单把i0看成是固定的已知量。

    合成电流2的夹角为θ+θ,合成电流3的夹角为θ+θ+θ。这样通过公式结合时序我们就知道a相在什么时间通多大电流,b相在什么时间通多大电流才能保证步进电机按我们设计好的细分数正常工作。

    转载:
    http://blog.csdn.net/jx_zhipeng/article/details/62217472

    展开全文
  • 单片机控制步进电机

    万次阅读 多人点赞 2019-06-06 19:07:38
    用单片机控制步进电机正转 反转 加速 减速;由LCD1602实时显示步进电机的状态;F-正转,B-反转;数字越大,转速越大; 仿真原理图如下: MCU和LCD1602显示模块: ULN2803驱动和步进电机模块: C语言代码如下...
  • 步进电机控制方法

    2020-07-21 09:58:50
    详细讲解步进电机控制方法,包括设计原理图,程序代码
  • 步进电机控制程序

    2020-07-27 23:32:35
    STM32 C语言编程,控制步进电机转动角度。 程序详细描述配置过程。
  • 步进电机控制

    千次阅读 多人点赞 2016-10-02 15:44:09
    首先,什么是步进电机: 然后是步进电机的分类: 这里重点讲一下二相和四相电机: 两相八线步进电机有两种接法: 我们怎么让步进电机动起来: 然后我们讲单片机控制方式: 一般的小功率步进电机,用...
  • 基于STM32F4单片机对步进电机控制(有代码)

    万次阅读 多人点赞 2019-01-05 11:13:08
    步进电机是将电脉冲控制信号转变为角位移或线位移的一种常用的数字控制执行元件,又称为脉冲电机。在驱动电源的作用下,步进电机受到脉冲的控制,其转子的角位移量和速度严格地与输入脉冲的数量和脉冲频率成正比。...
  • 基于STC89C51和L298N的42步进电机控制

    万次阅读 多人点赞 2020-06-19 09:17:19
    步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件。电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响,即给电机加一个脉冲信号,电机则转过一个步距角。步进电机必须由双环形...
  • 一、静态指标术语 二、动态指标术语 三、步进电机加减速过程控制技术 四、步进电机的细分驱动控制 五、步进电机控制策略
  • 步进电机失步(丢步)的原因和对策步进电机可以根据脉冲数和脉冲频率来对电机实现开环控制位置和速度,是一种便宜、简单好用的控制类电机,在自动化控制领域得到越来越广泛的应用。但由于步进电机不是闭环控制,选型...
  • PWM脉冲数控制步进电机

    万次阅读 多人点赞 2017-08-07 10:19:42
    说起步进电机,我们就聊一聊步进电机的那些事,我想起在这之前 还用了L298N来驱动电机,那真是一个悲惨的故事,过程就不说出了让大家开心开心了。故事结果就是步进电机理都不理我,就是不转。 可见驱动的适合很重要...
  • 步进电机控制系统的Simulink仿真模型

    千次阅读 热门讨论 2019-01-17 18:22:40
    电机模型如下: 双击示波器:
  • 树莓派通过GPIO控制步进电机(python)

    万次阅读 2015-01-03 11:00:56
    树莓派不建议直接使用舵机,使用步进电机步进电机驱动板更为方便易用
  • Arduino(2560)控制两个步进电机通过控制

    万次阅读 多人点赞 2018-05-01 14:41:33
    Arduino 2560控制两个步进电机用arduino和步进电机控制器驱动两个步进电机,具体的接线过程和怎样连接都在图片上。准备工具:两个步进电机(我用的是J42):Arduino板(我用的是2560):步进电机控制器:电源适配器:...
  • 单片机控制步进电机-电路连接

    千次阅读 多人点赞 2019-02-22 21:26:14
    单片机控制步进电机-线路连接 说明:如何利用单片机去控制步进电机?本案例讲解的内容是硬件连接部分,采用常用的电子器件去实现单片机控制步进电机的功能。后续会分别讲解单片机程序,S曲线生成方法,上位机等...
  • Arduino对57步进电机控制

    万次阅读 2017-04-16 00:05:15
    最近实验室的智能车从大车路线改到了小车路线,于是笔者也跟着学习一些新的知识。大车的控制可以使用标准的CAN协议,很多方便的指令都能够通过CAN来传输给ECU并得到...因此使用步进电机拉杆成为目前的解决方案之一。
  • 使用Arduino开发板精确控制步进电机

    千次阅读 2018-09-01 20:54:12
    我们可以将小型步进电机直接连接到Arduino开发板,以便非常精确地控制轴位置。 由于步进电机使用反馈来确定轴的位置,我们可以非常精确地控制该位置。因此,步进电机经常用于控制物体的位置,旋转物体,移动机器人...
  • 步进电机转速与脉冲频率的关系

    万次阅读 2017-08-08 00:09:37
     步进电机是一种与专门用于速度和位置精确控制的特种电机,它旋转是以固定的角度(称为“步距角”  )一步一步运行的,故称步进电机。其特点是没有累积误差,接收到控制器发来的每一个脉冲信号,在 ...  1、何为...
1 2 3 4 5 ... 20
收藏数 5,841
精华内容 2,336
关键字:

步进电机控制