找回密码
 立即注册
楼主: 梁工

三相无刷电机驱动-STC8H/STC32G系列-无HALL-PID控制-OLED显示-串口绘图 启动改进版本

  [复制链接]
  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-20 17:43:09 | 显示全部楼层
本意是想用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是原理图,请大佬指点一下。不甚感谢!
波形图.jpg

SCH_无刷电机驱动STC8H_2026-03-20.pdf

281.07 KB, 下载次数: 4

电机启动情况.mp4

1.54 MB, 下载次数: 2

点评

1、连续8个采样为0/1则比较器输出0/1:抗干扰,避免偶发干扰或PWM的影响,可靠检测到比较器状态。 2、预定位:用于转子初始电位 RollCheckIndex =0时,-60°定位,让转子定位于-60°,4096对应360°,则angle=  详情 回复 发表于 2026-3-23 00:26
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:342
  • 最近打卡:2026-05-11 11:09:37

85

主题

7369

回帖

1万

积分

超级版主

积分
15921
发表于 2026-3-23 00:26:45 | 显示全部楼层
垂柳*** 发表于 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 。

请提供你的电机的而定最高转速参数。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-23 10:59:02 | 显示全部楼层
感谢梁大师抽空解答,我咨询大疆客服,获悉该电机是2312型号 12V 10000转。我移植的驱动代码只针对STC8H8K64U芯片修改了PWM和ADC引脚,其他的没动,特别是没看动的函数,压根无从下手。请指导一下后续如何修改,谢谢
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-23 11:18:27 | 显示全部楼层
梁工:我弄的反电动势电路滤波电容用103,电阻是1k欧(1.5k用完了),除了主控芯片和这三个电阻外,其他的都原样抄您的作业。请指导一下变更了电阻,发生了什么变化,需要关注哪些,哪些关键点是后续调试需要查看和检测重点,请赐教一下调试流程,方便这学期24位小伙伴们动手学用STC芯片,谢谢。

点评

你的驱动电压是多少V?分压电阻选择原则是(MCU-VCC=5V):驱动电压 *分压=3.5左右。 按理应该很容易驱动起来,请仔细检查反电动势过0检测的顺序,我只想到是这个问题了。  详情 回复 发表于 2026-3-24 11:10
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-23 22:29:06 | 显示全部楼层

梁大师:请赐教一下.
  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位又怎么理解?

点评

1、重复128次读电流值并做低通滤波计算,得到0点电流时的ADC值。一般不少于32次就可以了。 2、这是最简单的数字一阶低通滤波算法:Y(n) = Y(n-1)*(1-a) + X(n)*a,a  详情 回复 发表于 2026-3-24 11:06
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:342
  • 最近打卡:2026-05-11 11:09:37

85

主题

7369

回帖

1万

积分

超级版主

积分
15921
发表于 2026-3-24 11:06:55 | 显示全部楼层
垂柳*** 发表于 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位的结果。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:342
  • 最近打卡:2026-05-11 11:09:37

85

主题

7369

回帖

1万

积分

超级版主

积分
15921
发表于 2026-3-24 11:10:21 | 显示全部楼层
垂柳*** 发表于 2026-3-23 11:18
梁工:我弄的反电动势电路滤波电容用103,电阻是1k欧(1.5k用完了),除了主控芯片和这三个电阻外,其他的 ...

你的驱动电压是多少V?分压电阻选择原则是(MCU-VCC=5V):驱动电压 *分压=3.5左右。

按理应该很容易驱动起来,请仔细检查反电动势过0检测的顺序,我只想到是这个问题了。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-24 23:05:16 | 显示全部楼层
学习梁大师无感无刷直流电机驱动源代码心得


简要了解STC公司芯片高级PWM中断产生机制
STC公司芯片PWM定时器本质上是一个16位计数器,它可以工作在向上计数、向下计数或中央对齐模式。PWM中断就像这个计数器在特定位置设立的路标。下面的流程图展示了在一个PWM周期内,中断是如何产生的
关键点解析:
更新中断:当计数器计到自动重载值(ARR 并归零时产生。这通常标志着一个完整PWM周期的结束。
比较中断:计数器计到比较值(CCR 时产生。在BLDC控制中,这个中断常用来在特定占空比时刻做点事情,比如触发ADC采样。
标志位必须软件清零:进入中断后,如果不手动清除中断标志位,程序会以为又有新中断进来,导致反复进出中断,这是初学者最容易犯错的地方。
二、结合STC公司梁大师的官方无感BLDC程序深度解析PWM中断的核心逻辑
1. 初始化阶段:中断源的配置
在无感BLDC程序中,PWM中断通常不是为了改变占空比,而是为了定时换相。这个阶段要理解两个中断逻辑:
更新中断:电机每转一圈,需要换相6次。但PWM周期通常远快于换相周期官方梁大师程序利用PWM的更新中断来做一个精准的时间片,查询读取比较器结果装载计算30电角度等。
比较中断:在无感控制中,比较器检测到反电动势过零后,需要延时30度电角度才能换相。这个30度延时就是利用PWM的比较中断实现的:设定一个比较值,计数器到达时触发中断,执行换相。
2. 运行阶段:中断服务函数的核心逻辑
中断服务函数我个人认为是复杂、也最容易绕晕的地方,反正我是被糊涂了好久梁大师的无感BLDC的中断分为两大阵营:PWM周期中断和比较器中断(两者配合使用),主要完成过零检测、计算延时、曲线强拖和跟踪电位器油门等任务,具体执行流程如下图。
过零检测:电机转子旋转时,未通电的那一相会产生反电动势。当这个电压过零时,芯片内部的比较器会产生一个中断。
计算延时:在比较器中断中,程序会计算从过零下一次换相的时间。这个时间通常是当前换相间隔的一半(即30度电角度)。
曲线强拖预设占空比,程序自主换相改变,拉着磁场往前走一步
PWM周期中断:与此同时,PWM本身的更新中断一直在运行,它负责根据油门(比如ADC采样值)更新占空比,从而控制电机转速。
总之对于基础薄弱的,初看源代码是一头雾水,主要是梁大师把PWM定时器、普通定时器和比较器联合使用,组成了一个状态机PWM中断:负责时间基准占空比更新。比较器中断:负责位置反馈(过零检测)。定时器中断:负责逻辑执行(换相、消磁延时)
以上就是这一周研学梁大师源代码的心得,不知道是否是正确,如有错误,请大师或者论坛管理员直接更正,旨在协助初学者顺利地学习领悟梁大师大作,助力国产芯片普及应用。
另外,希望梁大师赐教一下源代码中消磁pid控制这部分知识(我还没学明白),下一步计划对照梁大师代码,逐行解析源代码,从main主函数开始,以源代码行号为指引尝试解析程序运行流程,抛砖引玉。

deepseek_mermaid_20260324_75426e.png
deepseek_mermaid_20260324_ee731e.png
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-24 23:07:11 | 显示全部楼层
我是用12伏服务器电源供电,万用表测量电压是12.2伏
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:788
  • 最近打卡:2026-05-11 06:46:35
已绑定手机

31

主题

136

回帖

502

积分

高级会员

积分
502
发表于 2026-3-26 20:20:16 | 显示全部楼层
学习梁大师无感无刷直流电机驱动源代码心得(二)
三、分析程序运行流程和机制
梁大师无刷无感直流电动机驱动有几个版本,本稿是依据本论题首页“三相无刷电机驱动-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驱动电机。

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-5-11 12:33 , Processed in 0.123082 second(s), 88 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表