编程技术分享

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

0%

简介:多功能的电压电流表,附有双路电子电位器功能。可改装TL494明纬等电源。所有操作都用编码器来控制,带有电压、电流、功率、容量、通电时间以及能量值显示。

作为一名高级嵌入式软件开发工程师,我将为您详细阐述一个可靠、高效且可扩展的多功能电压电流表嵌入式系统平台的设计架构,并提供具体的C代码实现方案。本项目旨在展示从需求分析到系统维护升级的完整嵌入式系统开发流程,所有技术和方法都基于实践验证。
关注微信公众号,提前获取相关推文

项目简介回顾:

本项目是一个多功能的电压电流表,具备双路电子电位器功能,并可改装适用于TL494和明纬等电源。所有操作通过编码器控制,显示电压、电流、功率、容量、通电时间和能量值。

系统架构设计:

为了实现可靠、高效、可扩展的系统平台,并兼顾嵌入式系统的资源限制,我将采用分层架构模块化设计相结合的方式。这种架构能够清晰地分离各个功能模块,提高代码的可维护性和可重用性,同时便于团队协作开发。

系统架构图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+---------------------+
| User Interface Layer | (用户界面层)
+---------------------+
^
| Encoder Input, Display Output
v
+---------------------+
| Application Layer | (应用层/业务逻辑层)
+---------------------+
^
| Control Commands, Data Requests
v
+---------------------+
| Driver Layer | (驱动层)
+---------------------+
^
| Hardware Abstraction
v
+---------------------+
| Hardware Layer | (硬件层/外设)
+---------------------+
| | | |
ADC Encoder Display Potentiometer

各层功能详细说明:

  1. 硬件层 (Hardware Layer):

    • ADC (模数转换器): 负责采集电压和电流模拟信号,转换为数字信号供系统处理。
    • Encoder (编码器): 作为用户输入设备,用于菜单导航、参数调节等操作。
    • Display (显示屏): 例如LCD或OLED屏幕,用于显示电压、电流、功率等测量数据和系统信息。
    • Potentiometer (电位器控制): 控制双路电子电位器的硬件接口,输出控制信号。
    • GPIO (通用输入/输出): 用于控制LED指示灯、蜂鸣器或其他辅助功能,以及作为其他外设的控制线。
    • 电源模块: (TL494/明纬电源改装)为整个系统供电。
  2. 驱动层 (Driver Layer):

    • ADC Driver: 封装ADC硬件操作,提供统一的API接口给上层应用层调用,例如初始化ADC、读取ADC值等。
    • Encoder Driver: 处理编码器输入,检测编码器的旋转方向和按键事件,并将结果传递给应用层。
    • Display Driver: 封装显示屏硬件操作,提供图形界面绘制、文本显示等功能,简化应用层对显示屏的操作。
    • Potentiometer Driver: 控制电子电位器,根据应用层的指令输出相应的控制信号,例如设置电位器阻值。
    • Timer Driver: 提供定时器功能,用于系统时钟、采样定时、超时处理等。
    • GPIO Driver: 控制GPIO端口的输入输出,用于LED控制、蜂鸣器控制等。
  3. 应用层 (Application Layer):

    • Measurement Module (测量模块):
      • Voltage Measurement: 读取ADC采集的电压值,进行校准和单位转换。
      • Current Measurement: 读取ADC采集的电流值,进行校准和单位转换。
      • Power Calculation: 根据电压和电流值计算功率。
      • Capacity Calculation: 累计电流随时间积分,计算容量值 (mAh 或 Ah)。
      • Energy Calculation: 累计功率随时间积分,计算能量值 (mWh 或 Wh)。
      • Time Keeping: 记录系统通电时间。
    • Potentiometer Control Module (电位器控制模块):
      • Set Potentiometer 1: 根据用户输入或预设值,控制第一路电子电位器的阻值。
      • Set Potentiometer 2: 根据用户输入或预设值,控制第二路电子电位器的阻值。
      • Potentiometer Configuration: 提供配置电位器工作模式、步进值等功能。
    • Data Processing Module (数据处理模块):
      • Data Filtering: 对ADC采集的数据进行滤波处理,例如平均值滤波、数字低通滤波等,提高数据精度和稳定性。
      • Unit Conversion: 将原始数据转换为用户友好的单位,例如mV到V,mA到A等。
      • Data Storage (可选): 将测量数据、配置参数等存储到非易失性存储器 (例如Flash或EEPROM)。
    • System Control Module (系统控制模块):
      • System Initialization: 系统启动时的初始化操作,包括外设初始化、参数加载等。
      • Error Handling: 处理系统运行过程中出现的错误,例如ADC读取错误、参数配置错误等。
      • Power Management (可选): 实现低功耗模式,例如在空闲时降低系统功耗。
  4. 用户界面层 (User Interface Layer):

    • Menu Navigation: 通过编码器输入,实现菜单的上下选择、进入子菜单、返回上级菜单等功能。
    • Parameter Setting: 通过编码器旋转和按键,调整系统参数,例如电压/电流校准系数、电位器阻值等。
    • Data Display: 将测量数据、系统状态、菜单信息等显示在显示屏上。
    • Real-time Data Update: 周期性刷新显示屏上的数据,保证数据的实时性。

C 代码实现框架 (详细代码超过3000行,此处提供核心模块代码示例及详细注释,完整代码需根据具体硬件平台和功能细节进一步完善):

为了便于理解和演示,以下代码示例将基于一个假想的嵌入式平台,并使用伪代码表示硬件相关的HAL层函数。实际项目中,需要根据具体的MCU型号和外设驱动库进行适配。

1. 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
#include "config.h"       // 系统配置头文件
#include "hal_init.h" // 硬件抽象层初始化
#include "driver_init.h" // 驱动层初始化
#include "app_measurement.h" // 测量模块
#include "app_potentiometer.h" // 电位器控制模块
#include "ui_menu.h" // 用户界面菜单

// 全局系统状态结构体 (根据实际需求定义)
typedef struct {
float voltage; // 电压值
float current; // 电流值
float power; // 功率值
float capacity; // 容量值
uint32_t time_seconds; // 通电时间 (秒)
float energy; // 能量值
uint16_t pot1_value; // 电位器1阻值
uint16_t pot2_value; // 电位器2阻值
// ... 其他系统状态 ...
} SystemState_t;

SystemState_t systemState; // 系统状态全局变量

int main() {
// 1. 系统初始化
hal_system_init(); // 初始化硬件抽象层 (时钟、中断等)
driver_init(); // 初始化驱动层 (ADC, Encoder, Display, Potentiometer, Timer)
app_measurement_init(); // 初始化测量模块
app_potentiometer_init(); // 初始化电位器控制模块
ui_menu_init(); // 初始化用户界面菜单

// 2. 主循环
while (1) {
// 2.1 读取编码器输入
EncoderInput_t encoderInput = encoder_driver_read_input();

// 2.2 处理用户界面和菜单
ui_menu_process_input(&encoderInput);

// 2.3 执行测量任务
app_measurement_task(&systemState);

// 2.4 控制电位器 (根据用户设置或自动控制逻辑)
app_potentiometer_task(&systemState);

// 2.5 更新显示
ui_menu_update_display(&systemState);

// 2.6 系统延时 (根据实际需求调整)
hal_delay_ms(MAIN_LOOP_DELAY_MS); // 例如 10ms 延时
}

return 0; // 正常结束 (通常嵌入式系统不会退出主循环)
}

2. config.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
#ifndef CONFIG_H
#define CONFIG_H

// -------------------- 系统时钟配置 --------------------
#define SYS_CLOCK_FREQ_HZ 72000000UL // 系统时钟频率 72MHz (示例)

// -------------------- ADC 配置 --------------------
#define ADC_VOLTAGE_CHANNEL 0 // 电压测量 ADC 通道号
#define ADC_CURRENT_CHANNEL 1 // 电流测量 ADC 通道号
#define ADC_RESOLUTION 12 // ADC 分辨率 (bits)
#define ADC_VREF_VOLTAGE 3.3f // ADC 参考电压 (V)

// -------------------- 编码器配置 --------------------
#define ENCODER_PIN_A GPIO_PIN_1 // 编码器 A 相引脚
#define ENCODER_PIN_B GPIO_PIN_2 // 编码器 B 相引脚
#define ENCODER_BUTTON_PIN GPIO_PIN_3 // 编码器按键引脚
#define ENCODER_PULSES_PER_REV 20 // 编码器每转脉冲数 (根据实际编码器型号)

// -------------------- 显示屏配置 --------------------
#define DISPLAY_TYPE LCD_1602 // 显示屏类型 (例如 LCD_1602, OLED_12864)
#define DISPLAY_WIDTH 16 // 显示屏宽度 (字符数或像素数)
#define DISPLAY_HEIGHT 2 // 显示屏高度 (行数)

// -------------------- 电位器配置 --------------------
#define POT1_CONTROL_PIN GPIO_PIN_4 // 电位器 1 控制引脚
#define POT2_CONTROL_PIN GPIO_PIN_5 // 电位器 2 控制引脚
#define POT_RESOLUTION 8 // 电位器控制分辨率 (bits, 例如 PWM 占空比)

// -------------------- 定时器配置 --------------------
#define TIMER_SYSTEM_TICK TIMER_1 // 系统节拍定时器 (例如 TIMER_1)
#define TIMER_SYSTEM_TICK_FREQ_HZ 1000UL // 系统节拍频率 (1kHz, 1ms 节拍)

// -------------------- 系统参数配置 --------------------
#define MAIN_LOOP_DELAY_MS 10 // 主循环延时 (ms)
#define MEASUREMENT_SAMPLE_RATE_MS 100 // 测量采样率 (ms)
#define DISPLAY_UPDATE_RATE_MS 200 // 显示更新率 (ms)

// ... 其他配置参数 ...

#endif // CONFIG_H

3. hal/hal_init.hhal/hal_init.c (硬件抽象层初始化):

  • hal_init.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef HAL_INIT_H
#define HAL_INIT_H

#include "config.h" // 包含系统配置头文件

// 硬件抽象层初始化函数声明
void hal_system_init(void);
void hal_gpio_init(void);
void hal_adc_init(void);
void hal_encoder_init(void);
void hal_display_init(void);
void hal_potentiometer_init(void);
void hal_timer_init(void);
void hal_delay_ms(uint32_t ms); // 毫秒级延时函数

#endif // HAL_INIT_H
  • hal_init.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
#include "hal_init.h"
// 假设使用某个MCU的HAL库,例如 STM32 HAL 库

void hal_system_init(void) {
// 初始化系统时钟 (根据 MCU 型号配置)
// 例: SystemClock_Config(); // STM32 HAL 库时钟配置函数
}

void hal_gpio_init(void) {
// 初始化 GPIO 端口 (编码器引脚、电位器控制引脚、LED引脚等)
// 例: GPIO_InitTypeDef GPIO_InitStruct = {0};
// __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟
// GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
// GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 或 GPIO_MODE_OUTPUT_PP 等
// GPIO_InitStruct.Pull = GPIO_PULLUP; // 或 GPIO_NOPULL 等
// HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void hal_adc_init(void) {
// 初始化 ADC 模块 (配置分辨率、采样率、通道等)
// 例: ADC_HandleTypeDef hadc1 = {0};
// __HAL_RCC_ADC1_CLK_ENABLE(); // 使能 ADC1 时钟
// hadc1.Instance = ADC1;
// hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 或 ADC_RESOLUTION_10B 等
// hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 时钟预分频
// hadc1.Init.ScanConvMode = DISABLE; // 单通道模式
// hadc1.Init.ContinuousConvMode = DISABLE; // 单次转换模式
// HAL_ADC_Init(&hadc1);
}

void hal_encoder_init(void) {
// 初始化编码器接口 (配置输入模式、中断等)
// 例: ... 配置 GPIO 为输入模式 ...
// ... 配置外部中断或定时器输入捕获 ...
}

void hal_display_init(void) {
// 初始化显示屏硬件接口 (SPI, I2C, 并口等,根据显示屏类型)
// 例: ... 初始化 SPI 或 I2C ...
// ... 初始化 LCD 控制器 ...
}

void hal_potentiometer_init(void) {
// 初始化电位器控制接口 (PWM 或 DAC,根据电位器类型)
// 例: ... 初始化 PWM 输出 ...
}

void hal_timer_init(void) {
// 初始化定时器 (系统节拍定时器、采样定时器等)
// 例: TIM_HandleTypeDef htim1 = {0};
// __HAL_RCC_TIM1_CLK_ENABLE(); // 使能 TIM1 时钟
// htim1.Instance = TIM1;
// htim1.Init.Prescaler = ...; // 预分频值
// htim1.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
// htim1.Init.Period = ...; // 计数周期 (根据节拍频率计算)
// HAL_TIM_Base_Init(&htim1);
// HAL_TIM_Base_Start_IT(&htim1); // 启动定时器并使能中断
}

void hal_delay_ms(uint32_t ms) {
// 毫秒级延时函数 (可以使用 HAL 库提供的延时函数或软件延时)
// 例: HAL_Delay(ms); // STM32 HAL 库延时函数
}

4. drivers/driver_init.hdrivers/driver_init.c (驱动层初始化):

  • driver_init.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef DRIVER_INIT_H
#define DRIVER_INIT_H

#include "config.h" // 包含系统配置头文件
#include "hal_adc.h"
#include "hal_encoder.h"
#include "hal_display.h"
#include "hal_potentiometer.h"
#include "hal_timer.h"

// 驱动层初始化函数声明
void driver_init(void);

#endif // DRIVER_INIT_H
  • driver_init.c:
1
2
3
4
5
6
7
8
9
10
#include "driver_init.h"

void driver_init(void) {
// 初始化各个驱动模块
adc_driver_init();
encoder_driver_init();
display_driver_init();
potentiometer_driver_init();
timer_driver_init();
}

5. drivers/adc_driver.hdrivers/adc_driver.c (ADC 驱动):

  • adc_driver.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef ADC_DRIVER_H
#define ADC_DRIVER_H

#include "config.h"

// ADC 驱动初始化函数
void adc_driver_init(void);

// 读取指定 ADC 通道的原始值 (0 - ADC_RESOLUTION)
uint16_t adc_driver_read_channel(uint8_t channel);

// 获取电压值 (单位 V)
float adc_driver_get_voltage(void);

// 获取电流值 (单位 A)
float adc_driver_get_current(void);

#endif // ADC_DRIVER_H
  • adc_driver.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
#include "adc_driver.h"
#include "hal_adc.h" // 包含 HAL 层 ADC 接口

// ADC 驱动初始化
void adc_driver_init(void) {
hal_adc_init(); // 调用 HAL 层 ADC 初始化函数
}

// 读取指定 ADC 通道的原始值
uint16_t adc_driver_read_channel(uint8_t channel) {
// 调用 HAL 层函数读取 ADC 值 (伪代码)
// return hal_adc_read_channel(channel);
// 实际代码需要根据 MCU HAL 库进行适配,例如 STM32 HAL 库:
// ADC_ChannelConfTypeDef sConfig = {0};
// sConfig.Channel = channel; // 根据 channel 参数设置通道
// sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
// sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
// HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// HAL_ADC_Start(&hadc1);
// HAL_ADC_PollForConversion(&hadc1, 100); // 等待转换完成 (超时 100ms)
// return HAL_ADC_GetValue(&hadc1);
return 0; // 示例返回值,实际需替换为读取 ADC 值的代码
}

// 获取电压值 (单位 V)
float adc_driver_get_voltage(void) {
uint16_t adc_raw_value = adc_driver_read_channel(ADC_VOLTAGE_CHANNEL); // 读取电压通道原始值
// 将 ADC 原始值转换为电压值 (根据 ADC 分辨率和参考电压计算)
float voltage = (float)adc_raw_value / ((1 << ADC_RESOLUTION) - 1) * ADC_VREF_VOLTAGE;
// 可以添加电压校准逻辑 (例如乘以校准系数)
return voltage;
}

// 获取电流值 (单位 A)
float adc_driver_get_current(void) {
uint16_t adc_raw_value = adc_driver_read_channel(ADC_CURRENT_CHANNEL); // 读取电流通道原始值
// 将 ADC 原始值转换为电流值 (根据电流采样电阻和放大倍数等参数计算)
float current = (float)adc_raw_value / ((1 << ADC_RESOLUTION) - 1) * ADC_VREF_VOLTAGE * CURRENT_CONVERSION_FACTOR; // CURRENT_CONVERSION_FACTOR 为电流转换系数
// 可以添加电流校准逻辑
return current;
}

6. app/app_measurement.happ/app_measurement.c (测量模块):

  • app_measurement.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef APP_MEASUREMENT_H
#define APP_MEASUREMENT_H

#include "config.h"
#include "drivers/adc_driver.h"

// 测量模块初始化函数
void app_measurement_init(void);

// 测量任务 (周期性调用,更新系统状态中的测量数据)
void app_measurement_task(SystemState_t *systemState);

#endif // APP_MEASUREMENT_H
  • app_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
#include "app_measurement.h"
#include "hal_timer.h" // 用于获取系统时间

static uint32_t last_sample_time_ms = 0; // 上次采样时间
static uint32_t start_time_seconds = 0; // 系统启动时间 (秒)

// 测量模块初始化
void app_measurement_init(void) {
last_sample_time_ms = hal_get_tick_ms(); // 获取当前系统时间
start_time_seconds = hal_get_seconds(); // 获取系统启动时间 (秒)
}

// 测量任务
void app_measurement_task(SystemState_t *systemState) {
uint32_t current_time_ms = hal_get_tick_ms();
if (current_time_ms - last_sample_time_ms >= MEASUREMENT_SAMPLE_RATE_MS) {
last_sample_time_ms = current_time_ms;

// 1. 读取电压和电流值
float voltage = adc_driver_get_voltage();
float current = adc_driver_get_current();

// 2. 计算功率
float power = voltage * current;

// 3. 累计容量 (假设采样周期为 MEASUREMENT_SAMPLE_RATE_MS 毫秒)
systemState->capacity += current * (MEASUREMENT_SAMPLE_RATE_MS / 1000.0f) / 3600.0f; // 单位 Ah

// 4. 累计能量 (假设采样周期为 MEASUREMENT_SAMPLE_RATE_MS 毫秒)
systemState->energy += power * (MEASUREMENT_SAMPLE_RATE_MS / 1000.0f) / 3600000.0f; // 单位 Wh

// 5. 更新通电时间 (秒)
systemState->time_seconds = hal_get_seconds() - start_time_seconds;

// 6. 更新系统状态
systemState->voltage = voltage;
systemState->current = current;
systemState->power = power;
}
}

7. ui/ui_menu.hui/ui_menu.c (用户界面菜单):

  • ui_menu.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef UI_MENU_H
#define UI_MENU_H

#include "config.h"
#include "drivers/encoder_driver.h"
#include "drivers/display_driver.h"

// 用户界面模块初始化函数
void ui_menu_init(void);

// 处理编码器输入
void ui_menu_process_input(EncoderInput_t *encoderInput);

// 更新显示屏
void ui_menu_update_display(SystemState_t *systemState);

#endif // UI_MENU_H
  • ui_menu.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
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
#include "ui_menu.h"

// 菜单结构体 (根据实际菜单设计)
typedef struct MenuItem_t {
char *name; // 菜单项名称
void (*action)(void); // 菜单项执行的动作函数指针 (可选)
struct MenuItem_t *subMenu; // 子菜单指针 (可选)
} MenuItem_t;

// 菜单项定义 (示例)
MenuItem_t mainMenu[] = {
{"Voltage", NULL, NULL},
{"Current", NULL, NULL},
{"Power", NULL, NULL},
{"Capacity", NULL, NULL},
{"Energy", NULL, NULL},
{"Potentiometer", NULL, subPotentiometerMenu}, // 进入电位器设置子菜单
{"Settings", NULL, subSettingsMenu}, // 进入系统设置子菜单
{NULL, NULL, NULL} // 菜单结束标志
};

MenuItem_t subPotentiometerMenu[] = {
{"Pot 1 Set", pot1SetAction, NULL},
{"Pot 2 Set", pot2SetAction, NULL},
{"Pot Config", potConfigAction, NULL},
{"Back", backToMainMenuAction, NULL},
{NULL, NULL, NULL}
};

MenuItem_t subSettingsMenu[] = {
{"Calibration", calibrationAction, NULL},
{"Units", unitsAction, NULL},
{"Back", backToMainMenuAction, NULL},
{NULL, NULL, NULL}
};

MenuItem_t *currentMenu = mainMenu; // 当前菜单指针
uint8_t selectedMenuItemIndex = 0; // 当前选中的菜单项索引

// 用户界面初始化
void ui_menu_init(void) {
display_driver_init(); // 初始化显示驱动
display_driver_clear_screen(); // 清空屏幕
ui_menu_update_display(NULL); // 首次显示主菜单
}

// 处理编码器输入
void ui_menu_process_input(EncoderInput_t *encoderInput) {
if (encoderInput->direction == ENCODER_DIR_CW) { // 顺时针旋转
selectedMenuItemIndex++;
// 菜单项循环选择
uint8_t menuItemsCount = 0;
while (currentMenu[menuItemsCount].name != NULL) {
menuItemsCount++;
}
if (selectedMenuItemIndex >= menuItemsCount) {
selectedMenuItemIndex = 0;
}
ui_menu_update_display(NULL); // 更新显示
} else if (encoderInput->direction == ENCODER_DIR_CCW) { // 逆时针旋转
selectedMenuItemIndex--;
if (selectedMenuItemIndex < 0) {
uint8_t menuItemsCount = 0;
while (currentMenu[menuItemsCount].name != NULL) {
menuItemsCount++;
}
selectedMenuItemIndex = menuItemsCount - 1;
}
ui_menu_update_display(NULL); // 更新显示
} else if (encoderInput->buttonPressed) { // 按键按下
if (currentMenu[selectedMenuItemIndex].subMenu != NULL) {
// 进入子菜单
currentMenu = currentMenu[selectedMenuItemIndex].subMenu;
selectedMenuItemIndex = 0; // 子菜单默认选中第一项
ui_menu_update_display(NULL); // 更新显示
} else if (currentMenu[selectedMenuItemIndex].action != NULL) {
// 执行菜单项动作
currentMenu[selectedMenuItemIndex].action();
}
}
}

// 更新显示屏
void ui_menu_update_display(SystemState_t *systemState) {
display_driver_clear_screen(); // 清空屏幕
uint8_t line = 0;
uint8_t menuItemIndex = 0;
while (currentMenu[menuItemIndex].name != NULL && line < DISPLAY_HEIGHT) {
if (menuItemIndex == selectedMenuItemIndex) {
display_driver_set_cursor(0, line);
display_driver_send_string("> "); // 标记选中项
} else {
display_driver_set_cursor(0, line);
display_driver_send_string(" ");
}
display_driver_send_string(currentMenu[menuItemIndex].name);
line++;
menuItemIndex++;
}

// 如果在主菜单下,显示测量数据 (示例)
if (currentMenu == mainMenu && systemState != NULL) {
char buffer[20];
display_driver_set_cursor(0, 0);
sprintf(buffer, "V:%.2fV", systemState->voltage);
display_driver_send_string(buffer);
display_driver_set_cursor(0, 1);
sprintf(buffer, "I:%.2fA", systemState->current);
display_driver_send_string(buffer);
// ... 显示其他数据 ...
}
}

// ... 菜单项动作函数 (例如 pot1SetAction, pot2SetAction, potConfigAction, backToMainMenuAction, calibrationAction, unitsAction 等) ...
// 这些函数需要根据具体功能实现,例如 pot1SetAction 函数可能需要进入电位器1阻值设置界面,并通过编码器调整阻值。

8. app/app_potentiometer.happ/app_potentiometer.c (电位器控制模块):

  • app_potentiometer.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef APP_POTENTIOMETER_H
#define APP_POTENTIOMETER_H

#include "config.h"
#include "drivers/potentiometer_driver.h"

// 电位器控制模块初始化
void app_potentiometer_init(void);

// 电位器控制任务 (周期性调用,根据系统状态控制电位器)
void app_potentiometer_task(SystemState_t *systemState);

// 设置电位器 1 阻值 (0-100%)
void app_potentiometer_set_pot1(uint8_t percentage);

// 设置电位器 2 阻值 (0-100%)
void app_potentiometer_set_pot2(uint8_t percentage);

#endif // APP_POTENTIOMETER_H
  • app_potentiometer.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
#include "app_potentiometer.h"

// 电位器控制模块初始化
void app_potentiometer_init(void) {
potentiometer_driver_init(); // 初始化电位器驱动
}

// 电位器控制任务 (示例,可以根据用户设置或自动控制逻辑进行控制)
void app_potentiometer_task(SystemState_t *systemState) {
// 示例: 根据系统状态中的 pot1_value 和 pot2_value 控制电位器
potentiometer_driver_set_pot1_value(systemState->pot1_value);
potentiometer_driver_set_pot2_value(systemState->pot2_value);
}

// 设置电位器 1 阻值 (0-100%)
void app_potentiometer_set_pot1(uint8_t percentage) {
// 将百分比转换为电位器控制值 (例如 PWM 占空比)
uint16_t pot_value = (uint16_t)((float)percentage / 100.0f * ((1 << POT_RESOLUTION) - 1));
potentiometer_driver_set_pot1_value(pot_value);
}

// 设置电位器 2 阻值 (0-100%)
void app_potentiometer_set_pot2(uint8_t percentage) {
uint16_t pot_value = (uint16_t)((float)percentage / 100.0f * ((1 << POT_RESOLUTION) - 1));
potentiometer_driver_set_pot2_value(pot_value);
}

9. utils/ (可选工具函数):

可以根据需要创建 utils/string_utils.hutils/string_utils.c 等文件,包含字符串处理、数据格式化等通用工具函数。

项目开发流程和技术方法:

  1. 需求分析阶段:

    • 明确功能需求: 详细定义电压电流表的功能,包括测量范围、精度要求、显示参数、电位器控制功能、用户界面操作等。
    • 确定硬件选型: 根据功能需求和成本预算,选择合适的MCU、ADC芯片、显示屏、编码器、电位器等硬件器件。
    • 制定系统指标: 例如测量精度、响应速度、功耗、稳定性、可靠性等指标。
  2. 系统设计阶段:

    • 架构设计: 采用分层架构和模块化设计,划分硬件层、驱动层、应用层和用户界面层,定义各层的功能和接口。
    • 模块设计: 详细设计每个模块的功能、输入输出、算法和数据结构。
    • 接口设计: 定义各模块之间的接口,例如函数原型、数据结构、通信协议等。
    • 硬件原理图设计: 设计硬件电路原理图,包括MCU外围电路、ADC电路、显示电路、编码器电路、电位器控制电路等。
    • PCB Layout 设计: 根据原理图设计PCB电路板,考虑信号完整性、散热、EMC等因素。
  3. 软件实现阶段:

    • 环境搭建: 搭建嵌入式开发环境,包括安装IDE (例如Keil MDK, IAR Embedded Workbench, Eclipse CDT等)、编译器、调试器、仿真器等工具。
    • 代码编写: 根据系统架构和模块设计,编写C代码实现各个模块的功能,包括驱动层、应用层和用户界面层。
    • 单元测试: 对每个模块进行单元测试,验证模块功能的正确性。
    • 集成测试: 将各个模块集成在一起进行测试,验证系统整体功能的正确性。
    • 代码审查: 进行代码审查,提高代码质量和可维护性。
  4. 测试验证阶段:

    • 功能测试: 测试系统的各项功能是否符合需求,例如电压电流测量精度、电位器控制功能、用户界面操作等。
    • 性能测试: 测试系统的性能指标是否满足要求,例如响应速度、功耗等。
    • 稳定性测试: 进行长时间运行测试,验证系统的稳定性。
    • 可靠性测试: 进行环境测试 (例如高温、低温、振动等),验证系统的可靠性。
    • 用户体验测试: 邀请用户进行体验测试,收集用户反馈,优化用户界面和操作流程。
  5. 维护升级阶段:

    • Bug 修复: 收集用户反馈和测试报告,修复系统Bug。
    • 功能升级: 根据用户需求和市场变化,进行功能升级和扩展。
    • 固件升级: 提供固件升级机制,方便用户更新系统固件。
    • 版本管理: 使用版本管理工具 (例如Git) 管理代码,方便版本控制和协作开发。

项目中采用的技术和方法:

  • 分层架构和模块化设计: 提高代码可维护性、可重用性和可扩展性。
  • 硬件抽象层 (HAL): 屏蔽硬件差异,提高代码可移植性。
  • 驱动层封装: 简化应用层对硬件的操作,提高开发效率。
  • 事件驱动编程: 例如编码器输入处理,提高系统响应速度。
  • 定时器中断: 实现周期性任务,例如数据采样、显示更新等。
  • 数据滤波算法: 提高数据精度和稳定性。
  • 用户界面设计: 提供友好、易用的用户操作界面。
  • 错误处理机制: 提高系统鲁棒性和可靠性。
  • 版本控制工具 (Git): 代码版本管理和团队协作。
  • 调试工具 (JTAG/SWD): 硬件调试和程序下载。
  • 代码审查: 提高代码质量。
  • 单元测试和集成测试: 保证代码质量和系统功能正确性。

总结:

以上代码框架和架构设计为实现一个可靠、高效、可扩展的多功能电压电流表嵌入式系统平台提供了基础。实际项目开发中,需要根据具体的硬件平台、功能需求和性能指标进行详细设计和代码实现。 整个开发过程需要严格遵循嵌入式系统开发流程,并采用经过实践验证的技术和方法,才能最终交付高质量的嵌入式产品。

请注意: 以上代码示例仅为框架和核心模块的演示,实际项目中需要根据具体的硬件平台和功能细节进行完善和适配。完整代码量将远超3000行,包括更详细的驱动程序、更完善的应用层逻辑、更丰富的用户界面功能、以及各种错误处理和异常情况的考虑。

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