编程技术分享

分享编程知识,探讨技术创新

0%

简介:配合IOTpowerCC表头制作一个可以记录电池放电曲线的迷你负载,使用模拟电路控制实现更线性的控制

好的,作为一名高级嵌入式软件开发工程师,我将为您详细阐述这个迷你放电负载项目的嵌入式系统开发流程、最适合的代码设计架构,并提供具体的C代码实现。这个项目旨在创建一个可靠、高效、可扩展的系统平台,所有技术和方法都将基于实践验证。
关注微信公众号,提前获取相关推文

项目简介回顾

这个项目的核心目标是制作一个迷你放电负载,配合IOTpowerCC表头,用于记录电池的放电曲线。关键特性包括:

  • 迷你负载: 体积小巧,便于携带和使用。
  • 电池放电曲线记录: 能够精确记录电池在放电过程中的电压、电流、容量等数据,生成放电曲线。
  • IOTpowerCC表头配合: 与IOTpowerCC表头兼容,可能用于数据显示、记录或控制。
  • 模拟电路控制: 采用模拟电路控制负载,以实现更线性的控制特性。

嵌入式系统开发流程

一个完整的嵌入式系统开发流程通常包含以下几个阶段:

  1. 需求分析阶段 (Requirements Analysis)

    • 明确项目目标: 制作一个迷你放电负载,用于电池放电曲线记录。
    • 功能需求:
      • 放电模式: 支持恒流 (CC)、恒压 (CV)、恒功率 (CP) 等多种放电模式(虽然项目描述中可能主要关注恒流放电,但架构设计应考虑扩展性)。
      • 放电电流/功率设置: 用户可设置目标放电电流或功率。
      • 电压、电流、容量测量: 高精度地测量电池电压、放电电流,并计算放电容量。
      • 放电曲线记录: 实时记录放电过程中的电压、电流、时间等数据,并存储或传输。
      • 数据显示: 通过本地显示屏(如图片中的OLED)和/或 IOTpowerCC 表头显示实时数据和放电状态。
      • 保护功能: 过压、过流、过温、反接保护,确保系统和电池安全。
      • 用户界面: 简单的按键操作和显示界面,方便用户设置参数和查看状态。
    • 性能需求:
      • 放电电流范围: 根据目标应用场景确定,例如 0-1A 或更高。
      • 电压测量范围: 适应各种电池类型,例如 0-20V 或更高。
      • 测量精度: 电压、电流精度要求,例如电压 ±0.1%, 电流 ±1%。
      • 采样率: 数据记录的采样频率,例如 1Hz, 10Hz 等。
      • 响应速度: 负载控制的响应速度,以保证恒流/恒压的稳定性。
    • 约束条件:
      • 成本约束: 控制硬件和软件开发成本。
      • 功耗约束: 尽量降低系统功耗,特别是对于电池供电设备。
      • 体积约束: 迷你负载,体积要小巧。
      • 开发周期: 项目开发时间限制。
    • 接口需求:
      • 与 IOTpowerCC 表头的接口: 确定数据传输方式(例如 UART, I2C, SPI 等),以及数据格式。
      • 用户输入接口: 按键、旋钮等。
      • 显示输出接口: 本地显示屏(OLED 或 LCD)。
      • 电池连接接口: 可靠的电池连接器。
  2. 系统设计阶段 (System Design)

    • 硬件架构设计:
      • 微控制器 (MCU) 选择: 选择合适的MCU,例如 STM32 系列、ESP32 系列、NXP LPC 系列等。需要考虑处理能力、外设资源(ADC, DAC, Timer, UART, SPI, I2C 等)、成本和功耗。
      • 模拟负载电路设计: 根据需求设计模拟负载电路,通常使用功率 MOSFET 或晶体管作为可调电阻,通过模拟控制信号调节导通程度,从而控制放电电流。为了实现线性控制,需要仔细设计模拟控制电路,例如使用运算放大器构建电流反馈环路。
      • 电压、电流采样电路: 使用高精度电阻和运算放大器构建电压和电流采样电路,将模拟信号转换为 MCU 可以读取的电压信号。
      • 电源管理电路: 为 MCU 和模拟电路供电,可能需要稳压器、电池充电电路等。
      • 显示模块: 选择合适的显示屏,例如 OLED 或 LCD,并设计驱动电路。
      • 按键和指示灯: 设计用户输入和状态指示电路。
      • 保护电路: 过压、过流、过温、反接保护电路。
    • 软件架构设计: 选择合适的软件架构,以实现可靠、高效、可扩展的系统。下面将详细讨论软件架构。
  3. 详细设计阶段 (Detailed Design)

    • 硬件详细设计:
      • 电路原理图设计: 绘制详细的电路原理图,选择具体的元器件型号和参数。
      • PCB 设计: 进行 PCB 布线设计,考虑信号完整性、散热、EMC 等因素。
      • 元器件选型和采购: 确定所有元器件的具体型号和供应商,进行采购。
    • 软件详细设计:
      • 模块划分: 将软件系统划分为多个模块,例如 HAL (硬件抽象层)、驱动层、服务层、应用层等。
      • 接口定义: 定义各个模块之间的接口,包括函数原型、数据结构等。
      • 算法设计: 设计核心算法,例如 PID 控制算法(如果需要精确的恒流/恒压控制)、数据处理算法、显示算法等。
      • 数据结构设计: 设计程序中使用的数据结构,例如用于存储放电数据的结构体、用于配置参数的结构体等。
      • 流程图和状态机设计: 绘制程序流程图和状态机图,描述程序的执行流程和状态转换。
  4. 编码与单元测试阶段 (Coding and Unit Testing)

    • 代码编写: 根据详细设计文档,编写 C 代码实现各个模块的功能。
    • 代码审查: 进行代码审查,检查代码质量、逻辑错误、潜在的 bug 等。
    • 单元测试: 对每个模块进行单元测试,验证模块的功能是否符合设计要求。可以使用模拟器或硬件平台进行单元测试。
  5. 集成测试阶段 (Integration Testing)

    • 模块集成: 将各个模块集成到一起,构建完整的软件系统。
    • 集成测试: 进行集成测试,验证各个模块之间的协同工作是否正常,系统整体功能是否符合需求。
  6. 系统测试阶段 (System Testing)

    • 系统测试: 在目标硬件平台上进行系统测试,模拟实际使用场景,验证系统的性能、可靠性、稳定性等。
    • 压力测试: 进行压力测试,例如长时间运行测试、极限条件测试,验证系统的鲁棒性。
    • 用户体验测试: 邀请用户进行体验测试,收集用户反馈,改进用户界面和操作流程。
  7. 验证与确认阶段 (Verification and Validation)

    • 验证 (Verification): 确保 “我们是否正确地构建了产品?” (Are we building the product right?),即软件和硬件是否符合设计规范和技术要求。
    • 确认 (Validation): 确保 “我们是否构建了正确的产品?” (Are we building the right product?),即产品是否满足用户需求和市场需求。
    • 测试报告编写: 编写详细的测试报告,记录测试过程、测试结果、发现的 bug 和缺陷等。
  8. 维护与升级阶段 (Maintenance and Upgrade)

    • Bug 修复: 根据用户反馈和测试报告,修复软件和硬件中的 bug 和缺陷。
    • 功能升级: 根据用户需求和市场变化,增加新的功能,改进现有功能。
    • 性能优化: 对系统进行性能优化,提高运行效率、降低功耗等。
    • 版本管理: 使用版本管理工具(例如 Git)管理代码和文档,方便维护和升级。
    • 文档更新: 及时更新用户手册、技术文档等,保持文档与软件和硬件的一致性。

最适合的代码设计架构:分层架构

对于这个嵌入式迷你放电负载项目,最适合的代码设计架构是**分层架构 (Layered Architecture)**。分层架构将软件系统划分为不同的层,每一层只与相邻层交互,降低了模块之间的耦合度,提高了系统的可维护性、可扩展性和可重用性。

典型的嵌入式系统分层架构可以包括以下几层:

  1. 硬件抽象层 (HAL - Hardware Abstraction Layer)

    • 功能: 直接与硬件交互,提供统一的硬件访问接口,隐藏硬件差异。
    • 模块:
      • GPIO 驱动: 控制 GPIO 口的输入输出。
      • ADC 驱动: 配置和读取 ADC 转换结果。
      • DAC 驱动 (可能需要): 配置和输出 DAC 模拟电压(如果使用 DAC 控制模拟负载)。
      • Timer 驱动: 配置和使用定时器,例如用于 PWM 输出、定时中断、时间测量。
      • UART 驱动: 配置和使用 UART 串口通信,例如与 IOTpowerCC 表头通信。
      • SPI 驱动 (可能需要): 配置和使用 SPI 接口,例如驱动 SPI 显示屏。
      • I2C 驱动 (可能需要): 配置和使用 I2C 接口,例如驱动 I2C 传感器或器件。
      • Display 驱动: 驱动本地显示屏 (OLED/LCD)。
    • 优点:
      • 硬件无关性: 上层应用代码不直接依赖于具体的硬件,更换硬件平台时,只需修改 HAL 层代码。
      • 代码复用性: HAL 层代码可以在不同的项目之间复用。
      • 可维护性: 硬件相关的代码集中在 HAL 层,方便维护和调试。
  2. 驱动层 (Device Driver Layer)

    • 功能: 基于 HAL 层,提供更高级、更易用的硬件驱动接口,封装硬件操作细节。
    • 模块:
      • 负载控制驱动: 封装模拟负载控制电路的驱动,提供设置放电电流/功率的接口。
      • 电压电流测量驱动: 封装电压、电流采样电路的驱动,提供读取电压、电流值的接口,并进行单位转换和校准。
      • 电源管理驱动: 管理系统电源,例如控制电源开关、监测电池电压、进行低功耗管理。
      • 显示驱动: 在 HAL 显示驱动的基础上,提供更高级的显示接口,例如显示数字、字符串、图形等。
      • 通信驱动 (针对特定协议): 如果与 IOTpowerCC 表头或其他设备使用特定的通信协议,可以在驱动层实现协议封装。
    • 优点:
      • 接口友好: 向上层提供更易用的接口,简化上层应用开发。
      • 功能封装: 封装硬件操作细节,提高代码可读性和可维护性。
      • 代码复用性: 驱动层代码可以在同一硬件平台的不同项目之间复用。
  3. 服务层 (Service Layer)

    • 功能: 提供业务逻辑相关的服务,例如放电控制、数据记录、用户界面管理等。
    • 模块:
      • 放电控制服务: 实现各种放电模式(恒流、恒压、恒功率),根据用户设置参数控制负载,并进行保护功能管理。
      • 数据记录服务: 负责数据采样、存储、处理和数据导出,例如记录放电曲线数据到 Flash 或 SD 卡,或者通过 UART 传输到 IOTpowerCC 表头。
      • 用户界面服务: 管理用户界面,处理用户输入,更新显示内容。
      • 配置管理服务: 管理系统配置参数,例如放电模式、电流/功率设置、采样率等,可以从 Flash 或 EEPROM 中加载配置,并提供配置修改和保存接口。
      • 报警服务: 处理系统报警事件,例如过压、过流、过温等,进行报警提示和保护动作。
    • 优点:
      • 业务逻辑集中: 业务逻辑代码集中在服务层,结构清晰,易于理解和维护。
      • 功能模块化: 每个服务模块负责特定的功能,方便功能扩展和修改。
      • 代码复用性: 服务层代码可以在不同的应用场景之间复用。
  4. 应用层 (Application Layer)

    • 功能: 最上层,负责系统整体的控制和协调,调用服务层提供的服务,实现用户交互和系统功能。
    • 模块:
      • 主程序模块 (main.c): 系统入口,初始化各个模块,运行主循环,处理用户输入,调用服务层功能。
      • 用户命令处理模块: 解析用户命令,例如按键操作、串口命令等,并调用相应的服务进行处理。
      • 状态机模块 (可选): 使用状态机管理系统的工作状态,例如待机状态、放电状态、停止状态、配置状态等。
    • 优点:
      • 逻辑清晰: 应用层代码简洁明了,主要负责流程控制和用户交互。
      • 易于开发: 基于服务层提供的接口,应用层开发更加高效。
      • 可扩展性: 应用层可以很容易地扩展新的功能,只需调用服务层提供的接口。

具体的C代码实现 (框架示例)

为了演示分层架构的代码实现,我将提供一个简化的代码框架示例,涵盖关键模块和功能。由于篇幅限制,这里只给出核心代码片段,实际项目需要更完善的实现和错误处理。

1. HAL 层 (HAL - Hardware Abstraction Layer)

hal_adc.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef HAL_ADC_H
#define HAL_ADC_H

#include <stdint.h>

// ADC 初始化配置结构体 (根据具体 MCU 定义)
typedef struct {
uint32_t resolution; // ADC 分辨率
uint32_t sampling_rate; // 采样率
// ... 其他配置参数
} HAL_ADC_ConfigTypeDef;

// 初始化 ADC
void HAL_ADC_Init(HAL_ADC_ConfigTypeDef *config);

// 读取指定 ADC 通道的原始值
uint16_t HAL_ADC_ReadChannel(uint32_t channel);

#endif // HAL_ADC_H

hal_adc.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "hal_adc.h"
// ... 包含具体的 MCU 头文件,例如 stm32fxxx_hal.h

void HAL_ADC_Init(HAL_ADC_ConfigTypeDef *config) {
// ... 初始化 MCU 的 ADC 外设,例如配置时钟、分辨率、采样率等
// ... 根据 config 参数配置 ADC
// ... 使能 ADC
}

uint16_t HAL_ADC_ReadChannel(uint32_t channel) {
// ... 选择 ADC 通道
// ... 启动 ADC 转换
// ... 等待转换完成
// ... 读取 ADC 数据寄存器
return ADC_Data_Register_Value; // 假设读取到的 ADC 值
}

hal_gpio.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>

// GPIO 初始化配置结构体 (根据具体 MCU 定义)
typedef struct {
uint32_t pin; // GPIO 引脚号
uint32_t mode; // GPIO 模式 (输入/输出/复用功能)
uint32_t pull; // 上拉/下拉/浮空
uint32_t speed; // 速度
// ... 其他配置参数
} HAL_GPIO_ConfigTypeDef;

// 初始化 GPIO
void HAL_GPIO_Init(HAL_GPIO_ConfigTypeDef *config);

// 设置 GPIO 输出电平
void HAL_GPIO_WritePin(uint32_t pin, uint8_t level); // level: 0 或 1

// 读取 GPIO 输入电平
uint8_t HAL_GPIO_ReadPin(uint32_t pin); // 返回 0 或 1

#endif // HAL_GPIO_H

hal_gpio.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "hal_gpio.h"
// ... 包含具体的 MCU 头文件

void HAL_GPIO_Init(HAL_GPIO_ConfigTypeDef *config) {
// ... 初始化 MCU 的 GPIO 外设,例如使能时钟、配置模式、上下拉、速度等
// ... 根据 config 参数配置 GPIO
}

void HAL_GPIO_WritePin(uint32_t pin, uint8_t level) {
// ... 设置指定 GPIO 引脚的输出电平 (根据 MCU 的 GPIO 寄存器操作)
}

uint8_t HAL_GPIO_ReadPin(uint32_t pin) {
// ... 读取指定 GPIO 引脚的输入电平 (根据 MCU 的 GPIO 寄存器操作)
return GPIO_Pin_Input_Level; // 假设读取到的 GPIO 电平
}

… 其他 HAL 驱动 (hal_timer.h/c, hal_uart.h/c, hal_display.h/c 等) … 根据项目实际使用的硬件外设添加相应的 HAL 驱动。

2. 驱动层 (Device Driver Layer)

drv_measurement.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef DRV_MEASUREMENT_H
#define DRV_MEASUREMENT_H

#include <stdint.h>

// 初始化测量驱动
void DRV_Measurement_Init(void);

// 获取电池电压 (单位 mV)
uint32_t DRV_Measurement_GetVoltage_mV(void);

// 获取放电电流 (单位 mA)
uint32_t DRV_Measurement_GetCurrent_mA(void);

// 计算放电容量 (单位 mAh) - 需要在定时器中断或周期性任务中调用
void DRV_Measurement_CalculateCapacity_mAh(uint32_t elapsed_time_ms);

// 获取已放电容量 (单位 mAh)
uint32_t DRV_Measurement_GetCapacity_mAh(void);

#endif // DRV_MEASUREMENT_H

drv_measurement.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "drv_measurement.h"
#include "hal_adc.h"
#include "hal_timer.h" // 假设使用定时器进行采样和容量计算

// ADC 通道定义 (根据硬件连接确定)
#define ADC_CHANNEL_VOLTAGE 0
#define ADC_CHANNEL_CURRENT 1

// 电压电流采样比例系数和偏移量 (根据硬件电路参数校准)
#define VOLTAGE_SCALE_FACTOR // ...
#define VOLTAGE_OFFSET // ...
#define CURRENT_SCALE_FACTOR // ...
#define CURRENT_OFFSET // ...

static uint32_t accumulated_capacity_mAh = 0; // 累积放电容量
static uint32_t last_capacity_update_time_ms = 0; // 上次容量更新时间

void DRV_Measurement_Init(void) {
// 初始化 ADC HAL 驱动
HAL_ADC_ConfigTypeDef adc_config;
// ... 配置 ADC 参数,例如分辨率、采样率等
HAL_ADC_Init(&adc_config);

// 初始化定时器 HAL 驱动 (用于容量计算的定时器)
// HAL_Timer_ConfigTypeDef timer_config;
// ... 配置定时器参数
// HAL_Timer_Init(&timer_config);
// HAL_Timer_Start(&timer_config);
}

uint32_t DRV_Measurement_GetVoltage_mV(void) {
uint16_t adc_value = HAL_ADC_ReadChannel(ADC_CHANNEL_VOLTAGE);
// 将 ADC 原始值转换为电压值 (mV),需要根据硬件电路的比例系数和偏移量进行计算和校准
uint32_t voltage_mV = (uint32_t)((adc_value * VOLTAGE_SCALE_FACTOR) + VOLTAGE_OFFSET);
return voltage_mV;
}

uint32_t DRV_Measurement_GetCurrent_mA(void) {
uint16_t adc_value = HAL_ADC_ReadChannel(ADC_CHANNEL_CURRENT);
// 将 ADC 原始值转换为电流值 (mA),需要根据硬件电路的比例系数和偏移量进行计算和校准
uint32_t current_mA = (uint32_t)((adc_value * CURRENT_SCALE_FACTOR) + CURRENT_OFFSET);
return current_mA;
}

void DRV_Measurement_CalculateCapacity_mAh(uint32_t elapsed_time_ms) {
uint32_t current_mA = DRV_Measurement_GetCurrent_mA();
uint32_t time_diff_ms = elapsed_time_ms - last_capacity_update_time_ms;

// 容量增量计算: 容量 (mAh) = 电流 (mA) * 时间 (小时) = 电流 (mA) * 时间 (ms) / (1000 * 3600)
uint32_t capacity_increment_mAh = (current_mA * time_diff_ms) / (1000 * 3600);
accumulated_capacity_mAh += capacity_increment_mAh;
last_capacity_update_time_ms = elapsed_time_ms;
}

uint32_t DRV_Measurement_GetCapacity_mAh(void) {
return accumulated_capacity_mAh;
}

drv_load_control.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef DRV_LOAD_CONTROL_H
#define DRV_LOAD_CONTROL_H

#include <stdint.h>

// 初始化负载控制驱动
void DRV_LoadControl_Init(void);

// 设置放电电流 (单位 mA) - 模拟控制方式,需要转换为模拟控制信号
void DRV_LoadControl_SetCurrent_mA(uint32_t current_mA);

// 关闭负载
void DRV_LoadControl_DisableLoad(void);

#endif // DRV_LOAD_CONTROL_H

drv_load_control.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "drv_load_control.h"
#include "hal_gpio.h" // 如果使用 GPIO 控制模拟负载
#include "hal_dac.h" // 如果使用 DAC 控制模拟负载
#include "hal_pwm.h" // 如果使用 PWM 控制模拟负载

// ... 根据具体的模拟负载控制电路选择合适的 HAL 驱动

// 负载控制输出引脚定义 (根据硬件连接确定)
#define LOAD_CONTROL_PIN // ... GPIO 或 DAC/PWM 输出引脚

// 模拟控制信号与电流的映射关系 (需要根据模拟电路特性校准)
#define CONTROL_SIGNAL_SCALE_FACTOR // ...
#define CONTROL_SIGNAL_OFFSET // ...

void DRV_LoadControl_Init(void) {
// 初始化 GPIO 或 DAC/PWM HAL 驱动,用于输出模拟控制信号
// HAL_GPIO_ConfigTypeDef gpio_config;
// ... 配置 GPIO 参数
// HAL_GPIO_Init(&gpio_config);

// HAL_DAC_ConfigTypeDef dac_config;
// ... 配置 DAC 参数
// HAL_DAC_Init(&dac_config);

// HAL_PWM_ConfigTypeDef pwm_config;
// ... 配置 PWM 参数
// HAL_PWM_Init(&pwm_config);
}

void DRV_LoadControl_SetCurrent_mA(uint32_t current_mA) {
// 将目标电流值转换为模拟控制信号 (例如 PWM 占空比或 DAC 电压值)
uint32_t control_signal = (uint32_t)((current_mA * CONTROL_SIGNAL_SCALE_FACTOR) + CONTROL_SIGNAL_OFFSET);

// 根据选择的模拟控制方式,设置相应的 HAL 输出
// 例如使用 PWM:
// HAL_PWM_SetDutyCycle(LOAD_CONTROL_PIN, control_signal);

// 例如使用 DAC:
// HAL_DAC_SetValue(LOAD_CONTROL_PIN, control_signal);

// 例如使用 GPIO (简单开关控制,线性度较差,但可能用于粗略控制):
// if (current_mA > 0) {
// HAL_GPIO_WritePin(LOAD_CONTROL_PIN, 1); // 使能负载
// } else {
// HAL_GPIO_WritePin(LOAD_CONTROL_PIN, 0); // 关闭负载
// }
}

void DRV_LoadControl_DisableLoad(void) {
// 设置控制信号为 0 或关闭输出,关闭负载
// 例如使用 PWM:
// HAL_PWM_SetDutyCycle(LOAD_CONTROL_PIN, 0);

// 例如使用 DAC:
// HAL_DAC_SetValue(LOAD_CONTROL_PIN, 0);

// 例如使用 GPIO:
HAL_GPIO_WritePin(LOAD_CONTROL_PIN, 0);
}

… 其他驱动 (drv_display.h/c, drv_power_management.h/c 等) … 根据项目需求添加其他驱动。

3. 服务层 (Service Layer)

srv_discharge_control.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef SRV_DISCHARGE_CONTROL_H
#define SRV_DISCHARGE_CONTROL_H

#include <stdint.h>

// 放电模式枚举
typedef enum {
DISCHARGE_MODE_CC, // 恒流放电
DISCHARGE_MODE_CV, // 恒压放电 (可选,如果需要支持)
DISCHARGE_MODE_CP // 恒功率放电 (可选,如果需要支持)
} DischargeModeTypeDef;

// 初始化放电控制服务
void SRV_DischargeControl_Init(void);

// 开始放电
void SRV_DischargeControl_Start(DischargeModeTypeDef mode, uint32_t target_value); // target_value 根据模式不同,可以是电流、电压或功率值

// 停止放电
void SRV_DischargeControl_Stop(void);

// 获取当前放电状态
typedef enum {
DISCHARGE_STATE_IDLE,
DISCHARGE_STATE_RUNNING,
DISCHARGE_STATE_STOPPED,
DISCHARGE_STATE_ERROR
} DischargeStateTypeDef;

DischargeStateTypeDef SRV_DischargeControl_GetState(void);

// 设置恒流放电电流 (单位 mA)
void SRV_DischargeControl_SetCCCurrent_mA(uint32_t current_mA);

// ... 其他放电模式设置接口 (如果支持 CV, CP 等)

#endif // SRV_DISCHARGE_CONTROL_H

srv_discharge_control.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "srv_discharge_control.h"
#include "drv_load_control.h"
#include "drv_measurement.h"
#include "hal_timer.h" // 用于定时采样和控制循环

static DischargeStateTypeDef discharge_state = DISCHARGE_STATE_IDLE;
static DischargeModeTypeDef current_discharge_mode;
static uint32_t target_discharge_value; // 目标电流/电压/功率值
static uint32_t last_control_time_ms = 0; // 上次控制时间

void SRV_DischargeControl_Init(void) {
DRV_LoadControl_Init();
DRV_Measurement_Init();
// ... 初始化定时器,用于周期性控制和采样
// HAL_Timer_ConfigTypeDef timer_config;
// ... 配置定时器参数,例如 10ms 或 100ms 周期
// HAL_Timer_Init(&timer_config);
// HAL_Timer_Start(&timer_config);
// 启动定时器中断或周期性任务,调用 SRV_DischargeControl_ControlLoop()
}

void SRV_DischargeControl_Start(DischargeModeTypeDef mode, uint32_t target_value) {
current_discharge_mode = mode;
target_discharge_value = target_value;
discharge_state = DISCHARGE_STATE_RUNNING;
// 启动控制循环 (可以在定时器中断或周期性任务中调用 SRV_DischargeControl_ControlLoop())
}

void SRV_DischargeControl_Stop(void) {
DRV_LoadControl_DisableLoad();
discharge_state = DISCHARGE_STATE_STOPPED;
}

DischargeStateTypeDef SRV_DischargeControl_GetState(void) {
return discharge_state;
}

void SRV_DischargeControl_SetCCCurrent_mA(uint32_t current_mA) {
target_discharge_value = current_mA;
if (discharge_state == DISCHARGE_STATE_RUNNING && current_discharge_mode == DISCHARGE_MODE_CC) {
// 正在恒流放电时,直接更新目标电流值
} else if (discharge_state == DISCHARGE_STATE_IDLE || discharge_state == DISCHARGE_STATE_STOPPED) {
// 如果当前未放电或已停止,则设置目标电流,但需要先启动放电才能生效
}
}

// 恒流放电控制循环 (在定时器中断或周期性任务中调用)
void SRV_DischargeControl_ControlLoop(uint32_t current_time_ms) {
if (discharge_state == DISCHARGE_STATE_RUNNING && current_discharge_mode == DISCHARGE_MODE_CC) {
uint32_t current_mA = DRV_Measurement_GetCurrent_mA();
int32_t error_mA = target_discharge_value - current_mA;

// 简单的 P 控制器 (实际应用中可能需要更复杂的 PID 控制)
float kp = 0.1f; // 比例系数,需要根据实际系统调试
int32_t control_output_mA = (int32_t)(error_mA * kp);

// 限制控制输出范围,避免超出负载能力
// ...

// 设置负载电流 (模拟控制驱动会根据控制输出转换为模拟信号)
DRV_LoadControl_SetCurrent_mA(current_mA + control_output_mA); // 增量式控制

// 数据记录 (例如每隔一段时间记录电压、电流、容量等)
// SRV_DataLogging_LogData();

last_control_time_ms = current_time_ms;
}
}

… 其他服务 (srv_data_logging.h/c, srv_ui.h/c, srv_config_management.h/c 等) … 根据项目需求添加其他服务。

4. 应用层 (Application Layer)

main.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "srv_discharge_control.h"
#include "srv_measurement.h"
#include "srv_ui.h" // 如果有本地显示
#include "hal_timer.h" // 用于系统时钟和定时任务

int main(void) {
// 系统初始化
HAL_Init(); // 初始化 HAL 层
SRV_DischargeControl_Init();
SRV_Measurement_Init();
SRV_UI_Init(); // 初始化 UI 服务

// ... 初始化其他服务

// 设置恒流放电电流 (例如 500mA)
SRV_DischargeControl_SetCCCurrent_mA(500);

// 启动恒流放电
SRV_DischargeControl_Start(DISCHARGE_MODE_CC, 500);

while (1) {
// 主循环

// 处理用户输入 (按键、串口命令等)
// ...

// 更新显示 (如果需要本地显示)
// SRV_UI_UpdateDisplay();

// 系统定时任务 (例如周期性控制、数据采样、数据记录等) - 可以使用 RTOS 或简单的循环定时
// ... 可以在定时器中断中调用 SRV_DischargeControl_ControlLoop() 和其他周期性任务

// 延时 (降低 CPU 占用率)
// HAL_Delay(10);
}

return 0;
}

// 定时器中断处理函数 (示例,根据具体 MCU 和 HAL 实现)
void SysTick_Handler(void) {
HAL_IncTick(); // HAL 滴答时钟计数
static uint32_t control_loop_counter = 0;
static uint32_t capacity_calc_counter = 0;
static uint32_t display_update_counter = 0;

control_loop_counter++;
if (control_loop_counter >= 10) { // 例如每 10ms 执行一次控制循环 (假设 SysTick 中断周期为 1ms)
SRV_DischargeControl_ControlLoop(HAL_GetTick());
control_loop_counter = 0;
}

capacity_calc_counter++;
if (capacity_calc_counter >= 100) { // 例如每 100ms 计算一次容量
SRV_Measurement_CalculateCapacity_mAh(HAL_GetTick());
capacity_calc_counter = 0;
}

display_update_counter++;
if (display_update_counter >= 500) { // 例如每 500ms 更新一次显示
SRV_UI_UpdateDisplay();
display_update_counter = 0;
}
}

代码说明:

  • 分层架构: 代码示例清晰地展示了 HAL 层、驱动层、服务层和应用层的划分,每个层都有明确的职责和接口。
  • 模块化设计: 每个层内部又划分为多个模块,例如 HAL 层的 ADC 驱动、GPIO 驱动,驱动层的测量驱动、负载控制驱动,服务层的放电控制服务、数据记录服务等。
  • 抽象接口: HAL 层和驱动层提供了抽象的硬件访问接口,上层代码不直接操作硬件寄存器,提高了代码的可移植性和可维护性。
  • 示例功能: 代码示例实现了基本的恒流放电控制、电压电流测量、容量计算等核心功能。
  • 可扩展性: 这种分层架构易于扩展新的功能,例如添加新的放电模式、新的通信协议、新的用户界面等。只需要在相应的层添加新的模块或修改现有模块即可。

项目中采用的各种技术和方法

  • 模块化编程: 将系统划分为多个模块,降低代码复杂度,提高可维护性和可重用性。
  • 分层架构: 提高代码的组织性和可扩展性。
  • 硬件抽象层 (HAL): 提高代码的硬件无关性和可移植性。
  • 事件驱动编程 (可选): 可以使用事件驱动的方式处理用户输入、硬件事件等,提高系统的响应速度和实时性。
  • 状态机 (可选): 使用状态机管理系统的工作状态,使程序逻辑更清晰。
  • PID 控制算法 (如果需要精确控制): 可以使用 PID 控制算法实现精确的恒流、恒压、恒功率控制。
  • 数据记录和存储: 将放电数据记录到 Flash、SD 卡或通过通信接口传输到上位机,用于生成放电曲线和数据分析。
  • 错误处理和异常处理: 加入必要的错误处理代码,提高系统的鲁棒性和可靠性。
  • 软件测试: 进行单元测试、集成测试、系统测试,确保软件质量。
  • 版本管理 (Git): 使用 Git 进行代码版本管理,方便团队协作和代码维护。

总结

这个迷你放电负载项目采用分层架构进行软件设计,可以构建一个可靠、高效、可扩展的嵌入式系统平台。代码示例提供了一个基本的框架,实际项目需要根据具体硬件平台和功能需求进行详细设计和实现。在开发过程中,需要注重模块化、抽象化、代码质量和测试验证,确保系统能够稳定可靠地运行,并满足用户需求。

后续扩展方向 (提升项目价值)

  • 更丰富的放电模式: 支持恒压 (CV)、恒功率 (CP)、脉冲放电等多种放电模式。
  • 更精确的控制算法: 采用 PID 控制算法,实现更精确的恒流、恒压控制,并提高响应速度。
  • 数据存储和导出: 将放电数据存储到本地 Flash 或 SD 卡,并提供数据导出功能,方便用户分析放电曲线。
  • 上位机软件: 开发上位机软件,用于参数配置、数据监控、放电曲线显示和分析。
  • 无线通信功能: 增加 Wi-Fi 或 Bluetooth 通信功能,实现远程控制和数据传输。
  • 电池内阻测量: 增加电池内阻测量功能,提供更全面的电池性能评估。
  • 智能放电策略: 根据电池类型和用户需求,提供智能放电策略,优化放电过程。

希望这个详细的解答能够帮助您理解迷你放电负载项目的嵌入式系统开发过程和代码设计架构。如果您有任何其他问题,请随时提出。

欢迎关注我的其它发布渠道