本意是想用STC8H8k64u复刻梁工无刷电机大作,带着小伙伴们一起玩转无感无刷直流电动机,认真研究了梁大师的源代码三天,调整了相关pwm和ADC引脚设置代码,打样了PCB,用大疆精灵四电机实验,电机驱动不起来。由于才疏学浅,基础太薄弱,监测设备只有一台示波器和万用表,也不知道如何测试,关键是读不懂源代码中CMP_ISR比较器中断函数、PWMA_ISR方波中断函数、PID函数的意思,比如为何要连续8个采样为0/1则比较器输出0/1;预定位角度为何当RollCheckIndex =0时,angle要等于4096-680,RollCheckIndex =1时,angle要等于4096-340;RollCheckIndex =2时,angle却为0;RollCheckIndex转动检测索引为何只有4个,恳请大佬详细讲解程序工作流程电机启动步骤。图片是检测电机中间点与其中一相电压波形,视频是电机断断续续艰难转动情况,附件PDF是原理图,请大佬指点一下。不甚感谢!https://www.stcaimcu.com/forum.php?mod=image&aid=134066&size=300x300&key=7a60c01c7eabebc4&nocache=yes&type=fixnone
垂柳工作室 发表于 2026-3-20 17:43
本意是想用STC8H8k64u复刻梁工无刷电机大作,带着小伙伴们一起玩转无感无刷直流电动机,认真研究了梁大师的 ...
1、连续8个采样为0/1则比较器输出0/1:抗干扰,避免偶发干扰或PWM的影响,可靠检测到比较器状态。
2、预定位:用于转子初始电位
RollCheckIndex =0时,-60°定位,让转子定位于-60°,4096对应360°,则angle=-60°对应的查表位置 = 4096(1-60/360) = 4096-682,我取整为680了。
RollCheckIndex =1时,-30°定位,让转子定位于-30°,4096对应360°,则angle=-30°对应的查表位置 = 4096(1-30/360) = 4096-341,我取整为340了。
RollCheckIndex =2时,0°定位,让转子定位于0°,则angle=-0°对应的查表位置 = 0 。
请提供你的电机的而定最高转速参数。
感谢梁大师抽空解答,我咨询大疆客服,获悉该电机是2312型号 12V 10000转。我移植的驱动代码只针对STC8H8K64U芯片修改了PWM和ADC引脚,其他的没动,特别是没看动的函数,压根无从下手。请指导一下后续如何修改,谢谢
梁工:我弄的反电动势电路滤波电容用103,电阻是1k欧(1.5k用完了),除了主控芯片和这三个电阻外,其他的都原样抄您的作业。请指导一下变更了电阻,发生了什么变化,需要关注哪些,哪些关键点是后续调试需要查看和检测重点,请赐教一下调试流程,方便这学期24位小伙伴们动手学用STC芯片,谢谢。
梁大师:请赐教一下.
for(i=0; i<128; i++) //标定电流0值
{
adc6_I= ((adc6_I*7)>>3) + Get_ADC10bitResult(6); //低通滤波结果为13位, 6: ADC8(电流), 7:ADC9(电压),11:ADC11(电位器)
}
ADC6_I_zero = adc6_I; //求ADC平均值作为电流0点, 低通滤波结果为13位,
((adc6_I*7)>>3) + Get_ADC10bitResult(6) —>adc6_I × 7 ÷8 + Get_ADC10bitResult(6)
重复128次,貌似adc6_I最终值趋势等于8倍的Get_ADC10bitResult(6);ADC6_I_zero作为平均值怎么理解,低通滤波结果为13位又怎么理解?
垂柳工作室 发表于 2026-3-23 22:29
梁大师:请赐教一下.
for(i=0; i>3) + Get_ADC10bitResult(6); //低通滤波结果为13位, 6: ADC8(电流), ...
1、重复128次读电流值并做低通滤波计算,得到0点电流时的ADC值。一般不少于32次就可以了。
2、这是最简单的数字一阶低通滤波算法:Y(n) = Y(n-1)*(1-a) + X(n)*a,a<1,对应的是硬件一阶(一节)RC低通滤波,a值越小,对应的RC值越大,滤波效率越好,但响应越慢,要选择合适的a值。我的算法是为了避开小数计算而又不会出现截断误差,而使用整数计算,我这种算法基本没看到有人这样用的。我的算法实际是将输入扩大1/a倍,Y(n) = Y(n-1)*(1-1/a) + X(n)*a*1/a,即Y(n) = Y(n-1)*(1-1/a) + X(n),结果扩大了1/a倍,避免了浮点运算,但不会出现截断误差。本例中,Y(n) = Y(n-1)*7/8 + X(n),结果扩大8倍。本例ADC值为10位,扩大8倍就是增加了3位,则变为13位的结果。
垂柳工作室 发表于 2026-3-23 11:18
梁工:我弄的反电动势电路滤波电容用103,电阻是1k欧(1.5k用完了),除了主控芯片和这三个电阻外,其他的 ...
你的驱动电压是多少V?分压电阻选择原则是(MCU-VCC=5V):驱动电压 *分压=3.5左右。
按理应该很容易驱动起来,请仔细检查反电动势过0检测的顺序,我只想到是这个问题了。
学习梁大师无感无刷直流电机驱动源代码心得
一、简要了解STC公司芯片的高级PWM中断产生机制STC公司芯片的PWM定时器本质上是一个16位计数器,它可以工作在向上计数、向下计数或中央对齐模式。PWM中断就像这个计数器在特定位置设立的“路标”。下面的流程图展示了在一个PWM周期内,中断是如何产生的。 https://www.stcaimcu.com/forum.php?mod=image&aid=134360&size=300x300&key=d32722760e2630da&nocache=yes&type=fixnone关键点解析:更新中断:当计数器计到自动重载值(ARR) 并归零时产生。这通常标志着一个完整PWM周期的结束。比较中断:当计数器计到比较值(CCR) 时产生。在BLDC控制中,这个中断常用来在特定占空比时刻“做点事情”,比如触发ADC采样。标志位必须软件清零:进入中断后,如果不手动清除中断标志位,程序会以为又有新中断进来,导致反复进出中断,这是初学者最容易犯错的地方。二、结合STC公司梁大师的官方无感BLDC程序深度解析PWM中断的核心逻辑1. 初始化阶段:中断源的配置在无感BLDC程序中,PWM中断通常不是为了“改变占空比”,而是为了“定时”和“换相”。这个阶段要理解两个中断逻辑:更新中断:电机每转一圈,需要换相6次。但PWM周期通常远快于换相周期,官方梁大师程序利用PWM的更新中断来做一个精准的“时间片”,查询读取比较器结果,装载计算30度电角度等。比较中断:在无感控制中,比较器检测到反电动势过零后,需要延时30度电角度才能换相。这个30度延时就是利用PWM的比较中断实现的:设定一个比较值,计数器到达时触发中断,执行换相。2. 运行阶段:中断服务函数的核心逻辑中断服务函数我个人认为是最为复杂、也最容易“绕晕”的地方,反正我是被糊涂了好久。梁大师的无感BLDC的中断分为两大阵营:PWM周期中断和比较器中断(两者是配合使用),主要完成过零检测、计算延时、曲线强拖和跟踪电位器油门等任务,具体执行流程如下图。 https://www.stcaimcu.com/forum.php?mod=image&aid=134361&size=300x300&key=2738dec81440b2c1&nocache=yes&type=fixnone过零检测:电机转子旋转时,未通电的那一相会产生反电动势。当这个电压过零时,芯片内部的比较器会产生一个中断。计算延时:在比较器中断中,程序会计算从“过零”到“下一次换相”的时间。这个时间通常是当前换相间隔的一半(即30度电角度)。曲线强拖:预设占空比,程序自主换相改变,拉着磁场往前走一步。PWM周期中断:与此同时,PWM本身的更新中断一直在运行,它负责根据油门(比如ADC采样值)更新占空比,从而控制电机转速。总之对于基础薄弱的,初看源代码是一头雾水,主要是梁大师把PWM定时器、普通定时器和比较器联合使用,组成了一个“状态机”。PWM中断:负责“时间基准”和“占空比更新”。比较器中断:负责“位置反馈”(过零检测)。定时器中断:负责“逻辑执行”(换相、消磁延时)以上就是这一周研学梁大师源代码的心得,不知道是否是正确,如有错误,请大师或者论坛管理员直接更正,旨在协助初学者顺利地学习领悟梁大师大作,助力国产芯片普及应用。另外,希望梁大师赐教一下源代码中消磁和pid控制这部分知识(我还没学明白),下一步计划对照梁大师代码,逐行解析源代码,从main主函数开始,以源代码行号为指引尝试解析程序运行流程,抛砖引玉。
我是用12伏服务器电源供电,万用表测量电压是12.2伏
学习梁大师无感无刷直流电机驱动源代码心得(二)三、分析程序运行流程和机制梁大师无刷无感直流电动机驱动有几个版本,本稿是依据本论题首页“三相无刷电机驱动-STC8H-无HALL-Ver3-PID控制-OLED显示-串口绘图”进行梳理,助力初学者鉴赏梁大师作品。下面开始抛砖引玉,错误之处请大佬们批评指正。1、补全部分代码注解第100行至105行代码定义PWM输出对应的引脚(IO口)第120行与122行重复了,定义电机状态枚举型数据第144行代码定义了存储计数毫秒变量当值等于4时,4ms定时标志B_4ms=1第145行代码定义了存储计数毫秒变量,当值等于500时,500毫秒标志B_500ms=1第149行代码ADC6_I_zero存储电流平均值,貌似只在显示图像时使用,没参与控制电机。第153行代码定时器3益处标志,B_Timer3_OverFlow第175至184行代码跟串口有关,不涉及电机控制第189至197行delay_ms是延时毫秒函数。第209至219行delay_N_10us是延时10微秒函数第222至225行Delay_500ns是延时500纳秒函数第234行代码CMPCR1 = 0;比较器控制寄存器清零复位第295行ADC转换完成后,完成标志硬件自动置1即ADC_FLAG=1;~ADC_FLAG=0;ADC_CONTR &= ~ADC_FLAG,ADC_CONTR=0x00;即ADC控制寄存器清零复位。第295行返回16位数ADC转换值,ADC_RES存高字节,ADC_RESL存低字节,ADC_RES * 256即左移8位变16位数。第326、334、341、349、356、364行代码:PWMA_ENO= 0x00;表示PWM输出使能寄存器清零复位。第337至440行代码是PWM初始化设置,三个pwm通道没设置比较值设置为0(PWMA_CCRn = 0;n=1,2,3),PWMA_IER全部注释掉,即未开通PWMA使能中断功能,更新中断,捕获比较中断。第491行代码设置定时器0不分频工作第830行代码设置芯片引脚P2.3为准双向口第831行代码设置芯片引脚P3.0-P3.5,P3.7为准双向口第835至837行代码初始化PWM\ADC\CMP比较器第853行代码初始化PID函数2、程序运行流程(第一阶段开环强拖)程序运行入口函数是void main(void),程序从第826行开始逐行运行。先初始化PWM、ADC、CMP比较器、PID函数,接着执行第855至861行初始化LCD屏和串口,接着执行863至867行代码计算电流,这里循环128次,梁大师解释说是他独特算法,既规避了小数计算而又不会出现截断误差。((adc6_I*7)>>3) + Get_ADC10bitResult(6)=adc6_I×7÷8+Get_ADC10bitResult(6);最后值趋近第六通道ADC的八倍;ADC6_I_zero定义是16位数,ADC转换出来的数是10位,乘以8后,相当于右边移3位,从10位变13位精度数。接着程序顺序执行到第873行,进入死循环。第875至907行执行有关串口(串口和绘图跟驱动电机无多大关系,后面均略过不解析);由于前面第844行代码run_mode = MOTOR_IDLE,程序从第909行跳到执行第948至952行代码,然后返回三次从第875行执行,当第四次执行第898行代码,cnt_4ms = 4,将cnt_4ms置0复位,4ms时隙标志置1即B_4ms= 1;由于run_mode = MOTOR_IDLE,程序依然跳转执行第948至952行代码,然后转到执行第957至1005行大括号内代码:进入大括号后先复位4ms时隙标志B_4ms = 0;接着累加一次cnt_500ms,未到125,跳转执行第966行,将adc6_I、adc7_U、adc11_VR进行分别将ADC第6、7、11通道转换出来的ADC值乘以8分之七雷加,多次循环后将趋势为8倍;然后程序执行973至976行代码,通过adc11_VR值设置电机位置SetPosition;执行978、979两行代码累加PhaseTime4sum、PWM_Value两个数,但此时两数均为0;第980行代码累加换相时间PhaseSumCnt;此时TimeOut初始值等于0,程序跳转执行第991行代码,由于前面976行代码设置了SetPosition,程序执行993至998行代码,这里把run_mode = ROLL_CHECK;电机进入转动检测模式,PWMA_IER = 0x01开启了PWM更新中断,即这时候PWM中断以42.6微秒一次频率自动调用。到这里,man主函数里大循环依然继续,每1毫秒进入执行898行代码累加设置4毫秒标志,当4毫秒标志B_4ms = 1时,程序进入执行960行代码,累加设置500ms时隙标志,执行973至980行代码;当500毫秒标志为1时B_500ms = 1,执行1009至1112行代码绘图。与此同时PWM中断服务函数定期自动调用执行,即系统会自动跳转第583行执行PWM中断服务代码(第584至756行之间代码)。梁大师解释说第587至590行代码是连续检测8次,屏蔽干扰抗干扰,或PWM的影响,可靠检测到比较器状态。当程序第一次进入PWM中断服务函数时,run_mode在main函数大循环里设置为ROLL_CHECK,程序执行593行判断条件成立执行594至676行之间代码:这时两种情况,一种情况是比较器顺利检测到过零信号,电机不需要强拖启动,另一种是系统检测不到过零信号需要强拖启动。这里先按照需要强拖启动逻辑分析:这时系统通过不断自动调用PWM中断服务函数,执行累加StartTime,当StartTime超过ROLL_MaxTime 最大值为2849ms时,程序设置RollCheckIndex = 0;run_mode = PRE_POSITION;检测电机为预设位置,开始进入曲线强拖阶段(这个阶段第603到667行之间代码会被执行,但都假设没启动成功)。当程序再次进入PWM中断服务函数时,从第585行执行到593行,再跳转678行,此时程序执行682至688行代码,设置电机预设启动角angle,设置预定位占空比,全部打开PWM输出,装载PWM曲线强拉占空比,LoadPwm()(跳转执行566至574行之间代码),并将检测索引RollCheckIndex = 1,接着跳转执行722行代码AdcConvert(),即跳转执行558至564行之间代码;接着程序跳到755行复位中断标志PWMA_SR1 = 0,跳出PWM中断服务函数。这个时候,系统依然是main主循环和PWM中断服务函数在被执行。后续系统循环调用PWM中断服务函数时,程序从第585行执行到593行,再跳转678行,再跳转690行,累加变量SartCnt,再跳转到722行,跳转到755行后跳出PWM中断服务函数,直到变量SartCnt等于PRE_STATE_TIME1×23(预定位1时间),程序执行694-677行代码,调整检测索引RollCheckIndex = 2;接着系统再次调用PWM中断服务函数时,程序从第585行执行到593行,再跳转678行,再跳转690行,再跳转700行,累加变量SartCnt,等候预定位2时间,然后依然按相同套路,调用PWM中断服务函数,执行711-721之间代码,调整检测索引RollCheckIndex = 3;接着按类似步骤累加变量SartCnt,等预定位3时间,改变电机状态为启动,run_mode= STARTING;紧接着程序再次进入PWM中断服务函数,程序从从第585行执行到593行,跳转到678行,再跳转到726行,每4次累加一次SartCnt计数,强拖电机直至SartCnt > StartTime,结束强拖,设置run_mode = ROLL_CHECK;RollCheckIndex = 0;让程序下一次调用PWM中断服务函数时进入转动检测模式,检测电机过零信号。具体流程是进入PWM中断函数,程序从584行执行到593行,再多次轮番执行594-676行之间代码,期间程序设置比较器CMP+选择检测电机悬空相,相应改变检测索引RollCheckIndex,接着程序跳到755行复位中断标志PWMA_SR1 = 0; 跳出PWM中断服务函数。这个期间,系统在main大循环正常执行外,还定时调用PWM中断服务函数和比较器中断服务函数,直到run_mode = MOTOR_RUN为止,程序进入闭环pid驱动电机。