• 正文
  • 相关推荐
申请入驻 产业图谱

CW32L010步进电机驱动器-02-三层架构

1小时前
18
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

本文对应的视频:第二集《架构设计:资源分配、协作与三层架构》。 代码工程:

通过网盘分享的文件:CW32L010步进电机正弦微步驱动器.zip

链接: https://pan.baidu.com/s/1pR0wOT3D30N38uOFXtpbDg?pwd=L010 提取码: L010

一、资源分配

拿到一块新板子,常见的做法是翻开数据手册,逐个研究外设怎么用。这个做法对初学者来讲直接有效,但不适用于项目开发。板载外设和片上外设是制造商提供的,而项目的功能需求和外设使用需求,只有设计者自己清楚。先看芯片,等于让芯片的功能决定项目的形态。显然,这不符合需求驱动的基本开发逻辑。

正确做法是先列出功能需求,再逐项转化为开发需求,最后按计划实现。本项目开发的驱动器要实现的功能有四个:产生驱动波形、采样电流实现过流保护、接收上位机指令、电机转速可控。

四个需求对应四类外设资源。

驱动波形:使用高级定时器 ATIM产生互补;选择理由:需要互补输出与死区,普通定时器做不到

电流采样:12位ADC + 运放电路;选择理由:经采样电阻取得的信号弱,必须先放大再转换

指令通信:UART接口;选择理由:引脚占用少,驱动板预留引脚中仅剩的选择

转速调节:基础定时器 BTIM;选择理由:结构最简单、开销最小

驱动波形为什么必须用高级定时器?通用定时器只能输出相互独立的 PWM 通道,而电机驱动需要的是带死区的互补输出信号,两路信号的时序关系必须由硬件保证。软件翻转 GPIO 也能凑出 PWM,但时序稳定性易被中断破坏。

二、资源协作

资源分配完成,接下来的问题是这些外设怎么协同工作。整个系统由两条链路构成。

控制链路(命令操控电机):上位机发指令 -> UART 接收数据 -> 协议解析成参数 -> 计算电机运行的寄存器参数-> 写入对应寄存器。

保护链路(系统保护自己):采样电阻截取电机电流 -> 运放放大采样电压 -> ADC 采样 -> 过载判断 ->危险?锁存停机并切断 PWM:继续运行。

两条链路方向相反,互不干扰又互为协同:控制链路追求”按命令动作”,保护链路追求”异常时立刻停手”。保护链路的优先级高于控制链路,一旦过载,控制链路算出的任何参数都不再被执行。

三、采样触发方式

架构设计阶段有一个决策容易被当成实现细节跳过,那便是ADC 如何触发?

按复杂程度划分有两个选择。

软件轮询。由主循环定时去读 ADC,实现简单;缺点是主循环一旦忙于别的事,采样就不准时。

硬件触发。由定时器直接触发 ADC,转换完成后进中断,采样节奏完全由硬件保证。

本项目选择硬件触发,理由是保护链路的性质决定了该功能对实时性有较高的要求,因此必须严格控制采样时序以保证数据的准确性。

 这是一处架构层面的选择,不是实现层面的。软件轮询的代码量更少,但把”准时”这件事交给了主循环的忙闲;硬件触发把这件事交给了定时器硬件,代价是配置稍复杂。凡是安全相关的时序,都不该依赖软件调度的及时性。

四、通信资源的取舍

这块驱动板原本是作为电调开发板设计的,没有预留指令通信接口。板上的可用通信口只剩下一处 ESC 调速接口和预留的霍尔传感器接口,这两个接口都可以纳入考虑,具体如何选择会在通信接口篇章中分析。

通信接口计划选用UART接口,全双工工作模式、通信速率115200、8 位数据位、1 位停止位。

五、任务分层

架构设计最核心的一步是按实时性把任务分层。实时性划分的标准因项目而异,但总体按照任务错过后可容忍程度来划分,即任务错过后越难以容忍,实时性需求越高,反之越可以容忍,则实时性需求越低。

举例:

步进换相任务的执行时间约 5 微秒。若迟到几十微秒,对 5 微秒的任务来说就是十倍的时序误差,其执行搓过的直接结果表现为失步,因此认为该任务实时性需求更高。

协议解析,因其仅用于后续电机转动参数的计算和执行,这个任务的服务方是上位机,只要能接受命令到执行中间这个延迟,那就没问题,任务错过的后果也只是晚一点执行,容忍度较高,因此认为该任务实时性需求更低。

基于上述原则得到本项目各任务的三层结构(图 1):

图 1 任务实时性分层

5.1 各任务归到哪一层?

分层的判据是”响应时限”,具体到本项目的每个功能,其分层情况如下:

协议接收 -> 中断层。 字节到达就必须立即存入缓冲区。若改为在主循环里轮询读取,主循环一次的耗时就可能覆盖掉连续到达的字节。

步进控制 -> 中断层。 基础定时器每走一步触发一次中断,错过会引起电机失步。

过流保护 -> 中断层。 ADC 转换完成即判断,错过周期就会漏掉故障。

协议解析与电机控制 -> 任务层。 两者都不要求微秒级响应,交由调度器统一管理。

5.2 中断优先级

中断之间会互相打断,因此必须排定优先级。排序的依据是某中断被延迟后,影响的严重程度(图 2)。

图 2 三档中断优先级

实际代码中的优先级配置如下:

/* 系统时基: SysTick = 1ms, 见 core_cm0plus.h 的默认设置 *//* 实时控制与外设: 同优先级, 可互相打断 */NVIC_SetPriority(BTIM1_IRQn,2);/* 步进换相 */NVIC_SetPriority(ADC_IRQn,2);/* 过流保护, 注释: 次于 SysTick, 高于 GTIM1 */NVIC_SetPriority(UART2_IRQn,2);/* 协议接收 *//* 最轻的活: 仅清中断标志 */NVIC_SetPriority(GTIM1_IRQn,3);/* 注释: 最低优先级, 仅清标志 */

三档的分法值得说明三点。

步进换相与过流保护同级。两者都是实时控制的要害,且都需要微秒级响应,人为分出高低没有意义-同级意味着可以互相打断,谁先在硬件上触发谁先执行。

协议接收也是优先级 2。这与”中断层只放硬实时”的说法看似矛盾,实际不然:协议接收在中断里只做一件事-把字节存入缓冲区,耗时同样是微秒级。

系统时基的优先级最高。若时间基准本身被延迟,所有依赖计时的任务都会跟着漂移,这一层的错误会被放大到全系统

5.3 分层的代码实现

主循环里只有一个调度器调用:

/*── 主循环: 仅跑调度器, 无任何阻塞 ─────────────────*/while(1){Sched_Poll();}

中断层则相反,没有任何循环与等待:

voidBTIM1_IRQHandler(void){Motor_ExecuteStep();/* 查正弦表 -> 写 CCR -> 清标志, 约 5μs */}

任务层夹在两者之间,由静态任务表登记:

/*── 注册调度器任务 ─────────────────────────────────*//* 0号系统任务负责协议管道: burstDone->解析->执行->应答->删除 */Sched_Add(Motor_CtrlTask,10, RUN_FOREVER, SCHED_PRIO_NORMAL,"Ctrl");Sched_Add(Calib_Task,10, RUN_FOREVER, SCHED_PRIO_NORMAL,"Calib");

六、初始化顺序也是架构的一部分

三层结构不只体现在运行期,初始化顺序同样遵循它。main() 的顺序是:系统时钟 -> 调度器 -> 硬件外设 -> 电机 -> 通信 -> 标定 -> 注册任务 -> 主循环。

SystemClkInit();/* HSI 48MHz, PCLK 不分频      */InitTick(48000000);/* SysTick = 1ms               */Sched_Init();/* 初始化任务表, 系统心跳就绪    */Timer_PWM_Init();/* ATIM1: 18kHz + 4路互补 + 2μs死区 */ADC_InitCurrent();/* ADC: PB2(CH9), 单次转换       */

这里有两点需要注意。

调度器排在硬件之前。系统心跳必须先就绪,后面的模块才有统一的时基可用。若顺序颠倒,各模块只能各自用循环延时,分层立刻失效。

Chopper_Reset() 的注释写明”电机不在此启动”。上电后电机保持停止,直到上位机发来第一条命令。这一条把”初始化”与”运行”彻底分开:初始化只把硬件配置到可用状态,是否动作由运行期决定。上电不会意外转动,调试时也不必反复插拔电源。

七、小结

本篇讲解了如何按照需求分配、协调可用资源,也简单说明了项目开发中对任务进行实时性分层的必要性,可以总结为以下四点

从需求出发,把四项需求映射到四类外设

设计资源协作,搭好控制链路与保护链路

按实时性分三层:硬实时进中断,软实时进任务,最闲的进主循环

排定中断优先级,越影响时间基准的越优先

扫码加入QQ群3群| 610403240

相关推荐

以开放、共享、互助为理念,致力于构建武汉芯源半导体CW32系列MCU生态社区。无论是嵌入式MCU小自还是想要攻破技术难题的工程师,亦或是需求解决方案的产品经理都可在CW32生态社区汲取营养、共同成长。