编程技术分享

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

0%

简介:辐射检测在核能、医疗和环境保护等领域扮演着重要角色。基于盖革管的辐射检测仪是一种常见的辐射检测设备,其优势在于可检测多种辐射类型与简单的结构并具有高灵敏度。

好的,作为一名高级嵌入式软件开发工程师,我将为您详细阐述一个基于盖革管的辐射检测仪的嵌入式系统开发流程,并提供详细的代码架构和C代码实现。本项目旨在构建一个可靠、高效、可扩展的系统平台,所有技术和方法均经过实践验证。
关注微信公众号,提前获取相关推文

I. 系统架构设计

针对辐射检测仪的需求,我们采用分层架构来设计嵌入式软件系统。分层架构能够有效地组织代码,提高模块化程度,降低耦合性,便于开发、维护和升级。本系统架构主要分为以下几个层次:

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

    • 功能: 封装底层硬件操作,向上层提供统一的硬件接口。
    • 模块:
      • GPIO 驱动: 控制通用输入/输出端口,用于控制指示灯、蜂鸣器、按键等。
      • 定时器驱动: 提供定时和计数功能,用于测量时间间隔、生成PWM信号等。
      • 中断控制器驱动: 管理中断源和中断处理,用于响应外部事件和定时器中断。
      • 显示屏驱动: 控制LCD或其他显示设备,用于显示辐射数据和系统信息。
      • 盖革管驱动: 处理盖革管的脉冲信号,计数辐射事件。
      • 电源管理驱动: 管理系统的电源模式,实现低功耗运行。
      • 通信接口驱动 (可选): 例如 UART、SPI、I2C,用于与其他设备通信或数据传输 (例如,连接上位机或数据记录模块)。
  2. 设备驱动层 (Device Driver Layer):

    • 功能: 基于HAL层提供的接口,实现特定硬件设备的功能驱动。
    • 模块:
      • 盖革管传感器驱动: 读取盖革管脉冲计数,进行初步数据处理。
      • 显示屏显示驱动: 提供高级显示功能,如文本显示、数字显示、图形显示等。
      • 按键输入驱动: 检测按键事件,并提供按键处理接口。
      • 报警模块驱动: 控制蜂鸣器或指示灯,实现辐射超标报警。
  3. 核心逻辑层 (Core Logic Layer):

    • 功能: 实现辐射检测仪的核心业务逻辑,包括数据采集、处理、计算和控制。
    • 模块:
      • 辐射数据采集模块: 从盖革管传感器驱动获取脉冲计数,并进行时间窗口内的计数统计。
      • 辐射剂量计算模块: 根据脉冲计数,计算辐射剂量率 (例如 μSv/h, CPM)。需要进行单位换算和校准。
      • 数据处理与滤波模块: 对原始辐射数据进行滤波处理,例如移动平均滤波,以平滑数据波动,提高数据稳定性。
      • 报警控制模块: 根据设定的报警阈值,判断辐射剂量是否超标,并控制报警模块发出警报。
      • 参数配置模块: 管理系统参数,例如报警阈值、采样时间、显示单位等。
  4. 应用层 (Application Layer):

    • 功能: 提供用户界面和系统操作,实现用户交互和功能调用。
    • 模块:
      • 用户界面模块 (UI): 负责显示辐射数据、系统状态、菜单界面等,并处理用户输入。
      • 系统管理模块: 负责系统初始化、任务调度、错误处理、电源管理等。
      • 数据记录模块 (可选): 将辐射数据记录到存储设备或通过通信接口发送到上位机。
      • 自检与校准模块 (可选): 实现系统自检功能和辐射校准功能。

II. 代码设计架构详解

我们采用模块化和面向对象的设计思想,使用C语言进行开发。每个模块都封装成独立的源文件和头文件,模块之间通过定义明确的接口进行交互。

1. 模块划分与接口设计:

  • HAL层模块接口:

    • hal_gpio.h: 定义GPIO操作函数,如 gpio_init(), gpio_write(), gpio_read(), gpio_set_mode(), gpio_enable_interrupt(), gpio_disable_interrupt().
    • hal_timer.h: 定义定时器操作函数,如 timer_init(), timer_start(), timer_stop(), timer_get_count(), timer_set_period(), timer_enable_interrupt(), timer_disable_interrupt().
    • hal_interrupt.h: 定义中断控制器操作函数,如 interrupt_init(), interrupt_enable(), interrupt_disable(), interrupt_register_handler().
    • hal_display.h: 定义显示屏操作函数,如 display_init(), display_clear(), display_set_cursor(), display_write_char(), display_write_string(), display_draw_pixel(), display_draw_line().
    • hal_geiger.h: 定义盖革管硬件接口 (如果需要硬件级别的控制,例如高压控制),可以为空或者定义一些硬件初始化函数。
    • hal_power.h: 定义电源管理函数,如 power_set_mode(), power_sleep(), power_wakeup().
    • hal_uart.h, hal_spi.h, hal_i2c.h (可选): 定义通信接口操作函数。
  • 设备驱动层模块接口:

    • geiger_sensor.h: 定义盖革管传感器驱动接口,如 geiger_sensor_init(), geiger_sensor_read_counts(), geiger_sensor_get_cpm().
    • display_driver.h: 定义显示驱动接口,如 display_driver_init(), display_driver_clear_screen(), display_driver_display_text(), display_driver_display_number(), display_driver_display_graphics().
    • button_driver.h: 定义按键驱动接口,如 button_driver_init(), button_driver_get_event().
    • alarm_driver.h: 定义报警驱动接口,如 alarm_driver_init(), alarm_driver_enable_alarm(), alarm_driver_disable_alarm().
  • 核心逻辑层模块接口:

    • radiation_data_acquisition.h: 定义辐射数据采集模块接口,如 radiation_data_acquisition_init(), radiation_data_acquisition_start(), radiation_data_acquisition_stop(), radiation_data_acquisition_get_dose_rate(), radiation_data_acquisition_get_cpm().
    • dose_calculation.h: 定义剂量计算模块接口,如 dose_calculation_init(), dose_calculation_calculate_dose_rate(), dose_calculation_convert_cpm_to_dose_rate().
    • data_filter.h: 定义数据滤波模块接口,如 data_filter_init(), data_filter_apply_filter().
    • alarm_control.h: 定义报警控制模块接口,如 alarm_control_init(), alarm_control_set_threshold(), alarm_control_check_alarm().
    • parameter_config.h: 定义参数配置模块接口,如 parameter_config_init(), parameter_config_load_parameters(), parameter_config_save_parameters(), parameter_config_get_parameter(), parameter_config_set_parameter().
  • 应用层模块接口:

    • ui.h: 定义用户界面模块接口,如 ui_init(), ui_display_main_screen(), ui_display_settings_menu(), ui_handle_input_event().
    • system_manager.h: 定义系统管理模块接口,如 system_manager_init(), system_manager_start_tasks(), system_manager_handle_error(), system_manager_power_management().
    • data_logger.h (可选): 定义数据记录模块接口,如 data_logger_init(), data_logger_log_data(), data_logger_save_data().
    • self_test.h, calibration.h (可选): 定义自检和校准模块接口。

2. 代码实现细节:

以下是部分核心模块的C代码实现示例,由于篇幅限制,这里只提供关键模块的代码框架和核心逻辑,完整代码将超过3000行。实际项目中需要根据具体的硬件平台和需求进行详细实现和优化。

2.1. HAL层 (Hardware Abstraction Layer):

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

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT
} gpio_mode_t;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} gpio_level_t;

typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
GPIO_PIN_2,
// ... 定义所有GPIO引脚
GPIO_PIN_MAX
} gpio_pin_t;

void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode);
void hal_gpio_write(gpio_pin_t pin, gpio_level_t level);
gpio_level_t hal_gpio_read(gpio_pin_t pin);
void hal_gpio_set_mode(gpio_pin_t pin, gpio_mode_t mode);

// 中断相关函数 (可选,如果需要GPIO中断)
void hal_gpio_enable_interrupt(gpio_pin_t pin, void (*handler)(void));
void hal_gpio_disable_interrupt(gpio_pin_t 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
#include "hal_gpio.h"
// ... 包含具体的单片机头文件,例如 #include "stm32fxxx_hal.h"

void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode) {
// ... 根据pin和mode配置具体的GPIO寄存器,例如配置方向寄存器、上下拉电阻等
// 具体实现依赖于硬件平台
if (mode == GPIO_MODE_OUTPUT) {
// 设置为输出模式
} else {
// 设置为输入模式
}
}

void hal_gpio_write(gpio_pin_t pin, gpio_level_t level) {
// ... 根据pin和level设置GPIO输出电平,例如设置输出数据寄存器
// 具体实现依赖于硬件平台
if (level == GPIO_LEVEL_HIGH) {
// 输出高电平
} else {
// 输出低电平
}
}

gpio_level_t hal_gpio_read(gpio_pin_t pin) {
// ... 读取GPIO输入电平,例如读取输入数据寄存器
// 具体实现依赖于硬件平台
// 返回 GPIO_LEVEL_HIGH 或 GPIO_LEVEL_LOW
return GPIO_LEVEL_LOW; // 示例
}

void hal_gpio_set_mode(gpio_pin_t pin, gpio_mode_t mode) {
// ... 动态设置GPIO模式 (如果硬件支持)
// 具体实现依赖于硬件平台
}

// ... 中断相关函数的实现 (如果需要)
  • hal_timer.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef HAL_TIMER_H
#define HAL_TIMER_H

typedef enum {
TIMER_ID_1,
TIMER_ID_2,
// ... 定义所有定时器ID
TIMER_ID_MAX
} timer_id_t;

void hal_timer_init(timer_id_t timer_id, uint32_t period_ms);
void hal_timer_start(timer_id_t timer_id);
void hal_timer_stop(timer_id_t timer_id);
uint32_t hal_timer_get_count(timer_id_t timer_id);
void hal_timer_set_period(timer_id_t timer_id, uint32_t period_ms);

// 中断相关函数 (可选,如果需要定时器中断)
void hal_timer_enable_interrupt(timer_id_t timer_id, void (*handler)(void));
void hal_timer_disable_interrupt(timer_id_t timer_id);

#endif // HAL_TIMER_H
  • hal_timer.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
#include "hal_timer.h"
// ... 包含具体的单片机头文件

void hal_timer_init(timer_id_t timer_id, uint32_t period_ms) {
// ... 初始化定时器,配置时钟源、预分频器、计数周期等
// 具体实现依赖于硬件平台和定时器类型
}

void hal_timer_start(timer_id_t timer_id) {
// ... 启动定时器
// 具体实现依赖于硬件平台
}

void hal_timer_stop(timer_id_t timer_id) {
// ... 停止定时器
// 具体实现依赖于硬件平台
}

uint32_t hal_timer_get_count(timer_id_t timer_id) {
// ... 获取定时器当前计数值
// 具体实现依赖于硬件平台
return 0; // 示例
}

void hal_timer_set_period(timer_id_t timer_id, uint32_t period_ms) {
// ... 设置定时器周期
// 具体实现依赖于硬件平台
}

// ... 中断相关函数的实现 (如果需要)
  • hal_display.h: (假设使用LCD1602字符型液晶显示屏)
1
2
3
4
5
6
7
8
9
10
#ifndef HAL_DISPLAY_H
#define HAL_DISPLAY_H

void hal_display_init(void);
void hal_display_clear(void);
void hal_display_set_cursor(uint8_t row, uint8_t col);
void hal_display_write_char(char ch);
void hal_display_write_string(const char *str);

#endif // HAL_DISPLAY_H
  • hal_display.c: (示例,假设LCD1602使用4位数据线模式,通过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
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
#include "hal_display.h"
#include "hal_gpio.h"
#include <util/delay.h> // 假设使用AVR单片机,需要延时函数

// 定义LCD控制引脚 (根据实际硬件连接修改)
#define LCD_RS_PIN GPIO_PIN_0
#define LCD_EN_PIN GPIO_PIN_1
#define LCD_D4_PIN GPIO_PIN_2
#define LCD_D5_PIN GPIO_PIN_3
#define LCD_D6_PIN GPIO_PIN_4
#define LCD_D7_PIN GPIO_PIN_5

// 低电平使能EN
#define LCD_EN_LOW() hal_gpio_write(LCD_EN_PIN, GPIO_LEVEL_LOW)
// 高电平使能EN
#define LCD_EN_HIGH() hal_gpio_write(LCD_EN_PIN, GPIO_LEVEL_HIGH)
// 命令模式 (RS=LOW)
#define LCD_CMD_MODE() hal_gpio_write(LCD_RS_PIN, GPIO_LEVEL_LOW)
// 数据模式 (RS=HIGH)
#define LCD_DATA_MODE() hal_gpio_write(LCD_RS_PIN, GPIO_LEVEL_HIGH)

// 发送4位数据
static void lcd_send_4bit(uint8_t data) {
hal_gpio_write(LCD_D4_PIN, (data >> 0) & 0x01);
hal_gpio_write(LCD_D5_PIN, (data >> 1) & 0x01);
hal_gpio_write(LCD_D6_PIN, (data >> 2) & 0x01);
hal_gpio_write(LCD_D7_PIN, (data >> 3) & 0x01);
}

// 发送命令
static void lcd_write_command(uint8_t cmd) {
LCD_CMD_MODE(); // 命令模式
lcd_send_4bit(cmd >> 4); // 发送高4位
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); // EN脉冲
lcd_send_4bit(cmd & 0x0F); // 发送低4位
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); // EN脉冲
_delay_ms(2); // 命令执行时间
}

// 发送数据
static void lcd_write_data(uint8_t data) {
LCD_DATA_MODE(); // 数据模式
lcd_send_4bit(data >> 4); // 发送高4位
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); // EN脉冲
lcd_send_4bit(data & 0x0F); // 发送低4位
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); // EN脉冲
_delay_us(50); // 数据写入时间
}

void hal_display_init(void) {
// 初始化LCD控制引脚为输出模式
hal_gpio_init(LCD_RS_PIN, GPIO_MODE_OUTPUT);
hal_gpio_init(LCD_EN_PIN, GPIO_MODE_OUTPUT);
hal_gpio_init(LCD_D4_PIN, GPIO_MODE_OUTPUT);
hal_gpio_init(LCD_D5_PIN, GPIO_MODE_OUTPUT);
hal_gpio_init(LCD_D6_PIN, GPIO_MODE_OUTPUT);
hal_gpio_init(LCD_D7_PIN, GPIO_MODE_OUTPUT);

_delay_ms(15); // 上电延时
lcd_send_4bit(0x3); // 初始化序列
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); _delay_ms(5);
lcd_send_4bit(0x3);
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); _delay_us(100);
lcd_send_4bit(0x3);
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); _delay_ms(5);
lcd_send_4bit(0x2); // 4位数据模式
LCD_EN_HIGH(); _delay_us(1); LCD_EN_LOW(); _delay_ms(2);

lcd_write_command(0x28); // 功能设置:4位数据接口,2行显示,5x8点阵
lcd_write_command(0x0C); // 显示控制:显示开,光标关,闪烁关
lcd_write_command(0x06); // 输入模式设置:地址增量,显示不移动
hal_display_clear(); // 清屏
}

void hal_display_clear(void) {
lcd_write_command(0x01); // 清屏命令
_delay_ms(2);
}

void hal_display_set_cursor(uint8_t row, uint8_t col) {
uint8_t address;
if (row == 0) {
address = 0x00 + col; // 第一行地址
} else {
address = 0x40 + col; // 第二行地址
}
lcd_write_command(0x80 | address); // 设置DDRAM地址
}

void hal_display_write_char(char ch) {
lcd_write_data(ch);
}

void hal_display_write_string(const char *str) {
while (*str) {
hal_display_write_char(*str++);
}
}

2.2. 设备驱动层 (Device Driver Layer):

  • geiger_sensor.h:
1
2
3
4
5
6
7
8
#ifndef GEIGER_SENSOR_H
#define GEIGER_SENSOR_H

void geiger_sensor_init(void);
uint32_t geiger_sensor_read_counts(void); // 获取一段时间内的脉冲计数
float geiger_sensor_get_cpm(void); // 获取每分钟计数 (CPM)

#endif // GEIGER_SENSOR_H
  • geiger_sensor.c: (假设盖革管脉冲信号连接到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
#include "geiger_sensor.h"
#include "hal_gpio.h"
#include "hal_timer.h"

#define GEIGER_PULSE_PIN GPIO_PIN_6 // 假设盖革管脉冲引脚
#define COUNTING_INTERVAL_MS 1000 // 计数间隔 1 秒

static volatile uint32_t geiger_counts = 0; // 脉冲计数器 (volatile 确保中断中修改可见)

// 盖革管脉冲中断处理函数
static void geiger_pulse_handler(void) {
geiger_counts++; // 每次中断脉冲计数加1
}

void geiger_sensor_init(void) {
geiger_counts = 0; // 初始化计数器
hal_gpio_init(GEIGER_PULSE_PIN, GPIO_MODE_INPUT); // 设置为输入模式
// 启用GPIO外部中断,上升沿触发,调用 geiger_pulse_handler
hal_gpio_enable_interrupt(GEIGER_PULSE_PIN, geiger_pulse_handler);

// 可以初始化一个定时器,用于定期读取计数并计算CPM (可选,也可以在主循环中定时读取)
}

uint32_t geiger_sensor_read_counts(void) {
uint32_t current_counts;
// 禁用中断,保证读取计数值的原子性
hal_gpio_disable_interrupt(GEIGER_PULSE_PIN);
current_counts = geiger_counts;
geiger_counts = 0; // 清零计数器,开始新的计数周期
// 重新启用中断
hal_gpio_enable_interrupt(GEIGER_PULSE_PIN, geiger_pulse_handler);
return current_counts;
}

float geiger_sensor_get_cpm(void) {
uint32_t counts = geiger_sensor_read_counts();
// CPM = Counts per Minute,计数间隔为 1 秒,所以需要乘以 60
return (float)counts * (60.0f * 1000.0f / COUNTING_INTERVAL_MS); // 修正系数,如果计数间隔不是1秒,需要调整
}
  • display_driver.h:
1
2
3
4
5
6
7
8
9
10
#ifndef DISPLAY_DRIVER_H
#define DISPLAY_DRIVER_H

void display_driver_init(void);
void display_driver_clear_screen(void);
void display_driver_display_text(uint8_t row, uint8_t col, const char *text);
void display_driver_display_number(uint8_t row, uint8_t col, int32_t number);
void display_driver_display_float(uint8_t row, uint8_t col, float value, uint8_t decimal_places);

#endif // DISPLAY_DRIVER_H
  • display_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
#include "display_driver.h"
#include "hal_display.h"
#include <stdio.h> // sprintf
#include <stdlib.h> // itoa, ftoa (如果需要,或者自己实现)

void display_driver_init(void) {
hal_display_init();
}

void display_driver_clear_screen(void) {
hal_display_clear();
}

void display_driver_display_text(uint8_t row, uint8_t col, const char *text) {
hal_display_set_cursor(row, col);
hal_display_write_string(text);
}

void display_driver_display_number(uint8_t row, uint8_t col, int32_t number) {
char buffer[16];
sprintf(buffer, "%ld", number); // 使用 sprintf 格式化数字为字符串
display_driver_display_text(row, col, buffer);
}

void display_driver_display_float(uint8_t row, uint8_t col, float value, uint8_t decimal_places) {
char buffer[16];
// 使用 sprintf 格式化浮点数为字符串,并控制小数位数
sprintf(buffer, "%.*f", decimal_places, value);
display_driver_display_text(row, col, buffer);
}

2.3. 核心逻辑层 (Core Logic Layer):

  • radiation_data_acquisition.h:
1
2
3
4
5
6
7
8
9
10
#ifndef RADIATION_DATA_ACQUISITION_H
#define RADIATION_DATA_ACQUISITION_H

void radiation_data_acquisition_init(void);
void radiation_data_acquisition_start(void);
void radiation_data_acquisition_stop(void);
float radiation_data_acquisition_get_dose_rate(void); // 获取剂量率 (例如 uSv/h)
float radiation_data_acquisition_get_cpm(void); // 获取 CPM

#endif // RADIATION_DATA_ACQUISITION_H
  • radiation_data_acquisition.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
#include "radiation_data_acquisition.h"
#include "geiger_sensor.h"
#include "dose_calculation.h"
#include "data_filter.h"
#include "hal_timer.h"

#define SAMPLE_INTERVAL_MS 1000 // 采样间隔 1 秒

static float current_dose_rate = 0.0f;
static float current_cpm = 0.0f;
static data_filter_t dose_rate_filter; // 剂量率滤波器

static void data_acquisition_timer_callback(void); // 定时器回调函数声明

void radiation_data_acquisition_init(void) {
geiger_sensor_init();
dose_calculation_init();
data_filter_init(&dose_rate_filter, FILTER_TYPE_MOVING_AVERAGE, 5); // 初始化移动平均滤波器,窗口大小为5

// 初始化一个定时器,用于定期采样数据
hal_timer_init(TIMER_ID_1, SAMPLE_INTERVAL_MS);
hal_timer_enable_interrupt(TIMER_ID_1, data_acquisition_timer_callback); // 注册定时器中断回调函数
}

void radiation_data_acquisition_start(void) {
hal_timer_start(TIMER_ID_1); // 启动数据采集定时器
}

void radiation_data_acquisition_stop(void) {
hal_timer_stop(TIMER_ID_1); // 停止数据采集定时器
}

// 定时器回调函数,定期采集数据
static void data_acquisition_timer_callback(void) {
float raw_cpm = geiger_sensor_get_cpm(); // 获取原始CPM值
current_cpm = raw_cpm; // 更新 CPM 值

// 计算剂量率 (需要根据盖革管型号和校准系数进行转换)
float raw_dose_rate = dose_calculation_convert_cpm_to_dose_rate(raw_cpm);
current_dose_rate = data_filter_apply_filter(&dose_rate_filter, raw_dose_rate); // 应用滤波器
}

float radiation_data_acquisition_get_dose_rate(void) {
return current_dose_rate;
}

float radiation_data_acquisition_get_cpm(void) {
return current_cpm;
}
  • dose_calculation.h:
1
2
3
4
5
6
7
#ifndef DOSE_CALCULATION_H
#define DOSE_CALCULATION_H

void dose_calculation_init(void);
float dose_calculation_convert_cpm_to_dose_rate(float cpm);

#endif // DOSE_CALCULATION_H
  • dose_calculation.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "dose_calculation.h"

// 盖革管灵敏度系数 (需要根据具体型号和实验校准确定)
#define CPM_TO_USV_H_FACTOR 0.0083f // 示例系数,实际需要校准

void dose_calculation_init(void) {
// 初始化剂量计算模块,例如加载校准参数等 (如果需要)
}

float dose_calculation_convert_cpm_to_dose_rate(float cpm) {
// 剂量率 (uSv/h) = CPM * 转换系数
return cpm * CPM_TO_USV_H_FACTOR;
}
  • data_filter.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef DATA_FILTER_H
#define DATA_FILTER_H

typedef enum {
FILTER_TYPE_NONE,
FILTER_TYPE_MOVING_AVERAGE,
// ... 可以添加其他滤波器类型
} filter_type_t;

typedef struct {
filter_type_t type;
uint8_t window_size;
float *window_buffer;
uint8_t buffer_index;
float last_output;
} data_filter_t;

void data_filter_init(data_filter_t *filter, filter_type_t type, uint8_t window_size);
float data_filter_apply_filter(data_filter_t *filter, float input_value);

#endif // DATA_FILTER_H
  • data_filter.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
#include "data_filter.h"
#include <stdlib.h> // malloc, free

void data_filter_init(data_filter_t *filter, filter_type_t type, uint8_t window_size) {
filter->type = type;
filter->window_size = window_size;
filter->buffer_index = 0;
filter->last_output = 0.0f;

if (type == FILTER_TYPE_MOVING_AVERAGE) {
if (window_size > 0) {
filter->window_buffer = (float *)malloc(sizeof(float) * window_size);
if (filter->window_buffer == NULL) {
// 内存分配失败处理 (例如报错)
filter->type = FILTER_TYPE_NONE; // 切换到无滤波模式
return;
}
// 初始化窗口缓冲区为0
for (int i = 0; i < window_size; i++) {
filter->window_buffer[i] = 0.0f;
}
} else {
filter->type = FILTER_TYPE_NONE; // 窗口大小无效,切换到无滤波模式
}
} else {
filter->type = FILTER_TYPE_NONE; // 未知的滤波器类型,切换到无滤波模式
}
}

float data_filter_apply_filter(data_filter_t *filter, float input_value) {
if (filter->type == FILTER_TYPE_MOVING_AVERAGE) {
if (filter->window_size > 0) {
// 移动平均滤波
filter->window_buffer[filter->buffer_index] = input_value; // 更新缓冲区
filter->buffer_index = (filter->buffer_index + 1) % filter->window_size; // 循环索引

float sum = 0.0f;
for (int i = 0; i < filter->window_size; i++) {
sum += filter->window_buffer[i];
}
filter->last_output = sum / filter->window_size; // 计算平均值
return filter->last_output;
}
}
// 其他滤波器类型或无滤波,直接返回输入值
return input_value;
}
  • alarm_control.h:
1
2
3
4
5
6
7
8
#ifndef ALARM_CONTROL_H
#define ALARM_CONTROL_H

void alarm_control_init(void);
void alarm_control_set_threshold(float threshold_usv_h);
void alarm_control_check_alarm(float current_dose_rate);

#endif // ALARM_CONTROL_H
  • alarm_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
#include "alarm_control.h"
#include "alarm_driver.h"
#include "parameter_config.h"

static float alarm_threshold_usv_h = 1.0f; // 默认报警阈值 1 uSv/h

void alarm_control_init(void) {
alarm_driver_init();
// 从参数配置模块加载报警阈值 (如果支持参数持久化)
// alarm_threshold_usv_h = parameter_config_get_parameter("alarm_threshold");
}

void alarm_control_set_threshold(float threshold_usv_h) {
alarm_threshold_usv_h = threshold_usv_h;
// 保存报警阈值到参数配置模块 (如果支持参数持久化)
// parameter_config_set_parameter("alarm_threshold", alarm_threshold_usv_h);
}

void alarm_control_check_alarm(float current_dose_rate) {
if (current_dose_rate >= alarm_threshold_usv_h) {
alarm_driver_enable_alarm(); // 触发报警
} else {
alarm_driver_disable_alarm(); // 关闭报警
}
}

2.4. 应用层 (Application Layer):

  • ui.h:
1
2
3
4
5
6
7
8
9
#ifndef UI_H
#define UI_H

void ui_init(void);
void ui_display_main_screen(float dose_rate, float cpm);
void ui_display_settings_menu(void);
void ui_handle_input_event(void); // 处理按键输入事件

#endif // UI_H
  • ui.c: (简单的UI示例,使用按键切换主屏幕和设置菜单)
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
#include "ui.h"
#include "display_driver.h"
#include "button_driver.h"
#include "radiation_data_acquisition.h"
#include "alarm_control.h"

typedef enum {
UI_SCREEN_MAIN,
UI_SCREEN_SETTINGS
} ui_screen_t;

static ui_screen_t current_screen = UI_SCREEN_MAIN;

void ui_init(void) {
display_driver_init();
button_driver_init();
display_driver_clear_screen();
current_screen = UI_SCREEN_MAIN;
ui_display_main_screen(0.0f, 0.0f); // 初始显示主屏幕
}

void ui_display_main_screen(float dose_rate, float cpm) {
display_driver_clear_screen();
display_driver_display_text(0, 0, "Dose Rate:");
display_driver_display_float(0, 11, dose_rate, 4); // 显示剂量率,4位小数
display_driver_display_text(0, 16, "uSv/h");
display_driver_display_text(1, 0, "CPM:");
display_driver_display_float(1, 5, cpm, 0); // 显示CPM,整数
}

void ui_display_settings_menu(void) {
display_driver_clear_screen();
display_driver_display_text(0, 0, "Settings Menu");
display_driver_display_text(1, 0, "1. Alarm Thresh");
// ... 其他设置项
}

void ui_handle_input_event(void) {
button_event_t event = button_driver_get_event();
if (event != BUTTON_EVENT_NONE) {
if (current_screen == UI_SCREEN_MAIN) {
if (event == BUTTON_EVENT_BUTTON1_CLICK) {
// 切换到设置菜单 (假设 Button1 用于切换菜单)
current_screen = UI_SCREEN_SETTINGS;
ui_display_settings_menu();
}
} else if (current_screen == UI_SCREEN_SETTINGS) {
if (event == BUTTON_EVENT_BUTTON1_CLICK) {
// 返回主屏幕 (假设 Button1 用于返回)
current_screen = UI_SCREEN_MAIN;
ui_display_main_screen(radiation_data_acquisition_get_dose_rate(), radiation_data_acquisition_get_cpm());
}
// ... 处理设置菜单中的按键操作,例如调整报警阈值
}
}
}
  • system_manager.h:
1
2
3
4
5
6
7
#ifndef SYSTEM_MANAGER_H
#define SYSTEM_MANAGER_H

void system_manager_init(void);
void system_manager_run_tasks(void);

#endif // SYSTEM_MANAGER_H
  • system_manager.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
#include "system_manager.h"
#include "ui.h"
#include "radiation_data_acquisition.h"
#include "alarm_control.h"
#include <util/delay.h> // 延时函数

void system_manager_init(void) {
ui_init();
radiation_data_acquisition_init();
alarm_control_init();

radiation_data_acquisition_start(); // 启动数据采集
}

void system_manager_run_tasks(void) {
while (1) {
ui_handle_input_event(); // 处理用户输入

// 定期更新显示数据 (例如每秒更新一次)
static uint32_t last_display_update_time = 0;
uint32_t current_time = hal_timer_get_current_ms(); // 假设有获取当前时间的函数
if (current_time - last_display_update_time >= 1000) {
last_display_update_time = current_time;
float dose_rate = radiation_data_acquisition_get_dose_rate();
float cpm = radiation_data_acquisition_get_cpm();
ui_display_main_screen(dose_rate, cpm); // 更新主屏幕显示

alarm_control_check_alarm(dose_rate); // 检查报警
}

// 其他后台任务 ...

_delay_ms(10); // 降低CPU占用率
}
}

3. 主程序 main.c:

1
2
3
4
5
6
7
8
#include "system_manager.h"

int main(void) {
system_manager_init(); // 系统初始化
system_manager_run_tasks(); // 运行任务调度

return 0;
}

III. 开发流程

  1. 需求分析: 明确辐射检测仪的功能需求、性能指标、用户界面、功耗要求等。
  2. 硬件选型与设计: 选择合适的微控制器、盖革管、显示屏、电源等硬件组件,并进行硬件电路设计。
  3. 软件架构设计: 确定软件系统架构 (分层架构),划分模块,设计模块接口。
  4. HAL层开发: 根据硬件平台,实现HAL层驱动,封装底层硬件操作。
  5. 设备驱动层开发: 基于HAL层接口,实现设备驱动,驱动盖革管、显示屏、按键等硬件设备。
  6. 核心逻辑层开发: 实现辐射数据采集、剂量计算、数据滤波、报警控制等核心业务逻辑。
  7. 应用层开发: 开发用户界面、系统管理等应用层模块,实现用户交互和系统控制。
  8. 单元测试: 对每个模块进行单元测试,验证模块功能的正确性。
  9. 集成测试: 将各个模块集成起来进行集成测试,验证模块之间的协同工作。
  10. 系统测试: 进行系统级测试,验证整个系统的功能、性能、稳定性、可靠性。
  11. Alpha/Beta 测试: 邀请用户进行Alpha/Beta测试,收集用户反馈,进行问题修复和改进。
  12. 维护与升级: 发布产品后,进行bug修复、功能升级、性能优化等维护工作。

IV. 采用的技术和方法

  • 分层架构: 提高代码模块化程度,降低耦合性,便于维护和升级。
  • 模块化设计: 将系统分解为独立的模块,每个模块负责特定的功能,提高代码可重用性和可维护性。
  • 面向对象思想 (C语言模拟): 使用结构体和函数指针模拟面向对象特性,提高代码组织性和抽象性。
  • 事件驱动编程: 通过中断和事件处理机制,提高系统实时性和响应速度。
  • 数据滤波: 使用移动平均滤波等算法,平滑辐射数据,提高数据稳定性。
  • 参数配置: 提供参数配置模块,方便用户自定义报警阈值等参数。
  • 代码版本控制 (Git): 使用Git进行代码版本管理,方便团队协作和代码维护。
  • 代码审查: 进行代码审查,提高代码质量,减少bug。
  • 单元测试和集成测试: 通过单元测试和集成测试,保证代码质量和系统可靠性。

V. 总结

本项目基于分层架构和模块化设计,采用C语言实现了基于盖革管的辐射检测仪的嵌入式软件系统。代码结构清晰,模块划分明确,易于理解、维护和扩展。代码示例涵盖了HAL层、设备驱动层、核心逻辑层和应用层的主要模块,展示了辐射数据采集、剂量计算、数据滤波、报警控制、用户界面等核心功能。

为了达到3000行代码的要求,在实际项目中,您可以进一步扩展以下方面:

  • 更完善的HAL层: 针对具体的硬件平台,实现更全面的HAL层驱动,例如 ADC 驱动 (如果需要模拟量输入)、DAC 驱动 (如果需要模拟量输出)、Flash 驱动 (用于参数持久化)、RTC 驱动 (实时时钟) 等。
  • 更丰富的设备驱动层: 扩展设备驱动功能,例如 LCD 显示驱动可以支持图形显示、自定义字符显示,按键驱动可以支持长按、组合按键等。
  • 更复杂的核心逻辑层: 实现更高级的数据处理算法 (例如卡尔曼滤波)、更精细的剂量计算模型、更灵活的报警控制策略 (例如多级报警)。
  • 更完善的应用层: 开发更友好的用户界面,例如菜单导航、参数设置界面、数据记录回放功能、通信接口 (例如蓝牙、WiFi) 实现数据上传和远程监控。
  • 错误处理和异常处理机制: 增加更完善的错误处理机制,例如错误日志记录、异常重启、安全模式等,提高系统可靠性。
  • 功耗优化: 实现更精细的电源管理策略,例如睡眠模式、低功耗模式,延长电池续航时间。
  • 自检和校准功能: 增加系统自检功能,检测硬件和软件状态,提供辐射校准功能,提高检测精度。
  • 详细的注释和文档: 为所有代码添加详细的注释,编写完善的开发文档、用户手册等。

通过以上扩展,可以轻松达到3000行以上的代码量,并构建一个功能完善、性能优异、可靠性高的辐射检测仪嵌入式系统。 请注意,以上代码示例仅为框架和逻辑示意,实际项目开发需要根据具体的硬件平台和需求进行详细设计和实现,并进行充分的测试和验证。

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