STM32无刷电机全套开发资料(源码、原理图、PCB工程及说明文档)

目录

1、原理图、PCB、BOOM表

2、设计描述 

2.1 前言

2.2 设计电路规范

 3、代码

4、资料清单

资料下载地址:STM32无刷电机全套开发资料(源码、原理图、PCB工程及说明文档)

1、原理图、PCB、BOOM表

 

2、设计描述 

2.1 前言

        经过一个星期的画PCB,今天终于化了,整体看上去还比较满意,具体的性能还得等后期制板、测试才知道。

        这个电路属于低频功率型板,相对高频板信号质量就要求不高了,所以也就不用考虑信号完整性等问题了。

    等完成原理图的设计之后,我就请教了好几个人,在布线的时候需要注意的事项,但是没有得到什么有价值的答案,原因是他们不了解我,只是站在自己的去看待这件事了,所以对于他们就没什么难度了。

    在原理图的设计时,参照了“阿嘉”和“六哥”的方案,大体没有太大的改动(六哥的已经商品化,相对有保证),只是完善了自己的接口和一些器件的选型。对于原理图的分析请看前两篇博客,有什么不对的地方欢迎指出。

    原理图设计完成之后,接下来就是为各个器件添加封装了。这个过程我检查的还是比较仔细的,因为上次已经在这里出现过一次问题了。对于电阻,电容的封装大体选用0603,对于个别选用0805。对于重要的器件,参考了数据手册和IPC-7351进行选择,应该不会有问题。

    接下来就是导入网表,开始布局。相对来说这个比较简单所以布局也就比较随意,基本原则就是按照功能模块进行布局的。在布线的时候我更比较随意了,也不想什么规则。当布到一半的时候,好多走线都无法完成了,连打过孔的机会都不给我了。我想肯定是出问题了,应该是布局的问题,大概看这个图看了一天,光看不画,看看哪里出问题了。于是就开始了第二版,有了第一次的不顺利,第二次自然就顺利了很多。大概用了不到一个星期就画完了,这个图我自己看上去很是满意。

   前面这些跟流水账似的,没有什么实质性的意思。

   在画之前我就找好了人帮我检查这个图了,周五我把图发给我了相思谷(一个网友),把帮我之处了很多问题,下面就总结一下问题。

2.2 设计电路规范

 1、线间距。

      这里应该遵循3W规则,所谓3W就是为了减少线间串扰,应保证线间距足够大,当线中心不少于3倍线宽,则可 保持70%的电场不互相干扰。如要达到98%的电场不互相干扰,可使用10W的间距。——这是查阅华为PCB布线规则所得。

      这里我就没有遵循这个原则,我的线间距大概只有1倍线宽。

     2、电源线过细。

      这里我查阅了华为PCB教程得到了下面一个表格。这里线宽跟所能承受最大电流的关系表

  3、电源环路。(用图说明)

高亮部分的GND形成了一个环路而且是一个严重的闭环。在看看VS电源线环路 

 这是VS电源线没有闭环,但是接近闭环了。

  这里我就困惑了,如果整体敷铜接地,那在敷铜层也是闭环的GND,而且整体敷铜可增强抗干扰的能力。

这个问题“相思谷”没有给我解答清楚。他让我查阅一下资料,我查阅了很多资料最终未果。于是翻看自己的QQ好友看看谁能帮助我,看后傻了,全是软件的,硬件方面的就有猫大,还没在线。这时候突然想起了流星赶月了,这是个大神。于是就翻看猫大的群就找他了,还不错这人挺热心。下面就看看他的问题吧。

   电源环路没有问题,主要是低阻抗,电调的板子电流很多,不敷铜不合适。他又指出控制部分形成环路确实不好,线上分布电感明显,应该尽量保证单面敷铜连续,就是尽量保证不出现死铜。我的电源环路最要是走线环路,没有平面,是阻抗。

    听完他说的我还没懂,只是迷迷糊糊有个印象。在继续深问就会显得自己弱智了,我也含糊的问答了一下,想不太明白,我在看看。这个原因剩下的就是继续查资料吧。

    在《电路设计技术与技巧》这本书中写到:“地回路”会孕育一个低频的电磁干扰。磁场在一个环形的、封闭的电路中,感应出一个感应电流。还有导线的低频等效模型是一个电阻,由于高阻抗的作用,各个GND值就有会有压差,而不是一个值了。这里指出了一个规则:永远采用分社的电源地线,用不同的导线来分别承载由每个电源所提供的电源。如图:

        与使用分开的地线的原因相同:使用一条公用的电源供给线,会在电源电压上形成一个公共的串联电压降,只是这里被加入到了电源供给线上。

    我还问了他布线的技巧,让他给我推荐一些资料。这个问题他给我的为回答是:整体铺地,单点接地。双层板要调整元件布局,使各个元件接地路劲最短,且地平面集中。泛泛看资料作用不大,很难推荐,布线技巧不是靠临时看书就可以解决的,需要实际操练。

   他的回答很简单,但是很受用,解决了我好多困惑。

   这些就是针对相思谷提出问题的理论补充了,看起来问题很是蛮多的,于是决定重新布局再画一版。针对这次画板,我打算请多个人检查,初步打算有相思谷,华航申老师,猫大、流星。第一版的相思谷已经初验了,还有工程已经发给申老师了还没给我回复呢。第二版再去找猫大跟流星,针对他们提出的问题进行总结,然后修改。最后在拿去找老师检查。估计周一申老师就会检查完,到时候在把他提出的问题进行总结。

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

 3、代码

/*This file is part of AutoQuad ESC32.AutoQuad ESC32 is free software: you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with AutoQuad ESC32.  If not, seeCopyright © 2011, 2012, 2013  Bill 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_IWDGIWDG_ReloadCounter();
#endif
}// setup the hardware independent watchdog
// 初始化并开启独立看门狗
uint16_t runIWDGInit(int ms)
{
#ifndef RUN_ENABLE_IWDGreturn 0;
#elseuint16_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 registersIWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//允许访问IWDG_PR和IWDG_RLR寄存器// IWDG counter clock: LSI/4IWDG_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/400reloadVal = RUN_LSI_FREQ*ms/4000;if (reloadVal < 1)reloadVal = 1;else if (reloadVal > 0xfff)reloadVal = 0xfff;prevReloadVal = IWDG->RLR;IWDG_SetReload(reloadVal);// Reload IWDG counterIWDG_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占空比设置为0timerCancelAlarm2();state = ESC_STATE_DISARMED;pwmIsrAllOn();digitalHi(statusLed);   // turn offdigitalLo(errorLed);    // turn ondisarmReason = reason;  // 设置停机原因.给上位机查看状态使用
}//手动运行
void runArm(void) {int i;fetSetDutyCycle(0);timerCancelAlarm2();digitalHi(errorLed);digitalLo(statusLed);   // turn onif (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 modefor (i = 0; i < runMode + 1; i++) {fetBeep(250, 600);timerDelay(10000);}//        fetBeep(150, 800);
}//电机开始运行
void runStart(void) {// reset integral bevore new motor startuprunRpmPIDReset();//先复位I值if ((p[START_ALIGN_TIME] == 0) && (p[START_STEPS_NUM] == 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) / 100ret = 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 rateif (p[PWM_LOWPASS]) {filteredSetpoint = (p[PWM_LOWPASS] * filteredSetpoint + (float)setpoint) / (1.0f + p[PWM_LOWPASS]);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[PWM_RPM_SCALE] * (setpoint-pwmLoValue) / (pwmHiValue - pwmLoValue);// limit to configured maximumtargetRpm = (target > p[PWM_RPM_SCALE]) ? p[PWM_RPM_SCALE] : target;}// THRUST Modeelse if (runMode == CLOSED_LOOP_THRUST){//闭环推力模式float targetThrust;  // desired trustfloat target;        // target(rpm)// Calculate targetThrust based on input and MAX_THRUSTtargetThrust = 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 targetThrusttarget = ((sqrtf(p[THR1TERM] * p[THR1TERM] + 4.0f * p[THR2TERM] * targetThrust) - p[THR1TERM] ) / ( 2.0f * p[THR2TERM] ));}// 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[PWM_RPM_SCALE]) ? p[PWM_RPM_SCALE] : 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 startingd = (t >= d) ? (t - d) : (TIMER_MASK - d + t);// timeout if PWM signal disappearsif (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 offsetdigitalTogg(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 forwardff = ((target*target* p[FF1TERM] + target*p[FF2TERM]) / avgVolts) * fetPeriod;error = (target - rpm);//计算出偏差if (error > 1000.0f)error = 1000.0f;if (error > 0.0f) {rpmP = error * p[PTERM];  //PrpmI += error * p[ITERM]; //I}else {rpmP =  error * p[PTERM] * p[PNFAC];rpmI += error * p[ITERM] * p[INFAC];}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 outputif (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 hererpm = p[RPM_MEAS_LP] * rpm + ((32768.0f * runRPMFactor) / (float)adcCrossingPeriod) * (1.0f - p[RPM_MEAS_LP]); // increased resolution, variable filter here// run closed loop controlif (runMode == CLOSED_LOOP_RPM){//运行在闭环模式下fetSetDutyCycle(runRpmPID(rpm, targetRpm));return 1;}// run closed loop control also for THRUST modeelse 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 edgesEXTI_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 InterruptNVIC_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.2VPWR_PVDLevelConfig(PWR_PVDLevel_2V2);//配置pvd电压等级.当电压小于2.2V的时候产生中断// Enable the PVD OutputPWR_PVDCmd(ENABLE);
}void runInit(void) {runSetupPVD();runSetConstants();runMode = p[STARTUP_MODE];//启动 运行模式//系统tickcount时钟SysTick_Config(SystemCoreClock / 1000); // 1msNVIC_SetPriority(SysTick_IRQn, 2);            // lower priority// setup hardware watchdogrunIWDGInit(20);
}#define RUN_CURRENT_ITERM        1.0f
#define RUN_CURRENT_PTERM        10.0f
#define RUN_MAX_DUTY_INCREASE        1.0ffloat currentIState;//根据PID计算出PWM占空比的值
static int32_t runCurrentPID(int32_t duty) {float error;float pTerm, iTerm;error = avgAmps - p[MAX_CURRENT];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 setif (p[MAX_CURRENT] > 0.0f){//如果实际的占空比和设置的占空比不一样.那么会实时改变CPU的PWM寄存器.// if current limiter is calibrated - best performance   使用电流限制器校准.性能最好if (p[CL1TERM] != 0.0f){maxVolts = p[CL1TERM] + p[CL2TERM]*rpm + p[CL3TERM]*p[MAX_CURRENT] + p[CL4TERM]*rpm*maxCurrentSQRT + p[CL5TERM]*maxCurrentSQRT;maxDuty = maxVolts * (fetPeriod / avgVolts);if (duty > maxDuty)fetActualDutyCycle = maxDuty;elsefetActualDutyCycle = 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 watchdogrunFeedIWDG();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模式elsebinaryCheck(); //二进制模式runMilis++;
}//低电压中断
void PVD_IRQHandler(void) {// voltage dropping too lowif (EXTI_GetITStatus(EXTI_Line16) != RESET) {// shut everything downrunDisarm(REASON_LOW_VOLTAGE);// turn on both LEDsdigitalLo(statusLed);digitalLo(errorLed);EXTI_ClearITPendingBit(EXTI_Line16);}
}void runSetConstants(void) {int32_t startupMode = (int)p[STARTUP_MODE];float maxCurrent = p[MAX_CURRENT];//运行模式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[MOTOR_POLES] * 6.0f);maxCurrentSQRT = sqrtf(maxCurrent);p[MOTOR_POLES] = (int)p[MOTOR_POLES];p[STARTUP_MODE] = startupMode;p[MAX_CURRENT] = maxCurrent;// Calculate MAX_THRUST from PWM_RPM_SCALE (which is MAX_RPM) and THRxTERMs// Based on "thrust = rpm * a1 + rpm^2 * a2"maxThrust = p[PWM_RPM_SCALE] * p[THR1TERM] + p[PWM_RPM_SCALE] * p[PWM_RPM_SCALE] * p[THR2TERM];
}

4、资料清单

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/799850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++模仿qq界面

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口的大小this->resize(645,497);//设置窗口名字this->setWindowTitle("QQ");//设置窗口图标this->setWindowIcon(QIcon("C:\\zhouzhouMyfile\\qt_proj…

如何使用Excel搭建一套低值易耗品管理系统

低值易耗品是企业日常运营中不可或缺的一部分&#xff0c;虽然其单个价值不高&#xff0c;但因其使用频繁且数量众多&#xff0c;对其的管理同样重要。利用Excel搭建一套低值易耗品管理系统&#xff0c;不仅成本低廉&#xff0c;而且易于操作和维护。本文将指导您如何使用Excel…

Mac运行不畅?一招教你轻松清理缓存,恢复流畅体验!

在日常使用Mac电脑时&#xff0c;面对系统运行速度下降或频繁卡顿的问题&#xff0c;不仅影响工作效率&#xff0c;用户体验也随之大打折扣。探究 Mac 运行变慢的背后原因&#xff0c;涵盖了一系列潜在因素&#xff0c;诸如存储空间接近极限、系统资源紧张、启动加载项过多等。…

1.4 掌握Scala运算符

大家好&#xff0c;今天我们将一起探讨Scala中的运算符。在Scala中&#xff0c;运算符实际上就是方法的一种特殊形式。这意味着我们可以像调用方法一样使用运算符。 首先&#xff0c;让我们了解一下Scala中的运算符种类。Scala支持多种类型的运算符&#xff0c;包括算术运算符…

IT廉连看——SpringBoot——SpringBoot快速入门

IT廉连看——SpringBoot——SpringBoot快速入门 1、idea创建工程 &#xff08;1&#xff09;普通Maven工程创建 工程名spring-boot-test 2、添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/PO…

windows7安装高版本Node

项目需要安装高版本得node&#xff0c;windows7最高支持到v13&#xff0c;需要手动下载zip安装包进行解压&#xff0c;配置环境变量&#xff0c;添加路径到path&#xff0c;如果安装了nvm需要卸载&#xff0c;否则配置得高版本node可能不生效&#xff0c;成功后如下&#xff1a…

LeetCode 24.两两交换链表中的节点

给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff1a;[2,1,4…

Java常用API_正则表达式_字符串的替换和截取方法——小练习

我将通过一个练习题来展示这两个方法 练习题&#xff1a; 有一段字符串&#xff1a;小张qwertyuiop123小李asdfghjkl456小王 要求1&#xff1a;把字符串中三个姓名之间的字母替换成vs 要求2&#xff1a;把字符串中的三个姓名切割出来 编写代码&#xff1a; public class Tes…

js,uniapp,vue,小写数字转化为大写

应用场景&#xff1a; 把1、2、3&#xff0c;转为一、二、三 方法&#xff1a; retBigSrt(num) {const changeNum [零, 一, 二, 三, 四, 五, 六, 七, 八, 九]const unit [, 十, 百]num parseInt(num)const getWan (temp) > {const strArr temp.toString().split().re…

马斯克推出Grok-1.5,接近GPT-4级别的性能

定于下周发布的Grok-1.5带来了增强的推理和解决问题的能力&#xff0c;并接近于已知的开源和闭源的LLM的性能&#xff0c;包括OpenAI的GPT-4和Anthropic的Claude 3。 马斯克指出&#xff0c;Grok-1.5将为xAI在X平台上的ChatGPT挑战者聊天机器人提供支持&#xff0c;而Grok-2(新…

交易要想成功澳福总结几点

100%使用基本面分析的投资者能保证每次交易都能成功吗&#xff1f;100%使用技术分析的投资者能保证每次交易都能成功吗&#xff1f;在fpmarkets澳福看来无论是基本面分析还是技术分析都不能保证100%交易成功。 投资者不能只依赖一种分析进行投资交易。在fpmarkets澳福看来成功的…

GD32F470_US-016 模拟电压输出 双量程 模拟量 超声波测距模块 高精度

2.18 US-016电压式超声波测距传感器 US-016是市场上唯有的一款模拟量输出的超声波测距模块&#xff0c;输出的模拟电压和距离值成正比&#xff0c;可以方便的和其他系统相连&#xff0c;US-016工作稳定可靠。 US-016超声波测距模块可实现2cm~3m的非接触测距功能&#xff0c;供…

【LeetCode热题100】74. 搜索二维矩阵(二分)

一.题目要求 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;…

Dapr(四) Dapr核心组件的使用二

结合前三期 Dapr(一) 基于云原生了解Dapr(Dapr(一) 基于云原生了解Dapr-CSDN博客) Dapr(二) 分布式应用运行时搭建及服务调用(Dapr(二) 分布式应用运行时搭建及服务调用-CSDN博客) Dapr(三) Dapr核心组件的使用一(Dapr(三) Dapr核心组件的使用一-CSDN博客)。下一期使用Dapr中的…

LangChain-12 Routing By Semantic Similarity 让GPT根据不明确的问题 自动选择工具集 根据语义自动路由

问题背景 平常我们设计程序的时候&#xff0c;会这么写&#xff1a; // 随便举例 String type paramDTO.getType(); if (type.equals("吃饭")) {// do } else if (type.equals("喝水")) {// do } else {// do }此时如果type传入的不是数字&#xff0c;那…

docker使用arthas基本教程

供参考也是自己的笔记 docker容器下使用遇到的问题&#xff1a;大致是连接不上1号进程 我这边主要的问题是用户权限问题&#xff0c;docker容器使用aaa用户启动&#xff0c;那个在docker容器内&#xff0c;需要使用aaa用于启动 docker 容器如何使用arthas #实现下载好arthas …

2604B吉时利2604B数字源表

181/2461/8938产品概述&#xff1a; 型号2604B用于台式应用&#xff0c;不包括TSP-Link、接触检查或数字I/O功能。关于系统使用&#xff0c;请参见型号2602B 吉时利2600B系列源表SMU仪器是业界领先的电流/电压源和测量解决方案&#xff0c;采用了吉时利第三代SMU技术。2600B系…

计算机毕业设计java 基于Android的拼图游戏app

当今社会&#xff0c;随着电子信息技术的发展&#xff0c;电子游戏也成为人们日常生活的一部分。这种娱乐方式结合了日新月异的技术&#xff0c;在游戏软件中结合了多种复杂技术。拼图游戏流行在各种电子产品上&#xff0c;从计算机&#xff0c;掌上游戏机到如今的手机&#xf…

山海鲸智慧交通:可视化技术助力城市交通管理升级

随着城市化进程的加速和汽车保有量的不断增加&#xff0c;交通拥堵、安全事故频发等问题日益凸显。在这样的背景下&#xff0c;智慧交通成为了解决城市交通难题的重要途径。山海鲸智慧交通可视化解决方案&#xff0c;以其创新的技术和卓越的性能&#xff0c;为城市交通管理提供…

1688详情API接口:解锁多元化应用场景java php c++

随着互联网的快速发展&#xff0c;数据交换和信息共享已成为企业日常运营不可或缺的一部分。在这样的背景下&#xff0c;API&#xff08;应用程序接口&#xff09;接口作为实现数据互通的重要工具&#xff0c;受到了越来越多企业的青睐。1688详情API接口作为阿里巴巴旗下的重要…