两只袜子 发表于 2022-4-19 10:11

(分享)STM32无刷电调全套开发资料(源码、原理图、PCB工...

本帖最后由 两只袜子 于 2022-4-19 10:17 编辑

SC32硬件---PCB小结(第一版)Altium Designer画的原理图和PCB图如下:






两只袜子 发表于 2022-4-19 10:17

经过一个星期的画PCB,今天终于化了,整体看上去还比较满意,具体的性能还得等后期制板、测试才知道。这个电路属于低频功率型板,相对高频板信号质量就要求不高了,所以也就不用考虑信号完整性等问题了。    等完成原理图的设计之后,我就请教了好几个人,在布线的时候需要注意的事项,但是没有得到什么有价值的答案,原因是他们不了解我,只是站在自己的去看待这件事了,所以对于他们就没什么难度了。    在原理图的设计时,参照了“阿嘉”和“六哥”的方案,大体没有太大的改动(六哥的已经商品化,相对有保证),只是完善了自己的接口和一些器件的选型。对于原理图的分析请看前两篇博客,有什么不对的地方欢迎指出。    原理图设计完成之后,接下来就是为各个器件添加封装了。这个过程我检查的还是比较仔细的,因为上次已经在这里出现过一次问题了。对于电阻,电容的封装大体选用0603,对于个别选用0805。对于重要的器件,参考了数据手册和IPC-7351进行选择,应该不会有问题。    接下来就是导入网表,开始布局。相对来说这个比较简单所以布局也就比较随意,基本原则就是按照功能模块进行布局的。在布线的时候我更比较随意了,也不想什么规则。当布到一半的时候,好多走线都无法完成了,连打过孔的机会都不给我了。我想肯定是出问题了,应该是布局的问题,大概看这个图看了一天,光看不画,看看哪里出问题了。于是就开始了第二版,有了第一次的不顺利,第二次自然就顺利了很多。大概用了不到一个星期就画完了,这个图我自己看上去很是满意。   前面这些跟流水账似的,没有什么实质性的意思。   在画之前我就找好了人帮我检查这个图了,周五我把图发给我了相思谷(一个网友),把帮我之处了很多问题,下面就总结一下问题。

两只袜子 发表于 2022-4-19 10:19

1、线间距。      这里应该遵循3W规则,所谓3W就是为了减少线间串扰,应保证线间距足够大,当线中心不少于3倍线宽,则可 保持70%的电场不互相干扰。如要达到98%的电场不互相干扰,可使用10W的间距。——这是查阅华为PCB布线规则所得。      这里我就没有遵循这个原则,我的线间距大概只有1倍线宽。   2、电源线过细。      这里我查阅了华为PCB教程得到了下面一个表格。这里线宽跟所能承受最大电流的关系表    3、电源环路。(用图说明)
    高亮部分的GND形成了一个环路而且是一个严重的闭环。在看看VS电源线环路
   这是VS电源线没有闭环,但是接近闭环了。
   这里我就困惑了,如果整体敷铜接地,那在敷铜层也是闭环的GND,而且整体敷铜可增强抗干扰的能力。这个问题“相思谷”没有给我解答清楚。他让我查阅一下资料,我查阅了很多资料最终未果。于是翻看自己的QQ好友看看谁能帮助我,看后傻了,全是软件的,硬件方面的就有猫大,还没在线。这时候突然想起了流星赶月了,这是个大神。于是就翻看猫大的群就找他了,还不错这人挺热心。下面就看看他的问题吧。   电源环路没有问题,主要是低阻抗,电调的板子电流很多,不敷铜不合适。他又指出控制部分形成环路确实不好,线上分布电感明显,应该尽量保证单面敷铜连续,就是尽量保证不出现死铜。我的电源环路最要是走线环路,没有平面,是阻抗。

两只袜子 发表于 2022-4-19 10:26

1、线间距。      这里应该遵循3W规则,所谓3W就是为了减少线间串扰,应保证线间距足够大,当线中心不少于3倍线宽,则可 保持70%的电场不互相干扰。如要达到98%的电场不互相干扰,可使用10W的间距。——这是查阅华为PCB布线规则所得。      这里我就没有遵循这个原则,我的线间距大概只有1倍线宽。   2、电源线过细。      这里我查阅了华为PCB教程得到了下面一个表格。这里线宽跟所能承受最大电流的关系表    3、电源环路。(用图说明)
    高亮部分的GND形成了一个环路而且是一个严重的闭环。在看看VS电源线环路
   这是VS电源线没有闭环,但是接近闭环了。
   这里我就困惑了,如果整体敷铜接地,那在敷铜层也是闭环的GND,而且整体敷铜可增强抗干扰的能力。这个问题“相思谷”没有给我解答清楚。他让我查阅一下资料,我查阅了很多资料最终未果。于是翻看自己的QQ好友看看谁能帮助我,看后傻了,全是软件的,硬件方面的就有猫大,还没在线。这时候突然想起了流星赶月了,这是个大神。于是就翻看猫大的群就找他了,还不错这人挺热心。下面就看看他的问题吧。   电源环路没有问题,主要是低阻抗,电调的板子电流很多,不敷铜不合适。他又指出控制部分形成环路确实不好,线上分布电感明显,应该尽量保证单面敷铜连续,就是尽量保证不出现死铜。我的电源环路最要是走线环路,没有平面,是阻抗。

两只袜子 发表于 2022-4-19 10:26

听完他说的我还没懂,只是迷迷糊糊有个印象。在继续深问就会显得自己弱智了,我也含糊的问答了一下,想不太明白,我在看看。这个原因剩下的就是继续查资料吧。    在《电路设计技术与技巧》这本书中写到:“地回路”会孕育一个低频的电磁干扰。磁场在一个环形的、封闭的电路中,感应出一个感应电流。还有导线的低频等效模型是一个电阻,由于高阻抗的作用,各个GND值就有会有压差,而不是一个值了。这里指出了一个规则:永远采用分社的电源地线,用不同的导线来分别承载由每个电源所提供的电源。如图:    与使用分开的地线的原因相同:使用一条公用的电源供给线,会在电源电压上形成一个公共的串联电压降,只是这里被加入到了电源供给线上。    我还问了他布线的技巧,让他给我推荐一些资料。这个问题他给我的为回答是:整体铺地,单点接地。双层板要调整元件布局,使各个元件接地路劲最短,且地平面集中。泛泛看资料作用不大,很难推荐,布线技巧不是靠临时看书就可以解决的,需要实际操练。   他的回答很简单,但是很受用,解决了我好多困惑。   这些就是针对相思谷提出问题的理论补充了,看起来问题很是蛮多的,于是决定重新布局再画一版。针对这次画板,我打算请多个人检查,初步打算有相思谷,华航申老师,猫大、流星。第一版的相思谷已经初验了,还有工程已经发给申老师了还没给我回复呢。第二版再去找猫大跟流星,针对他们提出的问题进行总结,然后修改。最后在拿去找老师检查。估计周一申老师就会检查完,到时候在把他提出的问题进行总结。

两只袜子 发表于 2022-4-19 10:27

总结:上面检查的方式太局限了,但这是非常有效的方法。通过他们的检查我可以充分补充理论知识,而且这些人尤其是猫大跟流星在硬件方面的造诣很深。结识他们就相当找到了一个柱子,顺着他们向上爬吧。这种方法是我看于争的视频想到的方法。他们的经验转换化自己的经验,相信通过这两天电路板肯定帮我在低频电路设计提升一个层次。同时在跟他们的交流过程中发现了一个问题,猫大跟流星的理论基础太厚了,他们很说空经验,他们的经验全是有理论支撑的。这个正好符合了于争视频的快速积累经验的理论。

[*]/*
[*]    This file is part of AutoQuad ESC32.
[*]
[*]    AutoQuad ESC32 is free software: you can redistribute it and/or modify
[*]    it under the terms of the GNU General Public License as published by
[*]    the Free Software Foundation, either version 3 of the License, or
[*]    (at your option) any later version.
[*]
[*]    AutoQuad ESC32 is distributed in the hope that it will be useful,
[*]    but WITHOUT ANY WARRANTY; without even the implied warranty of
[*]    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
[*]    GNU General Public License for more details.
[*]    You should have received a copy of the GNU General Public License
[*]    along with AutoQuad ESC32.If not, see
[*]
[*]    Copyright © 2011, 2012, 2013Bill Nesbitt
[*]*/
[*]
[*]#include "run.h"
[*]#include "main.h"
[*]#include "timer.h"
[*]#include "adc.h"
[*]#include "fet.h"
[*]#include "pwm.h"
[*]#include "cli.h"
[*]#include "binary.h"
[*]#include "config.h"
[*]#include "misc.h"
[*]#include "stm32f10x_exti.h"
[*]#include "stm32f10x_pwr.h"
[*]#include "stm32f10x_iwdg.h"
[*]#include "stm32f10x_dbgmcu.h"
[*]#include <math.h>
[*]
[*]uint32_t runMilis;   //systick中断中自加.没有什么控制用途
[*]static uint32_t oldIdleCounter;//上次main函数中,死循环次数.
[*]float idlePercent;   //空闲时间百分比(在main循环里,什么事情也不做.main死循环运行的时间)
[*]float avgAmps, maxAmps; //平均电流, 最大电流
[*]float avgVolts;      //当前ADC采集转换后的电池电压(也就是12v)
[*]
[*]float rpm;         //当前转速(1分钟多少转) 测量值 在runRpm函数中计算出来.在runThrotLim中还要继续使用.
[*]float targetRpm;   //目标转速 设定值(只在闭环 或 闭环推力模式下使用此变量)
[*]
[*]static float rpmI;
[*]static float runRPMFactor;
[*]static float maxCurrentSQRT;//最大电流 平方根 后
[*]uint8_t disarmReason;//此变量没啥作用.只用于给上位机显示当前的 调试代码(或者说停止电机的原因)
[*]uint8_t commandMode; //串口通讯的模式, cli是ascii模式, binary是二进制通讯模式
[*]static uint8_t runArmCount;
[*]volatile uint8_t runMode;//运行模式 (开环模式, RPM模式, 推力模式, 伺服模式)
[*]static float maxThrust;
[*]
[*]//执行看门狗喂狗
[*]void runFeedIWDG(void) {
[*]#ifdef RUN_ENABLE_IWDG
[*]    IWDG_ReloadCounter();
[*]#endif
[*]}
[*]
[*]// setup the hardware independent watchdog
[*]// 初始化并开启独立看门狗
[*]uint16_t runIWDGInit(int ms)
[*]{
[*]#ifndef RUN_ENABLE_IWDG
[*]    return 0;
[*]#else
[*]      uint16_t prevReloadVal;
[*]      int reloadVal;
[*]
[*]      IWDG_ReloadCounter();//喂狗
[*]
[*]      DBGMCU_Config(DBGMCU_IWDG_STOP, ENABLE);//当在jtag调试的时候.停止看门狗
[*]
[*]      // IWDG timeout equal to 10 ms (the timeout may varies due to LSI frequency dispersion)
[*]      // Enable write access to IWDG_PR and IWDG_RLR registers
[*]      IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//允许访问IWDG_PR和IWDG_RLR寄存器
[*]
[*]      // IWDG counter clock: LSI/4
[*]      IWDG_SetPrescaler(IWDG_Prescaler_4);
[*]
[*]      // Set counter reload value to obtain 10ms IWDG TimeOut.
[*]      //Counter Reload Value      = 10ms/IWDG counter clock period
[*]      //                              = 10ms / (RUN_LSI_FREQ/4)
[*]      //                              = 0.01s / (RUN_LSI_FREQ/4)
[*]      //                              = RUN_LSI_FREQ/(4 * 100)
[*]      //                              = RUN_LSI_FREQ/400
[*]      reloadVal = RUN_LSI_FREQ*ms/4000;
[*]
[*]      if (reloadVal < 1)
[*]                reloadVal = 1;
[*]      else if (reloadVal > 0xfff)
[*]                reloadVal = 0xfff;
[*]
[*]      prevReloadVal = IWDG->RLR;
[*]
[*]      IWDG_SetReload(reloadVal);
[*]
[*]      // Reload IWDG counter
[*]      IWDG_ReloadCounter();
[*]
[*]      // Enable IWDG (the LSI oscillator will be enabled by hardware)
[*]      IWDG_Enable();
[*]
[*]      return (prevReloadVal*4000/RUN_LSI_FREQ);
[*]#endif
[*]}
[*]
[*]//esc32 非正常停止运行 进入初始化
[*]void runDisarm(int reason) {
[*]      fetSetDutyCycle(0);//fet占空比设置为0
[*]
[*]      timerCancelAlarm2();
[*]      state = ESC_STATE_DISARMED;
[*]      pwmIsrAllOn();
[*]
[*]      digitalHi(statusLed);   // turn off
[*]      digitalLo(errorLed);    // turn on
[*]      disarmReason = reason;// 设置停机原因.给上位机查看状态使用
[*]}
[*]
[*]//手动运行
[*]void runArm(void) {
[*]      int i;
[*]
[*]      fetSetDutyCycle(0);
[*]      timerCancelAlarm2();
[*]      digitalHi(errorLed);
[*]      digitalLo(statusLed);   // turn on
[*]
[*]      if (runMode == SERVO_MODE) {
[*]                state = ESC_STATE_RUNNING;
[*]      }
[*]      else {
[*]                state = ESC_STATE_STOPPED;
[*]                if (inputMode == ESC_INPUT_UART)
[*]                        runMode = OPEN_LOOP;
[*]                fetSetBraking(0);
[*]      }
[*]
[*]      // extra beeps signifying run mode
[*]      for (i = 0; i < runMode + 1; i++) {
[*]                fetBeep(250, 600);
[*]                timerDelay(10000);
[*]      }
[*]
[*]//      fetBeep(150, 800);
[*]}
[*]
[*]//电机开始运行
[*]void runStart(void) {
[*]      // reset integral bevore new motor startup
[*]      runRpmPIDReset();//先复位I值
[*]
[*]      if ((p == 0) && (p == 0)) {
[*]                state = ESC_STATE_STARTING;//设置为准备启动状态
[*]                fetStartCommutation(0);//换向启动
[*]      }
[*]      else {
[*]                motorStartSeqInit();//普通启动
[*]      }
[*]}
[*]
[*]//电机停止运行
[*]void runStop(void) {
[*]    runMode = OPEN_LOOP;
[*]    fetSetDutyCycle(0);
[*]}
[*]
[*]//设置运行的占空比 duty = 0~100
[*]uint8_t runDuty(float duty) {
[*]    uint8_t ret = 0;
[*]
[*]    if (duty >= 0.0f || duty <= 100.0f) {
[*]                runMode = OPEN_LOOP;
[*]                fetSetBraking(0);
[*]                fetSetDutyCycle((uint16_t)(fetPeriod*duty*0.01f));//最大周期 * 占空比(0~100) / 100
[*]                ret = 1;
[*]    }
[*]
[*]    return ret;
[*]}
[*]
[*]//pwm.c中断中调用或串口命令输入调用
[*]void runNewInput(uint16_t setpoint) {
[*]      static uint16_t lastPwm;
[*]      static float filteredSetpoint = 0;
[*]
[*]      // Lowpass Input if configured
[*]      // TODO: Make lowpass independent from pwm update rate
[*]      if (p) {
[*]                filteredSetpoint = (p * filteredSetpoint + (float)setpoint) / (1.0f + p);
[*]                setpoint = filteredSetpoint;
[*]      }
[*]
[*]      if (state == ESC_STATE_RUNNING && setpoint != lastPwm)
[*]      {
[*]                if (runMode == OPEN_LOOP)
[*]                {
[*]                        //开环模式
[*]                        fetSetDutyCycle(fetPeriod * (int32_t)(setpoint-pwmLoValue) / (int32_t)(pwmHiValue - pwmLoValue));
[*]                }
[*]                else if (runMode == CLOSED_LOOP_RPM)
[*]                {
[*]                        //闭环转速模式
[*]                        float target = p * (setpoint-pwmLoValue) / (pwmHiValue - pwmLoValue);
[*]
[*]                        // limit to configured maximum
[*]                        targetRpm = (target > p) ? p : target;
[*]                }
[*]                // THRUST Mode
[*]                else if (runMode == CLOSED_LOOP_THRUST)
[*]                {
[*]                        //闭环推力模式
[*]                        float targetThrust;// desired trust
[*]                        float target;      // target(rpm)
[*]
[*]                        // Calculate targetThrust based on input and MAX_THRUST
[*]                        targetThrust = maxThrust * (setpoint-pwmLoValue) / (pwmHiValue - pwmLoValue);
[*]
[*]                        // Workaraound: Negative targetThrust will screw up sqrtf() and create MAX_RPM on throttle min. Dangerous!
[*]                        if (targetThrust > 0.0f) {
[*]                              // Calculate target(rpm) based on targetThrust
[*]                              target = ((sqrtf(p * p + 4.0f * p * targetThrust) - p ) / ( 2.0f * p ));
[*]                        }
[*]                        // targetThrust is negative (pwm_in < pwmLoValue)
[*]                        else {
[*]                              target = 0.0f;
[*]                        }
[*]
[*]                        // upper limit for targetRpm is configured maximum PWM_RPM_SCALE (which is MAX_RPM)
[*]                        targetRpm = (target > p) ? p : target;
[*]                }
[*]                else if (runMode == SERVO_MODE)
[*]                {
[*]                        //伺服模式下
[*]                        fetSetAngleFromPwm(setpoint);
[*]                }
[*]
[*]                lastPwm = setpoint;
[*]      }
[*]      else if ((state == ESC_STATE_NOCOMM || state == ESC_STATE_STARTING) && setpoint <= pwmLoValue)
[*]      {
[*]                fetSetDutyCycle(0);
[*]                state = ESC_STATE_RUNNING;
[*]      }
[*]      else if (state == ESC_STATE_DISARMED && setpoint > pwmMinValue && setpoint <= pwmLoValue)
[*]      {
[*]                runArmCount++;
[*]                if (runArmCount > RUN_ARM_COUNT)
[*]                        runArm();
[*]      }
[*]      else {
[*]                runArmCount = 0;
[*]      }
[*]
[*]      if (state == ESC_STATE_STOPPED && setpoint >= pwmMinStart) {
[*]                //电机开始运行
[*]                runStart();
[*]      }
[*]}
[*]
[*]//电调运行看门狗. 主要是判断电调的当前一些状态.做出停机等处理
[*]static void runWatchDog(void)
[*]{
[*]      register uint32_t t, d, p;
[*]
[*]      //__asm volatile ("cpsid i");
[*]      //CPSID_I();
[*]      __disable_irq();
[*]      t = timerMicros;      //当前的系统tick时间
[*]      d = detectedCrossing;
[*]      p = pwmValidMicros;   //在PWM输入模式下.把timerMicros的时间赋值给此变量
[*]      //__asm volatile ("cpsie i");
[*]      //CPSIE_I();
[*]      __enable_irq();
[*]
[*]      if (state == ESC_STATE_STARTING && fetGoodDetects > fetStartDetects) //这里要检测到fetStartDetects好的检测,才允许切换电机状态
[*]      {
[*]                //是启动状态.切换到 运行状态
[*]                state = ESC_STATE_RUNNING;
[*]                digitalHi(statusLed);   // turn off
[*]      }
[*]      else if (state >= ESC_STATE_STOPPED)
[*]      {
[*]                //运行模式状态下.会一直在这里检测状态.如果状态不对出错.会调用runDisarm函数停止
[*]
[*]                // running or starting
[*]                d = (t >= d) ? (t - d) : (TIMER_MASK - d + t);
[*]
[*]                // timeout if PWM signal disappears
[*]                if (inputMode == ESC_INPUT_PWM)
[*]                {
[*]                        //PWM模式 判断PWM输入是否超时
[*]                        p = (t >= p) ? (t - p) : (TIMER_MASK - p + t);
[*]
[*]                        if (p > PWM_TIMEOUT)
[*]                              runDisarm(REASON_PWM_TIMEOUT);//pwm输入超时
[*]                }
[*]
[*]                if (state >= ESC_STATE_STARTING && d > ADC_CROSSING_TIMEOUT)
[*]                {
[*]                        if (fetDutyCycle > 0) {
[*]                              runDisarm(REASON_CROSSING_TIMEOUT);//错误停止
[*]                        }
[*]                        else
[*]                        {
[*]                              runArm();//手动运行起来
[*]                              pwmIsrRunOn();//PWM开启输入比较
[*]                        }
[*]                }
[*]                else if (state >= ESC_STATE_STARTING && fetBadDetects > fetDisarmDetects)//运行状态中检测到错误的个数后.进入这个判断
[*]                {
[*]                        //在运行过程中,出现错误.停止运行
[*]                        if (fetDutyCycle > 0)
[*]                              runDisarm(REASON_BAD_DETECTS);//错误停止
[*]                }
[*]                else if (state == ESC_STATE_STOPPED)
[*]                {
[*]                        //停止模式
[*]                        adcAmpsOffset = adcAvgAmps;      // record current amperage offset
[*]                }
[*]      }
[*]      else if (state == ESC_STATE_DISARMED && !(runMilis % 100))
[*]      {
[*]                //停止模式下
[*]                adcAmpsOffset = adcAvgAmps;      // record current amperage offset
[*]                digitalTogg(errorLed);
[*]      }
[*]}
[*]
[*]void runRpmPIDReset(void) {
[*]    rpmI = 0.0f;
[*]}
[*]
[*]//这个应该是计算PID
[*]//rpm:测量的转速值
[*]//target:目标的转速值
[*]static int32_t runRpmPID(float rpm, float target) {
[*]      float error;
[*]      float ff, rpmP;
[*]      float iTerm = rpmI;
[*]      float output;
[*]
[*]      // feed forward
[*]      ff = ((target*target* p + target*p) / avgVolts) * fetPeriod;
[*]
[*]      error = (target - rpm);//计算出偏差
[*]
[*]      if (error > 1000.0f)
[*]                error = 1000.0f;
[*]
[*]      if (error > 0.0f) {
[*]                rpmP = error * p;//P
[*]                rpmI += error * p; //I
[*]      }
[*]      else {
[*]                rpmP =error * p * p;
[*]                rpmI += error * p * p;
[*]      }
[*]
[*]      if (fetBrakingEnabled)
[*]      {
[*]                //开启了制动模式
[*]                if (rpm < 300.0f) {
[*]                        fetSetBraking(0);
[*]                }
[*]                else if (error <= -100.0f) {
[*]                        fetSetBraking(1);
[*]                }
[*]                else if (fetBraking && error > -25.0f){
[*]                        fetSetBraking(0);
[*]                }
[*]      }
[*]
[*]      output = ff + (rpmP + rpmI) * (1.0f / 1500.0f) * fetPeriod;
[*]
[*]      // don't allow integral to continue to rise if at max output
[*]      if (output >= fetPeriod)
[*]                rpmI = iTerm;
[*]
[*]      return output;
[*]}
[*]
[*]//计算出电机转速,根据当前转速计算出PID输出值,设置占空比
[*]static uint8_t runRpm(void)
[*]{
[*]    if (state > ESC_STATE_STARTING)
[*]      {
[*]                //电机处于运行状态 计算出当前转速rpm
[*]                //      rpm = rpm * 0.90f + (runRPMFactor / (float)crossingPeriod) * 0.10f;
[*]                //      rpm -= (rpm - (runRPMFactor / (float)crossingPeriod)) * 0.25f;
[*]                //      rpm = (rpm + (runRPMFactor / (float)crossingPeriod)) * 0.5f;
[*]                //      rpm = (rpm + ((32768.0f * runRPMFactor) / (float)adcCrossingPeriod)) * 0.5f; // increased resolution, fixed filter here
[*]                rpm = p * rpm + ((32768.0f * runRPMFactor) / (float)adcCrossingPeriod) * (1.0f - p); // increased resolution, variable filter here
[*]
[*]                // run closed loop control
[*]                if (runMode == CLOSED_LOOP_RPM)
[*]                {
[*]                        //运行在闭环模式下
[*]                        fetSetDutyCycle(runRpmPID(rpm, targetRpm));
[*]                        return 1;
[*]                }
[*]                // run closed loop control also for THRUST mode
[*]                else if (runMode == CLOSED_LOOP_THRUST)
[*]                {
[*]                        //运行在闭环推力模式
[*]                        fetSetDutyCycle(runRpmPID(rpm, targetRpm));
[*]                        return 1;
[*]                }
[*]                else
[*]                {
[*]                        return 0;
[*]                }
[*]      }
[*]      else
[*]      {
[*]                //电机在停止状态下
[*]                rpm = 0.0f;
[*]                return 0;
[*]    }
[*]}
[*]
[*]static void runSetupPVD(void) {
[*]    EXTI_InitTypeDef EXTI_InitStructure;
[*]    NVIC_InitTypeDef NVIC_InitStructure;
[*]
[*]    // Configure EXTI Line16(PVD Output) to generate an interrupt on rising and falling edges
[*]    EXTI_ClearITPendingBit(EXTI_Line16);
[*]    EXTI_InitStructure.EXTI_Line = EXTI_Line16;
[*]    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
[*]    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
[*]    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
[*]    EXTI_Init(&EXTI_InitStructure);
[*]
[*]    // Enable the PVD Interrupt
[*]    NVIC_InitStructure.NVIC_IRQChannel = PVD_IRQn;
[*]    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
[*]    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
[*]    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
[*]    NVIC_Init(&NVIC_InitStructure);
[*]
[*]    // Configure the PVD Level to 2.2V
[*]    PWR_PVDLevelConfig(PWR_PVDLevel_2V2);//配置pvd电压等级.当电压小于2.2V的时候产生中断
[*]
[*]    // Enable the PVD Output
[*]    PWR_PVDCmd(ENABLE);
[*]}
[*]
[*]void runInit(void) {
[*]    runSetupPVD();
[*]    runSetConstants();
[*]    runMode = p;//启动 运行模式
[*]
[*]      //系统tickcount时钟
[*]    SysTick_Config(SystemCoreClock / 1000); // 1ms
[*]    NVIC_SetPriority(SysTick_IRQn, 2);            // lower priority
[*]
[*]    // setup hardware watchdog
[*]    runIWDGInit(20);
[*]}
[*]
[*]#define RUN_CURRENT_ITERM      1.0f
[*]#define RUN_CURRENT_PTERM      10.0f
[*]#define RUN_MAX_DUTY_INCREASE      1.0f
[*]
[*]float currentIState;
[*]
[*]//根据PID计算出PWM占空比的值
[*]static int32_t runCurrentPID(int32_t duty) {
[*]    float error;
[*]    float pTerm, iTerm;
[*]
[*]    error = avgAmps - p;
[*]
[*]    currentIState += error;
[*]    if (currentIState < 0.0f)
[*]                currentIState = 0.0f;
[*]    iTerm = currentIState * RUN_CURRENT_ITERM;
[*]
[*]    pTerm = error * RUN_CURRENT_PTERM;
[*]    if (pTerm < 0.0f)
[*]                pTerm = 0.0f;
[*]
[*]    duty = duty - iTerm - pTerm;
[*]
[*]    if (duty < 0)
[*]                duty = 0;
[*]
[*]    return duty;
[*]}
[*]
[*]//计算得到实际的占空比fetActualDutyCycle
[*]//参数duty:实际上就是fetDutyCycle传递进来的.想要运行的周期
[*]static void runThrotLim(int32_t duty)
[*]{
[*]      float maxVolts; //最大的电压
[*]      int32_t maxDuty;//最大的周期
[*]
[*]      // only if a limit is set
[*]      if (p > 0.0f)
[*]      {
[*]                //如果实际的占空比和设置的占空比不一样.那么会实时改变CPU的PWM寄存器.
[*]
[*]                // if current limiter is calibrated - best performance   使用电流限制器校准.性能最好
[*]                if (p != 0.0f)
[*]                {
[*]                        maxVolts = p + p*rpm + p*p + p*rpm*maxCurrentSQRT + p*maxCurrentSQRT;
[*]                        maxDuty = maxVolts * (fetPeriod / avgVolts);
[*]
[*]                        if (duty > maxDuty)
[*]                              fetActualDutyCycle = maxDuty;
[*]                        else
[*]                              fetActualDutyCycle = duty;
[*]                }
[*]                // otherwise, use PID - less accurate, lower performance使用PID来计算.不大准确.性能低
[*]                else
[*]                {
[*]                        fetActualDutyCycle += fetPeriod * (RUN_MAX_DUTY_INCREASE * 0.01f);
[*]                        if (fetActualDutyCycle > duty)
[*]                              fetActualDutyCycle = duty;
[*]                        fetActualDutyCycle = runCurrentPID(fetActualDutyCycle);//用PID来计算出当前要运行的占空比
[*]                }
[*]      }
[*]      else {
[*]                fetActualDutyCycle = duty;
[*]      }
[*]
[*]      //设置到CPU寄存器里.算出来的实际PWM占空比
[*]      _fetSetDutyCycle(fetActualDutyCycle);
[*]}
[*]
[*]//系统tickcount中断
[*]void SysTick_Handler(void) {
[*]    // reload the hardware watchdog
[*]    runFeedIWDG();
[*]
[*]
[*]    avgVolts = adcAvgVolts * ADC_TO_VOLTS;                     //转换后的电池电压(一般是12V) = ADC采集电压原始值 * 电压算法
[*]    avgAmps = (adcAvgAmps - adcAmpsOffset) * adcToAmps;      //平均电流 = (当前电流 - 停止时候的电流) * 转换公式
[*]    maxAmps = (adcMaxAmps - adcAmpsOffset) * adcToAmps;      //最大电流 = (最大电流 - 停止时候的电流) * 转换公式
[*]
[*]
[*]    if (runMode == SERVO_MODE)
[*]      {
[*]                //伺服模式
[*]                fetUpdateServo();
[*]    }
[*]    else
[*]      {
[*]                runWatchDog();//检测电调的状态.做出相应的停机处理
[*]                runRpm();   //计算RPM,计算PID,设置运行PWM占空比
[*]                runThrotLim(fetDutyCycle);//计算得到实际PWM占空比.如果有偏差.那么在这里会实时改变PWM的占空比值
[*]    }
[*]
[*]
[*]      //计算空闲时间百分比 通过串口发送给上位机没什么用途
[*]    idlePercent = 100.0f * (idleCounter-oldIdleCounter) * minCycles / totalCycles;
[*]//空闲时间百分比 = 100 * (本次循环次数 - 上次循环次数) * 最小周期 / 总共周期
[*]    oldIdleCounter = idleCounter;
[*]    totalCycles = 0;
[*]
[*]
[*]      //处理串口数据 和串**互使用的
[*]    if (commandMode == CLI_MODE)
[*]                cliCheck();    //ascii模式
[*]    else
[*]                binaryCheck(); //二进制模式
[*]
[*]    runMilis++;
[*]}
[*]
[*]//低电压中断
[*]void PVD_IRQHandler(void) {
[*]    // voltage dropping too low
[*]    if (EXTI_GetITStatus(EXTI_Line16) != RESET) {
[*]                // shut everything down
[*]                runDisarm(REASON_LOW_VOLTAGE);
[*]
[*]                // turn on both LEDs
[*]                digitalLo(statusLed);
[*]                digitalLo(errorLed);
[*]
[*]                EXTI_ClearITPendingBit(EXTI_Line16);
[*]    }
[*]}
[*]
[*]void runSetConstants(void) {
[*]    int32_t startupMode = (int)p;
[*]    float maxCurrent = p;
[*]
[*]      //运行模式
[*]    if (startupMode < 0 || startupMode >= NUM_RUN_MODES)
[*]                startupMode = 0;
[*]
[*]    if (maxCurrent > RUN_MAX_MAX_CURRENT)
[*]                maxCurrent = RUN_MAX_MAX_CURRENT;
[*]    else if (maxCurrent < RUN_MIN_MAX_CURRENT)
[*]                maxCurrent = RUN_MIN_MAX_CURRENT;
[*]
[*]    runRPMFactor = (1e6f * (float)TIMER_MULT * 120.0f) / (p * 6.0f);
[*]    maxCurrentSQRT = sqrtf(maxCurrent);
[*]
[*]    p = (int)p;
[*]    p = startupMode;
[*]    p = maxCurrent;
[*]
[*]    // Calculate MAX_THRUST from PWM_RPM_SCALE (which is MAX_RPM) and THRxTERMs
[*]    // Based on "thrust = rpm * a1 + rpm^2 * a2"
[*]    maxThrust = p * p + p * p * p;
[*]}
[*]

复制代码

elephant00 发表于 2022-4-19 10:31


下臂MOS接地线如此细   8mil??? 这是在开玩笑嘛!!可靠性未知呀!

jcky001 发表于 2022-4-20 10:00


这个电调可以用在航模上吗?非常不错的资料

cr315 发表于 2022-4-21 10:00


高手在民间啊!强烈支持!

laocuo1142 发表于 2022-4-21 10:00


非常不错,谢谢分享!!!

laocuo1142 发表于 2022-4-21 10:00

楼主厉害了,这个程序是在什么环境下开发的?

gxlww 发表于 2022-4-21 13:42

楼主,厉害啊!人才!
p p[]列表怎么来的?

gaoyang9992006 发表于 2022-7-13 16:52

谢谢分享,很有必要学习学习

yuchl 发表于 2022-7-13 22:22

谢谢分享

稳稳の幸福 发表于 2022-7-29 11:51

谢谢分享

hellosdc 发表于 2022-8-2 21:59

有时间需要好好看看   不错                                 

yeates333 发表于 2022-8-2 22:08

资料还是相当全面的                                 

touser 发表于 2022-8-2 22:19

资料还是相当全面的                                 

mikewalpole 发表于 2022-8-2 22:31

楼主的资料确实全面,非常感谢            

selongli 发表于 2022-8-3 17:56

相当全的资料,很适合初学者                                 
页: [1] 2 3 4
查看完整版本: (分享)STM32无刷电调全套开发资料(源码、原理图、PCB工...