编程技术分享

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

0%

简介:一种基于窗口比较器的便携式TTL逻辑电平测试笔

好的,作为一名高级嵌入式软件开发工程师,我将详细阐述基于窗口比较器的便携式TTL逻辑电平测试笔的嵌入式系统开发流程,并提供一个可靠、高效、可扩展的代码设计架构以及具体的C代码实现。
关注微信公众号,提前获取相关推文

项目概述

本项目旨在设计并实现一款便携式TTL逻辑电平测试笔。该测试笔利用窗口比较器原理,能够准确快速地判断被测信号的TTL逻辑电平状态(高电平、低电平或未定义/过渡状态),并通过LED指示灯直观显示测试结果。这款测试笔具有成本低廉、体积小巧、操作简便等优点,非常适合电子工程师、硬件爱好者以及嵌入式开发人员进行电路调试和故障排查。

嵌入式系统开发流程

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

  1. 需求分析
  2. 系统设计
  3. 硬件设计
  4. 软件设计
  5. 系统集成
  6. 测试验证
  7. 维护升级

接下来,我们将按照这个流程,详细阐述TTL逻辑电平测试笔的开发过程。

1. 需求分析

  • 功能需求:

    • 基本功能: 准确检测TTL逻辑电平信号的状态,包括高电平、低电平和未定义/过渡状态。
    • 指示功能: 通过LED指示灯清晰地显示测试结果。
    • 便携性: 设备体积小巧,方便携带和单手操作。
    • 低功耗: 采用低功耗设计,延长电池寿命(如果采用电池供电)。
    • 低成本: 采用低成本的元器件,降低制造成本。
    • 易用性: 操作简单直观,无需复杂的设置和操作。
  • 性能需求:

    • 响应速度: 测试结果应快速响应,延迟时间尽可能短。
    • 准确性: 测试结果应准确可靠,误判率低。
    • 输入电压范围: 能够适应标准的TTL逻辑电平电压范围 (通常为 0V - 5V)。
    • 工作温度范围: 能够在常见的环境温度范围内正常工作。
  • 约束条件:

    • 成本约束: 总成本控制在较低水平(例如,用户提到的15元超低成本)。
    • 尺寸约束: 设备尺寸要尽可能小巧,类似笔的形状。
    • 功耗约束: 功耗要尽可能低,尤其如果采用电池供电。

2. 系统设计

根据需求分析,我们进行系统总体设计。该系统主要由硬件和软件两部分组成。

  • 硬件系统设计:

    • 核心器件: 微控制器 (MCU) - 用于控制整个系统,读取输入电压,判断逻辑电平,并控制LED指示灯。 窗口比较器 - 用于精确比较输入电压与预设的TTL逻辑电平阈值。
    • 输入电路: 探针 - 用于连接被测电路的信号点。 输入保护电路 - 防止过压或过流损坏MCU和比较器。
    • 比较器电路: 窗口比较器 - 由两个比较器和一个逻辑门组成,设置高电平阈值和低电平阈值。
    • 指示电路: LED指示灯 (至少两个,分别指示高电平和低电平,可以考虑第三个LED指示未定义/过渡状态)。 限流电阻 - 保护LED,控制LED亮度。
    • 电源电路: USB供电接口 (考虑到便携性和低成本,USB供电较为方便)。 稳压电路 (如果需要,例如输入电压不稳定时)。
  • 软件系统设计:

    • 核心功能: ADC采样 - 读取输入电压的模拟值。 窗口比较逻辑 - 根据ADC值判断逻辑电平状态。 LED控制 - 根据逻辑电平状态控制LED指示灯。
    • 软件架构: 采用模块化设计,提高代码的可读性、可维护性和可扩展性。 可以采用状态机模式,管理不同的逻辑电平状态。

3. 硬件设计

基于系统设计,我们进行详细的硬件电路设计。

  • 微控制器 (MCU) 选型:

    • 考虑因素: 低成本、低功耗、内置ADC、GPIO资源充足、易于开发。
    • 可选型号: STMicroelectronics STM32F0系列、Microchip PIC16F系列、国产GD32F103系列等。 这里我们选择STM32F030F4P6,它是一款基于ARM Cortex-M0内核的32位微控制器,具有成本低廉、功耗低、性能适中、易于开发等优点,非常适合本项目的需求。
  • 窗口比较器电路设计:

    • 比较器芯片选型: 低功耗、响应速度快、单电源供电。 可选型号:LM393、LM339、LTC6702等。 这里我们选择LM393,这是一款经典的低功耗双比较器,成本低廉,性能稳定。
    • 阈值电压设置: TTL高电平阈值通常为2.4V,低电平阈值通常为0.8V。 使用电阻分压网络来设置这两个阈值电压。 例如,使用精密电阻,确保阈值电压的准确性。
  • 输入保护电路设计:

    • 保护措施: 串联电阻 - 限制输入电流。 TVS二极管 - 钳位过压,保护后级电路。
  • LED指示电路设计:

    • LED选型: 选择不同颜色的LED,例如红色指示高电平,绿色指示低电平,蓝色或黄色指示未定义/过渡状态。
    • 限流电阻计算: 根据LED的额定电流和MCU的GPIO输出电压,计算合适的限流电阻值,保证LED正常发光,并防止烧毁。
  • 电源电路设计:

    • USB供电: 使用Micro USB或Type-C接口,方便供电。
    • 稳压电路 (可选): 如果USB供电电压波动较大,可以考虑增加一个稳压芯片,例如LDO (Low Dropout Regulator),将5V USB电压稳定到3.3V或其他MCU所需的工作电压。 对于STM32F030F4P6,可以直接使用USB 5V供电,通过内部稳压器工作。
  • PCB设计:

    • 布局布线: 紧凑布局,减小PCB尺寸。 注意模拟电路和数字电路的隔离,减少噪声干扰。 电源线和地线要加粗,减小阻抗。 信号线尽量短而直,减少信号反射和干扰。
    • 双层板: 考虑到成本和复杂性,可以使用双层PCB板。
    • 元件选型: 选用贴片元件 (SMD),进一步减小体积。

硬件原理图 (示意图 - 实际原理图会更详细)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[USB 5V] --- [输入保护电阻] --- [探针] --- [窗口比较器输入] --- [MCU ADC输入]

[窗口比较器 (LM393)]
+ 输入端1 (非反相) --- [电阻分压网络 - 高电平阈值] --- [VCC]
- 输入端1 (反相) --- [探针输入]
输出端1 --- [逻辑门 (例如与非门)] --- [LED (高电平指示)] --- [限流电阻] --- [GND]

+ 输入端2 (非反相) --- [探针输入]
- 输入端2 (反相) --- [电阻分压网络 - 低电平阈值] --- [GND]
输出端2 --- [逻辑门 (例如与非门)] --- [LED (低电平指示)] --- [限流电阻] --- [GND]

[MCU (STM32F030F4P6)]
ADC_IN --- [探针输入]
GPIO_LED_HIGH --- [LED (高电平指示) 控制]
GPIO_LED_LOW --- [LED (低电平指示) 控制]
GPIO_LED_UNDEFINED --- [LED (未定义/过渡状态指示) 控制] (可选)
GND, VCC, USB 连接, 调试接口 (SWD) 等

4. 软件设计

软件部分主要负责控制MCU,实现ADC采样、窗口比较逻辑和LED指示。

  • 软件架构: 采用模块化设计,分为以下几个模块:

    • HAL (Hardware Abstraction Layer) 硬件抽象层: 封装底层硬件操作,提供统一的接口给上层应用使用,方便硬件的移植和维护。 包括GPIO驱动、ADC驱动等。
    • LogicLevelDetection 逻辑电平检测模块: 负责ADC采样、窗口比较逻辑、逻辑电平状态判断。
    • LEDControl LED控制模块: 负责控制LED指示灯的亮灭。
    • Application 应用层: 主程序,负责初始化各个模块,并循环执行逻辑电平检测和LED指示。
  • 状态机 (可选,但可以简化逻辑): 可以定义以下状态:

    • STATE_LOW: 低电平状态
    • STATE_HIGH: 高电平状态
    • STATE_UNDEFINED: 未定义/过渡状态
  • 软件流程图 (简化版):

1
[开始] --> [初始化 HAL, LogicLevelDetection, LEDControl 模块] --> [循环:] --> [读取 ADC 值] --> [LogicLevelDetection.DetectLogicLevel(ADC值) -> logicLevelStatus] --> [LEDControl.UpdateLEDs(logicLevelStatus)] --> [延时 (例如 10ms, 提高稳定性)] --> [循环:]

5. 系统集成

  • 硬件组装: 将所有元器件焊接在PCB板上,制作硬件原型。
  • 软件烧录: 使用SWD调试接口,将编译好的C代码烧录到STM32F030F4P6 MCU中。
  • 硬件调试: 检查硬件电路连接是否正确,电源是否正常,各部分电路是否工作正常。 使用万用表、示波器等工具进行硬件调试。
  • 软件调试: 使用调试器 (例如ST-Link和GDB),进行软件调试。 单步调试、设置断点、查看变量值等,验证软件逻辑是否正确。 重点调试ADC采样、窗口比较逻辑和LED控制部分。
  • 软硬件联调: 将硬件和软件结合起来进行联调。 测试实际的TTL逻辑电平检测功能是否正常,LED指示是否正确。 不断优化软件和硬件,解决问题,直到系统稳定可靠地工作。

6. 测试验证

  • 功能测试:

    • TTL高电平测试: 使用标准TTL高电平信号源 (例如 5V),测试测试笔是否能正确指示高电平状态。
    • TTL低电平测试: 使用标准TTL低电平信号源 (例如 0V),测试测试笔是否能正确指示低电平状态。
    • TTL过渡状态测试: 使用信号发生器产生在TTL高低电平阈值之间的信号,测试测试笔是否能正确指示未定义/过渡状态 (如果实现)。
    • 输入电压范围测试: 在TTL标准电压范围内,以及略微超出范围的电压下进行测试,验证测试笔的输入电压范围和鲁棒性。
  • 性能测试:

    • 响应速度测试: 使用示波器测量从输入信号变化到LED指示灯变化的时间,评估响应速度。
    • 准确性测试: 进行多次重复测试,统计误判率,验证测试笔的准确性。
    • 功耗测试: 使用电流表测量测试笔的工作电流,评估功耗水平。
  • 环境测试: 在不同的温度、湿度条件下进行测试,验证测试笔的环境适应性。

  • 可靠性测试: 进行长时间连续工作测试,验证测试笔的可靠性。

7. 维护升级

  • 维护:

    • 故障排查: 如果测试笔在使用过程中出现故障,需要进行故障排查和维修。
    • 定期维护: 例如,定期检查探针的接触是否良好,外壳是否损坏等。
  • 升级:

    • 软件升级: 如果需要增加新的功能或优化性能,可以进行软件升级。 例如,增加对其他逻辑电平标准 (CMOS) 的支持,优化窗口比较算法,提高精度和响应速度等。
    • 硬件升级: 如果需要提高性能或扩展功能,可以进行硬件升级。 例如,更换更高性能的MCU,使用更精确的比较器,增加蜂鸣器或其他指示方式等。 但是,对于这种低成本的简单设备,硬件升级可能不太实际,更倾向于重新设计新版本的产品。

代码设计架构

我们采用分层模块化的代码设计架构,以及事件驱动 (虽然对于这个简单项目,轮询更常用,但可以设计成事件驱动的框架,方便扩展) 的思想 (实际上这里更偏向于简单的轮询和状态更新)。

  • HAL层 (Hardware Abstraction Layer): hal_gpio.h, hal_adc.h

    • 封装MCU的底层硬件操作。
    • 提供GPIO初始化、电平设置、ADC初始化、ADC读取等函数接口。
    • 方便代码移植到不同的MCU平台。
  • Logic Level Detection模块: logic_level_detection.h, logic_level_detection.c

    • 实现逻辑电平检测的核心算法。
    • 定义逻辑电平状态枚举类型 (LogicLevelStatus)。
    • 提供 LogicLevelDetection_Init(), LogicLevelDetection_DetectLogicLevel(uint16_t adcValue) 等函数。
    • 内部实现窗口比较逻辑,根据ADC值判断逻辑电平状态。
  • LED Control模块: led_control.h, led_control.c

    • 控制LED指示灯。
    • 提供 LEDControl_Init(), LEDControl_UpdateLEDs(LogicLevelStatus status) 等函数。
    • 根据逻辑电平状态控制不同LED的亮灭。
  • Application层: main.c

    • 主程序入口。
    • 初始化HAL、LogicLevelDetection、LEDControl模块。
    • 循环读取ADC值,调用LogicLevelDetection模块进行逻辑电平检测,调用LEDControl模块更新LED指示。

C 代码实现 (示例代码 - 超过3000行代码无法在此完整展示,以下提供核心模块的示例代码,以及代码结构和详细注释,以便理解整个系统的实现思路。 实际项目中,代码量会根据具体的功能和细节实现而增加,但不会达到3000行如此夸张的程度,除非包含了非常详细的HAL库和大量的测试代码。 重点在于展示清晰的代码结构和核心逻辑实现。)

hal_gpio.h (HAL 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
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>

// 定义 GPIO 端口和引脚
typedef enum {
GPIO_PORT_A,
GPIO_PORT_B,
// ... 可以根据具体的 MCU 定义更多端口
} GPIO_Port_TypeDef;

typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
GPIO_PIN_2,
GPIO_PIN_3,
GPIO_PIN_4,
GPIO_PIN_5,
GPIO_PIN_6,
GPIO_PIN_7,
GPIO_PIN_8,
GPIO_PIN_9,
GPIO_PIN_10,
GPIO_PIN_11,
GPIO_PIN_12,
GPIO_PIN_13,
GPIO_PIN_14,
GPIO_PIN_15,
// ... 可以根据具体的 MCU 定义更多引脚
} GPIO_Pin_TypeDef;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
// ... 可以根据具体的 MCU 定义更多模式
} GPIO_Mode_TypeDef;

typedef enum {
GPIO_OUTPUT_TYPE_PUSH_PULL,
GPIO_OUTPUT_TYPE_OPEN_DRAIN,
// ... 可以根据具体的 MCU 定义更多输出类型
} GPIO_OutputType_TypeDef;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULL_UP,
GPIO_PULL_DOWN,
// ... 可以根据具体的 MCU 定义更多上下拉
} GPIO_Pull_TypeDef;

// GPIO 初始化结构体
typedef struct {
GPIO_Port_TypeDef Port; // GPIO 端口
GPIO_Pin_TypeDef Pin; // GPIO 引脚
GPIO_Mode_TypeDef Mode; // GPIO 模式
GPIO_OutputType_TypeDef OutputType; // GPIO 输出类型 (仅输出模式)
GPIO_Pull_TypeDef Pull; // GPIO 上下拉
} GPIO_InitTypeDef;

// 初始化 GPIO
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);

// 设置 GPIO 引脚输出电平
void HAL_GPIO_WritePin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin, uint8_t PinState);

// 读取 GPIO 引脚输入电平
uint8_t HAL_GPIO_ReadPin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin);

#endif // HAL_GPIO_H

hal_gpio.c (HAL GPIO 驱动源文件 - 示例,需要根据具体的MCU硬件寄存器操作实现)

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
#include "hal_gpio.h"

// 初始化 GPIO
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
// *** 示例代码,需要根据具体的 MCU 硬件寄存器操作实现 ***

// 1. 使能 GPIO 端口时钟 (例如 RCC_APB2PeriphClockCmd)
// 2. 配置 GPIO 模式 (例如 GPIO_InitStructure.GPIO_Mode)
// 3. 配置 GPIO 输出类型 (例如 GPIO_InitStructure.GPIO_OType)
// 4. 配置 GPIO 上下拉 (例如 GPIO_InitStructure.GPIO_PuPd)
// 5. 初始化 GPIO (例如 GPIO_Init)

// 这里只是一个占位符,实际需要根据 STM32F030F4P6 的寄存器操作来完成 GPIO 初始化
(void)GPIO_InitStruct; // 避免编译器警告,实际代码中需要使用 GPIO_InitStruct 的参数
// ... 具体 GPIO 初始化代码 ...
}

// 设置 GPIO 引脚输出电平
void HAL_GPIO_WritePin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin, uint8_t PinState) {
// *** 示例代码,需要根据具体的 MCU 硬件寄存器操作实现 ***

// 1. 根据 Port 和 Pin 确定 GPIO 寄存器地址
// 2. 根据 PinState 设置或清除 GPIO 输出数据寄存器 (例如 GPIO_SetBits, GPIO_ResetBits)

(void)Port; (void)Pin; (void)PinState; // 避免编译器警告,实际代码中需要使用 Port, Pin, PinState 参数
// ... 具体 GPIO 输出设置代码 ...
if (PinState) {
// 设置 GPIO 输出高电平
} else {
// 设置 GPIO 输出低电平
}
}

// 读取 GPIO 引脚输入电平
uint8_t HAL_GPIO_ReadPin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin) {
// *** 示例代码,需要根据具体的 MCU 硬件寄存器操作实现 ***

// 1. 根据 Port 和 Pin 确定 GPIO 寄存器地址
// 2. 读取 GPIO 输入数据寄存器 (例如 GPIO_ReadInputDataBit)
// 3. 返回引脚电平状态 (0 或 1)

(void)Port; (void)Pin; // 避免编译器警告,实际代码中需要使用 Port, Pin 参数
// ... 具体 GPIO 输入读取代码 ...
return 0; // 示例,实际应返回读取的引脚电平状态
}

hal_adc.h (HAL ADC 驱动头文件)

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
#ifndef HAL_ADC_H
#define HAL_ADC_H

#include <stdint.h>

// ADC 通道定义 (根据具体的 MCU 定义)
typedef enum {
ADC_CHANNEL_0,
ADC_CHANNEL_1,
// ... 可以根据具体的 MCU 定义更多 ADC 通道
} ADC_Channel_TypeDef;

// ADC 初始化结构体
typedef struct {
ADC_Channel_TypeDef Channel; // ADC 通道
// ... 可以根据具体的 MCU 定义更多 ADC 初始化参数,例如采样时间、分辨率等
} ADC_InitTypeDef;

// 初始化 ADC
void HAL_ADC_Init(ADC_InitTypeDef *ADC_InitStruct);

// 开始 ADC 转换并获取 ADC 值
uint16_t HAL_ADC_GetValue(ADC_Channel_TypeDef Channel);

#endif // HAL_ADC_H

hal_adc.c (HAL ADC 驱动源文件 - 示例,需要根据具体的MCU硬件寄存器操作实现)

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
#include "hal_adc.h"

// 初始化 ADC
void HAL_ADC_Init(ADC_InitTypeDef *ADC_InitStruct) {
// *** 示例代码,需要根据具体的 MCU 硬件寄存器操作实现 ***

// 1. 使能 ADC 时钟 (例如 RCC_APB2PeriphClockCmd)
// 2. 初始化 ADC 外设 (例如 ADC_Init)
// 3. 配置 ADC 通道 (例如 ADC_RegularChannelConfig)
// 4. 使能 ADC (例如 ADC_Cmd)

(void)ADC_InitStruct; // 避免编译器警告,实际代码中需要使用 ADC_InitStruct 的参数
// ... 具体 ADC 初始化代码 ...
}

// 开始 ADC 转换并获取 ADC 值
uint16_t HAL_ADC_GetValue(ADC_Channel_TypeDef Channel) {
// *** 示例代码,需要根据具体的 MCU 硬件寄存器操作实现 ***

// 1. 选择 ADC 通道 (例如 ADC_RegularChannelConfig)
// 2. 启动 ADC 转换 (例如 ADC_SoftwareStartConvCmd)
// 3. 等待 ADC 转换完成 (例如 ADC_GetFlagStatus(ADC_FLAG_EOC))
// 4. 获取 ADC 值 (例如 ADC_GetConversionValue)

(void)Channel; // 避免编译器警告,实际代码中需要使用 Channel 参数
// ... 具体 ADC 读取代码 ...
return 0; // 示例,实际应返回读取的 ADC 值
}

logic_level_detection.h (逻辑电平检测模块头文件)

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

#include <stdint.h>

// 逻辑电平状态枚举
typedef enum {
LOGIC_LEVEL_LOW,
LOGIC_LEVEL_HIGH,
LOGIC_LEVEL_UNDEFINED
} LogicLevelStatus;

// 初始化逻辑电平检测模块
void LogicLevelDetection_Init(void);

// 检测逻辑电平状态
LogicLevelStatus LogicLevelDetection_DetectLogicLevel(uint16_t adcValue);

#endif // LOGIC_LEVEL_DETECTION_H

logic_level_detection.c (逻辑电平检测模块源文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "logic_level_detection.h"

#define TTL_LOW_THRESHOLD_ADC_VALUE 820 // 低电平阈值 ADC 值 (例如 0.8V 对应 ADC 值) - 需根据实际硬件校准
#define TTL_HIGH_THRESHOLD_ADC_VALUE 2457 // 高电平阈值 ADC 值 (例如 2.4V 对应 ADC 值) - 需根据实际硬件校准

// 初始化逻辑电平检测模块
void LogicLevelDetection_Init(void) {
// 可以添加初始化代码,例如配置比较器阈值等 (如果软件可配置)
}

// 检测逻辑电平状态
LogicLevelStatus LogicLevelDetection_DetectLogicLevel(uint16_t adcValue) {
if (adcValue < TTL_LOW_THRESHOLD_ADC_VALUE) {
return LOGIC_LEVEL_LOW;
} else if (adcValue > TTL_HIGH_THRESHOLD_ADC_VALUE) {
return LOGIC_LEVEL_HIGH;
} else {
return LOGIC_LEVEL_UNDEFINED;
}
}

led_control.h (LED 控制模块头文件)

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef LED_CONTROL_H
#define LED_CONTROL_H

#include "logic_level_detection.h"

// 初始化 LED 控制模块
void LEDControl_Init(void);

// 更新 LED 指示灯状态
void LEDControl_UpdateLEDs(LogicLevelStatus status);

#endif // LED_CONTROL_H

led_control.c (LED 控制模块源文件)

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
#include "led_control.h"
#include "hal_gpio.h"

// 定义 LED 连接的 GPIO 引脚 (根据实际硬件连接修改)
#define LED_HIGH_PORT GPIO_PORT_A
#define LED_HIGH_PIN GPIO_PIN_0
#define LED_LOW_PORT GPIO_PORT_A
#define LED_LOW_PIN GPIO_PIN_1
#define LED_UNDEFINED_PORT GPIO_PORT_A // 可选,如果使用未定义状态指示
#define LED_UNDEFINED_PIN GPIO_PIN_2 // 可选

// 初始化 LED 控制模块
void LEDControl_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;

// 初始化 高电平 LED 引脚
GPIO_InitStruct.Port = LED_HIGH_PORT;
GPIO_InitStruct.Pin = LED_HIGH_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = GPIO_OUTPUT_TYPE_PUSH_PULL;
GPIO_InitStruct.Pull = GPIO_PULL_NONE;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_WritePin(LED_HIGH_PORT, LED_HIGH_PIN, 0); // 初始状态熄灭

// 初始化 低电平 LED 引脚
GPIO_InitStruct.Port = LED_LOW_PORT;
GPIO_InitStruct.Pin = LED_LOW_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = GPIO_OUTPUT_TYPE_PUSH_PULL;
GPIO_InitStruct.Pull = GPIO_PULL_NONE;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_WritePin(LED_LOW_PORT, LED_LOW_PIN, 0); // 初始状态熄灭

// 初始化 未定义状态 LED 引脚 (可选)
#ifdef LED_UNDEFINED_PORT
GPIO_InitStruct.Port = LED_UNDEFINED_PORT;
GPIO_InitStruct.Pin = LED_UNDEFINED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = GPIO_OUTPUT_TYPE_PUSH_PULL;
GPIO_InitStruct.Pull = GPIO_PULL_NONE;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_WritePin(LED_UNDEFINED_PORT, LED_UNDEFINED_PIN, 0); // 初始状态熄灭
#endif
}

// 更新 LED 指示灯状态
void LEDControl_UpdateLEDs(LogicLevelStatus status) {
switch (status) {
case LOGIC_LEVEL_HIGH:
HAL_GPIO_WritePin(LED_HIGH_PORT, LED_HIGH_PIN, 1); // 点亮高电平 LED
HAL_GPIO_WritePin(LED_LOW_PORT, LED_LOW_PIN, 0); // 熄灭低电平 LED
#ifdef LED_UNDEFINED_PORT
HAL_GPIO_WritePin(LED_UNDEFINED_PORT, LED_UNDEFINED_PIN, 0); // 熄灭未定义状态 LED
#endif
break;
case LOGIC_LEVEL_LOW:
HAL_GPIO_WritePin(LED_HIGH_PORT, LED_HIGH_PIN, 0); // 熄灭高电平 LED
HAL_GPIO_WritePin(LED_LOW_PORT, LED_LOW_PIN, 1); // 点亮低电平 LED
#ifdef LED_UNDEFINED_PORT
HAL_GPIO_WritePin(LED_UNDEFINED_PORT, LED_UNDEFINED_PIN, 0); // 熄灭未定义状态 LED
#endif
break;
case LOGIC_LEVEL_UNDEFINED:
HAL_GPIO_WritePin(LED_HIGH_PORT, LED_HIGH_PIN, 0); // 熄灭高电平 LED
HAL_GPIO_WritePin(LED_LOW_PORT, LED_LOW_PIN, 0); // 熄灭低电平 LED
#ifdef LED_UNDEFINED_PORT
HAL_GPIO_WritePin(LED_UNDEFINED_PORT, LED_UNDEFINED_PIN, 1); // 点亮未定义状态 LED
#endif
break;
default:
// 默认情况,熄灭所有 LED 或进行错误处理
HAL_GPIO_WritePin(LED_HIGH_PORT, LED_HIGH_PIN, 0);
HAL_GPIO_WritePin(LED_LOW_PORT, LED_LOW_PIN, 0);
#ifdef LED_UNDEFINED_PORT
HAL_GPIO_WritePin(LED_UNDEFINED_PORT, LED_UNDEFINED_PIN, 0);
#endif
break;
}
}

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
#include "hal_gpio.h"
#include "hal_adc.h"
#include "logic_level_detection.h"
#include "led_control.h"
#include "delay.h" // 假设有 delay 延时函数,实际项目中需要根据具体情况实现延时

// 定义 ADC 输入通道 (根据实际硬件连接修改)
#define ADC_INPUT_CHANNEL ADC_CHANNEL_0

int main(void) {
// 初始化 HAL 硬件抽象层 (需要根据具体的 MCU 初始化 HAL)
// ... HAL 初始化代码 ...
Delay_Init(); // 初始化延时函数 (如果使用)

// 初始化 LED 控制模块
LEDControl_Init();

// 初始化逻辑电平检测模块
LogicLevelDetection_Init();

// 初始化 ADC
ADC_InitTypeDef adcInitStruct;
adcInitStruct.Channel = ADC_INPUT_CHANNEL;
HAL_ADC_Init(&adcInitStruct);

while (1) {
// 读取 ADC 值
uint16_t adcValue = HAL_ADC_GetValue(ADC_INPUT_CHANNEL);

// 检测逻辑电平状态
LogicLevelStatus logicLevelStatus = LogicLevelDetection_DetectLogicLevel(adcValue);

// 更新 LED 指示灯
LEDControl_UpdateLEDs(logicLevelStatus);

// 延时一段时间,降低 ADC 采样频率 (可以根据实际需求调整)
Delay_ms(10); // 延时 10ms
}
}

delay.hdelay.c (简易延时函数 - 示例,实际项目中需要根据硬件定时器或SysTick实现更精确的延时)

delay.h

1
2
3
4
5
6
7
8
#ifndef DELAY_H
#define DELAY_H

void Delay_Init(void);
void Delay_ms(uint32_t ms);
void Delay_us(uint32_t us);

#endif // DELAY_H

delay.c (简易软件延时 - 不精确,仅用于示例,实际项目建议使用硬件定时器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "delay.h"

void Delay_Init(void) {
// 可以添加初始化代码,例如配置时钟等 (如果需要)
}

void Delay_ms(uint32_t ms) {
volatile uint32_t i, j;
for (i = 0; i < ms; i++) {
for (j = 0; j < 1000; j++); // 粗略延时,需要根据实际 MCU 时钟频率校准
}
}

void Delay_us(uint32_t us) {
volatile uint32_t i;
for (i = 0; i < us; i++); // 更粗略延时,需要根据实际 MCU 时钟频率校准
}

代码说明:

  • 模块化设计: 代码按照 HAL 层、逻辑电平检测模块、LED 控制模块和应用层进行模块化划分,结构清晰,易于维护和扩展。
  • HAL 抽象层: HAL 层将底层硬件操作封装起来,使得上层应用代码与具体的硬件平台解耦,提高了代码的可移植性。
  • 详细注释: 代码中包含了详细的注释,解释了每个函数和代码段的功能,方便理解代码逻辑。
  • 可配置性: 代码中使用了宏定义 (#define) 来定义 TTL 阈值、LED 引脚等参数,方便根据实际硬件配置进行修改。
  • 示例代码: 代码只是一个示例,需要根据具体的硬件平台 (例如 STM32F030F4P6) 和实际需求进行修改和完善。 例如,HAL 层的代码需要根据具体的 MCU 寄存器操作来实现。 ADC 阈值需要根据实际硬件电路进行校准。 延时函数需要根据实际 MCU 时钟频率进行精确校准或使用硬件定时器实现。

总结

这个基于窗口比较器的便携式TTL逻辑电平测试笔项目,从需求分析到代码实现,展示了一个完整的嵌入式系统开发流程。 代码架构采用分层模块化设计,提高了代码的可读性、可维护性和可扩展性。 C 代码示例提供了核心模块的实现思路,包括 HAL 层、逻辑电平检测、LED 控制和主程序。 实际项目中,需要根据具体的硬件平台和需求,进行详细的硬件设计、软件开发、系统集成、测试验证和维护升级。 虽然3000行代码对于这个简单的项目而言过于夸张,但以上提供的代码结构和详细的注释,以及对整个开发流程的阐述,希望能帮助理解嵌入式系统开发的基本方法和实践。 在实际开发中,代码量会根据具体的功能复杂度和细节实现而有所不同。

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