第2章:软件架构总览——分层架构思想、模块划分原则与实时性要求

各位好,我是老张。今天咱们聊聊光伏逆变器软件架构的顶层设计。说实话,我见过太多项目一开始就埋头写代码,结果后期改得痛不欲生。架构这东西,说白了就是给软件搭骨架,骨架歪了,后面怎么填肉都别扭。

我个人习惯,拿到一个逆变器项目,先花一周时间把架构想清楚。这一章,我就把这些年积累的分层思路、模块划分的坑、以及实时性怎么保证,掰开了讲给你听。

2.1 为什么必须分层?

你想想看,一个光伏逆变器里,有MPPT算法、有电网并网逻辑、有孤岛保护、有通讯协议栈、还有底层PWM驱动。如果全揉在一起,改一个地方可能牵动全局。我早期一个项目就吃过这个亏——改了个通讯协议,结果把MPPT的采样时序搞乱了,现场炸了好几台机器。

分层架构的核心思想就一句话:上层只管业务逻辑,下层只管硬件操作,中间层负责协调。这样每一层都能独立演进,互不干扰。

分层的好处:

  • 可移植性:换MCU平台,只改驱动层,应用层代码几乎不用动
  • 可测试性:每一层都可以单独写单元测试,不用搭整个硬件环境
  • 可维护性:新人接手,只看某一层的代码就能快速理解

下面这张图,是我常用的三层架构,画出来给你看:

应用层(Application Layer) MPPT算法 | 并网控制 | 孤岛保护 | 通讯协议 | 人机交互 业务逻辑,与硬件无关 特点:可移植、可复用、可独立测试 调用接口 中间件层(Middleware Layer) 任务调度 | 数据池管理 | 状态机引擎 | 日志系统 | 参数管理 承上启下,提供公共服务 特点:标准化接口、解耦、可配置 硬件抽象 驱动层(Driver Layer) PWM驱动 | ADC采样 | GPIO控制 | SPI/I2C | 定时器 直接操作寄存器,与硬件绑定 特点:平台相关、性能敏感、需严格测试

2.2 模块划分原则——别让代码变成一锅粥

模块怎么切?我总结了三句话:高内聚、低耦合、单一职责。听起来像教科书?嗯,但实际做起来很多人就忘了。

原则一:高内聚

一个模块只做一件事,并且把它做好。比如MPPT模块,就只管最大功率点追踪。别把通讯协议解析也塞进去。我曾经接手过一个项目,MPPT模块里居然还处理了WiFi配网逻辑——你说这找谁说理去?

原则二:低耦合

模块之间通过接口通信,别直接访问对方的全局变量。我习惯的做法是:每个模块暴露一个.h头文件,里面只放函数声明和结构体定义,所有内部实现都藏在.c文件里。这样别人想用你的模块,看头文件就够了。

原则三:单一职责

一个函数只做一件事。比如「读取ADC值并计算功率」这个函数,就应该拆成两个:adc_read()power_calc()。为什么?因为测试的时候,你可以单独测ADC读取准不准,再单独测功率计算对不对。

我的小技巧:

模块划分时,先画一个「模块依赖图」。箭头从依赖方指向被依赖方。如果发现箭头交叉成蜘蛛网,说明耦合太严重,赶紧重构。我一般要求依赖图是「树状结构」或者「有向无环图」。

2.3 实时性要求——逆变器是个急性子

光伏逆变器对实时性的要求,比普通嵌入式系统高得多。为什么?因为电网频率是50Hz,一个周期才20ms。你想想看,如果控制环路延迟超过1ms,波形就可能畸变,严重了直接炸机。

我按实时性要求,把任务分成三个等级:

等级 响应时间 典型任务 实现方式
硬实时 < 100μs PWM占空比更新、过流保护、过压保护 硬件中断 + 最高优先级任务
软实时 1ms ~ 10ms MPPT算法、锁相环(PLL)、电压电流环控制 定时器中断 + 中等优先级任务
非实时 > 100ms 通讯处理、日志记录、参数保存 后台任务 / 空闲任务

硬实时任务,说白了就是「晚一微秒就出事」。比如过流保护,电流一旦超过阈值,必须在几个微秒内关断PWM,否则功率管就烧了。我一般把这些任务放在中断服务函数里,并且中断优先级设到最高。

软实时任务,允许偶尔抖动,但不能持续超时。比如MPPT算法,如果某次计算晚了2ms,可能效率下降一点,但不会炸机。不过如果连续几次都超时,那功率曲线就会乱跳。我习惯用RTOS的定时器任务来跑这些,优先级设中等。

非实时任务,晚一点没关系。比如通过Modbus上报数据,晚个几百毫秒用户根本感觉不到。这些任务我放在最低优先级,有空就跑,没空就等。

避坑指南:

我曾经在一个项目里,把通讯任务和MPPT任务放在同一个优先级。结果通讯量大时,MPPT被频繁抢占,导致MPPT效率下降了5%。后来我把通讯任务降了一级,问题立刻解决。记住:实时性要求高的任务,优先级一定要高,而且不能被低优先级任务阻塞

2.4 一个实际案例:10kW三相逆变器的任务分配

拿我去年做的一个10kW三相并网逆变器举例,任务分配是这样的:

// 任务优先级定义(数值越小优先级越高)
#define PRIO_ISR_FAULT     0   // 硬件故障保护(硬实时)
#define PRIO_ISR_PWM       1   // PWM更新(硬实时)
#define PRIO_TASK_CTRL     2   // 电压电流环控制(软实时,1ms周期)
#define PRIO_TASK_MPPT     3   // MPPT算法(软实时,10ms周期)
#define PRIO_TASK_PLL      3   // 锁相环(软实时,5ms周期)
#define PRIO_TASK_COMM     4   // 通讯处理(非实时)
#define PRIO_TASK_LOG      5   // 日志记录(非实时)
#define PRIO_TASK_IDLE     6   // 空闲任务

你看,故障保护优先级最高,直接走硬件中断。PWM更新也走中断,但优先级略低。控制环路用RTOS任务,1ms跑一次。MPPT和PLL共享优先级3,但通过时间片轮转保证都不饿死。通讯和日志就放后台慢慢跑。

这样设计的好处是:即使通讯任务卡住了,控制环路和故障保护依然能正常工作。我测试过,哪怕通讯任务死锁,逆变器依然能稳定并网发电——这就是分层和优先级设计带来的鲁棒性。

2.5 总结一下

这一章我们聊了三件事:

  • 分层架构:应用层、中间件层、驱动层,各司其职,互不干扰
  • 模块划分:高内聚、低耦合、单一职责,别让代码变成一锅粥
  • 实时性要求:硬实时走中断,软实时用RTOS任务,非实时放后台

嗯,架构这东西,说起来简单,做起来全是细节。下一章我们深入聊聊应用层的核心——MPPT算法怎么设计。到时候我会拿一个实际项目的代码,一行一行给你拆解。


公众号:蓝海资料掘金营,微信deep3321