找回密码
 立即注册
查看: 460|回复: 6

SP-BLDC 单相直流无刷电机,有感驱动原理图参考和程序参考

[复制链接]
已绑定手机
已实名认证

146

主题

4038

回帖

1万

积分

超级版主

积分
10650
发表于 5 天前 | 显示全部楼层 |阅读模式

STC8H1K08 单相无刷电机(有感)驱动板


一块单相 BLDC 驱动板,风扇类负载用。有感方案,MCU 出两路逻辑信号
驱动集成 H 桥


主控:STC8H1K08-36I
  QFN20,17 个 I/O
  24MHz 内部 IRC,免晶振
  8K Flash / 1K xdata
  10 位 ADC,9 通道
  T0/T1/T2 三个定时器
  本工程只用 8 个 I/O


功率级:DRV8870DDAR/HG 集成 H 桥
  工作电压 8~36V(绝对最大 40V)
  输出 3.5A 峰值 / 2.5A 连续
  导通电阻 0.65Ω(上下管之和)
  内部死区 50~500ns,无需外接
  限流由 VREF+ISEN 外接电阻设定,本板 2.5A
  自带 OCP、对地/对电源短路保护、UVLO、OTP
  外围只要一颗采样电阻加一个分压,不用栅极驱动、自举电容、续流二极管


位置检测:MGH201A1T3 双极锁存霍尔
  SOT-23-3,工作电压 3.5~30V
  工作点 +40GS / 释放点 -40GS
  最大开关频率 10kHz,开漏输出(外接 10k 上拉)
  工作温度 -50~+150℃
  单相换相必须用双极锁存型,全极型和单极型都不行


调速:两路输入,自动仲裁
  PWM:接 P1.7。PWMB 的 CC5+CC6 同时捕上升沿和下降沿,上升沿兼作复位
       触发,捕到的值直接就是周期和高电平宽度,载波允许 100Hz~50kHz
  VSP:接 P1.0(ADC0),0~5V 模拟量,10k+100nF 滤波
  有 PWM 就用 PWM,200ms 没捕到边沿自动切 VSP
  VSP 端加了 10k 下拉,悬空时读到 0、电机停转
  软启动 800ms,占空比从 0 缓升


FG 反馈
  P3.5 推挽输出,固件里 FG = HALL 跟随霍尔,串 1k
  MCU 的 I/O 耐压只有 VDD+0.3V,要更高电平需外加 N-MOS 转换


固件
  T0 做 1ms 节拍,INT0 双边沿换相,ADC 和 PWMB 各一个中断
  中断里只置标志,换相在主循环做
  换相极性、PWM 频率、软启动时间等都是宏,改数重编即可


成本
  打样(各买 1 片)      约 6.58 元
  量产(1000 片以上)    约 3.28 元
  MCU 占 24%,H 桥占 27%,其余是阻容和少量外围


小结
  集成 H 桥 + 纯寄存器固件,功率级外围只剩一颗 IC 加一颗采样电阻,
  代码也能做到很小。适合风扇、水泵这类不需要复杂控制的场合。




SCH_Schematic1_1-P1_2026-09-17.png
程序预览:
  1. /*==============================================================================
  2. * 文件名称: main.c
  3. * 文件描述: 单相直流无刷电机(有感 / 霍尔)驱动例程,纯寄存器实现
  4. * 目标芯片: STC8H1K08-36I(QFN20 / TSSOP20 / SOP20)
  5. * 功率级  : 集成 H 桥 DRV8870DDAR/HG(ESOP-8)
  6. * 文件版本: V1.0
  7. * 修改记录:
  8. *   1. (2026-09-16) 创建文件
  9. *----------------------------------------------------------------------------
  10. * 整体实现:
  11. *   过流保护由 DRV8870 内部完成(OCP、电机对地/对电源短路保护、UVLO、OTP,
  12. *   以及由 VREF 与 ISEN 外接电阻设定的硬件限流),MCU 侧只负责霍尔换相、
  13. *   调速给定与 FG 输出。
  14. *
  15. * 功能:
  16. *   1) PWM 调速:外部方波占空比输入(P1.7)
  17. *   2) VSP 调速:模拟电压输入(P1.0 / ADC0)
  18. *   3) FG 反馈:输出霍尔信号(P3.5)
  19. *   4) 霍尔换相(P3.2 / INT0)与 H 桥驱动(P1.4 / P1.6)
  20. *
  21. * 使用前提:
  22. *   ISP 下载时须将 IRC 频率设为 24MHz。本程序不做时钟初始化,复位后使用
  23. *   上次下载时设定的高速 IRC 时钟。若下载时选用其它频率,PWM 载波、1ms
  24. *   节拍与测速结果会按比例偏移。
  25. *============================================================================*/
  26. #include <stc8h.h>
  27. #include <intrins.h>
  28. /*==============================================================================
  29. * 一、配置参数
  30. *============================================================================*/
  31. #define FOSC            24000000UL      /* 系统时钟,由 ISP 下载时设定 */
  32. #define PWM_FREQ_HZ     20000UL         /* H 桥载波频率 */
  33. #define PWM_ARR_V       ((unsigned int)(FOSC / PWM_FREQ_HZ - 1UL))
  34. /* 定时器 0 定时 1ms。模式 0 为 16 位自动重装载,中断内不需重装初值。
  35. * 重装值 = 65536 - FOSC/1000 = 65536 - 24000 = 41536 (0xA240) */
  36. #define T0_RELOAD_V     ((unsigned int)(65536UL - FOSC / 1000UL))
  37. /* 霍尔方向极性。上电试转后若转向与要求相反,改为 1(等价于换相表取反) */
  38. #define HALL_POLARITY   0
  39. /* PWM 输入捕获时基:PSCR = 3,捕获时钟 = FOSC/4 = 6MHz。
  40. * 20kHz 输入对应 300 个计数,量程充裕 */
  41. #define PWMIN_PSCR      3
  42. #define PWMIN_CLK_HZ    (FOSC / (PWMIN_PSCR + 1))
  43. #define PWMIN_TIMEOUT_MS 200            /* 无捕获超过此时长,判为无 PWM 信号 */
  44. #define DUTY_FULL       1000            /* 占空比满刻度(千分比) */
  45. #define SOFTSTART_MS    800             /* 软启动时长,占空比由 0 升到目标值 */
  46. /*==============================================================================
  47. * 二、引脚定义
  48. *
  49. * 一律使用 sbit 按位操作,不整字节写 P1 口:P1.7 是 PWM 输入脚,整字节写
  50. * 会把它的锁存器一起改掉,导致该脚读不到高电平。
  51. *============================================================================*/
  52. sbit IN1    = P1 ^ 4;       /* H 桥 IN1(PWMA 通道 3 的 P 输出) */
  53. sbit IN2    = P1 ^ 6;       /* H 桥 IN2(PWMA 通道 4 的 P 输出) */
  54. sbit PWMIN  = P1 ^ 7;       /* PWM 调速输入(只作输入,程序不驱动该脚) */
  55. sbit HALL   = P3 ^ 2;       /* 霍尔输入(INT0) */
  56. sbit FG     = P3 ^ 5;       /* FG 反馈输出 */
  57. /*==============================================================================
  58. * 三、模块内变量
  59. *============================================================================*/
  60. static volatile unsigned int  g_vsp;            /* VSP 的 8 位 ADC 值,0~255 */
  61. static volatile unsigned int  g_cap_period;     /* PWM 输入:周期计数值 */
  62. static volatile unsigned int  g_cap_high;       /* PWM 输入:高电平计数值 */
  63. static volatile unsigned char g_cap_fresh;      /* 有一次新的下降沿捕获 */
  64. static volatile bit           g_edge;           /* 霍尔边沿事件 */
  65. static volatile bit           g_tick;           /* 1ms 定时事件 */
  66. static volatile unsigned int  g_ms;             /* 毫秒计数 */
  67. static unsigned int  g_last_cap_ms;             /* 最近一次捕获的时刻 */
  68. static unsigned int  g_pwmin_duty;              /* PWM 输入换算的占空比(千分比) */
  69. static unsigned char g_pwmin_ok;                /* PWM 输入信号是否有效 */
  70. static unsigned char g_dir;                     /* 当前驱动状态,0=A,1=B */
  71. static unsigned int  g_duty;                    /* 当前占空比(千分比) */
  72. static unsigned int  g_duty_target;             /* 目标占空比(千分比) */
  73. /*==============================================================================
  74. * 四、中断服务程序
  75. *
  76. * 中断向量号:INT0 = 0,TMR0 = 1,ADC = 5,PWMB = 27。
  77. * 中断内只置标志或存数据,不做分支判断、不调用函数。
  78. *============================================================================*/
  79. /* 霍尔输入:INT0 双边沿中断。一个电周期换相两次,两种边沿都要响应,
  80. * 故不区分上升沿与下降沿,只置标志,换相在主循环完成。 */
  81. void INT0_Isr(void) interrupt 0
  82. {
  83.     g_edge = 1;
  84. }
  85. /* 1ms 节拍。模式 0 为自动重装载,中断内不需写 TH0/TL0。 */
  86. void TMR0_Isr(void) interrupt 1
  87. {
  88.     g_ms++;
  89.     g_tick = 1;
  90. }
  91. /* ADC 转换完成:清完成标志,取结果,再启动下一次转换。
  92. * ADCCFG = 0x0F 时结果左对齐,ADC_RES 即 10 位结果的高 8 位。 */
  93. void ADC_Isr(void) interrupt 5
  94. {
  95.     ADC_CONTR &= ~0x20;
  96.     g_vsp = ADC_RES;
  97.     ADC_CONTR |= 0x40;
  98. }
  99. /* PWMB 捕获中断:CC5 捕上升沿得到周期,CC6 捕下降沿得到高电平宽度。 */
  100. void PWMB_Isr(void) interrupt 27
  101. {
  102.     if (PWMB_SR1 & 0x02)                        /* CC5IF */
  103.     {
  104.         PWMB_SR1 &= ~0x02;                      /* 写 0 清标志 */
  105.         g_cap_period = PWMB_CCR5;               /* 复位模式下捕获值即周期 */
  106.     }
  107.     if (PWMB_SR1 & 0x04)                        /* CC6IF */
  108.     {
  109.         PWMB_SR1 &= ~0x04;
  110.         g_cap_high = PWMB_CCR6;                 /* 捕获值即高电平宽度 */
  111.         g_cap_fresh = 1;
  112.     }
  113. }
  114. /*==============================================================================
  115. * 五、初始化
  116. *============================================================================*/
  117. /*----------------------------------------------------------------------------
  118. * 函数名称: port_init
  119. * 功能描述: 配置端口工作模式与上电初始电平
  120. * 入口参数: 无
  121. * 函数返回: 无
  122. *
  123. * PxM1:PxM0 = 00 准双向 / 01 推挽 / 10 高阻输入 / 11 开漏。本工程各脚:
  124. *   P1.0  高阻输入   VSP 模拟输入
  125. *   P1.4  推挽输出   H 桥 IN1
  126. *   P1.6  推挽输出   H 桥 IN2
  127. *   P1.7  准双向     PWM 调速输入
  128. *   P3.2  准双向     霍尔输入
  129. *   P3.5  推挽输出   FG 反馈
  130. *
  131. * 输出脚用推挽而非准双向:准双向的高电平由内部 5.8~6.0kΩ 弱上拉提供,
  132. * 在 20kHz 下上升沿会被拉缓。输入脚保持准双向。
  133. *
  134. * 上电顺序:先写 P1.4/P1.6 的锁存器为低,再配置端口模式。8051 端口复位后
  135. * 锁存器为高电平,若先设为输出模式会瞬时输出高电平。
  136. *--------------------------------------------------------------------------*/
  137. static void port_init(void)
  138. {
  139.     IN1 = 0;
  140.     IN2 = 0;
  141.     P0M0 = 0x00; P0M1 = 0x00;    /* 未使用端口置准双向 */
  142.     P2M0 = 0x00; P2M1 = 0x00;
  143.     P4M0 = 0x00; P4M1 = 0x00;
  144.     P5M0 = 0x00; P5M1 = 0x00;
  145.     P6M0 = 0x00; P6M1 = 0x00;
  146.     P7M0 = 0x00; P7M1 = 0x00;
  147.     P1M1 = 0x01;                 /* P1.0 高阻输入 */
  148.     P1M0 = 0x50;                 /* P1.4、P1.6 推挽输出 */
  149.     P3M1 = 0x00;
  150.     P3M0 = 0x20;                 /* P3.5 推挽输出 */
  151.     /* 准双向口作输入使用时,锁存器必须为 1,否则内部强下拉会把外部信号按住 */
  152.     PWMIN = 1;
  153.     HALL  = 1;
  154.     FG    = 0;
  155. }
  156. /*----------------------------------------------------------------------------
  157. * 函数名称: timer0_init
  158. * 功能描述: 定时器 0 配置为 1ms 定时中断
  159. * 入口参数: 无
  160. * 函数返回: 无
  161. *
  162. * 用位操作改写 TMOD 与 AUXR,避免影响 T1 与其它模块。
  163. *--------------------------------------------------------------------------*/
  164. static void timer0_init(void)
  165. {
  166.     AUXR |= 0x80;                /* T0x12 = 1,T0 使用系统时钟不分频 */
  167.     TMOD &= 0xF0;                /* 清 T0 模式位,模式 0 = 16 位自动重装载 */
  168.     TH0 = (unsigned char)(T0_RELOAD_V >> 8);
  169.     TL0 = (unsigned char)(T0_RELOAD_V & 0x00FF);
  170.     TR0 = 1;
  171.     ET0 = 1;
  172. }
  173. /*----------------------------------------------------------------------------
  174. * 函数名称: hall_int_init
  175. * 功能描述: 霍尔输入对应的 INT0 配置为双边沿触发
  176. * 入口参数: 无
  177. * 函数返回: 无
  178. *--------------------------------------------------------------------------*/
  179. static void hall_int_init(void)
  180. {
  181.     IT0 = 0;                     /* 0 = 上升沿和下降沿均触发 */
  182.     EX0 = 1;
  183. }
  184. /*----------------------------------------------------------------------------
  185. * 函数名称: pwma_init
  186. * 功能描述: PWMA 通道 3、4 配置为 PWM 输出,分别驱动 H 桥 IN1、IN2
  187. * 入口参数: 无
  188. * 函数返回: 无
  189. *
  190. * 选通道 3、4 而非通道 1、2:通道 1 的引脚会落在 P1.0/P1.1,与 VSP 的 ADC
  191. * 输入及母线采样冲突。通道 3 落在 P1.4/P1.5,通道 4 落在 P1.6/P1.7,本程序
  192. * 只用其中的 P 输出,即 P1.4 与 P1.6。
  193. *
  194. * CCMR 取 0x68(PWM 模式 1 加预装载使能),占空比在周期边界生效,可避免
  195. * 周期中途改写比较值造成的脉宽异常。预装载需要一次更新事件把初值送入影子
  196. * 寄存器,故在启动计数前置 PWMA_EGR 的 UG 位。
  197. *
  198. * 本程序不使用 PWMA 的刹车功能,PWMA_BKR 只置 MOE。
  199. *--------------------------------------------------------------------------*/
  200. static void pwma_init(void)
  201. {
  202.     P_SW2 |= 0x80;               /* 使能 XFR 扩展寄存器访问 */
  203.     PWMA_PS &= (unsigned char)~0xF0;    /* C3PS = 00 -> P1.4/P1.5;C4PS = 00 -> P1.6/P1.7 */
  204.     PWMA_CCER2 = 0x00;           /* 写 CCMRx 前先清零 CCERx */
  205.     PWMA_CCMR3 = 0x68;           /* 通道 3:PWM 模式 1(OC3M = 110)+ OC3PE = 1 */
  206.     PWMA_CCMR4 = 0x68;           /* 通道 4:PWM 模式 1(OC4M = 110)+ OC4PE = 1 */
  207.     PWMA_CCER2 = 0x11;           /* CC3E = B0,CC4E = B4,使能两个通道 */
  208.     PWMA_PSCRH = 0x00;           /* 预分频 0,计数时钟为系统时钟 */
  209.     PWMA_PSCRL = 0x00;
  210.     PWMA_ARR = PWM_ARR_V;        /* 周期 1200 个计数,对应 20kHz */
  211.     PWMA_CCR3 = 0;               /* 比较值为 0 时输出保持为低 */
  212.     PWMA_CCR4 = 0;
  213.     PWMA_EGR = 0x01;             /* UG = 1,产生更新事件,将预装载值送入影子寄存器 */
  214.     PWMA_ENO = 0x00;             /* 暂不输出波形,引脚交由 GPIO 控制 */
  215.     PWMA_BKR = 0x80;             /* MOEA = 1,使能主输出 */
  216.     PWMA_CR1 = 0x01;             /* CEN = 1,开始计数 */
  217. }
  218. /*----------------------------------------------------------------------------
  219. * 函数名称: adc_init
  220. * 功能描述: ADC 配置为通道 0(P1.0)连续转换,中断方式读取
  221. * 入口参数: 无
  222. * 函数返回: 无
  223. *
  224. * ADCCFG = 0x0F 时 ADC 时钟为系统时钟/2/16,结果为左对齐,ADC_RES 即 10 位
  225. * 结果的高 8 位。对调速给定而言 8 位分辨率足够。
  226. *--------------------------------------------------------------------------*/
  227. static void adc_init(void)
  228. {
  229.     P_SW2 |= 0x80;               /* ADCTIM 位于 XFR 空间,访问前须使能 */
  230.     ADCTIM = 0x3F;               /* ADC 内部时序 */
  231.     ADCCFG = 0x0F;
  232.     ADC_CONTR = 0x80;            /* B7 ADC_POWER = 1,通道号默认 0(P1.0) */
  233.     EADC = 1;
  234.     ADC_CONTR |= 0x40;           /* B6 ADC_START = 1,启动转换 */
  235. }
  236. /*----------------------------------------------------------------------------
  237. * 函数名称: pwmb_init
  238. * 功能描述: PWMB 的 CC5 + CC6 捕获 P1.7 上的 PWM 输入,得到周期与高电平宽度
  239. * 入口参数: 无
  240. * 函数返回: 无
  241. *
  242. * 用两个捕获通道同时捕获同一管脚的上升沿与下降沿,并以上升沿作为复位触发,
  243. * 这样捕获值直接就是周期和高电平宽度,不需再做前后两次相减。
  244. *
  245. * 捕获脚选 P1.7:C5PS 的另外三个选项 P2.0 / P0.0 / P7.4 在 1K08 上都不存在,
  246. * 而 P1.6 已被 H 桥占用。
  247. *--------------------------------------------------------------------------*/
  248. static void pwmb_init(void)
  249. {
  250.     P_SW2 |= 0x80;
  251.     PWMB_PS &= ~0x03;
  252.     PWMB_PS |= 0x01;             /* C5PS = 01,捕获脚为 P1.7 */
  253.     PWMB_PSCR = PWMIN_PSCR;      /* 捕获时基 6MHz */
  254.     PWMB_ARR  = 0xFFFF;          /* 自由计数,由上升沿复位触发清零 */
  255.     PWMB_CCER1 = 0x00;           /* 写 CCMRx 前先清零 CCERx */
  256.     PWMB_CCMR1 = 0x01;           /* CC5 输入模式,映射到 TI5FP1 */
  257.     PWMB_CCMR2 = 0x02;           /* CC6 输入模式,映射到 TI5FP2 */
  258.     PWMB_CCER1 = 0x11;           /* 使能 CC5/CC6 捕获 */
  259.     PWMB_CCER1 |= 0x20;          /* CC6 捕获极性为下降沿 */
  260.     PWMB_SMCR  = 0x54;           /* TS = TI5FP1,SMS = 100 上升沿复位模式 */
  261.     PWMB_SR1   = 0x00;           /* 清全部状态标志 */
  262.     PWMB_IER   = 0x06;           /* B1 CC5IE、B2 CC6IE */
  263.     PWMB_CR1   = 0x01;           /* CEN = 1,开始计数 */
  264. }
  265. /*==============================================================================
  266. * 六、功率级驱动
  267. *
  268. * 驱动方式:
  269. *   同一时刻只使能一个通道的 PWM 输出,另一脚由 GPIO 固定输出低电平。
  270. *     状态 A: P1.4 输出 PWM,P1.6 由 GPIO 输出 0  -> IN1 = PWM, IN2 = 0
  271. *     状态 B: P1.6 输出 PWM,P1.4 由 GPIO 输出 0  -> IN1 = 0, IN2 = PWM
  272. *   DRV8870 真值表:IN1 IN2 = 10 正转、01 反转、00 滑行、11 刹车。
  273. *   PWM 关断期间两脚均为 0,即进入滑行(快衰减)。
  274. *
  275. * 电平由 GPIO 锁存器决定,而非用比较值取 0 或大于 ARR 的方式产生固定电平:
  276. * 后者取决于计数器与预装载、更新事件的时序,存在出现异常窄脉冲的可能;
  277. * ENO 置 0 后引脚直接由锁存器决定,结果是确定的。
  278. *
  279. * 状态切换顺序:先把即将变为静态的那一脚锁存器写成 0,再修改 PWMA_ENO。
  280. *============================================================================*/
  281. /*----------------------------------------------------------------------------
  282. * 函数名称: drive_apply_duty
  283. * 功能描述: 将占空比写入当前活动通道的比较寄存器
  284. * 入口参数: permille  占空比,千分比,0~1000
  285. * 函数返回: 无
  286. *--------------------------------------------------------------------------*/
  287. static void drive_apply_duty(unsigned int permille)
  288. {
  289.     unsigned int ccr;
  290.     if (permille > DUTY_FULL)
  291.     {
  292.         permille = DUTY_FULL;
  293.     }
  294.     ccr = (unsigned int)(((unsigned long)permille * (unsigned long)PWM_ARR_V) / (unsigned long)DUTY_FULL);
  295.     if (g_dir == 0)
  296.     {
  297.         PWMA_CCR3 = ccr;
  298.     }
  299.     else
  300.     {
  301.         PWMA_CCR4 = ccr;
  302.     }
  303. }
  304. /*----------------------------------------------------------------------------
  305. * 函数名称: drive_commutate
  306. * 功能描述: 依据霍尔电平确定并切换到对应的驱动状态
  307. * 入口参数: 无
  308. * 函数返回: 无
  309. * 说明    : 一个电周期只有两个驱动状态,霍尔电平直接给出当前应处的状态。
  310. *--------------------------------------------------------------------------*/
  311. static void drive_commutate(void)
  312. {
  313.     unsigned char d;
  314.     d = (unsigned char)((HALL ? 1 : 0) ^ HALL_POLARITY);
  315.     if (d != g_dir)
  316.     {
  317.         if (d == 0)
  318.         {
  319.             IN2 = 0;                 /* 先确定静态电平 */
  320.             PWMA_ENO = 0x10;         /* ENO3P = 1,P1.4 输出 PWM */
  321.         }
  322.         else
  323.         {
  324.             IN1 = 0;
  325.             PWMA_ENO = 0x40;         /* ENO4P = 1,P1.6 输出 PWM */
  326.         }
  327.         g_dir = d;
  328.     }
  329. }
  330. /*----------------------------------------------------------------------------
  331. * 函数名称: drive_coast
  332. * 功能描述: 停机,两桥臂输出低电平(IN1 = IN2 = 0,滑行)
  333. * 入口参数: 无
  334. * 函数返回: 无
  335. * 说明    : 先写锁存器确定电平,再将 ENO 清 0 把引脚交回 GPIO。
  336. *--------------------------------------------------------------------------*/
  337. static void drive_coast(void)
  338. {
  339.     IN1 = 0;
  340.     IN2 = 0;
  341.     PWMA_ENO = 0x00;
  342. }
  343. /*----------------------------------------------------------------------------
  344. * 函数名称: drive_brake
  345. * 功能描述: 短路制动,两桥臂输出高电平(IN1 = IN2 = 1,低边慢衰减)
  346. * 入口参数: 无
  347. * 函数返回: 无
  348. * 说明    : 仅适用于集成 H 桥。若换成"分立全 N 管 + 无死区半桥驱动"的功率级,
  349. *           两路同时为高会造成上下管直通,不可使用。
  350. *           本函数已实现但当前无调用者,供需要制动时使用。
  351. *--------------------------------------------------------------------------*/
  352. static void drive_brake(void)
  353. {
  354.     IN1 = 1;
  355.     IN2 = 1;
  356.     PWMA_ENO = 0x00;
  357. }
  358. /*==============================================================================
  359. * 七、调速指令采集
  360. *============================================================================*/
  361. /*----------------------------------------------------------------------------
  362. * 函数名称: pwmin_poll_1ms
  363. * 功能描述: 换算 PWM 输入的占空比,并判断信号是否有效
  364. * 入口参数: 无
  365. * 函数返回: 无
  366. *--------------------------------------------------------------------------*/
  367. static void pwmin_poll_1ms(void)
  368. {
  369.     unsigned int period;
  370.     unsigned int high;
  371.     if (g_cap_fresh)
  372.     {
  373.         g_cap_fresh = 0;
  374.         g_last_cap_ms = g_ms;
  375.         period = g_cap_period;
  376.         high   = g_cap_high;
  377.         if ((period > 4) && (high <= period))
  378.         {
  379.             unsigned long f = (unsigned long)PWMIN_CLK_HZ / (unsigned long)period;
  380.             /* 频率窗口:低于 100Hz 或高于 50kHz 视为无效信号 */
  381.             if ((f >= 100UL) && (f <= 50000UL))
  382.             {
  383.                 g_pwmin_duty = (unsigned int)(((unsigned long)high * (unsigned long)DUTY_FULL) / (unsigned long)period);
  384.                 g_pwmin_ok   = 1;
  385.             }
  386.             else
  387.             {
  388.                 g_pwmin_ok = 0;
  389.             }
  390.         }
  391.         else
  392.         {
  393.             g_pwmin_ok = 0;
  394.         }
  395.     }
  396.     /* 超时无捕获,判为无 PWM 信号,改用 VSP */
  397.     if ((unsigned int)(g_ms - g_last_cap_ms) > (unsigned int)PWMIN_TIMEOUT_MS)
  398.     {
  399.         g_pwmin_ok = 0;
  400.     }
  401. }
  402. /*----------------------------------------------------------------------------
  403. * 函数名称: vsp_to_permille
  404. * 功能描述: 将 8 位 ADC 值换算为占空比千分比
  405. * 入口参数: 无
  406. * 函数返回: 占空比,0~1000
  407. *--------------------------------------------------------------------------*/
  408. static unsigned int vsp_to_permille(void)
  409. {
  410.     unsigned int v = g_vsp;
  411.     if (v > 255)
  412.     {
  413.         v = 255;
  414.     }
  415.     return (unsigned int)(((unsigned long)v * (unsigned long)DUTY_FULL) / 255UL);
  416. }
  417. /*==============================================================================
  418. * 八、1ms 任务
  419. *============================================================================*/
  420. /*----------------------------------------------------------------------------
  421. * 函数名称: control_1ms
  422. * 功能描述: 采集调速指令、软启动斜坡、输出占空比
  423. * 入口参数: 无
  424. * 函数返回: 无
  425. * 说明    : 两路调速输入的仲裁规则为:PWM 输入有效时用 PWM,否则用 VSP。
  426. *--------------------------------------------------------------------------*/
  427. static void control_1ms(void)
  428. {
  429.     pwmin_poll_1ms();
  430.     if (g_pwmin_ok)
  431.     {
  432.         g_duty_target = g_pwmin_duty;
  433.     }
  434.     else
  435.     {
  436.         g_duty_target = vsp_to_permille();
  437.     }
  438.     /* 软启动与回落:每毫秒变化 1 个千分点,满刻度约 1s */
  439.     if (g_duty < g_duty_target)
  440.     {
  441.         g_duty++;
  442.     }
  443.     else if (g_duty > g_duty_target)
  444.     {
  445.         g_duty--;
  446.     }
  447.     drive_apply_duty(g_duty);
  448. }
  449. /*==============================================================================
  450. * 九、主函数
  451. *============================================================================*/
  452. /*----------------------------------------------------------------------------
  453. * 函数名称: main
  454. * 功能描述: 初始化各模块后进入主循环
  455. * 入口参数: 无
  456. * 函数返回: 无
  457. * 说明    : 初始化顺序为 端口 -> 时基 -> 中断源 -> 外设,最后开总中断。
  458. *           EA = 1 必须在所有模块初始化完成之后执行,否则 T0、INT0、ADC、
  459. *           PWMB 的中断都不会响应。
  460. *           换相在主循环中执行,中断只置标志,以免换相动作被中断打断。
  461. *--------------------------------------------------------------------------*/
  462. void main(void)
  463. {
  464.     port_init();                 /* 端口模式与桥臂上电初始电平 */
  465.     timer0_init();               /* T0,1ms 节拍 */
  466.     hall_int_init();             /* INT0,霍尔双边沿 */
  467.     pwma_init();                 /* PWMA 通道 3、4,H 桥 IN1/IN2 */
  468.     adc_init();                  /* ADC0,VSP */
  469.     pwmb_init();                 /* PWMB CC5/CC6,PWM 调速输入 */
  470.     g_dir         = 0xFF;        /* 置为无效值,使首次换相必然执行 */
  471.     g_duty        = 0;           /* 占空比从 0 起,配合软启动 */
  472.     g_duty_target = 0;
  473.     g_vsp         = 0;
  474.     g_pwmin_ok    = 0;
  475.     g_last_cap_ms = 0;
  476.     EA = 1;                      /* 开总中断 */
  477.     drive_commutate();           /* 按当前霍尔位置建立初始驱动状态,此时占空比为 0 */
  478.     while (1)
  479.     {
  480.         FG = HALL;               /* FG 反馈:直接输出霍尔信号 */
  481.         if (g_edge)
  482.         {
  483.             g_edge = 0;
  484.             drive_commutate();   /* 霍尔边沿换相 */
  485.         }
  486.         if (g_tick)
  487.         {
  488.             g_tick = 0;
  489.             control_1ms();       /* PWM 与 VSP 调速 */
  490.         }
  491.     }
  492. }
复制代码


文件下载
单相无刷电机 有感驱动.pdf (135.37 KB, 下载次数: 35)
one_bldc_hall_min.zip (66.64 KB, 下载次数: 34)
1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

已绑定手机

9

主题

409

回帖

1114

积分

金牌会员

积分
1114
发表于 5 天前 | 显示全部楼层
收藏了
回复

使用道具 举报 送花

5

主题

26

回帖

1109

积分

金牌会员

积分
1109
发表于 5 天前 | 显示全部楼层
学习了
回复

使用道具 举报 送花

14

主题

76

回帖

1086

积分

金牌会员

积分
1086
发表于 5 天前 | 显示全部楼层
收藏收藏!~!~
STC32G配置包:https://gitee.com/main.c/SxPackages.STC32G
STC8配置包:https://gitee.com/main.c/SxPackages.STC8
回复

使用道具 举报 送花

14

主题

76

回帖

1086

积分

金牌会员

积分
1086
发表于 5 天前 | 显示全部楼层
强烈要求楼主发实物图, 就像https://www.stcaimcu.com/forum.php?mod=viewthread&tid=17266这个帖子这样的
STC32G配置包:https://gitee.com/main.c/SxPackages.STC32G
STC8配置包:https://gitee.com/main.c/SxPackages.STC8
回复

使用道具 举报 送花

已绑定手机

1

主题

40

回帖

147

积分

注册会员

积分
147
发表于 5 天前 | 显示全部楼层
代码认真的看完了。谢谢分享,学习了。
回复

使用道具 举报 送花

1

主题

15

回帖

47

积分

新手上路

积分
47
发表于 4 天前 | 显示全部楼层
g_vsp 类型声明为 unsigned int 但只存 8 位 ADC 值(ADC_RES 高 8 位)。资源浪费 1 字节
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-9-22 10:39 , Processed in 0.073512 second(s), 83 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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