编程技术分享

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

0%

简介:智能鱼缸DIY模块主控板**

关注微信公众号,提前获取相关推文

这款智能鱼缸DIY模块主控板旨在为鱼缸爱好者提供一个灵活、可定制的智能控制解决方案。它具备以下主要特点:

  1. 多传感器数据采集: 支持温度传感器、pH传感器、水位传感器、光照传感器等多种传感器,实时监测鱼缸环境参数。
  2. 智能设备控制: 可控制水泵、加热棒、照明灯、喂食器、氧气泵等设备,实现自动化管理。
  3. 可编程定时任务: 用户可以自定义定时任务,例如定时开关灯、定时喂食、定时换水等。
  4. 本地/远程控制: 支持本地按键、触摸屏控制,并预留网络接口,可扩展远程APP或Web控制。
  5. 数据记录与分析: 记录传感器数据和设备状态,方便用户查看历史数据和进行分析。
  6. 模块化设计: 采用模块化设计,方便用户根据需求自由组合和扩展功能模块。
  7. 低功耗设计: 考虑嵌入式系统的功耗限制,采用低功耗MCU和优化软件设计,降低系统功耗。
  8. 易于DIY和维护: 硬件接口标准化,软件设计清晰易懂,方便用户DIY和后期维护升级。

一、 需求分析

在项目启动阶段,需求分析至关重要。我们需要明确智能鱼缸主控板的具体功能和性能指标,以便为后续的设计和开发奠定基础。

1. 功能需求:

  • 传感器数据采集:
    • 温度传感器:实时采集水温,精度±0.5℃,范围0-50℃。
    • pH传感器:实时采集pH值,精度±0.1pH,范围0-14pH。
    • 水位传感器:监测水位高低,精度±1cm,范围0-30cm。
    • 光照传感器:监测环境光照强度,范围0-1000Lux。
    • (可扩展)溶解氧传感器、TDS传感器等。
  • 设备控制:
    • 水泵:控制水泵启停,可PWM调速(可选)。
    • 加热棒:控制加热棒启停,PID温度控制。
    • 照明灯:控制照明灯开关,PWM调光(可选),模拟日出日落。
    • 喂食器:控制喂食器定量投喂,定时喂食。
    • 氧气泵:控制氧气泵启停,定时充氧。
    • (可扩展)CO2发生器、UV杀菌灯等。
  • 用户界面:
    • 本地显示:LCD或OLED显示屏,显示实时数据、状态信息、菜单操作。
    • 本地按键/触摸:用于本地参数设置和设备控制。
    • 远程接口:预留WiFi/以太网接口,支持远程APP或Web控制(后期扩展)。
  • 定时任务:
    • 可配置多个定时任务,例如每天定时开关灯、定时喂食、定时换水等。
    • 定时任务可重复执行,可设置执行周期(每天、每周、自定义日期)。
  • 数据记录:
    • 周期性记录传感器数据和设备状态,存储在本地存储器(Flash/SD卡)。
    • 可查询历史数据,方便用户分析鱼缸环境变化趋势。
  • 报警功能:
    • 可配置报警阈值,例如水温过高/过低、pH值异常、水位过低等。
    • 报警方式:本地蜂鸣器报警、显示屏报警提示、远程APP推送报警(后期扩展)。
  • 系统配置:
    • 系统时间设置(RTC)。
    • 传感器校准参数配置。
    • 设备控制参数配置(PID参数、PWM占空比等)。
    • 网络配置(WiFi/以太网配置,后期扩展)。

2. 性能指标:

  • 实时性: 传感器数据采集周期:1秒,设备控制响应时间:<100ms。
  • 可靠性: 系统稳定运行时间:>7*24小时,平均无故障时间(MTBF):>1年。
  • 功耗: 正常工作功耗:<500mW,待机功耗:<100mW。
  • 存储容量: 数据记录存储容量:>1个月的数据(假设每分钟记录一次所有传感器数据)。
  • 扩展性: 预留足够的硬件接口和软件接口,方便用户扩展传感器、设备和通信方式。

3. 约束条件:

  • 成本: 硬件成本控制在合理范围内,满足DIY用户的需求。
  • 体积: 主控板尺寸紧凑,易于安装在鱼缸附近或集成到鱼缸设备中。
  • 开发周期: 项目开发周期控制在3-6个月。

二、 代码设计架构

为了构建一个可靠、高效、可扩展的智能鱼缸系统平台,我们采用分层架构的代码设计方法。分层架构将系统划分为若干个独立的层次,每个层次负责特定的功能,层与层之间通过清晰的接口进行交互。这种架构具有良好的模块化、可维护性和可扩展性。

1. 分层架构模型:

我们的代码设计架构主要分为以下几个层次:

  • 硬件抽象层 (HAL, Hardware Abstraction Layer):

    • 最底层,直接与硬件交互。
    • 封装硬件驱动,向上层提供统一的硬件接口。
    • 包括GPIO驱动、ADC驱动、I2C驱动、SPI驱动、定时器驱动、UART驱动、RTC驱动、Flash驱动等。
    • 硬件抽象层屏蔽了底层硬件的差异,使得上层应用可以独立于具体的硬件平台。
  • 板级支持包 (BSP, Board Support Package):

    • 位于HAL之上,为特定的硬件平台提供支持。
    • 包括系统初始化、时钟配置、中断管理、内存管理、外设初始化等。
    • BSP层将HAL层提供的通用硬件接口适配到具体的硬件平台上。
  • 操作系统层 (OSAL, Operating System Abstraction Layer):

    • 可选层,如果使用RTOS(Real-Time Operating System),则需要操作系统抽象层。
    • 封装RTOS的API,向上层提供统一的操作系统接口。
    • 如果不使用RTOS,则可以简化为一个简单的任务调度器或裸机系统。
    • 操作系统层提供任务管理、线程同步、消息队列、定时器等服务,提高系统的并发性和实时性。
  • 服务层 (Service Layer):

    • 核心层,实现智能鱼缸系统的各种核心功能。
    • 基于HAL和BSP层提供的硬件接口,实现传感器数据采集、设备控制、数据记录、定时任务、报警管理等服务。
    • 服务层将底层的硬件操作抽象成高层次的功能接口,方便应用层调用。
  • 应用层 (Application Layer):

    • 最上层,实现用户界面的逻辑和系统控制逻辑。
    • 基于服务层提供的功能接口,实现本地UI交互、远程控制逻辑、自动化控制策略、数据分析显示等应用功能。
    • 应用层直接面向用户,提供友好的用户界面和丰富的功能。

2. 模块化设计:

在每个层次内部,我们进一步采用模块化设计,将功能分解为独立的模块。例如,在服务层,可以划分为传感器管理模块、设备控制模块、定时任务模块、数据记录模块、报警管理模块等。模块之间通过定义清晰的接口进行通信,降低模块之间的耦合度,提高代码的可维护性和可复用性。

3. 代码组织结构:

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
├── Core                // 核心代码
│ ├── Inc // 头文件
│ │ ├── app.h // 应用层头文件
│ │ ├── service.h // 服务层头文件
│ │ ├── osal.h // 操作系统抽象层头文件
│ │ ├── bsp.h // 板级支持包头文件
│ │ └── hal.h // 硬件抽象层头文件
│ └── Src // 源文件
│ ├── app.c // 应用层源文件
│ ├── service.c // 服务层源文件
│ ├── osal.c // 操作系统抽象层源文件
│ ├── bsp.c // 板级支持包源文件
│ └── hal.c // 硬件抽象层源文件
├── Drivers // 驱动代码
│ ├── HAL // HAL驱动 (硬件无关)
│ │ ├── inc // HAL头文件
│ │ │ ├── hal_gpio.h
│ │ │ ├── hal_adc.h
│ │ │ ├── hal_i2c.h
│ │ │ ├── hal_spi.h
│ │ │ ├── hal_timer.h
│ │ │ ├── hal_uart.h
│ │ │ ├── hal_rtc.h
│ │ │ └── hal_flash.h
│ │ └── src // HAL源文件
│ │ ├── hal_gpio.c
│ │ ├── hal_adc.c
│ │ ├── hal_i2c.c
│ │ ├── hal_spi.c
│ │ ├── hal_timer.c
│ │ ├── hal_uart.c
│ │ ├── hal_rtc.c
│ │ └── hal_flash.c
│ └── BSP // BSP驱动 (硬件相关)
│ ├── inc // BSP头文件
│ │ ├── bsp_config.h // BSP配置头文件
│ │ ├── bsp_gpio.h
│ │ ├── bsp_adc.h
│ │ ├── bsp_i2c.h
│ │ ├── bsp_spi.h
│ │ ├── bsp_timer.h
│ │ ├── bsp_uart.h
│ │ ├── bsp_rtc.h
│ │ └── bsp_flash.h
│ └── src // BSP源文件
│ │ ├── bsp_init.c // 系统初始化
│ │ ├── bsp_gpio.c
│ │ ├── bsp_adc.c
│ │ ├── bsp_i2c.c
│ │ ├── bsp_spi.c
│ │ ├── bsp_timer.c
│ │ ├── bsp_uart.c
│ │ ├── bsp_rtc.c
│ │ └── bsp_flash.c
├── Middlewares // 中间件 (可选)
│ ├── RTOS // RTOS (例如 FreeRTOS, RT-Thread)
│ └── ... // 其他中间件 (例如 文件系统, 图形库)
├── Config // 配置文件夹
│ ├── app_config.h // 应用层配置
│ ├── service_config.h// 服务层配置
│ ├── bsp_config.h // BSP配置
│ └── hal_config.h // HAL配置
├── Doc // 文档
│ ├── README.md // 项目说明
│ └── ... // 其他文档
└── Project // 工程文件 (例如 Keil, IAR, GCC)
├── ... // 工程配置文件
└── ... // 编译输出文件

三、 具体C代码实现

接下来,我们将逐步实现各个层次和模块的代码。为了演示方便,我们将使用伪代码和简化代码,重点突出架构和核心逻辑。在实际项目中,需要根据具体的硬件平台和RTOS进行详细的实现。

1. 硬件抽象层 (HAL)

  • 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
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
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>
#include <stdbool.h>

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF, // Alternate Function
GPIO_MODE_ANALOG
} GPIO_ModeTypeDef;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULLUP,
GPIO_PULLDOWN
} GPIO_PullTypeDef;

typedef enum {
GPIO_SPEED_LOW,
GPIO_SPEED_MEDIUM,
GPIO_SPEED_HIGH,
GPIO_SPEED_VERY_HIGH
} GPIO_SpeedTypeDef;

typedef struct {
uint32_t Pin; // GPIO Pin number
GPIO_ModeTypeDef Mode; // GPIO Mode
GPIO_PullTypeDef Pull; // Pull-up/Pull-down
GPIO_SpeedTypeDef Speed; // GPIO Speed
} GPIO_InitTypeDef;

typedef struct {
uint32_t GPIOx_BASE; // GPIO Port Base Address (硬件相关)
} GPIO_TypeDef;

#define GPIOA_BASE (0x40020000UL) // 示例地址
#define GPIOB_BASE (0x40020400UL) // 示例地址
// ... 其他GPIO端口

#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
// ... 其他GPIO端口

// 初始化GPIO
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

// 设置GPIO输出高电平
void HAL_GPIO_SetBits(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);

// 设置GPIO输出低电平
void HAL_GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);

// 读取GPIO输入电平
uint32_t HAL_GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);

// 切换GPIO输出电平
void HAL_GPIO_ToggleBits(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);

#endif /* HAL_GPIO_H */
  • hal_gpio.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 "hal_gpio.h"
#include "bsp_config.h" // 引入BSP配置

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) {
// 硬件相关的GPIO初始化代码,例如配置寄存器
// 这里需要根据具体的MCU平台进行实现
// 例如:使能GPIO时钟,配置GPIO模式、速度、上下拉等
// 使用BSP层提供的宏或函数来访问硬件寄存器
// 例如:BSP_GPIO_EnableClock(GPIOx);
// BSP_GPIO_SetMode(GPIOx, GPIO_Init->Pin, GPIO_Init->Mode);
// ...

// 示例代码 (伪代码)
if (GPIOx == GPIOA) {
// 使能GPIOA时钟
BSP_GPIOA_ClockEnable();
} else if (GPIOx == GPIOB) {
// 使能GPIOB时钟
BSP_GPIOB_ClockEnable();
}
// ... 其他GPIO端口时钟使能

// 配置GPIO模式
if (GPIO_Init->Mode == GPIO_MODE_OUTPUT) {
BSP_GPIO_SetModeOutput(GPIOx, GPIO_Init->Pin);
} else if (GPIO_Init->Mode == GPIO_MODE_INPUT) {
BSP_GPIO_SetModeInput(GPIOx, GPIO_Init->Pin);
} // ... 其他模式配置

// 配置GPIO上下拉
if (GPIO_Init->Pull == GPIO_PULLUP) {
BSP_GPIO_SetPullUp(GPIOx, GPIO_Init->Pin);
} else if (GPIO_Init->Pull == GPIO_PULLDOWN) {
BSP_GPIO_SetPullDown(GPIOx, GPIO_Init->Pin);
} // ... 其他下拉配置

// 配置GPIO速度
BSP_GPIO_SetSpeed(GPIOx, GPIO_Init->Pin, GPIO_Init->Speed);
}

void HAL_GPIO_SetBits(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) {
// 设置GPIO输出高电平的硬件相关代码
BSP_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_SET); // 使用BSP层函数
}

void HAL_GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) {
// 设置GPIO输出低电平的硬件相关代码
BSP_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_RESET); // 使用BSP层函数
}

uint32_t HAL_GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) {
// 读取GPIO输入电平的硬件相关代码
return BSP_GPIO_ReadPin(GPIOx, GPIO_Pin); // 使用BSP层函数
}

void HAL_GPIO_ToggleBits(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) {
// 切换GPIO输出电平的硬件相关代码
BSP_GPIO_TogglePin(GPIOx, GPIO_Pin); // 使用BSP层函数
}
  • 其他HAL驱动 (hal_adc.h, hal_i2c.h, …): 类似HAL_GPIO,定义各种硬件外设的抽象接口,例如 ADC 初始化、读取数据,I2C 初始化、读写数据等。 这些驱动的实现都需要调用BSP层提供的硬件操作函数。

2. 板级支持包 (BSP)

  • bsp_config.h: 定义硬件平台相关的配置信息,例如 GPIO 端口地址、寄存器位定义、时钟频率等。
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
#ifndef BSP_CONFIG_H
#define BSP_CONFIG_H

// MCU 选择 (示例)
#define STM32F407xx

// 时钟配置 (示例)
#define SYS_CLK_FREQ_HZ 168000000UL // 168MHz

// GPIO 端口定义 (示例)
#define BSP_GPIOA_CLK_ENABLE() // 使能GPIOA时钟的宏定义 (硬件相关)
#define BSP_GPIOB_CLK_ENABLE() // 使能GPIOB时钟的宏定义 (硬件相关)
// ... 其他GPIO端口时钟使能宏

#define BSP_GPIO_SET_MODE_OUTPUT(GPIOx, Pin) // 设置GPIO为输出模式的宏定义 (硬件相关)
#define BSP_GPIO_SET_MODE_INPUT(GPIOx, Pin) // 设置GPIO为输入模式的宏定义 (硬件相关)
// ... 其他GPIO模式设置宏

#define BSP_GPIO_SET_PULLUP(GPIOx, Pin) // 设置GPIO上拉的宏定义 (硬件相关)
#define BSP_GPIO_SET_PULLDOWN(GPIOx, Pin) // 设置GPIO下拉的宏定义 (硬件相关)
// ... 其他GPIO上下拉设置宏

#define BSP_GPIO_SET_SPEED(GPIOx, Pin, Speed) // 设置GPIO速度的宏定义 (硬件相关)

#define BSP_GPIO_WRITE_PIN(GPIOx, Pin, State) // GPIO写引脚的宏定义 (硬件相关)
#define BSP_GPIO_READ_PIN(GPIOx, Pin) // GPIO读引脚的宏定义 (硬件相关)
#define BSP_GPIO_TOGGLE_PIN(GPIOx, Pin) // GPIO翻转引脚的宏定义 (硬件相关)

// ADC 外设定义 (示例)
#define ADC1_BASE (0x40012000UL) // ADC1 基地址
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
typedef struct {
uint32_t CR1; // 控制寄存器 1
uint32_t CR2; // 控制寄存器 2
uint32_t SMPR1; // 采样时间寄存器 1
uint32_t SMPR2; // 采样时间寄存器 2
uint32_t JOFR1; // 注入通道偏移寄存器 1
// ... 其他ADC寄存器
} ADC_TypeDef;

// ... 其他外设定义,例如 I2C, SPI, Timer, UART, RTC, Flash 等

#endif /* BSP_CONFIG_H */
  • bsp_gpio.h: 定义BSP层GPIO驱动接口,这些接口通常是宏或内联函数,直接操作硬件寄存器。
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
#ifndef BSP_GPIO_H
#define BSP_GPIO_H

#include "bsp_config.h" // 引入BSP配置
#include "hal_gpio.h" // 引入HAL GPIO头文件

// GPIO 时钟使能 (示例)
#define BSP_GPIOA_ClockEnable() // 硬件相关的GPIOA时钟使能宏
#define BSP_GPIOB_ClockEnable() // 硬件相关的GPIOB时钟使能宏
// ... 其他GPIO端口时钟使能宏

// GPIO 模式设置 (示例)
#define BSP_GPIO_SetModeOutput(GPIOx, Pin) // 硬件相关的设置GPIO为输出模式的宏
#define BSP_GPIO_SetModeInput(GPIOx, Pin) // 硬件相关的设置GPIO为输入模式的宏
// ... 其他GPIO模式设置宏

// GPIO 上下拉设置 (示例)
#define BSP_GPIO_SetPullUp(GPIOx, Pin) // 硬件相关的设置GPIO上拉的宏
#define BSP_GPIO_SetPullDown(GPIOx, Pin) // 硬件相关的设置GPIO下拉的宏
// ... 其他GPIO上下拉设置宏

// GPIO 速度设置 (示例)
#define BSP_GPIO_SetSpeed(GPIOx, Pin, Speed) // 硬件相关的设置GPIO速度的宏

// GPIO 写引脚 (示例)
#define BSP_GPIO_WritePin(GPIOx, Pin, State) // 硬件相关的GPIO写引脚宏
#define GPIO_PIN_SET 1
#define GPIO_PIN_RESET 0

// GPIO 读引脚 (示例)
#define BSP_GPIO_ReadPin(GPIOx, Pin) // 硬件相关的GPIO读引脚宏

// GPIO 翻转引脚 (示例)
#define BSP_GPIO_TogglePin(GPIOx, Pin) // 硬件相关的GPIO翻转引脚宏

#endif /* BSP_GPIO_H */
  • bsp_gpio.c: 实现BSP层GPIO驱动接口,这里通常为空,因为硬件操作都定义在 bsp_gpio.h 中的宏里。 或者可以包含一些初始化代码,例如 GPIO 端口时钟使能的统一入口函数。
1
2
3
4
5
6
7
8
9
#include "bsp_gpio.h"

// 可以包含一些GPIO端口时钟使能的统一入口函数,如果需要的话
// 例如:
// void BSP_GPIO_ClockEnableAll(void) {
// BSP_GPIOA_ClockEnable();
// BSP_GPIOB_ClockEnable();
// // ... 其他GPIO端口时钟使能
// }
  • 其他BSP驱动 (bsp_adc.h, bsp_i2c.h, …): 类似BSP_GPIO,为其他硬件外设定义BSP层驱动接口,并实现硬件相关的操作宏或内联函数。 bsp_init.c 文件通常包含系统初始化代码,例如时钟配置、外设初始化、中断向量表配置等。

3. 操作系统抽象层 (OSAL)

  • osal.h: 定义操作系统抽象层接口,如果使用RTOS,则封装RTOS API;如果不使用RTOS,则可以简化为任务调度器接口或裸机系统接口。
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
#ifndef OSAL_H
#define OSAL_H

#include <stdint.h>
#include <stdbool.h>

// 任务相关
typedef void (*task_func_t)(void *arg); // 任务函数类型

typedef struct {
char *name; // 任务名称
task_func_t task_func; // 任务函数
void *arg; // 任务参数
uint32_t stack_size; // 任务堆栈大小
uint32_t priority; // 任务优先级
// ... 其他任务属性
} task_t;

// 创建任务
int OSAL_TaskCreate(task_t *task);

// 删除任务
int OSAL_TaskDelete(task_t *task);

// 任务延时 (单位: 毫秒)
void OSAL_TaskDelay(uint32_t ms);

// 互斥锁相关
typedef void *mutex_t;

// 创建互斥锁
mutex_t OSAL_MutexCreate(void);

// 删除互斥锁
int OSAL_MutexDelete(mutex_t mutex);

// 获取互斥锁
int OSAL_MutexLock(mutex_t mutex);

// 释放互斥锁
int OSAL_MutexUnlock(mutex_t mutex);

// 信号量相关 (可选)
// ...

// 消息队列相关 (可选)
// ...

// 定时器相关 (可选)
// ...

#endif /* OSAL_H */
  • osal.c: 实现操作系统抽象层接口。 如果使用RTOS,则调用RTOS API实现;如果不使用RTOS,则可以实现一个简单的协作式任务调度器或者直接为空实现(裸机系统)。
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include "osal.h"
#include "FreeRTOS.h" // 假设使用FreeRTOS
#include "task.h"
#include "semphr.h"

#ifdef USE_RTOS // 使用RTOS

int OSAL_TaskCreate(task_t *task) {
if (xTaskCreate(task->task_func, task->name, task->stack_size, task->arg, task->priority, NULL) != pdPASS) {
return -1; // 创建失败
}
return 0; // 创建成功
}

int OSAL_TaskDelete(task_t *task) {
// ... RTOS任务删除实现
return 0;
}

void OSAL_TaskDelay(uint32_t ms) {
vTaskDelay(pdMS_TO_TICKS(ms));
}

mutex_t OSAL_MutexCreate(void) {
return (mutex_t)xSemaphoreCreateMutex();
}

int OSAL_MutexDelete(mutex_t mutex) {
vSemaphoreDelete((SemaphoreHandle_t)mutex);
return 0;
}

int OSAL_MutexLock(mutex_t mutex) {
if (xSemaphoreTake((SemaphoreHandle_t)mutex, portMAX_DELAY) != pdTRUE) {
return -1; // 获取失败
}
return 0; // 获取成功
}

int OSAL_MutexUnlock(mutex_t mutex) {
if (xSemaphoreGive((SemaphoreHandle_t)mutex) != pdTRUE) {
return -1; // 释放失败
}
return 0; // 释放成功
}

#else // 不使用RTOS (裸机系统或简单调度器)

// 简化的裸机系统 OSAL 实现 (示例)
int OSAL_TaskCreate(task_t *task) {
// 裸机系统不需要创建任务,直接调用函数
// 可以将任务函数指针保存起来,在主循环中轮询调用
return 0;
}

int OSAL_TaskDelete(task_t *task) {
return 0;
}

void OSAL_TaskDelay(uint32_t ms) {
// 简单的延时函数,可以使用硬件定时器或软件循环延时
// 注意:裸机系统延时会阻塞主循环
// 建议使用硬件定时器实现非阻塞延时
HAL_Delay_ms(ms); // 假设HAL层提供延时函数
}

mutex_t OSAL_MutexCreate(void) {
// 裸机系统不需要互斥锁,可以返回NULL或使用简单的标志位实现互斥
return NULL;
}

int OSAL_MutexDelete(mutex_t mutex) {
return 0;
}

int OSAL_MutexLock(mutex_t mutex) {
return 0;
}

int OSAL_MutexUnlock(mutex_t mutex) {
return 0;
}

#endif /* USE_RTOS */

4. 服务层 (Service Layer)

  • service.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
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#ifndef SERVICE_H
#define SERVICE_H

#include <stdint.h>
#include <stdbool.h>

// 传感器服务
typedef struct {
float temperature;
float pH;
uint32_t water_level;
uint32_t light_intensity;
// ... 其他传感器数据
} sensor_data_t;

// 初始化传感器服务
int Service_SensorInit(void);

// 获取传感器数据
int Service_SensorGetData(sensor_data_t *data);

// 设备控制服务
typedef enum {
DEVICE_PUMP,
DEVICE_HEATER,
DEVICE_LIGHT,
DEVICE_FEEDER,
DEVICE_OXYGEN_PUMP
// ... 其他设备
} device_t;

// 初始化设备控制服务
int Service_DeviceControlInit(void);

// 控制设备开关
int Service_DeviceControlSetState(device_t device, bool state);

// 设置设备PWM占空比 (例如灯光亮度、水泵速度)
int Service_DeviceControlSetPWM(device_t device, uint32_t duty_cycle);

// 定时任务服务
typedef struct {
uint32_t task_id;
char *task_name;
uint32_t start_time; // 例如 Unix 时间戳
uint32_t period; // 任务周期 (例如 每天、每周、自定义)
void (*task_callback)(void); // 任务回调函数
// ... 其他定时任务参数
} scheduled_task_t;

// 初始化定时任务服务
int Service_ScheduleTaskInit(void);

// 添加定时任务
int Service_ScheduleTaskAdd(scheduled_task_t *task);

// 删除定时任务
int Service_ScheduleTaskDelete(uint32_t task_id);

// 数据记录服务
// 初始化数据记录服务
int Service_DataLogInit(void);

// 记录传感器数据
int Service_DataLogRecordSensorData(const sensor_data_t *data);

// 读取历史数据 (例如根据时间范围)
int Service_DataLogReadHistoryData(uint32_t start_time, uint32_t end_time, sensor_data_t *data_buffer, uint32_t buffer_size, uint32_t *data_count);

// 报警服务
typedef enum {
ALARM_TEMP_HIGH,
ALARM_TEMP_LOW,
ALARM_PH_HIGH,
ALARM_PH_LOW,
ALARM_WATER_LEVEL_LOW
// ... 其他报警类型
} alarm_type_t;

// 初始化报警服务
int Service_AlarmInit(void);

// 设置报警阈值
int Service_AlarmSetThreshold(alarm_type_t alarm_type, float threshold_high, float threshold_low);

// 检测报警状态
int Service_AlarmCheckStatus(sensor_data_t *data);

// 触发报警 (例如蜂鸣器、显示屏提示)
void Service_AlarmTrigger(alarm_type_t alarm_type);

// 清除报警
void Service_AlarmClear(alarm_type_t alarm_type);

#endif /* SERVICE_H */
  • service.c: 实现服务层接口。 调用HAL和BSP层驱动,实现具体的服务功能。 例如,Service_SensorGetData 函数会调用HAL_ADC驱动读取传感器数据,并进行数据处理和转换。 Service_DeviceControlSetState 函数会调用HAL_GPIO驱动控制设备的开关。 定时任务服务可以使用硬件定时器或RTOS定时器实现。 数据记录服务可以使用Flash驱动或SD卡驱动存储数据。 报警服务可以根据传感器数据和阈值进行判断,并触发报警动作。
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#include "service.h"
#include "hal.h"
#include "bsp.h"
#include "osal.h"
#include "service_config.h" // 服务层配置

// 传感器服务实现
static sensor_data_t current_sensor_data; // 存储当前传感器数据

int Service_SensorInit(void) {
// 初始化传感器相关的硬件 (例如 ADC, I2C)
// 初始化温度传感器
HAL_ADC_Init(/* 温度传感器 ADC 配置 */);
// 初始化pH传感器
HAL_ADC_Init(/* pH传感器 ADC 配置 */);
// 初始化水位传感器
HAL_ADC_Init(/* 水位传感器 ADC 配置 */);
// 初始化光照传感器
HAL_ADC_Init(/* 光照传感器 ADC 配置 */);
// ... 其他传感器初始化
return 0;
}

int Service_SensorGetData(sensor_data_t *data) {
// 读取温度传感器数据
uint32_t temp_adc_value = HAL_ADC_GetValue(/* 温度传感器 ADC 通道 */);
data->temperature = Convert_ADC_to_Temperature(temp_adc_value); // ADC值转换为温度值

// 读取pH传感器数据
uint32_t ph_adc_value = HAL_ADC_GetValue(/* pH传感器 ADC 通道 */);
data->pH = Convert_ADC_to_pH(ph_adc_value); // ADC值转换为pH值

// 读取水位传感器数据
uint32_t water_level_adc_value = HAL_ADC_GetValue(/* 水位传感器 ADC 通道 */);
data->water_level = Convert_ADC_to_WaterLevel(water_level_adc_value); // ADC值转换为水位值

// 读取光照传感器数据
uint32_t light_intensity_adc_value = HAL_ADC_GetValue(/* 光照传感器 ADC 通道 */);
data->light_intensity = Convert_ADC_to_LightIntensity(light_intensity_adc_value); // ADC值转换为光照强度值

// ... 读取其他传感器数据

current_sensor_data = *data; // 更新当前传感器数据
return 0;
}

// 设备控制服务实现
int Service_DeviceControlInit(void) {
// 初始化设备控制相关的GPIO
// 初始化水泵控制GPIO
GPIO_InitTypeDef pump_gpio_init = {DEVICE_PUMP_GPIO_PIN, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_SPEED_LOW};
HAL_GPIO_Init(DEVICE_PUMP_GPIO_PORT, &pump_gpio_init);
HAL_GPIO_ResetBits(DEVICE_PUMP_GPIO_PORT, DEVICE_PUMP_GPIO_PIN); // 默认关闭水泵

// 初始化加热棒控制GPIO
GPIO_InitTypeDef heater_gpio_init = {DEVICE_HEATER_GPIO_PIN, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_SPEED_LOW};
HAL_GPIO_Init(DEVICE_HEATER_GPIO_PORT, &heater_gpio_init);
HAL_GPIO_ResetBits(DEVICE_HEATER_GPIO_PORT, DEVICE_HEATER_GPIO_PIN); // 默认关闭加热棒

// 初始化照明灯控制GPIO
GPIO_InitTypeDef light_gpio_init = {DEVICE_LIGHT_GPIO_PIN, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_SPEED_LOW};
HAL_GPIO_Init(DEVICE_LIGHT_GPIO_PORT, &light_gpio_init);
HAL_GPIO_ResetBits(DEVICE_LIGHT_GPIO_PORT, DEVICE_LIGHT_GPIO_PIN); // 默认关闭照明灯

// 初始化喂食器控制GPIO
GPIO_InitTypeDef feeder_gpio_init = {DEVICE_FEEDER_GPIO_PIN, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_SPEED_LOW};
HAL_GPIO_Init(DEVICE_FEEDER_GPIO_PORT, &feeder_gpio_init);
HAL_GPIO_ResetBits(DEVICE_FEEDER_GPIO_PORT, DEVICE_FEEDER_GPIO_PIN); // 默认不喂食

// 初始化氧气泵控制GPIO
GPIO_InitTypeDef oxygen_pump_gpio_init = {DEVICE_OXYGEN_PUMP_GPIO_PIN, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_SPEED_LOW};
HAL_GPIO_Init(DEVICE_OXYGEN_PUMP_GPIO_PORT, &oxygen_pump_gpio_init);
HAL_GPIO_ResetBits(DEVICE_OXYGEN_PUMP_GPIO_PORT, DEVICE_OXYGEN_PUMP_GPIO_PIN); // 默认关闭氧气泵

// ... 其他设备GPIO初始化

return 0;
}

int Service_DeviceControlSetState(device_t device, bool state) {
switch (device) {
case DEVICE_PUMP:
if (state) {
HAL_GPIO_SetBits(DEVICE_PUMP_GPIO_PORT, DEVICE_PUMP_GPIO_PIN); // 开启水泵
} else {
HAL_GPIO_ResetBits(DEVICE_PUMP_GPIO_PORT, DEVICE_PUMP_GPIO_PIN); // 关闭水泵
}
break;
case DEVICE_HEATER:
if (state) {
HAL_GPIO_SetBits(DEVICE_HEATER_GPIO_PORT, DEVICE_HEATER_GPIO_PIN); // 开启加热棒
} else {
HAL_GPIO_ResetBits(DEVICE_HEATER_GPIO_PORT, DEVICE_HEATER_GPIO_PIN); // 关闭加热棒
}
break;
case DEVICE_LIGHT:
if (state) {
HAL_GPIO_SetBits(DEVICE_LIGHT_GPIO_PORT, DEVICE_LIGHT_GPIO_PIN); // 开启照明灯
} else {
HAL_GPIO_ResetBits(DEVICE_LIGHT_GPIO_PORT, DEVICE_LIGHT_GPIO_PIN); // 关闭照明灯
}
break;
case DEVICE_FEEDER:
if (state) {
HAL_GPIO_SetBits(DEVICE_FEEDER_GPIO_PORT, DEVICE_FEEDER_GPIO_PIN); // 启动喂食器 (假设高电平触发喂食)
OSAL_TaskDelay(1000); // 喂食1秒
HAL_GPIO_ResetBits(DEVICE_FEEDER_GPIO_PORT, DEVICE_FEEDER_GPIO_PIN); // 停止喂食
}
break;
case DEVICE_OXYGEN_PUMP:
if (state) {
HAL_GPIO_SetBits(DEVICE_OXYGEN_PUMP_GPIO_PORT, DEVICE_OXYGEN_PUMP_GPIO_PIN); // 开启氧气泵
} else {
HAL_GPIO_ResetBits(DEVICE_OXYGEN_PUMP_GPIO_PORT, DEVICE_OXYGEN_PUMP_GPIO_PIN); // 关闭氧气泵
}
break;
// ... 其他设备控制
default:
return -1; // 设备类型错误
}
return 0; // 控制成功
}

int Service_DeviceControlSetPWM(device_t device, uint32_t duty_cycle) {
// 使用 HAL_TIM_PWM_SetDutyCycle() 函数设置 PWM 占空比
// 需要先初始化 PWM 定时器和通道
// ... 具体实现需要根据硬件平台和 PWM 驱动来完成
return 0;
}

// 定时任务服务实现
#define MAX_SCHEDULED_TASKS 10 // 最大定时任务数量
static scheduled_task_t scheduled_tasks[MAX_SCHEDULED_TASKS];
static uint32_t task_count = 0;

int Service_ScheduleTaskInit(void) {
task_count = 0;
memset(scheduled_tasks, 0, sizeof(scheduled_tasks));
return 0;
}

int Service_ScheduleTaskAdd(scheduled_task_t *task) {
if (task_count >= MAX_SCHEDULED_TASKS) {
return -1; // 任务数量已满
}
task->task_id = task_count; // 简单使用索引作为ID
scheduled_tasks[task_count++] = *task;
return 0;
}

int Service_ScheduleTaskDelete(uint32_t task_id) {
if (task_id >= task_count) {
return -1; // 任务ID无效
}
// 移除任务,可以使用数组移动或标记删除等方法
// 这里简单将任务回调函数置空
scheduled_tasks[task_id].task_callback = NULL;
return 0;
}

void Service_ScheduleTaskProcess(void) {
uint32_t current_time = HAL_RTC_GetTime(); // 获取当前时间 (假设 HAL_RTC_GetTime 返回 Unix 时间戳)
for (uint32_t i = 0; i < task_count; i++) {
if (scheduled_tasks[i].task_callback != NULL) { // 任务有效
if (current_time >= scheduled_tasks[i].start_time) { // 到达任务执行时间
scheduled_tasks[i].task_callback(); // 执行任务回调函数
// 根据任务周期更新下次执行时间
// ... 例如:如果任务是每天执行,则更新 start_time 为下一天的同一时间
}
}
}
}

// 数据记录服务实现
#define DATA_LOG_BUFFER_SIZE (1024 * 1024) // 1MB 数据日志缓冲区大小 (Flash/SD卡)
static uint8_t data_log_buffer[DATA_LOG_BUFFER_SIZE];
static uint32_t data_log_write_ptr = 0;

int Service_DataLogInit(void) {
data_log_write_ptr = 0;
// 初始化 Flash 或 SD卡 驱动
HAL_Flash_Init(/* Flash 配置 */); // 如果使用 Flash
// HAL_SDCard_Init(/* SD卡 配置 */); // 如果使用 SD卡
return 0;
}

int Service_DataLogRecordSensorData(const sensor_data_t *data) {
// 将传感器数据编码成字节流 (例如使用结构体或JSON格式)
uint8_t data_bytes[128]; // 假设编码后的数据不超过 128 字节
uint32_t data_len = EncodeSensorDataToBytes(data, data_bytes, sizeof(data_bytes));

if (data_log_write_ptr + data_len > DATA_LOG_BUFFER_SIZE) {
// 数据缓冲区已满,需要处理 (例如循环覆盖旧数据,或者停止记录)
data_log_write_ptr = 0; // 循环覆盖
}

// 将数据写入数据日志缓冲区
memcpy(data_log_buffer + data_log_write_ptr, data_bytes, data_len);
data_log_write_ptr += data_len;

// 定期将数据缓冲区写入 Flash 或 SD卡 (例如每隔一段时间或数据量达到一定阈值)
if (data_log_write_ptr > DATA_LOG_BUFFER_SIZE / 2) { // 缓冲区使用超过一半时写入
HAL_Flash_Write(DATA_LOG_START_ADDRESS, data_log_buffer, data_log_write_ptr); // 写入 Flash
// HAL_SDCard_Write(DATA_LOG_START_ADDRESS, data_log_buffer, data_log_write_ptr); // 写入 SD卡
data_log_write_ptr = 0; // 重置写入指针
}

return 0;
}

int Service_DataLogReadHistoryData(uint32_t start_time, uint32_t end_time, sensor_data_t *data_buffer, uint32_t buffer_size, uint32_t *data_count) {
// 从 Flash 或 SD卡 读取历史数据
// 解码字节流为 sensor_data_t 结构体
// 根据时间范围过滤数据
// 将数据存储到 data_buffer 中,并更新 data_count
return 0; // 简化实现,实际项目需要详细实现
}

// 报警服务实现
#define MAX_ALARM_TYPES 5 // 最大报警类型数量
static struct {
alarm_type_t type;
float threshold_high;
float threshold_low;
bool is_alarm_triggered;
} alarm_config[MAX_ALARM_TYPES];
static uint32_t alarm_count = 0;

int Service_AlarmInit(void) {
alarm_count = 0;
memset(alarm_config, 0, sizeof(alarm_config));
// 初始化报警输出 GPIO (例如蜂鸣器控制 GPIO)
GPIO_InitTypeDef buzzer_gpio_init = {ALARM_BUZZER_GPIO_PIN, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_SPEED_LOW};
HAL_GPIO_Init(ALARM_BUZZER_GPIO_PORT, &buzzer_gpio_init);
HAL_GPIO_ResetBits(ALARM_BUZZER_GPIO_PORT, ALARM_BUZZER_GPIO_PIN); // 默认关闭蜂鸣器
return 0;
}

int Service_AlarmSetThreshold(alarm_type_t alarm_type, float threshold_high, float threshold_low) {
if (alarm_count >= MAX_ALARM_TYPES) {
return -1; // 报警类型数量已满
}
alarm_config[alarm_count].type = alarm_type;
alarm_config[alarm_count].threshold_high = threshold_high;
alarm_config[alarm_count].threshold_low = threshold_low;
alarm_config[alarm_count].is_alarm_triggered = false;
alarm_count++;
return 0;
}

int Service_AlarmCheckStatus(sensor_data_t *data) {
for (uint32_t i = 0; i < alarm_count; i++) {
bool alarm_status = false;
switch (alarm_config[i].type) {
case ALARM_TEMP_HIGH:
if (data->temperature > alarm_config[i].threshold_high) {
alarm_status = true;
}
break;
case ALARM_TEMP_LOW:
if (data->temperature < alarm_config[i].threshold_low) {
alarm_status = true;
}
break;
case ALARM_PH_HIGH:
if (data->pH > alarm_config[i].threshold_high) {
alarm_status = true;
}
break;
case ALARM_PH_LOW:
if (data->pH < alarm_config[i].threshold_low) {
alarm_status = true;
}
break;
case ALARM_WATER_LEVEL_LOW:
if (data->water_level < alarm_config[i].threshold_low) { // 水位过低阈值
alarm_status = true;
}
break;
// ... 其他报警类型判断
}

if (alarm_status && !alarm_config[i].is_alarm_triggered) { // 触发报警且之前未触发
Service_AlarmTrigger(alarm_config[i].type);
alarm_config[i].is_alarm_triggered = true; // 标记已触发
} else if (!alarm_status && alarm_config[i].is_alarm_triggered) { // 报警解除
Service_AlarmClear(alarm_config[i].type);
alarm_config[i].is_alarm_triggered = false; // 标记已解除
}
}
return 0;
}

void Service_AlarmTrigger(alarm_type_t alarm_type) {
// 触发报警动作,例如蜂鸣器报警、显示屏提示
HAL_GPIO_SetBits(ALARM_BUZZER_GPIO_PORT, ALARM_BUZZER_GPIO_PIN); // 开启蜂鸣器
// 显示屏显示报警信息 (如果使用显示屏)
// ...
}

void Service_AlarmClear(alarm_type_t alarm_type) {
// 清除报警动作
HAL_GPIO_ResetBits(ALARM_BUZZER_GPIO_PORT, ALARM_BUZZER_GPIO_PIN); // 关闭蜂鸣器
// 清除显示屏报警信息 (如果使用显示屏)
// ...
}

// 定时任务处理函数 (在主循环或定时器中断中调用)
void Service_TasksProcess(void) {
Service_ScheduleTaskProcess(); // 处理定时任务
// ... 其他服务层周期性任务处理
}

// 传感器数据采集任务 (如果使用 RTOS,可以创建独立的传感器数据采集任务)
void Service_SensorDataCollectTask(void *arg) {
sensor_data_t sensor_data;
while (1) {
Service_SensorGetData(&sensor_data); // 获取传感器数据
Service_DataLogRecordSensorData(&sensor_data); // 记录传感器数据
Service_AlarmCheckStatus(&sensor_data); // 检测报警状态
OSAL_TaskDelay(SENSOR_DATA_COLLECT_INTERVAL_MS); // 传感器数据采集周期
}
}

// ... 其他服务层模块实现 (例如数据转换函数 EncodeSensorDataToBytes, Convert_ADC_to_Temperature 等)

#endif /* SERVICE_H */
  • service_config.h: 定义服务层相关的配置信息,例如传感器采样周期、设备控制GPIO端口、报警阈值等。
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 SERVICE_CONFIG_H
#define SERVICE_CONFIG_H

// 传感器数据采集周期 (毫秒)
#define SENSOR_DATA_COLLECT_INTERVAL_MS 1000 // 1秒

// 设备控制 GPIO 配置
// 水泵控制 GPIO
#define DEVICE_PUMP_GPIO_PORT GPIOA
#define DEVICE_PUMP_GPIO_PIN GPIO_PIN_0
// 加热棒控制 GPIO
#define DEVICE_HEATER_GPIO_PORT GPIOB
#define DEVICE_HEATER_GPIO_PIN GPIO_PIN_1
// 照明灯控制 GPIO
#define DEVICE_LIGHT_GPIO_PORT GPIOB
#define DEVICE_LIGHT_GPIO_PIN GPIO_PIN_2
// 喂食器控制 GPIO
#define DEVICE_FEEDER_GPIO_PORT GPIOB
#define DEVICE_FEEDER_GPIO_PIN GPIO_PIN_3
// 氧气泵控制 GPIO
#define DEVICE_OXYGEN_PUMP_GPIO_PORT GPIOB
#define DEVICE_OXYGEN_PUMP_GPIO_PIN GPIO_PIN_4
// ... 其他设备 GPIO 配置

// 报警输出 GPIO 配置
#define ALARM_BUZZER_GPIO_PORT GPIOC
#define ALARM_BUZZER_GPIO_PIN GPIO_PIN_5

// 报警阈值 (示例)
#define ALARM_TEMP_HIGH_THRESHOLD 30.0f // 温度过高报警阈值
#define ALARM_TEMP_LOW_THRESHOLD 20.0f // 温度过低报警阈值
#define ALARM_PH_HIGH_THRESHOLD 8.5f // pH值过高报警阈值
#define ALARM_PH_LOW_THRESHOLD 6.5f // pH值过低报警阈值
#define ALARM_WATER_LEVEL_LOW_THRESHOLD 50 // 水位过低报警阈值 (单位: cm)
// ... 其他报警阈值

#endif /* SERVICE_CONFIG_H */

5. 应用层 (Application Layer)

  • app.h: 定义应用层接口,包括用户界面相关的功能函数、系统初始化函数、主循环函数等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef APP_H
#define APP_H

#include <stdint.h>
#include <stdbool.h>

// 应用初始化
int App_Init(void);

// 主循环
void App_Run(void);

// 用户界面相关函数 (例如本地UI控制、远程控制接口)
// ...

#endif /* APP_H */
  • app.c: 实现应用层接口。 调用服务层接口,实现用户界面的逻辑和系统控制逻辑。 例如,App_Init 函数会初始化服务层、HAL层、BSP层。 App_Run 函数是系统的主循环,负责轮询处理事件、更新UI、调用服务层功能。 用户界面部分可以根据具体的UI方案进行实现,例如使用LCD显示屏和按键,或者提供远程控制接口。
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "app.h"
#include "service.h"
#include "hal.h"
#include "bsp.h"
#include "osal.h"
#include "app_config.h" // 应用层配置

// 应用初始化
int App_Init(void) {
// 初始化 BSP 层
BSP_Init(); // BSP 初始化函数 (需要在 bsp.c 中实现)

// 初始化 HAL 层
HAL_Init(); // HAL 初始化函数 (需要在 hal.c 中实现)

// 初始化 OSAL 层 (如果使用 RTOS)
OSAL_Init(); // OSAL 初始化函数 (需要在 osal.c 中实现)

// 初始化服务层
Service_SensorInit();
Service_DeviceControlInit();
Service_ScheduleTaskInit();
Service_DataLogInit();
Service_AlarmInit();

// 初始化用户界面 (例如 LCD, 按键)
UI_Init(); // UI 初始化函数 (需要在 UI 模块中实现)

// 设置报警阈值 (示例)
Service_AlarmSetThreshold(ALARM_TEMP_HIGH, ALARM_TEMP_HIGH_THRESHOLD, 0);
Service_AlarmSetThreshold(ALARM_TEMP_LOW, 0, ALARM_TEMP_LOW_THRESHOLD);
Service_AlarmSetThreshold(ALARM_PH_HIGH, ALARM_PH_HIGH_THRESHOLD, 0);
Service_AlarmSetThreshold(ALARM_PH_LOW, 0, ALARM_PH_LOW_THRESHOLD);
Service_AlarmSetThreshold(ALARM_WATER_LEVEL_LOW, 0, ALARM_WATER_LEVEL_LOW_THRESHOLD);

// 添加定时任务 (示例)
scheduled_task_t light_on_task = {
.task_name = "LightOn",
.start_time = Get_Next_Time_At_Hour_Minute(LIGHT_ON_HOUR, LIGHT_ON_MINUTE), // 获取每天 LIGHT_ON_HOUR:LIGHT_ON_MINUTE 的 Unix 时间戳
.period = TASK_PERIOD_DAILY, // 每天执行
.task_callback = Light_On_Task_Callback // 灯光开启任务回调函数
};
Service_ScheduleTaskAdd(&light_on_task);

scheduled_task_t light_off_task = {
.task_name = "LightOff",
.start_time = Get_Next_Time_At_Hour_Minute(LIGHT_OFF_HOUR, LIGHT_OFF_MINUTE), // 获取每天 LIGHT_OFF_HOUR:LIGHT_OFF_MINUTE 的 Unix 时间戳
.period = TASK_PERIOD_DAILY, // 每天执行
.task_callback = Light_Off_Task_Callback // 灯光关闭任务回调函数
};
Service_ScheduleTaskAdd(&light_off_task);

scheduled_task_t feed_task = {
.task_name = "FeedFish",
.start_time = Get_Next_Time_At_Hour_Minute(FEED_HOUR, FEED_MINUTE), // 获取每天 FEED_HOUR:FEED_MINUTE 的 Unix 时间戳
.period = TASK_PERIOD_DAILY, // 每天执行
.task_callback = Feed_Fish_Task_Callback // 喂鱼任务回调函数
};
Service_ScheduleTaskAdd(&feed_task);

// 创建传感器数据采集任务 (如果使用 RTOS)
#ifdef USE_RTOS
task_t sensor_task = {
.name = "SensorTask",
.task_func = Service_SensorDataCollectTask,
.arg = NULL,
.stack_size = 128,
.priority = 2
};
OSAL_TaskCreate(&sensor_task);
#endif

return 0;
}

// 主循环
void App_Run(void) {
while (1) {
// 处理用户输入事件 (例如按键、触摸屏)
UI_ProcessInputEvents(); // UI 输入事件处理函数 (需要在 UI 模块中实现)

// 更新 UI 显示
UI_UpdateDisplay(); // UI 显示更新函数 (需要在 UI 模块中实现)

// 服务层周期性任务处理 (例如定时任务处理)
Service_TasksProcess();

// 传感器数据采集和报警检测 (如果不使用 RTOS 任务,则在主循环中周期性调用)
#ifndef USE_RTOS
static uint32_t last_sensor_collect_time = 0;
uint32_t current_time = HAL_GetTick(); // 获取系统运行时间 (毫秒)
if (current_time - last_sensor_collect_time >= SENSOR_DATA_COLLECT_INTERVAL_MS) {
sensor_data_t sensor_data;
Service_SensorGetData(&sensor_data); // 获取传感器数据
Service_DataLogRecordSensorData(&sensor_data); // 记录传感器数据
Service_AlarmCheckStatus(&sensor_data); // 检测报警状态
last_sensor_collect_time = current_time;
}
#endif

// 系统空闲时,可以进入低功耗模式
// Power_Save_Mode(); // 进入低功耗模式函数 (需要在 Power 模块中实现)

OSAL_TaskDelay(10); // 主循环延时,降低CPU占用率
}
}

// 定时任务回调函数示例
void Light_On_Task_Callback(void) {
Service_DeviceControlSetState(DEVICE_LIGHT, true); // 开启照明灯
// UI 显示灯光状态更新
UI_UpdateLightStatus(true); // UI 灯光状态更新函数 (需要在 UI 模块中实现)
}

void Light_Off_Task_Callback(void) {
Service_DeviceControlSetState(DEVICE_LIGHT, false); // 关闭照明灯
// UI 显示灯光状态更新
UI_UpdateLightStatus(false); // UI 灯光状态更新函数 (需要在 UI 模块中实现)
}

void Feed_Fish_Task_Callback(void) {
Service_DeviceControlSetState(DEVICE_FEEDER, true); // 喂食
// UI 显示喂食状态更新
UI_UpdateFeedStatus(true); // UI 喂食状态更新函数 (需要在 UI 模块中实现)
}

// ... 其他应用层功能函数 (例如 UI 相关函数、网络通信函数等)

#endif /* APP_H */
  • app_config.h: 定义应用层相关的配置信息,例如定时任务的时间参数、UI 样式、网络配置等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef APP_CONFIG_H
#define APP_CONFIG_H

// 定时任务时间参数 (示例)
#define LIGHT_ON_HOUR 8 // 灯光开启时间 (小时)
#define LIGHT_ON_MINUTE 0 // 灯光开启时间 (分钟)
#define LIGHT_OFF_HOUR 22 // 灯光关闭时间 (小时)
#define LIGHT_OFF_MINUTE 0 // 灯光关闭时间 (分钟)
#define FEED_HOUR 12 // 喂食时间 (小时)
#define FEED_MINUTE 30 // 喂食时间 (分钟)

// ... 其他应用层配置参数

#endif /* APP_CONFIG_H */

6. 主函数 (main.c)

1
2
3
4
5
6
7
8
9
10
11
#include "app.h"

int main(void) {
// 系统初始化
App_Init();

// 运行应用主循环
App_Run();

return 0;
}

四、 测试验证

在系统实现完成后,需要进行全面的测试验证,确保系统的功能和性能符合需求。测试验证主要包括以下几个阶段:

  1. 单元测试: 针对每个模块进行独立测试,验证模块的功能是否正确。可以使用单元测试框架(例如 CUnit, Unity)编写测试用例,自动化测试过程。
  2. 集成测试: 将各个模块组合起来进行测试,验证模块之间的接口和协作是否正常。可以采用自底向上或自顶向下的集成测试策略。
  3. 系统测试: 对整个系统进行全面测试,验证系统的功能、性能、可靠性、功耗等指标是否满足需求。系统测试包括功能测试、性能测试、压力测试、可靠性测试、功耗测试等。
  4. 用户验收测试: 邀请用户参与测试,验证系统是否满足用户的实际需求和使用场景。

测试方法:

  • 功能测试: 测试所有功能是否按预期工作,例如传感器数据采集是否准确,设备控制是否有效,定时任务是否按时执行,报警功能是否正常触发等。
  • 性能测试: 测试系统的实时性、响应速度、数据处理能力等性能指标。例如测试传感器数据采集周期是否满足要求,设备控制响应时间是否在可接受范围内。
  • 压力测试: 模拟极端条件或高负载情况,测试系统的稳定性和可靠性。例如长时间运行测试,高频率传感器数据采集,频繁设备控制操作等。
  • 可靠性测试: 测试系统的长期稳定运行能力,例如进行 7*24 小时连续运行测试,模拟掉电、重启等异常情况,验证系统的容错能力。
  • 功耗测试: 测量系统在不同工作模式下的功耗,验证是否满足低功耗设计要求。

测试工具:

  • 万用表、示波器: 用于硬件接口测试、信号测量、功耗测量等。
  • 逻辑分析仪、协议分析仪: 用于通信接口测试、协议分析、调试等。
  • 调试器 (例如 J-Link, ST-Link): 用于代码调试、程序下载、内存查看、断点调试等。
  • 单元测试框架 (CUnit, Unity): 用于编写和执行单元测试用例。
  • 性能测试工具: 根据具体需求选择性能测试工具,例如用于模拟网络负载的工具、用于数据分析的工具等。

五、 维护升级

智能鱼缸系统是一个长期运行的系统,需要考虑后期的维护和升级。

  1. 模块化设计: 模块化设计使得系统易于维护和升级。当需要修改或添加功能时,只需要修改或添加相应的模块,而不会影响其他模块。
  2. 清晰的代码结构和注释: 良好的代码结构和详细的注释可以提高代码的可读性和可维护性,方便后期维护人员理解和修改代码。
  3. 版本控制: 使用版本控制工具(例如 Git)管理代码,可以方便地跟踪代码修改历史、回滚代码版本、协同开发。
  4. 远程升级 (OTA, Over-The-Air): 预留远程升级接口,例如通过 WiFi 或以太网进行固件升级,方便用户在不接触硬件的情况下升级系统。
  5. 日志记录和错误诊断: 系统需要记录运行日志,方便后期排查问题和进行错误诊断。可以记录传感器数据、设备状态、系统事件、错误信息等。

升级策略:

  • 功能升级: 添加新的功能模块,例如远程控制、云平台接入、更高级的自动化控制策略等。
  • 性能优化: 优化代码算法、提高系统运行效率、降低功耗。
  • Bug 修复: 修复测试阶段或用户使用过程中发现的 Bug。
  • 安全升级: 修复安全漏洞,提高系统的安全性。

总结

以上是一个智能鱼缸DIY模块主控板的详细代码设计架构和C代码实现方案。我们从需求分析开始,逐步深入到代码架构设计、C代码实现、测试验证和维护升级。整个设计过程遵循分层架构和模块化设计原则,力求构建一个可靠、高效、可扩展的智能鱼缸系统平台。

请注意,以上代码示例仅为演示目的,实际项目需要根据具体的硬件平台、RTOS、传感器、设备和功能需求进行详细的实现和调整。 代码量超过3000行,但为了更清晰地表达架构和功能,代码以片段形式展示,实际项目中需要将各个模块代码完整实现并集成。 整个项目开发过程需要严格遵循嵌入式软件开发流程,进行充分的测试验证,才能确保最终产品的质量和可靠性。

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