让电机动起来!Arduino驱动步进电机教程

1. 简述

平时我们会遇到不同种类的电机,主要常见的是步进电机,无刷直流电机,有刷直流电机,其实这几种电机的工作原理类似,都是用电流产生磁场吸引电机里的转子旋转,这也就是为什么我们用一些电机驱动芯片(并不是所有的驱动)可以驱动一个步进电机或者两个直流电机。这篇博客主要是讲什么是步进电机以及如何驱动步进电机,如何设置定时器。如果大家对我的博客有任何疑问,欢迎大家和我讨论。

2. 所需的电子元件

  • Arduino
  • A4988 电机驱动
  • NEMA (17HS16-2004S1)步进电机
  • 电压源

电路图如下(摘自Pololu A4988,链接):
让电机动起来!Arduino驱动步进电机教程

我搭出来的电路的模样:
让电机动起来!Arduino驱动步进电机教程

3. 步进电机和电机驱动

3.1 datasheets

首先我们需要研究一下步进电机和它的驱动的datasheet。

3.1.1 步进电机的datasheet

我用第一张图给大家简单地讲解下步进电机是如何工作的:2A和2B是一条“导线”的两端,1A和1B是另外一条“导线”的两端,通过把这两条导线在电机内缠绕,会让他们在通电的时候各自产生磁场。磁场的方向根据洛伦兹法则可以得出,和电流的方向,绕线的方向有关。产生的磁场会吸引电机内部中心的转子旋转。通过两根导线交替产生不同方向的磁场,以此来吸引转子周而复始的旋转。更多的大家可以百度。

下面这张图是NEMA (17HS16-2004S1)的datasheet:
让电机动起来!Arduino驱动步进电机教程

我们注意到在上图的电机datasheet里面额定电压是2V,额定电流是2A每个Phase,这个Phase指的就是上面提到的2A/2B或者是1A/1B的导线。我们应该如何理解这里的额定电压和电流?它们指的是:当我给2A和2B端或1A和1B端加上2V的电压,会有2A的电流流过导线,根据欧姆定律,我们得到导线两端的电阻是1欧(在datasheet上,Resistance/Phase那一行也写着1欧姆)。

所以我们可不可以认为交替地在2A/2B和1A/1B上加上2V的电压就可以让电机转动?答案是:是的。我们加的是额定电压,当然可以让电机转动。但问题是电机不会有足够的转速和扭矩,原因是根据法拉第电磁感应定律,除了我们电压所产生的电流外,还会有电磁感应产生的电流,而电磁感应的电流和电压所产生的电流是反向的,因此虽然我们在导线两端加上了额定电压,但是在最初的一段时间内,得到的电流会比额定电流少很多。如果电流不够大,那么产生的磁场是不够移动转子的。这时候我们想到的办法是:可以提高加在导线两端的打压来抵消由磁场产生的反向电流,而电机的驱动就是提高两端的电压,通常可以提高到额定电压的10倍,当然我们需要限制驱动芯片提供给电机的电流,这个我们后面会更详细地说。

3.1.2 步进电机驱动datasheet

市面上有很多步进电机的驱动,除了这次用的A4988,还有A3988,DRV8871等等。它们的工作方式也相似,都是1)在电机每个Phase两端加几倍于额定电压的电压值,2)控制每个Phase的最大电流,这样的步进电机驱动被称为chopper driver,就是说为了提供不超过某个上限电流,它会在特定的时间点停止为电机提供电压。如下图所示:
让电机动起来!Arduino驱动步进电机教程

可以看到图中,当电流超过了上限值I,驱动就会停止提供电压,这时因为磁场作用,电流并不会瞬间为0,而是会逐渐减小(时间常数 = LR)。当电流小于上限值I,驱动又会重新提供电压。所以通过这种达到,既在电机每个Phase两端提供了高电压,又控制了电流。根据上面电机的datasheet中的AMP/PHASE,我们得到我们需要保证每个Phase的电流不超过2A。

下面我们来看看A4988的datasheet:
首先我们需要弄懂的是,Arduino应该如何让驱动开始驱动步进电机工作。如下图所示:
让电机动起来!Arduino驱动步进电机教程

和第一张图对应,STEP需要输入脉冲信号(最上面RESET信号,我认为标识错了,因为STEP和下面的Phase1,Phase2都是正常工作时的样子)。电机驱动会控制电流输出,图中的Phase1和Phase2。有几点我们可以注意到,首先每次STEP的上升沿,是Phase1或者Phase2发生变化的地方,而且STEP在每个上升沿只会有一个Phase发生方向的改变。比如第二个上升沿,Phase 1的电流还是70.71%,而Phase 2的电流就从-70.71%改变到+70.71%,这里的正负号代表电流的方向,这个是和步进电机的机械结构相关的,比如我们不会把步进电机从0度直接到180度,而是会0度先到90度,再到180度。第二点,我们可以想到,如果想加快电机的转动速度,我们需要做的就是减少每个脉冲的宽度,当然这个脉冲的宽度会有最小值,最小值在于当我们设置了某个脉冲宽度,电机驱动在没有等到电机中的转子转到指定的位置的时候,就产生了下一次的脉冲,换句话说就是机械装置还没来得转到合适的地方,电信号就产生改变。第三点,Phase1和Phase2的电流可以有以下这四种组合,(Phase1, Phase2) = (+70.71%, +70.71%), (+70.71%, -70.71%), (-70.71%, -70.71%), (-70.71%, +70.71%),每一个组合代表了转子的一个旋转位置,从一个组合到下一个组合会使得转子从当前位置旋转到下一个位置。

现在我们知道我们需要PWM(脉冲方波)发送给驱动,这样驱动产生合适的电流控制电机了。下一步我们需要搞懂的是如何设置最大的电流上限,根据电机的datasheet,我们知道每个Phase的最大电流不可以超过2A。如下图截取自A4988 datasheet:
让电机动起来!Arduino驱动步进电机教程

根据上面的截图,我们知道 Imax = Vref / (8 * Rs)。在pololu的A4988 的电路图如下图所以:
让电机动起来!Arduino驱动步进电机教程

Rcs是68毫欧,Vref有一个连接一个滑动变阻器组成一个分压电路,所以我们需要用保证Vref 不高于1.088V即可。

具体方法大家可以百度,把滑动变阻器调节到合适的位置即可,比如参考这个链接

4. 硬件定时器

现在我们来说说,Arduino如何产生PWM脉冲。嵌入式的定时器是非常有用的功能,很多东西都是在定时器的基础上搭起来的,比如说操作系统。Arduino使用的是Atmel328P芯片,大家可以下载Atmel328P的芯片手册,这款芯片上一共有三个定时器,两个8-bit timer,一个16-bit timer。我选择的是8-bit的TIMER2。想了解定时器的功能和寄存器功能的话,可以读一下atmel328p datasheet。我在这里主要和大家介绍下这个定时器不同的计数方式,参考TCCR2A里的WGM21和WGM20,TCCR2B里的WGM22。TIMER2可以控制两个GPIO来产生PWM输出,分别是OC2A和OC2B。

  • Normal Mode

第一种是正常工作的模式,这时8bit的计数寄存器会从0计数到255,然后返回0,重新计数。我设置计数器使用Normal mode,每62.5ns计数一次。每次compare match的时候就toggle OC2A和OC2B pin,OCR2A = 64, OCR2B = 192,所以当计数器计数到64时,会toggle OC2A pin,计数到192时,会toggle OC2B pin。

可以想象到,计数256次(62.5ns * 256 = 16us),OC2A和OC2B会toggle一次,而且OC2A和OC2B toggle的时间会相差128次计数,因为OCR2A和OCR2B相差了128。

产生的波形如下图,里面的黄色和蓝色的波形代表OC2A和OC2B pin上产生的电压,测量点A到测量点B是toggle两次的时间(16us*2 = 32us):
让电机动起来!Arduino驱动步进电机教程

  • PWM, Phase Correct (Mode 1)

第二种计数模式是PWM Phase Correct模式。在计数方式上,它会从0计数到255,然后再从255到0。而它是PWM 模式的,和第一种的区别就在于,我们可以设置当计数到255和到compare match的时候OC2A, OC2B pin的电平高低,从而控制脉冲的宽度。

产生的波形如下图,OCR2A = OCR2B = 32,一个pin设置为compare match时设高电平,0xFF时设低电平;另外一个相反,compare match时低电平,0xFF时高电平。从测量点A到测量点B的时间应该是计数器从32->255->32,一共447*62.5us=27.9us。

让电机动起来!Arduino驱动步进电机教程

  • CTC

第三种计数模式是CTC模式。这种模式的计数方式是从0到OCRA,再返回0,因此与第一种normal mode不同的是,这种模式可以到OCRA而不是0xff,换句话说是周期是可以调整的。如果是toggle pin的话,duty cycle一般是固定的50%(当然可以产生中断重新设置OCRA寄存器从而改变周期)。

产生的波形如下图,OCR2A = 64,OCR2B = 10,OC2A和OC2B都是toggle,所以当计数器counter等于64时,OC2A 会toggle然后计数器清零,当计数器counter等于10的时候,OC2B会toggle,但并不影响计数器继续计数。A 和B的周期都应该是(62.5*64 = 4us)

让电机动起来!Arduino驱动步进电机教程

  • Fast PWM(Mode 3)

第四种是Fast PWM模式,且在0xFF清零。这种和第二种PWM, Phase Correct (Mode 1)非常相似,只不过计数方式是从0到255再返回0。

产生的波形如下图,OCR2A = OCR2B = 32,一个pin设置为compare match时设高电平,0xFF时设低电平;另外一个相反,compare match时低电平,0xFF时高电平。从测量点A到测量点B一共是一个周期,就是256*62.5 = 16us

让电机动起来!Arduino驱动步进电机教程

  • PWM, Phase Correct (Mode 5) 和 Fast PWM(Mode 7)

第五种和第六种我放在一起,因为两种模式比较相似,这两种产生的波形的周期和duty cycle都可以非常方便地改变。
拿Fast PWM(Mode 7)举例来说,因为在计数器和OCRA一样的时候会清零,而且得决定在compare match和清零的时候发生什么事(参考WGM和COM bit),所以OCA pin是无法输出PWM波的(清零和compare match在一起)。我们通常把OCR2A和OCR2B配合只用,OCR2A决定周期,OCR2B决定duty cycle,OC2B pin产生所需要的PWM波形,比如说我要产生一个周期是8usec(62.5nsec128),duty cycle 25%(62.5nsec32),可以让OCR2A = 128, OCR2B = 32,然后设置在清零时置OC2B高电平,compare match时置低电平。

产生的波形如下图所示:
让电机动起来!Arduino驱动步进电机教程

以上讲的所有类型的PWM设置的代码都在GitHub上,大家可以任意下载修改测试。GitHub Repo链接

5. PWM驱动电机

到目前,我们已经知道硬件该如何连接,该如何设置最大电流,该如何设置Atmel328P的PWM。

我使用了Timer2 Fast PWM (Mode 7)来产生周期可变的PWM信号,这样可以方便地让电机开始时从最慢逐渐变快,在电机要停下来的时候从最快的速度逐渐变慢直到停止,这样可以增大电机的扭矩和稳定性。

整个控制步进电机的代码在GitHub上,链接