huo_hu 发表于 2020-5-23 23:32

数字接口 stm32F4+l6205 步进电机驱动

<div class='showpostmsg'> 本帖最后由 huo_hu 于 2023-3-17 12:53 编辑

<p><strong>此内容由EEWORLD论坛网友<font size="3">huo_hu</font>原创,如需转载或用于商业用途需征得作者同意并注明出处</strong></p>

<p>本教程制作一款stm32f4驱动的l6205步进电机程序,带有变细分控制;自动力矩调整;速度实时更新;加速度力矩补偿;掉电芯片保护;自动步值到位等功能。L6205具有体积小电路简单的优点,并且有插装的型号很适合学习用。设计倾向于针对特定的电机进行优化,性能和可扩展性比一般驱动器强很多。本次从0开始一步一步给大家详细介绍和讲解,不当之处欢迎指正,文章所设计的文字和代码请勿用于商业目的。</p>

<p>&nbsp;</p>

<p><span style="font-size: 20px;">第一部分 硬件</span></p>

<p>本设计用到的是stm32f407的最小系统板,其它f4的系统板应该都可以,需要定时器1的6个通道管脚,和一个刹车管脚,原理图如下图</p>

<p></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">原理图可以参照l6205的手册,需要说明几点:</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">1.和stm32管脚连接部分,INA1和2接高级定时器1的通道1 C1和C1N,INB1和2接C2和C2N,ENA和ENB接C3,C4,ENAB接定时器的通道管脚受刹车位控制可以迅速切换到无效电平起保护作用。这7条线加个限流电阻再送stm32更好些,接高级定时器8也可以软件上稍不同。</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">2.右边部分是一个倍压升压电路,D1 D2为1N4148的快速二极管,如果芯片正常L6205上电以后会从VCP管脚输出一个幅值11V左右200KHz的方波,经过C1电容隔离和C2滤波以后在VBOOT管脚会有一个比VCC高10V左右的直流电压(二极管有压降),判断芯片是否正常可以先测量这个电压是否满足要求。这个电压是给H桥的上桥臂的两个mos管门级供电的。</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">3.CF1和CF2是储能电容,视工作电流而定,尽量大一点效果好。D3为整流二级管防止倒灌,D4为发光二极管蓝色或绿色比较好,作用是利用正向压降将VCC降低到stm32的逻辑电压(也可用2.5V的稳压二极管或者用电压比较器芯片),R2根据VCC的实际值计算,尽量大些防止往stm32灌电流,最好在二极管上并个电阻到地防止发光二极管短路的情况下高压输入到io口烧毁stm32。这部分电路目的是提供一个掉电保护电平送到定时器的刹车输入端。一个极端的情况会烧坏l6205芯片就是在电机运转时(ENA和B处于使能的状态)突然切断电源供电,这时储能电容不足以提供给电机运转导致VCC迅速下降,而VBOOT的电压基本维持之前的状态不变造成上桥臂的过压烧毁。正常上电工作后T1_BreakIn为高电平,在切断电源供电时T1_BreakIn第一时间会呈现低电平,再通过stm32定时器的配置失能ENAB的输出就可以起到保护芯片的作用。</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">4.布线时地接入端尽量靠近芯片,手册上有提到。</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">5.另外在后面制做力矩表时有个电流检测,我用的是max4080用消耗电流估算电机力矩值,有兴趣的可以加上,max4080的输出接stm32的adc输入口就行了。</span></span></p>

<p>&nbsp;</p>

<p><span style="font-size: 20px;">第二部分&nbsp;<span style="font-family: &quot;Times New Roman&quot;;"><span style="-ms-text-justify: inter-ideograph;"><span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;"><font face="宋体">配置</font>tim1<font face="宋体">查表法运行</font></span></span></span></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;"><font face="宋体">当前版本</font>cbue 5.6.1<font face="宋体">,</font><font face="Tahoma">fw 1.25.0 </font><font face="宋体">,</font><font face="Tahoma">keil 5.24</font></span></span></span></span></span>&nbsp;<span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;"><font face="宋体">建立最小系统,过程略</font>...</span></span></span></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;"><font face="宋体">注意这里最好使用外置晶振</font>HSE<font face="宋体">,内步</font><font face="Tahoma">HSI</font><font face="宋体">抖动很厉害,效果不好。</font></span></span></span></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;">1:<font face="宋体">定时器</font><font face="Tahoma">1</font><font face="宋体">内部时钟,通道</font><font face="Tahoma">1 1N 2 2N 3 4 pwm</font><font face="宋体">输出,使能</font><font face="Tahoma">Break</font><font face="宋体">(用其它管脚也可以)</font></span></span></span></span></span></span></span></span></p>

<p style="text-align: justify;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">2:<font face="宋体">中央对齐模式</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">3:<font face="宋体">通道</font><font face="Calibri">pwm</font><font face="宋体">模式</font><font face="Calibri">2</font><font face="宋体">,关闭预装载,通道极性低电平</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">4<font face="宋体">:通道</font><font face="Calibri">3</font><font face="宋体">,</font><font face="Calibri">4</font><font face="宋体">也一样设置</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">5<font face="宋体">:使能溢出中断</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">6<font face="宋体">:关闭默认的中断服务,自己编写中断服务程序</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">7<font face="宋体">:定时器用到的管脚全部下拉,</font><font face="Calibri">TIM1</font><font face="宋体">的</font><font face="Calibri">GPIO Settings</font><font face="宋体">里操作也可以</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">8<font face="宋体">:定时器使用</font><font face="Calibri">LL</font><font face="宋体">库,</font><font face="Calibri">hal</font><font face="宋体">库也行,冗余的东西有点多,因为大多数地方都用的寄存器操作,所以</font><font face="Calibri">hal</font><font face="宋体">库意义不大。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;">9<font face="宋体">:点</font><font face="Calibri">GENERATE CODE</font><font face="宋体">建立</font><font face="Calibri">keil v5</font><font face="宋体">工程,添加目录(后面所有涉及的程序代码都在这个目录下),添加组添加代码,</font><font face="Calibri">main.c</font><font face="宋体">里加头文件,调初始化函数。具体参看一下附件</font><font face="Calibri">1</font><font face="宋体">,不截图了。</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><font face="宋体">附件</font>1<font face="宋体">:查表输出电机正反转</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 0pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;">10<font face="宋体">:取消</font>stepmotor.h<font face="宋体">里的最后一行注释,编译下载,用示波器验证一下以下内容</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><font face="宋体">第一个程序会从</font>PE8,PE9<font face="宋体">(</font><font face="Calibri">C1</font><font face="宋体">,</font><font face="Calibri">C1N) </font><font face="宋体">;</font><font face="Calibri">PE10,PE11</font><font face="宋体">(</font><font face="Calibri">C2</font><font face="宋体">,</font><font face="Calibri">C2N</font><font face="宋体">) 轮流输出</font><font face="Calibri">32</font><font face="宋体">个变化占空比的</font><font face="Calibri">pwm</font><font face="宋体">波,</font><font face="Calibri">C1</font><font face="宋体">输出完切换到</font><font face="Calibri">C1N</font><font face="宋体">输出。</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;">PE13<font face="宋体">和</font><font face="Calibri">PE14</font><font face="宋体">输出高电平。</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;">PE15<font face="宋体">悬空或接低电平正常输出,</font><font face="Calibri">PE15</font><font face="宋体">接高电平以后所有通道均输出低电平。</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><font face="宋体">验证没有问题后注释掉</font>stepmotor.h<font face="宋体">里的最后一行</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: Calibri;">//#define BREAK_POLARITY_FORDEBUG</span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><font face="宋体">编译,接通</font>VCC<font face="宋体">电源和电机(安全起见推荐</font><font face="Calibri">12V</font><font face="宋体">我这边是</font><font face="Calibri">42</font><font face="宋体">的电机),下载运行,应该会看到电机转动,测一下电流电机空载几十毫安就可以正常转动。</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 16px;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-family: 宋体;"><font face="宋体">再说明几点:</font></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">首先是关于刹车控制位,正常情况输入的是高电平,低电平为故障状态,附件</font>1<font face="宋体">的程序对刹车位置反以允许</font><font face="Calibri">L6205</font><font face="宋体">不上电输出</font><font face="Calibri">pwm</font><font face="宋体">波做调试,调试完后再恢复之前的设置。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">刹车位设置好极性以后会在低电平到来的第一时间切断所有通道的输出状态,此后不管通道值如何设置所有输出都是低电平,这部分是硬件触发的,不需要软件参与,所以也没有再开刹车中断操作。之后的运行因为考虑到电机已经失去控制,步值和速度状态已经不对了,应该将之前的所有设置和状态都清除,不再做改变只等待再次开机指令。这部分清除工作放在</font>poweroff<font face="宋体">函数里以后添加现在是空的。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">DBTR<font face="宋体">寄存器的</font><font face="Calibri">MOE</font><font face="宋体">位是控制所有通道输出的,置位后开始输出,当</font><font face="Calibri">Breakin管脚</font><font face="宋体">低电平时自动清除即关闭输出,此后当</font><font face="Calibri">Breakin</font><font face="宋体">高电平时通过软件置位</font><font face="Calibri">MOE</font><font face="宋体">并且才能再次开启输出,如果不满足条件置位不会成功。</font><font face="Calibri">DBTR</font><font face="宋体">寄存器里有个</font><font face="Calibri">AOE</font><font face="宋体">(</font><font face="Calibri">auto output</font><font face="宋体">)位,意指</font><font face="Calibri">MOE</font><font face="宋体">位的置位跟随</font><font face="Calibri">Breakin</font><font face="宋体">输入管脚,即</font><font face="Calibri">Breakin</font><font face="宋体">高电平后即自动置位</font><font face="Calibri">MOE</font><font face="宋体">开启通道的</font><font face="Calibri">pwm</font><font face="宋体">输出,这个</font><font face="Calibri">AOE</font><font face="宋体">位置</font><font face="Calibri">1</font><font face="宋体">可以实现跟随</font><font face="Calibri">Breakin状态</font><font face="宋体">自动输出,但是后面做指令强制关机时要注意关机时清除</font><font face="Calibri">AOE</font><font face="宋体">否则又再次开机输出了。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">还有一个是</font>poweron <font face="宋体">函数,这里考虑到系统上电初期的初始化动作会比较复杂,可能需要根据实际情况添加部分代码。比如一个滑台控制系统,刚上电时并不知道电机的初始位置在哪里,通常滑台都有行程开关,上电后开始向固定方向运动以触发行程开关的信号,此后电机的真实位置才能确定,这部分操作需要自己编写代码实现,因此单独的构建一个</font><font face="Calibri">poweron</font><font face="宋体">函数完成这部分功能。另外</font><font face="Calibri">Poweron</font><font face="宋体">函数应该能保证不复位的情况下关机以后再次开机,以实现指令开关机,所以置通道</font><font face="Calibri">3</font><font face="宋体">和</font><font face="Calibri">4</font><font face="宋体">输出高电平,开始输出力矩,初始化位置等都放在这个函数里。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">关于软件最重要的就是定时器的溢出中断服务,我全贴上来</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">uint32_t M1_Step=0;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">const uint16_t SPWM_Table = {</span></span> <span style="font-size: 12pt;"><span style="font-family: Calibri;">//M=1 N=32 C=16384 <font face="宋体">锯齿波数据共</font><font face="Calibri">64</font><font face="宋体">个</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">412,1236,2056,2870,3678,4476,5262,6034,6792,7531,8251,8950,9626,10278,10903,11500,12068,</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">12606,13112,13585,14023,14427,14794,15125,15418,15673,15890,16068,16206,16305,16364,16384,</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">16364,16305,16207,16071,15896,15683,15434,15148,14827,14471,14082,13660,13206,12722,12208,</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">11667,11099,10505,9888,9248,8587,7906,7208,6493,5764,5022,4269,3506,2735,1958,1176,392</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">};</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">#define N_SIZE</span></span> <span style="font-size: 12pt;"><span style="font-family: Calibri;">64</span></span> <span style="font-size: 12pt;"><span style="font-family: Calibri;">//</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">void TIM1_UP_TIM10_IRQHandler(void) {</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">LL_TIM_ClearFlag_UPDATE(TIM1);</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">if (TIM1-&gt;CR1 &amp; TIM_CR1_DIR) {//<font face="宋体">关点输出完毕</font><font face="Calibri">,</font><font face="宋体">开始输出开点</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">/*<font face="宋体">设置极性</font><font face="Calibri">*/</font></span></span> </span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER &amp;= ~(TIM_CCER_CC1E|TIM_CCER_CC1NE|TIM_CCER_CC2E|TIM_CCER_CC2NE);</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">if (M1_Step &amp; N_SIZE) {</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC1E;//A+</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">if ((uint32_t)M1_Step &amp; N_SIZE/2)</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC2E;//B+</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">else</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC2NE;//B-</span></span> </span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">} else {</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC1NE;//A-</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">if (M1_Step &amp; N_SIZE/2)</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC2NE;//B-</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">else</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC2E;//B+</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">}//<font face="宋体">设置极性</font><font face="Calibri">end</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">//<font face="宋体">输出开点</font><font face="Calibri">pwm</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">M1_Step++;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCR1=SPWM_Table[~M1_Step&amp;(N_SIZE-1)]/2;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCR2=SPWM_Table[(~M1_Step+(N_SIZE/2))&amp;(N_SIZE-1)]/2;</span></span> </span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">} else {</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">//<font face="宋体">输出关点</font><font face="Calibri">pwm</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCR1=SPWM_Table/2;</span></span> <span style="font-size: 12pt;"><span style="font-family: Calibri;">//<font face="宋体">除</font><font face="Calibri">2</font><font face="宋体">是半力矩输出</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCR2=SPWM_Table[(M1_Step+(N_SIZE/2))&amp;(N_SIZE-1)]/2;</span></span> <span style="font-size: 12pt;"><span style="font-family: Calibri;">}</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">}</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">因为定时器设置成中央对齐模式,所以这个溢出</font>update<font face="宋体">包括上溢和下溢,用</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CR1 </span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">的</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">&nbsp;TIM_CR1_DIR</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;">&nbsp;<font face="宋体">位来区分,关于计数值的变化</font>stm32<font face="宋体">手册上有说明。上面的示波器看到每半个正弦周期输出的是</font><font face="Calibri">32</font><font face="宋体">个</font><font face="Calibri">pwm</font><font face="宋体">波实际上是</font><font face="Calibri">64</font><font face="宋体">次溢出更新通道值的结果。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">中断服务程序里分为三大块:输出极性(即选择</font>1<font face="宋体">或</font><font face="Calibri">1N &nbsp;2</font><font face="宋体">或</font><font face="Calibri">2N</font><font face="宋体">输出),步值前进,查表输出。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">查表里面有几个逻辑运算得到表地址,赋值最后的</font>/2<font face="宋体">是输出正弦满幅值的一半,目的是怕电流太大起个保护作用,</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">&amp;(N_SIZE-1)</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">即与</font>63<font face="宋体">保证在数据表内,开点和关点的区别是从前往后还是从后往前,所以有个</font><font face="Calibri">~</font><font face="宋体">操作,</font><font face="Calibri">A</font><font face="宋体">相输出即</font><font face="Calibri">CCR1</font><font face="宋体">和</font><font face="Calibri">B</font><font face="宋体">相输出即</font><font face="Calibri">CCR2</font><font face="宋体">的差别是相差半个表所以</font><font face="Calibri">+32</font><font face="宋体">,列出</font><font face="Calibri">AB</font><font face="宋体">开关的序列就清楚了。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">Step&nbsp; 0 &nbsp;1 &nbsp;2 &nbsp;... &nbsp;31 &nbsp;32 &nbsp;33 &nbsp;... &nbsp;61 &nbsp;62 &nbsp;63 &nbsp;0</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">A<font face="宋体">开 &nbsp; </font><font face="Calibri">63 &nbsp;&nbsp; 62 &nbsp;&nbsp; 61 &nbsp; &nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 33 &nbsp; &nbsp; &nbsp; 32 &nbsp; &nbsp; &nbsp; 31 &nbsp; &nbsp;&nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp; 2 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 1 &nbsp; &nbsp; &nbsp; &nbsp; 0 &nbsp; &nbsp; &nbsp;&nbsp; 63</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">B<font face="宋体">开 &nbsp; </font><font face="Calibri">31 &nbsp; 30 &nbsp; &nbsp; 29 &nbsp; &nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp; 1 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 0 &nbsp; &nbsp; &nbsp; &nbsp; 63 &nbsp; &nbsp;&nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 34 &nbsp; &nbsp;&nbsp; 33 &nbsp; &nbsp; &nbsp; 32 &nbsp; &nbsp; &nbsp; 31</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">A<font face="宋体">关 &nbsp; </font><font face="Calibri">0 &nbsp; &nbsp;&nbsp; 1 &nbsp; &nbsp; &nbsp; 2 &nbsp; &nbsp; &nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp; 31 &nbsp; &nbsp; &nbsp; 32 &nbsp; &nbsp; &nbsp; 33 &nbsp; &nbsp;&nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 61 &nbsp; &nbsp;&nbsp; 62 &nbsp; &nbsp; &nbsp;&nbsp; 63 &nbsp; &nbsp; &nbsp; 0</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">B<font face="宋体">关 &nbsp; </font><font face="Calibri">32 &nbsp; 33 &nbsp; &nbsp; 34 &nbsp; &nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 63 &nbsp; &nbsp;&nbsp; 0 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 1 &nbsp; &nbsp; &nbsp; ... &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 29 &nbsp; &nbsp; 30 &nbsp; &nbsp; &nbsp;&nbsp; 31 &nbsp; &nbsp; &nbsp; 32</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">&nbsp;&nbsp;<font face="宋体">步值前进就一句</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">M1_Step++;</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">如果改成</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">M1_Step</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;">--;<font face="宋体">其它部分都不动你就会看到电机朝相反的方向转。</font><font face="Calibri">--</font><font face="宋体">就是步值后退喽。如果改成</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">M1_Step+</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;">=2<font face="宋体">;你会看到转速加倍。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">&nbsp;&nbsp;<font face="宋体">再有就是极性输出,因为我们不设定每次步值前进的位数,所以每次输出完关点的</font>pwm<font face="宋体">就执行一次更新,上来清除所有的输出(</font><font face="Calibri">1</font><font face="宋体">,</font><font face="Calibri">1N</font><font face="宋体">,</font><font face="Calibri">2</font><font face="宋体">,</font><font face="Calibri">2N</font><font face="宋体">)再根据条件设置。每输出完一次表数据即翻转一次所以</font><font face="Calibri">M1_Step&amp;64</font><font face="宋体">即</font><font face="Calibri">A</font><font face="宋体">相的输出极性,</font><font face="Calibri">A</font><font face="宋体">极性正就从</font><font face="Calibri">C1</font><font face="宋体">输出,</font><font face="Calibri">A</font><font face="宋体">极性负就从</font><font face="Calibri">C1N</font><font face="宋体">输出即可以写成</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">if (M1_Step &amp; N_SIZE) {</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC1E;//A+</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">} else {</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">TIM1-&gt;CCER |= TIM_CCER_CC1NE;//A</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">}</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">然后是</font>B<font face="宋体">相的极性,</font><font face="Calibri">B</font><font face="宋体">的极性落后</font><font face="Calibri">A</font><font face="宋体">半周所以用</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">(M1_Step &amp; N_SIZE</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;">/2</span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">)</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">来确定,根据</font>A<font face="宋体">的正负再取一次反即上面程序的写法。</font><font face="Calibri">A B</font><font face="宋体">极性的真值表列出来就好理解了,B的极性就是两个位的异或。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">Step &nbsp; 00&nbsp; 01&nbsp; 10 &nbsp;11</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体"> A+ </font> &nbsp;<font face="宋体">+ + - -</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体"> A-   - - + +</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体"> B+   + - - +</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体"> B-   - + + -</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">最后是这三大部分的逻辑关系,细心的读者可能会有疑问,先出极性再步进的顺序不对啊,这里我仔细验证过,极性设置是带缓冲的,要到下一个周期溢出时才会更新,所以没问题。另外说一下定时器设置时关闭了预装载是个条件,如果开启预装载</font>pwm<font face="宋体">输出会再延后一个周期,程序要改。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">这个查表程序完全围绕着一个</font>M1_Step<font face="宋体">变量展开,这个变量是程序的核心,之所以叫</font><font face="Calibri">M1</font><font face="宋体">是打算以后用</font><font face="Calibri">Tim8</font><font face="宋体">再控制一个电机叫</font><font face="Calibri">M2</font><font face="宋体">。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">这个程序之后我们计算一下当前的转速,后面会用到:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">和转速有关的变量有这么几个:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">定时器的时钟源频率</font>T=168M<font face="宋体">;</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">当前细分度</font>N=32<font face="宋体">,即半个正弦周期的三角波数量也就是半周期</font><font face="Calibri">pwm</font><font face="宋体">波个数(表内数据</font><font face="Calibri">64</font><font face="宋体">个数据是锯齿波,</font><font face="Calibri">2</font><font face="宋体">个锯齿波拼凑一个三角波);</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">定时器溢出回</font>0<font face="宋体">周期</font><font face="Calibri">ARR=16384</font><font face="宋体">;</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">如果用正弦频率做单位算式为</font>SPEED=T/2/ARR/N=T/2/(ARR*N)<font face="宋体">实际应用时</font><font face="Calibri">HZ</font><font face="宋体">单位太大精度不够,再放大</font><font face="Calibri">100</font><font face="宋体">倍用宏函数表示为</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">#define TIME_CLK</span></span> <span style="font-size: 12pt;"><span style="font-family: 宋体;">168</span></span> <span style="font-size: 12pt;"><span style="font-family: 宋体;">//<font face="宋体">定时器时钟频率</font><font face="Calibri">MHZ</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">#define GET_TIMECYCLE(vv) (((uint64_t)TIME_CLK*1000*1000/2*100)/vv)</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">如果参数</font>vv=ARR*N<font face="宋体">的值,则运算出正弦频率</font><font face="Calibri">HZ*100</font><font face="宋体">,如果参数</font><font face="Calibri">vv=</font><font face="宋体">正弦频率</font><font face="Calibri">HZ*100</font><font face="宋体">则运算出</font><font face="Calibri">ARR*N</font><font face="宋体">的值;用</font><font face="Calibri">64</font><font face="宋体">位数据运算保证精度,</font><font face="Calibri">vv</font><font face="宋体">可以是</font><font face="Calibri">32</font><font face="宋体">位数据类型。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">计算结果程序当前正弦频率</font>=168000000/2*100/16384/32=16024=160.24HZ</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">因为</font>200<font face="宋体">步电机走完</font><font face="Calibri">100</font><font face="宋体">个周期电机转一圈,所以</font><font face="Calibri">160.24HZ/100=1.6024</font><font face="宋体">转</font><font face="Calibri">/</font><font face="宋体">秒;</font><font face="Calibri">1.6024*60=96.144</font><font face="宋体">转</font><font face="Calibri">/</font><font face="宋体">分钟。(</font><font face="Calibri">HZ</font><font face="宋体">单位</font><font face="Calibri">/100*60</font><font face="宋体">即换算成转</font><font face="Calibri">/</font><font face="宋体">分钟)</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">这部分为后面做个铺垫。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">最后再做一个扩充(和电机控制没有关系),实际上上面的程序步进值可以是任意值,每次步进的大小就是转速。我们稍加改动就可以做一个码盘同步的控制器,转动编码器时电机跟着转并保持固定的比例(</font>1:1<font face="宋体">)。用另外一个定时器工作在编码器模式,此后这个定时器的</font><font face="Calibri">CNT</font><font face="宋体">直接和位置相关,这部分有逻辑控制不需要软件参与。然后把这个</font><font face="Calibri">CNT</font><font face="宋体">按一定的比例赋值给</font><font face="Calibri">T1_Step</font><font face="宋体">就可以实现随动功能。理论上只要每次赋值的步差值不超过一个整步值就不会丢步,定时器的溢出速度足够快(约</font><font face="Calibri">5.1K</font><font face="宋体">),可以保证这一点。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 24pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">视频链接:https://bbs.eeworld.com.cn/forum.php?mod=attachment&amp;aid=MzE2MTMyfDVmMzNjMWVifDE1OTEwNzUxODh8MzYyNDE2fDU0ODUzNQ%3D%3D</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">效果和这个一样,没有再从新录制</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">首先编码器的</font>A B<font face="宋体">相输出接定时器的通道</font><font face="Calibri">1</font><font face="宋体">和</font><font face="Calibri">2</font><font face="宋体">,编码器需要供电</font><font face="Calibri">3.3V</font><font face="宋体">和地,需要看编码器手册确认一下,有的不支持</font><font face="Calibri">3.3V</font><font face="宋体">但基本都是开漏输出。</font><font face="Calibri">Cube</font><font face="宋体">配置定时器工作在编码器模式,这里选用定时器</font><font face="Calibri">4</font><font face="宋体">,滤波功能既然有设一下不浪费,用</font><font face="Calibri">cube</font><font face="宋体">设置成编码器模式非常简单</font><font face="Calibri">:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">管脚输入上拉。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">定时器同样使用</font>LL<font face="宋体">库,略</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;">&nbsp;</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">在</font>StepMotor_Init<font face="宋体">函数最后加一句启动定时器</font><font face="Calibri">4</font><font face="宋体">:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">SET_BIT(TIM4-&gt;CR1, TIM_CR1_CEN);</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">码盘同步算式:码盘一圈计数</font>8000<font face="宋体">; 正弦</font><font face="Calibri">100</font><font face="宋体">个周期对应电机转动一圈</font><font face="Calibri">step</font><font face="宋体">计数值增加</font><font face="Calibri">100*64*2;</font></span></span> </span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">中断服务里用这句替换</font>M1_Step++;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">M1_Step=(uint32_t)TIM4-&gt;CNT*(32*2)/80;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">下载运行</font>...<font face="宋体">这里的最后说明一下当码盘转动过快时可能有丢步的现象,原因不是</font><font face="Calibri">stm32</font><font face="宋体">反应不及时,而是电机高速转动时输出一半的力矩不足造成的(大力矩时静止状态损耗偏大)。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 21pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">最后结束第二部分时提醒大家一下,电机的工作电流除了和</font>L6205<font face="宋体">的驱动电压有关系以外和电机的相电阻有关系,电机的转动起来以后力矩会下降,下降的程度和相电感有关系,在实验时最好串个电流表观测电流,不要长时间大电流工作。</font><font face="Calibri">L6205</font><font face="宋体">有过热保护,电流超过</font><font face="Calibri">200ma</font><font face="宋体">最好加散热片。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;">&nbsp;</p>

<p><span style="font-size: 20px;">第三部分 <span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 15pt;"><span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;"><font face="宋体">按正弦幅值对称输</font></span></span></span></span></span></span></span></span></span></span></p>

<p><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 15pt;"><span style="font-family: 宋体;"><span style="color: rgb(51, 51, 51);"><span style="letter-spacing: 0pt;"><span style="text-transform: none;"><span style="font-style: normal;">&nbsp; <span style="font-size: 16px;"><font face="宋体">因为用到正弦运算,所以</font>keil <font face="宋体">里添加</font><font face="Tahoma">dsp</font><font face="宋体">库:</font></span></span></span></span></span></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">然后点</font>OK</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">ISR.c<font face="宋体">中添加头文件:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">#define ARM_MATH_DSP</span></span> <span style="font-size: 12pt;"><span style="font-family: Calibri;">//ARM_MATH_CM4</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">#include &quot;arm_math.h&quot;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">#include &quot;stdlib.h&quot;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">老版本宏名称为</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">ARM_MATH_CM4</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">,</font>stdlib.h<font face="宋体">包含了绝对值函数。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">接下来要规划几个变量:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">M1_Step<font face="宋体">同样是电机</font><font face="Calibri">1</font><font face="宋体">的记步变量,用</font><font face="Calibri">int64_t </font><font face="宋体">定义即带符号的</font><font face="Calibri">64</font><font face="宋体">位数据,另外宏定义</font><font face="Calibri">#define HALF_SINCYCLE_VALUE 0x40000000u</font><font face="宋体">即一个正弦周期对应的幺</font><font face="Calibri">1</font><font face="宋体">值为</font><font face="Calibri">0x80000000u</font><font face="宋体">,</font><font face="Calibri">M1_Step</font><font face="宋体">每加或减一个此数值调制输出走完一个正弦周期。相对应的</font><font face="Calibri">AB</font><font face="宋体">相极性判断条件修改为</font><font face="Calibri">(uint32_t)M1_Step &amp; HALF_SINCYCLE_VALUE </font><font face="宋体">和 </font><font face="Calibri">(uint32_t)M1_Step &amp; HALF_SINCYCLE_VALUE/2</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">与之对应的是步进量</font>int32_t M1_HalfStepAcc<font face="宋体">,设定转速时有</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">M1_HalfStepAcc=(int32_t)dir*(HALF_SINCYCLE_VALUE/n/2);</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;">dir<font face="宋体">是方向</font><font face="Calibri">1</font><font face="宋体">正转</font><font face="Calibri">-1</font><font face="宋体">反转,步值前进时</font><font face="Calibri">M1_Step+=(int64_t)M1_HalfStepAcc *2</font><font face="宋体">就行了。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">M1_M<font face="宋体">为输出正弦波的幅值,约定</font><font face="Calibri">65536</font><font face="宋体">为满幅值</font><font face="Calibri">1</font><font face="宋体">,数值越大电机输出力矩越大。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">M1_N<font face="宋体">为电机的细分度,即半个正弦周期三角波的个数。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">M1_Arr<font face="宋体">为定时器的计数周期值,每次溢出中断里更新</font><font face="Calibri">TIM1-&gt;ARR = (M1_Arr-1);</font><font face="宋体">此数值应该小于</font><font face="Calibri">65535</font><font face="宋体">并且不能太小,太小的周期值设定会造成中断无法响应。占空比计算及速度设定和此数值相关,理论上通道占空比的设定值不应大于此数值,实际情况大于时输出有效电平。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">现在需要计算每个步值的占空比:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">图为一个正弦周期中的第</font>n<font face="宋体">个三角波,紫色线为正弦波,我们假设细分度足够大,此时正弦线可以近似为直线,所以左右对称。前面约定了半个周期数值则细分度为</font><font face="Calibri">N</font><font face="宋体">时一个</font><font face="Calibri">pwm</font><font face="宋体">周期为</font><font face="Calibri">0x40000000/N</font><font face="宋体">这个数也是一次步进的数值</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">StepAcc</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">。第</font>n<font face="宋体">个三角波的</font><font face="Calibri">X</font><font face="宋体">点坐标(图中</font><font face="Calibri">P</font><font face="宋体">点)为(</font><font face="Calibri">n+0.5</font><font face="宋体">)</font><font face="Calibri">*StepAcc </font><font face="宋体">(</font><font face="Calibri">n=0,1,2,3....</font><font face="宋体">),线段</font><font face="Calibri">Y</font><font face="宋体">点坐标为</font><font face="Calibri">sin</font><font face="宋体">((</font><font face="Calibri">n+0.5</font><font face="宋体">)</font><font face="Calibri">*StepAcc</font><font face="宋体">)。图中可以看出线段</font><font face="Calibri">L</font><font face="宋体">在幅值中所占的比例和待求值与</font><font face="Calibri">ARR</font><font face="宋体">的比例相等,所以</font><font face="Calibri">Y</font><font face="宋体">点坐标</font><font face="Calibri">*ARR</font><font face="宋体">的数值即为占空比数值。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">即</font>sin<font face="宋体">((</font><font face="Calibri">n+0.5</font><font face="宋体">)</font><font face="Calibri">*StepAcc</font><font face="宋体">)</font><font face="Calibri">*M1_Arr</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">程序中使用了整数正弦运算</font>arm_sin_q31<font face="宋体">,函数参数</font><font face="Calibri">0~0x40000000</font><font face="宋体">对应</font><font face="Calibri">0~</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">&pi;</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">(实际是</font>0~0x80000000<font face="宋体">对应</font><font face="Calibri">0~2</font></span></span><span style="font-size: 12pt;"><span style="font-family: Calibri;">&pi;</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">),返回值</font>0~0x80000000<font face="宋体">对应正弦计算结果</font><font face="Calibri">0~1</font><font face="宋体">。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">arm_sin_q31((int32_t)M1_Step);</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;">&nbsp;&nbsp;&nbsp;M1_Setp<font face="宋体">变量舍去高</font><font face="Calibri">32</font><font face="宋体">位视为</font><font face="Calibri">X</font><font face="宋体">值即半周期的相位值进行运算。返回值</font><font face="Calibri">/0x80000000</font><font face="宋体">对应正弦计算结果</font><font face="Calibri">0~1</font><font face="宋体">,再乘</font><font face="Calibri">M1_M/65536</font><font face="宋体">为设定的正弦幅值(约定</font><font face="Calibri">M1_M=65536</font><font face="宋体">代表幅值</font><font face="Calibri">1</font><font face="宋体">),再乘定时器周期值</font><font face="Calibri">M1_Arr</font><font face="宋体">即为占空比数值。整数运算如果先除就除成</font><font face="Calibri">0</font><font face="宋体">了,所以调整运算顺序先做乘法,最后变成下面语句:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;">si=arm_sin_q31((int32_t)M1_Step);</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">si=(uint64_t)(abs(si))*M1_M/65536*M1_Arr/0x80000000;</span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: Calibri;">M1_CalDat</span></span><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">函数每次前进半个步值,然后计算,计算后再前进半个步值。半个步值就是上面式中的</font>0.5<font face="宋体">个</font><font face="Calibri">StepAcc</font><font face="宋体">。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">上述是步进电机</font>A<font face="宋体">相的占空比计算,</font><font face="Calibri">B</font><font face="宋体">相和</font><font face="Calibri">A</font><font face="宋体">相相差</font><font face="Calibri">90</font><font face="宋体">度相位,所以可以用余弦的绝对值来计算即上面语句正弦替换为余弦就是</font><font face="Calibri">B</font><font face="宋体">相占空比数值。再进一步</font><font face="Calibri">dsp</font><font face="宋体">中有一个正弦余弦同时运算的函数</font><font face="Calibri">arm_sin_cos_q31</font><font face="宋体">,所不同的是参数里有两个指针,计算后指针内容即结果。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">程序里添加了对细分值修改的函数,调试界面手动修改</font>M1_ReSetting_N<font face="宋体">即可看到转速变化,负数即反转。</font></span></span></span></span></span></p>

<p style="text-align: justify;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">实际运行时小力矩低转速有步值位移量不均匀的情况,经过分析是</font>mos<font face="宋体">管开关有延时造成的,过短的pwm不能开启mos管做输出。所以每个占空比加增加一个补偿量即可达到很好的效果,补偿量就是在</font><font face="Calibri">.h</font><font face="宋体">里</font><font face="Calibri">#define SWITCH_DELAY</font><font face="宋体">定义的数值。</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 16px;"><span style="font-family: 宋体;">另外比较新一点的Keil版本支持调试变量动态刷新,设置在这里,在启动调试以后可以勾选。</span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"><span style="font-size: 12pt;"><span style="line-height: 21pt;"><span style="font-family: Calibri;"><span style="font-size: 12pt;"><span style="font-family: 宋体;"><font face="宋体">视频细分度</font>=50000<font face="宋体">,每分钟</font><font face="Calibri">0.06</font><font face="宋体">转</font><font face="Calibri">4</font><font face="宋体">米外激光笔运动情况:</font></span></span></span></span></span></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;">附件:对称正弦幅值输出</p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;"></p>

<p class="p" style="padding: 0pt; text-align: left; text-indent: 12pt;">&nbsp;</p>

<p><span style="font-size: 20px;">第四部分 非对称的迭代运算</span></p>

<p><span style="font-size: 16px;">&nbsp; &nbsp;<span style="font-family: 宋体;">&nbsp; 上面那个对称输出的方法也可以适应中等速度运转,但是有个问题无解,就是A相数据和B相数据不完全相同,这种差异是运算误差造成的,根本原因是N偏小时整数运算精度不够即</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">&nbsp;&nbsp;&nbsp;&nbsp;M1_HalfStepAcc=(int32_t)pdir*(HALF_SINCYCLE_VALUE/pn/2);pn为细分度设置<br />
这句做整数运算时舍弃掉了一部分数据,细分度越小偏差越大。虽然可以在软件中加入代码修正每个周期的偏差,但是当细分度变化时情况会比较复杂不容易处理,百分之百的按理论值输出是不可能的。用迭代计算求解也不能解决这个问题,唯一的办法是对算法进行改进,需要对细分度的设置做出限定:N=2的整数次幂,此时计算是没有误差的。我们先做出这个约定,在下一部分&ldquo;调速&rdquo;里再用软件来实现。<br />
&nbsp; 接下来看迭代计算,所谓迭代就是反复求sin(x)=y 再通过直线方程用这个y求x,如此循环。实际上一章节的计算方法和迭代计算方法只差一步之遥,上一章节sin运算的参数是 P点(上图)的相位值,迭代sin计算是参数是P点相位值附加一个参数,这个参数就是上图中&ldquo;待求值&rdquo;的相位值(这里不贴图了,和上面的图一样)。运算中需要一个数组存放计算结果,这里选择存储y值。计算分2步:<br />
1.从数组中取出y值,折算成对应的x值,这个折算过程就是乘以三角波的直线斜率,选取适当的幺1值以后斜率就可以用1/M1_N来代替,左斜线是1/M1_N右斜就是-1/M1_N,再附加P点相位值。<br />
这一步就是程序中这一句 tmp=phase+M1_CalDataBuffer/M1_N*64;<br />
如果是计算关点进行+运算,如果是计算开点用-运算。+-可以认为是斜率的符号,也可以认为关点在P点右边比P点大所以加,开点在P点左边比P小所以减。<br />
2.用sin(X)*M1_M计算得到y值,再放回到数组里即<br />
M1_CalDataBuffer=((uint64_t)abs(arm_sin_q31(tmp))*M1_M)&gt;&gt;(16+8);<br />
如果是电机的A相数据运算用sin,如果是电机B相数据运算用cos。后面部分*64 和右移24位是乘除法数值对应调整,此数值和幺1值及精度保留有关,同时放大缩小不影响结果,不再细致讨论了。</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">&nbsp; 上面两句执行一次就迭代一次,若干次以后结果就是最终数据,这个计算过程在中断服务里调用,每次调用后的计算结果即y点坐标再用来折算成输出pwm的占空比值,这个过程就是将y值按M1_Arr周期值的比例输出:占空比设定值=((uint64_t)M1_CalDataBuffer*M1_Arr)&gt;&gt;(15+8);</span></span></p>

<div><span style="font-size: 16px;"><span style="font-family: 宋体;">迭代数组里存放的是y坐标,而不是占空比的计算结果,这样做有一个优点就是迭代运算的结果与定时器周期值无关。因为后面做速度调整时需要不断修改定时器周期值,当周期值变化时迭代数据区的数值不会改变,从而加速代码的效率。迭代数据区的数据变化的情况只发生在M1_M即输出正弦波幅值变化的时候和M1_N细分度发生变化的时候。<br />
&nbsp; 另外每次步进时需要计算四个数据,A点开,B点开,A点关,B点关,理论上应该有四个数组来存放并且分别运算。如果你把这四个数组的值列出来会发现其实它们的值都是一样的,只是位置不同而已。A开的数据倒序就是A关的数据,同理B开倒序就是B关;A开的数据从折半的中间位置开始顺序就是B开。四个数据区分别运算出同样的结果不划算,本程序经过优化以后这四个数组合并为一个,只是迭代的数组存放地址运算一下即可。代码里计算前对变量oadd和cadd的赋值就是这部分内容。<br />
&nbsp;&nbsp; &nbsp;cadd=((uint32_t)M1_Step&amp;(HALF_SINCYCLE_VALUE-1))/(HALF_SINCYCLE_VALUE/CALDATABUFFERSIZE);<br />
&nbsp;&nbsp; &nbsp;//关点迭代存储地址<br />
&nbsp;&nbsp; &nbsp;oadd =((uint32_t)(-M1_Step-2*M1_HalfStepAcc)&amp;(HALF_SINCYCLE_VALUE-1))/(HALF_SINCYCLE_VALUE/CALDATABUFFERSIZE);<br />
举个例子就能看出来结果,假设细分度N=64,程序中的计算缓冲区大小为512个单元,则每步的cadd分别为:0,8,16...,488,496,504 ;同时oadd=504,496,488,...,16,8,0;<br />
512/64=8,所以每次步进8个单元,当步值穿越中心点后开点和关点重叠到同一个地址上。同样的方法处理A相和B相的地址(用的异或运算)可以让AB相数据也重叠。这样当步值步进到四分之一个正弦半周期时数据区的所有数据都迭代计算一遍,从而提高了运行效率。你可以试一下四个计算注释掉任意三个都能得到相同得结果。<br />
&nbsp; 再说明一点,这样安排有个优点就是N变化时迭代有初值。迭代计算需要计算几次以后才能得到真实的结果,因此有比较接近结果的初值会减少计算量。举个例子:当N=64时迭代计算的地址为0,8,16,24,32...,之后因为速度调整(加速)将N值修改为32后迭代计算地址变为0,16,32...,跳过了第8地址单元故之前的第8地址单元数据保持不变,后面又做减速调整时N值恢复为64,则在第8单元从新开始计算,只要固定的速度对应的幅值基本接近迭代数据就基本不变,所以很快就能得到第8单元的结果。实际上迭代的2~3次以后就接近%90了,无初值也没有那么严重。<br />
&nbsp; 另外程序允许M值超1调整再说明一下。理论上正弦波幅值=1即M1_M=65536时输出完整的正弦波,此时力矩最大。当转速比较高时正弦的完整性对电机影响不大,高转速时总是希望更大的力矩值。M1_M允许赋值为大于65536的幅值,此时正弦波退化为消顶的梯形波,占空比值大于M1_Arr的情况全都=M1_Arr即输出高电平,这里主要是防止占空比大于65535。防止运算超范围也是M1_CalDataBuffer=((uint64_t)abs(arm_sin_q31(tmp))*M1_M)&gt;&gt;(16+8);需要做64位整数运算的原因。<br />
&nbsp; 程序中迭代计算部分用宏定义给出方便修改,函数原型复制了一份在ISR_Bak.c中供参考。计算中用到stm32f4的dsp库的就只有正弦余弦函数,如果三角函数用查表运算则完全可以在没有浮点库的芯片stm32f1系列上运行。<br />
附件:非对称迭代计算&nbsp;</span></span></div>

<p>&nbsp;</p>

<p><span style="font-size: 20px;">第五部分&nbsp;调速<br />
&nbsp;&nbsp; </span><span style="font-family: 宋体;"><span style="font-size: 16px;">上面章节的程序的已经可以实现最基本的功能了,改变M1_M可调整输出力矩,通过M1_SettingParm可设定速度,只是每次调用都需要给定细分度和定时器周期值。本章节实现根据指定的加速度累计速度值,并分解速度参数自行设定运行。首先添加一个结构体封装电机的各个参数,参看StepMotor.h里的定义不贴出来了。<br />
&nbsp; 第一个是步值,程序采用了相对记步的方式,通过函数对步值进行设定和获取当前步值<br />
static int32_t Relative_Step=0;<br />
//设定和返回相对记步<br />
void M1_Set_Step(int32_t ss) {<br />
&nbsp;&nbsp; int32_t tmp=(M1_Step/(SINCYCLE_VALUE/256));</span></span></p>

<p><span style="font-family: 宋体;"><span style="font-size: 16px;">&nbsp; &nbsp; Relative_Step=tmp-ss;<br />
}<br />
int32_t M1_Get_Step(void) {<br />
&nbsp;&nbsp; &nbsp;int32_t tmp=M1_Step/(SINCYCLE_VALUE/256);&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;return (tmp-Relative_Step);&nbsp;&nbsp; &nbsp;<br />
}<br />
&nbsp; 实际上就是实现相对坐标定位,Set函数给定的参数就是当前电机的位置值,此后M1.Step以此为参照点折算成相对步值。另外u64的数据类型显得比较冗余没有必要,因此移位以后转换为u32数据类型24位整步+8位微步的数据。这个Step是带符号的。<br />
&nbsp; 第二个是HZ*100倍为单位的速度值,驱动函数检测此变量的修改,然后按新数值设定电机运行参数。这里有几个要注意的地方设定值和实际运行值有偏差,这个偏差发生在两种情况:第一种是速度值太小不能设置,程序目前参数能支持4的最小速度(即输出0.04HZ正弦波),更小的数值会造成计算溢出,当然你也可以自己通过其它方式实现。第二种偏差情况是细分度过小引起的计算误差,当细分度小于16时偶尔会有+-1的数值误差。速度值也是带符号数,速度为正时步值加,速度为负时步值减。<br />
&nbsp; 第三个是带符号加速度值Acc,因为在驱动函数每10ms调用一次累加速度值,所以单位为 速度单位/10ms即每10ms速度加或减0.01HZ。设定为正数经过一段时间后速度会达到正向最大值,设定为负数经过一段时间会达到反转最大速度。设置为0时保持当前速度不变。<br />
调速部分就是在M1Speed.c中的SpeedDriver函数,这个函数由大循环每10ms调用一次实现速度调整。参看上传的附件比较长,我就不粘贴了。memory_spd缓存当前速度值,每次比较判断M1.Speed的不同时说明外部操作修改了需要重新设置。M1_GetSpeed函数可以返回真实的速度值,但这个值并不是M1.Speed,原因在于调速需要保留之前的调速结果。举个例子说明一下,M1.Speed=0,Acc=1时一次函数调用会将M1.Speed设置为1,即虽然没有成功但目标变化为1,再次调用为2...直到设置为4时电机以最小速度启动。如果不保留123的状态Acc=1时速度值始终无法增加导致无法启动错误,M1.Speed可以理解为上次驱动调用后的结果。关于这个变量说了这么多实际就是速度值精度的问题,如果精度定为0.05HZ那么都不会有问题,原则上也不应当设定比精度更小的数值。<br />
&nbsp; 调速函数接下来根据速度待设定值反算出N细分度和Arr的乘积值,即程序里nc=...的赋值语句,然后判断速度边界然后再根据nc计算细分度的设定值。速度设定分两种情况:无迭代时(N&gt;512)(512是宏指定的迭代数组的大小),此时Arr固定为16384。有迭代时N=512,256,128,...8,4。有迭代的情况N必须是2的整数次幂,尾数由Arr来调整,因为N和Arr的乘积决定转速,所以N缩小一倍同时Arr放大一倍结果不变。实际程序中选取Arr的取值范围为16384~8192之间(即宏ARR_MAX指定的参数值)。<br />
&nbsp; 通过nc计算N值使用二进制移位即可实现,参见while(tmp&gt;=(ARR_MAX*2)) { &nbsp;...这段循环。循环结束时nn的值就是待设定的细分度。之后是一段判断条件,最终Arr设定值由nc和设定的细分度运算出来即nc/nn/2。参数计算好以后,通过M1_SettingParm函数做修改,这个函数和前几章节不同之处在于增加了一句<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (pn&lt;M1_N)&nbsp; M1_Step &amp;=~((uint64_t)(HALF_SINCYCLE_VALUE/pn)-1);<br />
这句是很有必要的,它的作用时当细分度变小时清除二进制整步值的尾数,这样才能保证每周期从0开始。<br />
&nbsp; ISR.c里的变量比较重要原则上不该允许其它程序修改,以后不需要调试这些变量以后可以用static来修饰保护。程序下载以后调试环境修改M1.Acc为1或-1即可看到电机的加速过程,运行时要接一个电流表随时调整M1_M的值保持适当的力矩输出。</span></span></p>

<p>&nbsp;</p>

<p><span style="font-size: 20px;">第六部分 力矩查表</span><br />
&nbsp; &nbsp; <span style="font-size: 16px;"><span style="font-family: 宋体;">上面调速的过程运行时你会看到当速度增加时L6205输入电流会持续减小,原因是电机运转时线圈中的感生电动势的增加,理论上这个电动势应该和速度成正比。为减少存储占用使用细分度力矩表,相同的细分度区间认为是正比例变化,表项对应细分度值,同时表内包含次细分下的最小速度和最大速度值及最小力矩值和最大力矩值。<br />
typedef struct {<br />
&nbsp;&nbsp; &nbsp;uint32_t Speed_Low;<br />
&nbsp;&nbsp; &nbsp;uint32_t Speed_High;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;uint32_t Momen_Low;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;uint32_t Momen_High;&nbsp;&nbsp; &nbsp;<br />
} MomentItem_TypeDef;<br />
差表输出时按比例分配力矩最大值和最小值的差就行了。参见M1Moment.c,设置时再附加一个加速度力矩补偿参数和加速度乘积。表中的数据为匀速运动时测定的数据,当电机加速时应该用更大的力矩输出才能保证不丢步。另外一个情况是电机减速运动减速运动也要输出更大的力矩,这点和直流电机不太一样。加速度已知的情况要补偿多少力矩呢?这个和你电机的负载情况有关,更准确的说和你电机所带负载的转动惯量有关系,你可以想象电机带着一根很长的轻质细杆做加速运动,电机想加速转动却扭不动的情况,这时候必须对加速度进行限制别无它法。力矩补偿值需要根据实际的负载情况做测试以后确定。<br />
&nbsp; 程序里在M1Speed里加入了一句SpeedFlg=9;(2的9次方=512)使得细分度大于512时和等于512的细分标记相同,从而表项即为M1.SpeedFlg的最低5位。M1_Moment.c的前面部分是测试力矩表项,前面的初始化完成以后会将各细分值下力矩表的Speed_Low值和Speed_High值填充,然后速度设为0等待。之后在调试界面手动修改M1_Acc为1等待一段时间电机会停留在下一个力矩表测试点,这时根据电流表读数调整M1_M到合适的电流,当前力矩值自动填充到表。完成这个测试点后再次将M1_Acc置为1加速到下一个测试点。最后完成时复制MomentTable的内容后启用下半部分输出即可。</span></span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">&nbsp; 关于力矩输出这块儿再说明几点,首先步进电机的输出力矩取决于线圈中的电流,电流越大输出力矩也越大,但是和直流电机不同,过量的电流会无谓的损耗。步进电机的转动完全是靠输出正弦余弦波的相位来控制的(可以参看我另外一篇贴子),步进电机并不会因为输出力矩大而转的更快。<br />
&nbsp; 提高系统能够输出的最大力矩的唯一方法就是提高驱动电路的供电电压,供电电压确定以后我们可以把力矩输出分配到这几部分。<br />
&nbsp; 1.维持匀速运行过程不丢步,电机运行的过程中一定要保证力矩输出值大于当前速度下的最小维持转动力矩,这样才能保证不丢步,低转速下丢步会造成步值计数偏差,高转速下丢步必然会堵转。这个最小维持转动力矩主要取决于电机线圈的电感量或者说是矩频特性。<br />
&nbsp; 2.维持系统做加减速,加速或减速都需要在上面的维持转动力矩的基础上进一步增加力矩来保证加减速的过程不丢步,需要增加的力矩取决于系统的转动惯量。<br />
&nbsp; 3.另外在转速比较高时随着开关频率的增加开关损耗会逐渐增加,进入高速运转时这个不容忽视。<br />
&nbsp; 由上可以看出一个固定的系统最大转速和最大加速度是两个重要指标,和电机特性和负载特性息息相关。不同的电机矩频特性差异很大,主要是线圈匝数影响很大,同时42系列电机也有好几种。这个参数可以用相间电感来测量,电感偏大的电机想高速运动是不现实的,但电感大的电机在低速时同样的电流输出力矩更大。</span></span></p>

<p>&nbsp;</p>

<div><span style="font-family: 宋体;"><span style="font-size: 16px;">附件:第567章节代码,覆盖相应文件即可&nbsp;</span></span></div>

<p style="text-align: justify;"><span style="font-size: 16px;"><span style="font-family: Calibri;"><span style="-ms-text-justify: inter-ideograph;"><span style="font-family: 宋体;"><font face="宋体">视频:</font>24V 电机17H185H-04AC<font face="宋体"> </font><font face="Calibri">Acc=2000</font><font face="宋体">急速正反转,达到最大速度后加速度符号取反,位置有偏差是反转介入的时间差引起的。</font></span></span></span></span></p>

<p style="text-align: justify;"></p>

<p>&nbsp;</p>

<p><span style="font-size: 20px;">第七部分 自动运行到指定步值</span></p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">&nbsp; 前面几部分已经把电机的基本控制都实现了,修改Acc即可按指定的加速度调整电机速度,力矩会根据不同的速度自动设置。最后一部分,想要实现的功能是自动步值到位,就是你告诉我想要去的位置,然后就不用管了,系统自动调整加速度到达位置后停止。系统应该能满足如下需求:<br />
(1)根据指定的速度上限和加速度上限运行,以满足不同的带负载需要。<br />
(2)任何时刻都可以修改目标位置,系统根据目标位置的变化自动调整加减速方向。<br />
(3)最短的时间一次运行到位避免回调。<br />
(4)到位后用状态位表示已经完成。<br />
要实现这样的功能我最初的想法也和大家一样根据目标点位置做逻辑,然后编程序就是一大堆的if else if else...,这样子不行,程序代码乱糟糟越弄越复杂,根本没法儿调试。最后改变思路把逻辑简化了程序就好办了,你可以看到最后实现就几行代码就能完成。<br />
&nbsp; 首先为达到随时可以修改目标位置的功能需要一个全局量缓存当前设定值,然后每10ms执行驱动函数,驱动函数上来先比较设定值和缓存值是否相同,如果不同清除几个标记重新设定,如果相同继续执行下面的程序一直到最后到达目标位置,这部分很好理解就不贴代码了。<br />
&nbsp; 因为状态调整时的速度大小和方向是任意的,所以接下来要回答的一个问题是在当前速度(包括方向)下以最大减速度减速会落在哪里?这个好办,根据公式2aS=Vt*Vt-V0*V0求S就行,因为末速度是0所以S=Vt*Vt/2/a。要正确运用这个公式还要把现在程序里的各个变量的单位和关系梳理以下。<br />
以下凡出现程序变量的地方都表示没有单位的数值。<br />
&nbsp; 步值M1.Step单位是256倍放大的整步单位,这个是由M1_Get_Step函数来定的,返回相对步值时有个运算。一个整步即对应一个正弦半周期,200个正弦半周期电机一转,所以M1.Step从0走到51200电机转一周。<br />
&nbsp; 速度M1.Speed单位是100倍放大的HZ数,1HZ就是每秒一个正弦周期,也就是每秒2个整步,可以写成M1.Speed/100*2 整步/秒,对应成步值单位再*256。<br />
&nbsp; 加速度单位是每10毫秒加减一个速度单位,所以Vt/a上下都约掉一个速度单位就等于M1.Speed/Acc*10毫秒=M1.Speed/Acc*0.01秒。<br />
带入公式<br />
S=Vt/2*(Vt/a)=(M1.Speed/100*2*256)/2*M1.Speed/Acc*0.01单位为和M1.Step一样的步值单位即256放大的整步。<br />
化简以一下=M1.Speed*M1.Speed*256/10000/Acc<br />
&nbsp; 再看一下符号的处理,原本的公式中a是带符号的加速度,而我们这里加速度没有设定方向,加速度方向应该是和速度方向相反才能做减速运动,实际的Acc是不带方向的正数,所求的位移量的方向应该和速度方向一致。换句话说如果是正转减速以后的位移符号为正,如果反转减速以后位移量为负,所以最后计算结果的符号和速度方向相同。再对上面式子进行一次处理变为<br />
abs(M1.Speed)*M1.Speed*256/10000/Acc &nbsp;(Acc运动控制时能够设置的最大加速度值,无方向的正数)<br />
&nbsp; 这个计算结果再加上当前位置点即为减速以后的落点(包括静止速度为0的情况),再用设定目标点的位置减去这个数即为步差值。说了这么一大堆实际上就是解释底下三行代码的来历,看起来有些复杂,但是单位换算是在编程的过程中是必须弄清楚而且不能错的。AccMax是初始化时设定的运行过程中允许使用的最大加速度的绝对值。<br />
int32_t tmp;<br />
tmp=((int64_t)abs(M1.Speed)*(M1.Speed)*256/10000);<br />
tmp=M1.Step+tmp/(M1.Run_AccMax);<br />
return (CompTargetStep-tmp);<br />
&nbsp; 这个返回值我们起个名字叫步差值可能不是特别准确无所谓了,它的含义是在当前速度状态下如果现在立刻做最大减速运动将会到达的位置点与目标设定点的差值。数值为负说明将会降落在目标点左侧,数值为正将会降落在目标点右侧。这个数怎么用呢?别眨眼最核心的部分来了,如果这个数值的符号和速度的符号相同意味着当前运行状态能够到达目标位置,能够到达换句话说就是可以加速;反之如果符号位不同意味着不能到达目标位置也就是说需要减速。上面那句话就是核心的逻辑,我们再把可以加速用逻辑替换一下就是加速度符号的设置和速度方向一致,需要减速用逻辑替换就是加速度符号和速度方向相反。把这个逻辑展开以后你会发现速度符号只是中间一个过度,最后可以约掉,因此最后化简为下面逻辑语句:<br />
if (步差值&lt;0) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;setac=-(M1.Run_AccMax);<br />
&nbsp;&nbsp; &nbsp;} else<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;setac=M1.Run_AccMax;<br />
&nbsp; 化简的过程我就不做了,你也能够想象的出来,步差非正即负-再套速度非正即负-再套速度非正即负-再套加速度非正即负,最后结果就是步差符号决定加速度符号。这个逻辑语句就能够完成步值到位的功能,其它部分可以都不要,每驱动执行一次取步差置加速度方向你就会看到最基本的功能,改变目标点的设置,步值跟随目标点变化。变化的过程包括静止状态开始加速(步差值的绝对值极大),持续加速直到速度上限后保持最大速度(步差值的绝对值逐渐变小),在临近减速最小位置开始减速直到指定的目标位置,整个减速过程步差值的绝对值保持在接近于0的水平。即使调速到位的开始状态速度非0仍然是正确的,反正当时调试时看到这个结果能成立我也是惊掉下巴了,原来这么简单撒。<br />
&nbsp; 接下来就是处理最大速度上限,调速程序中其实有速度上限的约束,这里只是想提供给用户临时指定一个比调速中的速度上限更小的数值设定的功能,达到速度上限就不再加速即<br />
if (abs(M1.Speed) &gt;= M1.Run_SpeedMax ) {&nbsp;&nbsp; &nbsp;M1.Acc=0;return;}<br />
&nbsp; 这样写有个bug就是在速度到达最大速度值以后无法反向减速,因此后面又附加一个能够到达位置的逻辑条件,如果不能到达的情况可以反向设置加速度。<br />
&nbsp; 目前为止的代码完成以后如果调试运行,你会发现有个问题就是M1.Step步值在目标点附近左右摇摆停不下来,探究原因以后得出结论并不是数值精度的问题,根本原因是驱动函数每10ms调用调整一次,这个时间段相对于一个微步的移动时间来说是很大的,不足以控制电机最后精确到达指定位置。另外因为步进时间间隔因为调速的原因是变化的,在中断服务程序外精确控制有个如何同步的问题,会比较麻烦。因此需要对ISR程序加以改动,引入一个微步运行模式,思路就是在中断服务程序里每次释放一个微步的方式前进(或后退)直到最后完成,一个微步就是M1.Step的最小单位。自动运行到位的驱动函数中判断,如果当前位置已经非常接近目标位置了就计算出还差多少个微步可以到达,设定这个数值以后切换到微步进模式就可以精确到位了。程序中还有其它一些细节不一一祥述了,如果有疑问我再作答。<br />
&nbsp; 当前代码能够满足加速度上限为150的情况准确到位值没有回调。如果想做的更好些可以在最后的减速阶段实施pid控制,思路就是根据步差的数值调整加速度,这段以后再改进。<br />
&nbsp; 另外一部分扩展是电机指令控制和串口上位机这个难度不大,网上例子也挺多,相信大家能够搞出来。想提醒大家注意的地方就是keil环境里字符串和变量之间的转换。数值转字符串用sprintf就行,字符串转变量在标准输入输出库里面已经有这个功能,下面这个函数就可以完成根本不用自己编写:<br />
//转换字符为数值,非0:有非法字符<br />
uint8_t ChgStrVal(uint32_t addr,int32_t * ret) {<br />
&nbsp;&nbsp; &nbsp;char * errch=0;<br />
&nbsp;&nbsp; &nbsp;*ret = strtol((char *)(addr),&amp;errch,10);<br />
&nbsp;&nbsp; &nbsp;return *errch;<br />
}<br />
&nbsp; 视频:24V电机17H185H-04AC 每3秒正反转10圈 电流最大130毫安</span></span></p>

<div><span style="font-size: 16px;"><span style="font-family: 宋体;">&nbsp;&nbsp;&nbsp;&nbsp;</span></span></div>

<p>&nbsp;</p>

<p><span style="font-size: 20px;">第八部分 补充说明</span><br />
&nbsp; &nbsp; <span style="font-family: 宋体;"><span style="font-size: 16px;">与大多数脉冲式步进电机驱动不同,本设计量化了步值、速度和加速度为后续进一步开发提供方便。设计中完全摒弃了几个传统设计概念。首先一个是最小启动频率,这个概念源自于脉冲式步进电机驱动,驱动器上一般有很多拨码开关,选择细分和电流,然后外部控制电路输入一个方波脉冲,这个脉冲的最大频率意为可以在静止状态启动步进电机转动。其实这个指标没有什么科学依据,如果是空载启动频率没有多大意义。再一个是S曲线加速,网上能找到一大把关于S曲线加速的文章,各种花式计算,只是说要减小启停阶段的加速度,反正我是没太明白原理是怎么来的。再一个是spwm的计算器,我也下载过几个,计算结果和本设计中的数值对不上,后来分析是那些计算没有考虑0.5个细分度的相位偏移,也就是第一个三角波的中间点和正弦相位0值相切,所以第一个数据为0,这样计算是不对的。应该是三角波起始点和正弦波起始点是相同的,所以第一个开点数据一定不为0(除非幅值为0)。</span></span></p>

<p><span style="font-family: 宋体;"><span style="font-size: 16px;">&nbsp; 另外一个是关于电流检测,最理想的情况是让一个步进电机的控制系统能够适应所有不同类型的步进电机。你可能会这样想,如果能实时检测到电机线圈里的电流值反馈给控制器就能形成闭环,自动调整线圈里的电流。然而实时检测线圈的电流不是一件容易的事,因为线圈是由pwm的开关输出控制的,运动中的电感线圈在到导通的瞬间电流情况非常复杂,仅检测出一个瞬间值没有多大作用。当然你也可以通过积分和滤波求出有效值或均值,但是实时性又得不到满足,等到几个周期以后再实施调整恐怕已经晚了,一旦发生堵转除了再启动已经没有别的办法了。另外一个情况是电机加速,因为加速动作是由控制器控制产生的,所以第一时间控制器就知道应该增加力矩以推动电机加速,没有必要等到反馈信号再做决定,这部分是闭环电路不能取代的。如果你是一个驱动器的生产商当然会希望驱动器能够适应各种电机的情况做个万能驱动器,然而在没有参数的情况下做优化是不现实的。如果不知道系统负载的情况也就不能准做出力矩补偿,前面说了加速度力矩补偿和转动惯量相关,而转动惯量必须要转动起来以后才能确定。如果不能准确设定系统的最大加速度参数最后一个章节的步值到位功能也就没办法实现了,因为缺少参数。</span></span></p>

<p><span style="font-family: 宋体;"><span style="font-size: 16px;">&nbsp; </span></span><span style="font-size: 16px;"><span style="font-family: 宋体;">最后感谢大家的支持,有不当之处恳请指正。</span></span></p>

<p>&nbsp;</p>

<p><span style="font-size: 16px;"><span style="font-family: 宋体;">&nbsp; </span></span>&nbsp; 看到有人问中央对齐的pwm波,这里再展开讨论一下。我们在调制输出正弦波的时候有个重要的操作,就是换向,换向是指从调制正半周输出切换到调制负半周输出。这段代码在定时器中断服务里是这样写的<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM1-&gt;CCER &amp;= ~(TIM_CCER_CC1E|TIM_CCER_CC1NE|TIM_CCER_CC2E|TIM_CCER_CC2NE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ....&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TIM1-&gt;CCER |= TIM_CCER_CC2E;//B+&nbsp;<br />
看似是先清0然后再置位,而实际的情况是程序跑得飞快,清0的过程在IO口上反映不出来,如果看波形的话ENA或ENB在半周期的输出过程维持着高或低电平不变。换向的时候ENAB会产生电平翻转,这是个非常重要的时刻。中央对齐的一个优点除了可以输出对称波形以外还有一个优点就是换向和桥打开输出电流不在同一个时间点。正弦半周最后一个输出点和第一个输出点都是占空比最小的pwm,换向发生在最后一个pwm和第一个pwm的中间位置,这个时候pwm输出肯定是关闭的。换向和pwm开关都时有延时的,而且开和关的延时时间不一致。如果不是中央对齐模式就避免不了换向和pwm导通同时进行,调整pwm极性解决不了这个问题,要么是pwm周期前开要么是pwm周期后开。你可以想象这个换向延时还没走完pwm已经导通了,所以不可避免有部分负半周的输出到了正半周。而且糟糕的情况还不止这些,电机是一种感性负载,当电机开始转动以后,即便是pwm关闭输出,线圈里也会存在剩余电流,如果换向没完成的时候,pwm一旦打开输出就会把所有的反向电流全部吸收干净,相当于这边分电流和电源自己消耗了。当然你也可以开启死区控制来缓解,死区的问题是后面调速的时候死区参数不是一个固定的数值调整起来比较麻烦。尽量用中央对齐输出就好了。</p>

<p>&nbsp;</p>
</div><script>                                var loginstr = '<div class="locked">查看精华帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                               
                                if(parseInt(discuz_uid)==0){
                                                                                        (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                }
</script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

okhxyyo 发表于 2020-5-24 15:58

<p>谢谢楼主分享~~写的清晰明白,受益了!</p>

jack4103 发表于 2020-6-18 16:55

<p>收藏了!!</p>

不是吃的 发表于 2020-6-28 09:24

<p>学习了,感谢楼主</p>

qzhyjd 发表于 2020-6-30 13:44

<p>感谢楼主分享</p>

hotsauce1861 发表于 2020-7-3 07:57

<p><img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/onion/Onion--38.gif" width="50" />支持,好贴,正在学习</p>

宋元浩 发表于 2020-7-3 12:18

<p>感谢楼主分享,好帖</p>

不是吃的 发表于 2020-7-6 11:53

楼主这个芯片是内部电流环么

huo_hu 发表于 2020-7-6 21:34

不是吃的 发表于 2020-7-6 11:53
楼主这个芯片是内部电流环么

<p>没明白什么意思</p>

不是吃的 发表于 2020-7-7 10:43

本帖最后由 不是吃的 于 2020-7-7 10:49 编辑

<div class="quote">
<blockquote><font size="2"><a href="forum.php?mod=redirect&amp;goto=findpost&amp;pid=2990155&amp;ptid=1109055" target="_blank"><font color="#999999">huo_hu 发表于 2020-7-6 21:34</font></a></font> 没明白什么意思</blockquote>
</div>

<p>就是怎么控制电流的大小的,是用mos管搭建驱动电路,原来是用电流采样,让后用电流合成细分驱动电机</p>

huo_hu 发表于 2020-7-7 22:00

不是吃的 发表于 2020-7-7 10:43
huo_hu 发表于 2020-7-6 21:34 没明白什么意思


就是怎么控制电流的大小的,是用mos管搭建驱动电路,原 ...

<p>电流采样是个办法,但是滤波不好弄,电路比较复杂</p>

<p>本例电流是由输出正弦的幅值来控制的,是开环控制。</p>

<p>如果你能做好电流采样改成闭环只是分分钟的事。</p>

不是吃的 发表于 2020-7-9 21:15

huo_hu 发表于 2020-7-7 22:00
电流采样是个办法,但是滤波不好弄,电路比较复杂

本例电流是由输出正弦的幅值来控制的,是开环控制。 ...

<p>大佬可以换个tb67h450芯片试试,带电流硬件可调,感觉性能可以更好</p>

huo_hu 发表于 2020-7-9 23:31

不是吃的 发表于 2020-7-9 21:15
大佬可以换个tb67h450芯片试试,带电流硬件可调,感觉性能可以更好

<p>这种桥驱动模块挺多的,以前用过lmd18200,三洋也有一款lv8548。</p>

<p>你说的这个是单桥驱动,和l9170差不多,l9170以前弄过一下,印象里开关速度不行,做直流电机没问题,因为正转不可能马上切换到反转,但是做步进电机不行。</p>

不是吃的 发表于 2020-7-10 12:26

huo_hu 发表于 2020-7-9 23:31
这种桥驱动模块挺多的,以前用过lmd18200,三洋也有一款lv8548。

你说的这个是单桥驱动,和l9170差不 ...

<p>这个开关频率在500ns,应该还可以,看别人做过,能起跳急停,大佬这个开关频率达到多少做步进电机驱动才合格</p>

huo_hu 发表于 2020-7-10 22:16

不是吃的 发表于 2020-7-10 12:26
这个开关频率在500ns,应该还可以,看别人做过,能起跳急停,大佬这个开关频率达到多少做步进电机驱动才合格
...

<p>500ns行不过没用过,行是肯定都行,PWM频率低开关延时长些也无所谓</p>

04141640 发表于 2020-7-14 17:27

写的很详细,收藏起来慢慢看,感谢楼主分享

jcx0324 发表于 2020-9-10 11:27

<p>载波频率是多少的?</p>

huo_hu 发表于 2020-9-10 11:52

jcx0324 发表于 2020-9-10 11:27
载波频率是多少的?

<p>就是定时器时钟源呗</p>

freebsder 发表于 2020-9-10 19:19

<p>很厉害,差点错过好帖。</p>

czhxp 发表于 2020-9-15 13:09

<p>收藏了</p>
页: [1] 2 3
查看完整版本: 数字接口 stm32F4+l6205 步进电机驱动