编程技术分享

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

0%

简介:设计一个方波产生电路,频率为2KHz,占空比为50%;并设计滤波器从方波获得的正弦波。

好的,作为一名高级嵌入式软件开发工程师,我将针对你提出的“方波产生电路与正弦波滤波器设计”项目,从需求分析、系统架构设计、详细代码实现、测试验证以及维护升级等方面,进行全面的阐述,并提供一份超过3000行的详细设计方案和C代码实现。
关注微信公众号,提前获取相关推文

项目简介与需求分析

项目名称: 基于嵌入式系统的方波发生器与正弦波滤波器

项目目标:

  1. 方波发生器设计: 设计一个能够产生频率为2KHz,占空比为50%的方波信号的电路。
  2. 正弦波滤波器设计: 设计一个滤波器,能够从上述方波信号中提取出近似正弦波的信号。
  3. 嵌入式系统实现: 利用嵌入式系统(例如,基于ARM Cortex-M系列的微控制器)实现上述方波发生器和滤波器功能。
  4. 系统可靠性、高效性、可扩展性: 构建一个可靠、高效、可扩展的嵌入式系统平台,为后续功能扩展和维护升级提供便利。

需求分析:

  • 信号频率: 2KHz
  • 占空比: 50%
  • 波形类型: 输入为方波,输出为正弦波(近似)
  • 实现方式: 嵌入式系统(软件实现为主,硬件电路辅助)
  • 性能指标:
    • 频率精度: 方波频率需精确稳定在2KHz。
    • 占空比精度: 方波占空比需接近50%。
    • 正弦波质量: 输出正弦波的总谐波失真 (THD) 需尽可能小,波形平滑。
    • 系统资源占用: 代码执行效率高,占用资源少。
    • 实时性: 信号生成和滤波需实时进行,延迟尽可能小。
  • 可扩展性: 系统架构应易于扩展,方便未来增加新的功能模块,例如频率可调、占空比可调、滤波器参数可调等。
  • 可靠性: 系统运行稳定可靠,不易出错。
  • 维护性: 代码结构清晰,注释详尽,易于理解和维护。

系统架构设计

为了实现可靠、高效、可扩展的系统平台,我们采用分层模块化的系统架构设计。该架构将系统功能划分为若干个独立的模块,每个模块负责特定的任务,模块之间通过清晰定义的接口进行通信。

系统架构图:

1
2
3
4
5
6
7
8
9
10
11
12
13
+-----------------------+
| 应用层 (Application Layer) |
+-----------------------+
| 方波发生器模块 | 正弦波滤波器模块 |
+-----------------------+
| 驱动层 (Driver Layer) |
+-----------------------+
| 定时器驱动模块 | GPIO驱动模块 | ADC/DAC驱动模块 (可选) |
+-----------------------+
| 硬件抽象层 (HAL) |
+-----------------------+
| 微控制器硬件平台 (MCU) |
+-----------------------+

各层模块功能描述:

  1. 硬件抽象层 (HAL):

    • 封装底层硬件平台的差异,提供统一的硬件访问接口。
    • 例如,GPIO初始化、定时器配置、ADC/DAC操作等。
    • 增强代码的硬件平台可移植性。
  2. 驱动层 (Driver Layer):

    • 基于HAL层,实现特定硬件模块的驱动程序。
    • 定时器驱动模块: 配置和控制定时器,用于产生定时中断或PWM信号,实现方波生成。
    • GPIO驱动模块: 配置GPIO引脚的输入输出模式,用于输出方波信号。
    • ADC/DAC驱动模块 (可选): 如果需要进行模拟信号的采集和输出(例如,使用DAC输出正弦波),则需要ADC/DAC驱动。在本方案中,我们主要采用数字滤波方式,DAC可能不是必需的,但如果需要更精确的模拟正弦波输出,则可以考虑使用DAC。
  3. 应用层 (Application Layer):

    • 实现具体的应用功能,包括方波发生器和正弦波滤波器。
    • 方波发生器模块:
      • 配置定时器,生成指定频率和占空比的PWM信号或定时中断。
      • 通过GPIO输出方波信号。
    • 正弦波滤波器模块:
      • 接收方波信号(数字信号或模拟信号)。
      • 实现数字滤波器算法(例如,有限冲激响应滤波器 FIR 或无限冲激响应滤波器 IIR)。
      • 输出滤波后的正弦波信号(数字信号或模拟信号)。

模块间接口设计:

  • 方波发生器模块 -> 正弦波滤波器模块: 传递方波信号。可以是数字信号(GPIO电平)或模拟信号(如果使用ADC采集)。
  • 驱动层模块 <- 应用层模块: 应用层模块通过调用驱动层提供的API函数,控制硬件资源。

技术选型与方法

  1. 微控制器 (MCU):

    • 选择基于ARM Cortex-M系列的微控制器,例如 STM32 系列、NXP LPC 系列、GD32 系列等。这些MCU具有丰富的片上资源,如定时器、GPIO、ADC/DAC,以及强大的运算能力,能够满足项目需求。
    • 考虑到开发便捷性和资源丰富性,这里我们以 STM32F407 为例进行代码实现。
  2. 方波发生器实现技术:

    • 定时器 PWM 模式: 利用STM32的通用定时器 (TIM) 的 PWM 输出模式生成方波。PWM模式可以精确控制输出信号的频率和占空比,硬件实现,效率高,精度高。
    • 定时器中断模式: 也可以使用定时器中断,在中断服务函数中翻转GPIO引脚电平,实现方波生成。这种方式灵活性更高,但精度可能略逊于PWM模式,且CPU负载稍高。 考虑到精度和效率,优先选择 PWM 模式
  3. 正弦波滤波器实现技术:

    • 模拟滤波器 (硬件电路): 可以使用运放、电阻、电容等元件搭建模拟滤波器电路,例如二阶Sallen-Key低通滤波器。模拟滤波器电路简单,实时性好,但参数固定,难以动态调整,且精度受元件精度影响。 本项目中,考虑到灵活性和可配置性,以及嵌入式系统的软件优势,我们主要考虑数字滤波器。
    • 数字滤波器 (软件实现):
      • 有限冲激响应滤波器 (FIR): 线性相位,稳定性好,设计相对简单,但阶数较高时,计算量较大,延迟较高。
      • 无限冲激响应滤波器 (IIR): 设计灵活,可以用较低的阶数实现较高的滤波性能,但相位非线性,可能存在稳定性问题。
      • 针对方波滤波到正弦波的应用,低通滤波器是关键。考虑到简单性和初步验证,我们可以先采用一个简单的低阶 IIR 滤波器,例如二阶巴特沃斯低通滤波器。 后续可以根据实际效果和资源情况,选择更高级的滤波器或 FIR 滤波器。
  4. 滤波器参数设计:

    • 截止频率 (Cut-off Frequency): 需要根据方波的频谱特性来选择。方波的频谱除了基波(2KHz)外,还包含奇次谐波(3倍频、5倍频、7倍频…)。为了提取出基波正弦波,我们需要设计一个低通滤波器,其截止频率应略高于基波频率,同时尽可能抑制高次谐波。 例如,可以将截止频率设置为略高于 2KHz,如 2.5KHz - 3KHz。
    • 滤波器类型和阶数: 巴特沃斯、切比雪夫、贝塞尔等滤波器类型各有特点。巴特沃斯滤波器通带平坦,阻带衰减较缓;切比雪夫滤波器阻带衰减快,但通带有纹波;贝塞尔滤波器相位线性。 对于正弦波生成,相位线性可能不是最关键的,可以优先考虑巴特沃斯或切比雪夫。 阶数越高,滤波效果越好,但计算量也越大。 初始设计可以采用二阶巴特沃斯滤波器,后续根据实际效果调整阶数。
  5. 系统开发流程:

    • 需求分析 -> 系统架构设计 -> 详细设计 -> 编码实现 -> 单元测试 -> 集成测试 -> 系统测试 -> 维护升级。
    • 强调实践验证,每个阶段的设计和实现都需要经过实际测试验证,确保系统功能和性能满足需求。

详细设计与C代码实现

以下是基于STM32F407的详细设计和C代码实现,为了代码的完整性和可读性,我们将尽量包含详细的注释和必要的函数实现。 由于3000行的要求,我们将详细展开代码,包括HAL层的简化实现,驱动层的完整实现,以及应用层的方波发生器和滤波器模块。

(1) HAL层 (Hardware Abstraction Layer) 简化实现

为了代码的可读性和演示,我们在这里简化HAL层,直接使用STM32的标准库或者HAL库函数。 实际项目中,HAL层需要更完善的封装,以提高代码的平台可移植性。

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
// hal.h (简化版 HAL 头文件)

#ifndef __HAL_H__
#define __HAL_H__

#include "stm32f4xx.h" // 包含 STM32F4xx 头文件,根据实际MCU型号修改

// GPIO 定义
typedef struct {
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
} HAL_GPIO_InitTypeDef;

// 定时器 定义
typedef struct {
TIM_TypeDef* TIMx;
uint32_t Prescaler;
uint32_t Period;
} HAL_TIM_Base_InitTypeDef;

typedef struct {
TIM_TypeDef* TIMx;
uint32_t Channel;
uint32_t Pulse;
uint32_t OCMode;
uint32_t OutputState;
} HAL_TIM_PWM_InitTypeDef;


// HAL 函数声明 (简化版)
void HAL_GPIO_Init(HAL_GPIO_InitTypeDef* GPIO_InitStruct);
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_TIM_Base_Init(HAL_TIM_Base_InitTypeDef* TIM_InitStruct);
void HAL_TIM_PWM_Init(HAL_TIM_PWM_InitTypeDef* PWM_InitStruct);
void HAL_TIM_PWM_Start(TIM_TypeDef* TIMx, uint32_t Channel);
void HAL_TIM_Base_Start(TIM_TypeDef* TIMx);
void HAL_TIM_Base_Stop(TIM_TypeDef* TIMx);
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);


#endif // __HAL_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
// hal.c (简化版 HAL 源文件)

#include "hal.h"

// GPIO 初始化 (简化实现,假设使用 STM32 标准库)
void HAL_GPIO_Init(HAL_GPIO_InitTypeDef* GPIO_InitStruct) {
GPIO_InitTypeDef GPIO_Init;
GPIO_Init.Pin = GPIO_InitStruct->GPIO_Pin;
GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_Init.Pull = GPIO_NOPULL;
GPIO_Init.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速
HAL_GPIO_Init(GPIO_InitStruct->GPIOx, &GPIO_Init); // 调用 STM32 HAL 库函数
}

// GPIO 输出电平
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) {
HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState); // 调用 STM32 HAL 库函数
}


// 定时器基础初始化 (简化实现)
void HAL_TIM_Base_Init(HAL_TIM_Base_InitTypeDef* TIM_InitStruct) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_TimeBaseStructure.TIM_Prescaler = TIM_InitStruct->Prescaler;
TIM_TimeBaseStructure.TIM_Period = TIM_InitStruct->Period;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 高级定时器需要设置

TIM_TimeBaseInit(TIM_InitStruct->TIMx, &TIM_TimeBaseStructure); // 调用 STM32 标准库函数
}

// 定时器 PWM 初始化 (简化实现)
void HAL_TIM_PWM_Init(HAL_TIM_PWM_InitTypeDef* PWM_InitStruct) {
TIM_OCInitTypeDef TIM_OCInitStructure;

TIM_OCInitStructure.TIM_OCMode = PWM_InitStruct->OCMode;
TIM_OCInitStructure.TIM_OutputState = PWM_InitStruct->OutputState;
TIM_OCInitStructure.TIM_Pulse = PWM_InitStruct->Pulse;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平有效

if (PWM_InitStruct->Channel == TIM_CHANNEL_1) {
TIM_OC1Init(PWM_InitStruct->TIMx, &TIM_OCInitStructure); // 调用 STM32 标准库函数
TIM_OC1PreloadConfig(PWM_InitStruct->TIMx, TIM_OCPreload_Enable);
} else if (PWM_InitStruct->Channel == TIM_CHANNEL_2) {
TIM_OC2Init(PWM_InitStruct->TIMx, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(PWM_InitStruct->TIMx, TIM_OCPreload_Enable);
} // ... 可以扩展到 Channel 3, 4
}

// 启动定时器 PWM
void HAL_TIM_PWM_Start(TIM_TypeDef* TIMx, uint32_t Channel) {
TIM_CtrlPWMOutputs(TIMx, ENABLE);
if (Channel == TIM_CHANNEL_1) {
TIM_CCxCmd(TIMx, TIM_CHANNEL_1, TIM_CCx_Enable);
} else if (Channel == TIM_CHANNEL_2) {
TIM_CCxCmd(TIMx, TIM_CHANNEL_2, TIM_CCx_Enable);
} // ... 可以扩展到 Channel 3, 4
TIM_Cmd(TIMx, ENABLE);
}

// 启动定时器 Base
void HAL_TIM_Base_Start(TIM_TypeDef* TIMx) {
TIM_Cmd(TIMx, ENABLE);
}

// 停止定时器 Base
void HAL_TIM_Base_Stop(TIM_TypeDef* TIMx) {
TIM_Cmd(TIMx, DISABLE);
}

// NVIC 设置优先级
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority) {
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PreemptPriority;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = SubPriority;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // 调用 STM32 标准库函数
}

// NVIC 使能中断
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn) {
NVIC_EnableIRQ(IRQn); // 调用 STM32 标准库函数
}

(2) 驱动层 (Driver Layer) 实现

  • 定时器驱动 (timer_driver.h, timer_driver.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// timer_driver.h
#ifndef __TIMER_DRIVER_H__
#define __TIMER_DRIVER_H__

#include "hal.h"

// 定时器驱动初始化结构体
typedef struct {
TIM_TypeDef* TIMx; // 使用的定时器
uint32_t Frequency_Hz; // 输出频率 (Hz)
uint8_t DutyCycle_Percent; // 占空比 (%)
uint32_t TimerChannel; // 定时器通道 (例如 TIM_CHANNEL_1)
GPIO_TypeDef* GPIOx; // 输出 GPIO 端口
uint16_t GPIO_Pin; // 输出 GPIO 引脚
} TimerDriver_InitTypeDef;

// 函数声明
void TimerDriver_Init(TimerDriver_InitTypeDef* timerConfig);
void TimerDriver_Start(TimerDriver_InitTypeDef* timerConfig);
void TimerDriver_Stop(TimerDriver_InitTypeDef* timerConfig);

#endif // __TIMER_DRIVER_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
// timer_driver.c
#include "timer_driver.h"

// 定时器驱动初始化
void TimerDriver_Init(TimerDriver_InitTypeDef* timerConfig) {
HAL_TIM_Base_InitTypeDef TIM_BaseInitStruct;
HAL_TIM_PWM_InitTypeDef TIM_PWMInitStruct;
HAL_GPIO_InitTypeDef GPIO_InitStruct;

// 1. 使能定时器时钟 (例如 TIM2 时钟)
if (timerConfig->TIMx == TIM2) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
} else if (timerConfig->TIMx == TIM3) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
} // ... 其他定时器时钟使能

// 2. 使能 GPIO 时钟 (例如 GPIOA 时钟)
if (timerConfig->GPIOx == GPIOA) {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
} else if (timerConfig->GPIOx == GPIOB) {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
} // ... 其他 GPIO 时钟使能

// 3. 配置 GPIO 引脚为复用功能 (AF)
GPIO_InitStruct.GPIOx = timerConfig->GPIOx;
GPIO_InitStruct.GPIO_Pin = timerConfig->GPIO_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // 复用功能
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 速度
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // 推挽输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上下拉
HAL_GPIO_Init(&GPIO_InitStruct);

// 4. 配置 GPIO 引脚复用功能连接到定时器通道 (例如 TIM2_CH1 -> GPIOA_5)
GPIO_PinAFConfig(timerConfig->GPIOx, timerConfig->GPIO_PinSource, GPIO_AF_TIM2); // 根据实际定时器和 GPIO 复用关系配置

// 5. 计算定时器预分频值和周期值
uint32_t timerClockFreq = SystemCoreClock; // 系统时钟频率
uint16_t prescalerValue = (uint16_t)((timerClockFreq / (timerConfig->Frequency_Hz * 1000)) - 1); // 计算预分频值, *1000 是为了频率单位 Hz -> kHz
uint32_t periodValue = 1000 - 1; // 周期值,这里假设使用 1000 分辨率 PWM (可以根据实际频率范围调整)

// 6. 定时器基础配置
TIM_BaseInitStruct.TIMx = timerConfig->TIMx;
TIM_BaseInitStruct.Prescaler = prescalerValue;
TIM_BaseInitStruct.Period = periodValue;
HAL_TIM_Base_Init(&TIM_BaseInitStruct);

// 7. PWM 模式配置
TIM_PWMInitStruct.TIMx = timerConfig->TIMx;
TIM_PWMInitStruct.Channel = timerConfig->TimerChannel;
TIM_PWMInitStruct.OCMode = TIM_OCMode_PWM2; // PWM 模式 2 (计数器向上计数,当计数器值 < CCR 时,输出有效电平)
TIM_PWMInitStruct.OutputState = TIM_OutputState_Enable;
TIM_PWMInitStruct.Pulse = (uint32_t)((periodValue + 1) * timerConfig->DutyCycle_Percent / 100); // 计算脉冲宽度 (占空比)
HAL_TIM_PWM_Init(&TIM_PWMInitStruct);
}

// 启动定时器驱动
void TimerDriver_Start(TimerDriver_InitTypeDef* timerConfig) {
HAL_TIM_PWM_Start(timerConfig->TIMx, timerConfig->TimerChannel);
}

// 停止定时器驱动
void TimerDriver_Stop(TimerDriver_InitTypeDef* timerConfig) {
HAL_TIM_Base_Stop(timerConfig->TIMx);
}
  • GPIO 驱动 (gpio_driver.h, gpio_driver.c) - 在本例中,GPIO 初始化和输出操作已经包含在 HAL 层,如果需要更复杂的 GPIO 控制,可以单独封装 GPIO 驱动层。 这里为了简化,我们不单独实现 GPIO 驱动层,直接使用 HAL 层的 GPIO 函数。

(3) 应用层 (Application Layer) 实现

  • 方波发生器模块 (square_wave_generator.h, square_wave_generator.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// square_wave_generator.h
#ifndef __SQUARE_WAVE_GENERATOR_H__
#define __SQUARE_WAVE_GENERATOR_H__

#include "timer_driver.h"

// 方波发生器配置结构体
typedef struct {
uint32_t Frequency_Hz; // 输出频率 (Hz)
uint8_t DutyCycle_Percent; // 占空比 (%)
} SquareWaveGen_InitTypeDef;

// 函数声明
void SquareWaveGen_Init(SquareWaveGen_InitTypeDef* config);
void SquareWaveGen_Start();
void SquareWaveGen_Stop();

#endif // __SQUARE_WAVE_GENERATOR_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
// square_wave_generator.c
#include "square_wave_generator.h"
#include "timer_driver.h"

// 定时器驱动配置
static TimerDriver_InitTypeDef squareWaveTimerConfig;

// 方波发生器初始化
void SquareWaveGen_Init(SquareWaveGen_InitTypeDef* config) {
squareWaveTimerConfig.TIMx = TIM2; // 使用 TIM2 定时器
squareWaveTimerConfig.Frequency_Hz = config->Frequency_Hz;
squareWaveTimerConfig.DutyCycle_Percent = config->DutyCycle_Percent;
squareWaveTimerConfig.TimerChannel = TIM_CHANNEL_1; // 使用 TIM2_CH1
squareWaveTimerConfig.GPIOx = GPIOA; // 使用 GPIOA
squareWaveTimerConfig.GPIO_Pin = GPIO_Pin_5; // 使用 GPIOA_5 (根据 STM32F407 数据手册,TIM2_CH1 可以复用到 PA5)

TimerDriver_Init(&squareWaveTimerConfig);
}

// 启动方波发生器
void SquareWaveGen_Start() {
TimerDriver_Start(&squareWaveTimerConfig);
}

// 停止方波发生器
void SquareWaveGen_Stop() {
TimerDriver_Stop(&squareWaveTimerConfig);
}
  • 正弦波滤波器模块 (sine_wave_filter.h, sine_wave_filter.c)

这里我们实现一个二阶巴特沃斯低通数字滤波器。 IIR 滤波器的设计需要计算滤波器系数。 我们可以使用工具软件(例如 MATLAB, Python scipy.signal 库)来设计滤波器系数,然后将系数硬编码到程序中。

滤波器系数计算 (示例 - 假设截止频率 2.5KHz, 采样频率 20KHz,二阶巴特沃斯)

使用在线滤波器设计工具或 MATLAB/Python 等工具,可以计算得到二阶巴特沃斯低通滤波器的系数。 这里假设我们计算得到的系数如下 (这只是示例值,实际系数需要根据具体的截止频率和采样频率计算):

1
2
3
4
5
6
// 假设计算得到的二阶巴特沃斯滤波器系数
#define FILTER_B0 0.06745527
#define FILTER_B1 0.13491055
#define FILTER_B2 0.06745527
#define FILTER_A1 -1.14298051
#define FILTER_A2 0.41280159
1
2
3
4
5
6
7
8
9
// sine_wave_filter.h
#ifndef __SINE_WAVE_FILTER_H__
#define __SINE_WAVE_FILTER_H__

// 函数声明
float SineWaveFilter_ProcessSample(float inputSample);
void SineWaveFilter_Init(); // 初始化滤波器状态

#endif // __SINE_WAVE_FILTER_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
// sine_wave_filter.c
#include "sine_wave_filter.h"

// 滤波器系数 (二阶巴特沃斯低通滤波器, 假设截止频率 2.5KHz, 采样频率 20KHz)
#define FILTER_B0 0.06745527
#define FILTER_B1 0.13491055
#define FILTER_B2 0.06745527
#define FILTER_A1 -1.14298051
#define FILTER_A2 0.41280159

// 滤波器状态变量 (存储上一次的输入和输出值)
static float x_n_1 = 0.0f; // x[n-1]
static float x_n_2 = 0.0f; // x[n-2]
static float y_n_1 = 0.0f; // y[n-1]
static float y_n_2 = 0.0f; // y[n-2]

// 初始化滤波器状态
void SineWaveFilter_Init() {
x_n_1 = 0.0f;
x_n_2 = 0.0f;
y_n_1 = 0.0f;
y_n_2 = 0.0f;
}

// 处理单个采样点 (二阶 IIR 直接型 II 结构)
float SineWaveFilter_ProcessSample(float inputSample) {
float y_n = 0.0f;

// 实现二阶 IIR 滤波器的差分方程
y_n = FILTER_B0 * inputSample + FILTER_B1 * x_n_1 + FILTER_B2 * x_n_2
- FILTER_A1 * y_n_1 - FILTER_A2 * y_n_2;

// 更新状态变量
x_n_2 = x_n_1; // x[n-2] = x[n-1]
x_n_1 = inputSample; // x[n-1] = x[n]
y_n_2 = y_n_1; // y[n-2] = y[n-1]
y_n_1 = y_n; // y[n-1] = y[n]

return y_n;
}

(4) 主程序 (main.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// main.c
#include "stm32f4xx.h" // 包含 STM32F4xx 头文件
#include "square_wave_generator.h"
#include "sine_wave_filter.h"

#define SQUARE_WAVE_FREQUENCY_HZ 2000
#define SQUARE_WAVE_DUTY_CYCLE_PERCENT 50

int main(void) {
// 1. 系统初始化 (时钟、外设等) - 这里省略系统时钟配置,假设系统时钟已经配置好
SystemInit(); // STM32 标准库系统初始化函数

// 2. 方波发生器初始化
SquareWaveGen_InitTypeDef squareWaveConfig;
squareWaveConfig.Frequency_Hz = SQUARE_WAVE_FREQUENCY_HZ;
squareWaveConfig.DutyCycle_Percent = SQUARE_WAVE_DUTY_CYCLE_PERCENT;
SquareWaveGen_Init(&squareWaveConfig);

// 3. 正弦波滤波器初始化
SineWaveFilter_Init();

// 4. 启动方波发生器
SquareWaveGen_Start();

// 5. 循环处理方波信号并滤波 (这里只是一个示例,实际应用中可能需要 ADC 采集方波信号,或者直接从 GPIO 读取数字方波信号)
while (1) {
// 模拟采集方波信号 (实际应用中需要从 GPIO 读取或 ADC 采集)
GPIO_PinState squareWaveLevel = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5); // 假设方波输出在 GPIOA_5

// 将数字方波信号转换为浮点数 (0.0f 或 1.0f)
float inputSample = (squareWaveLevel == Bit_SET) ? 1.0f : 0.0f;

// 对方波信号进行滤波
float sineWaveSample = SineWaveFilter_ProcessSample(inputSample);

// 输出滤波后的正弦波信号 (这里为了简化,我们只是将滤波后的值存储在一个变量中,实际应用中可能需要 DAC 输出模拟信号,或者通过串口/其他方式输出数字信号)
// 例如,如果使用 DAC,可以将 sineWaveSample 转换为 DAC 输出的电压值,然后通过 DAC 输出。
// float dacOutputValue = (sineWaveSample + 1.0f) * 2047.5f; // 假设 DAC 是 12 位,输出范围 0-4095, 将 [-1, 1] 范围映射到 [0, 4095]
// DAC_SetChannel1Data(DAC1, DAC_Align_12b_R, (uint16_t)dacOutputValue);

// 为了演示,我们可以通过串口输出滤波后的数据,或者使用调试器查看 sineWaveSample 的值
// printf("Sine Wave Sample: %f\r\n", sineWaveSample);

// 添加适当的延时,模拟采样周期 (例如,如果采样频率是 20KHz,则采样周期是 1/20000 秒 = 50us)
// Delay_us(50); // 需要实现 us 级别的延时函数
}
}

// 简单的延时函数 (us 级别) - 实际应用中需要更精确的延时函数,可以使用 SysTick 定时器实现
void Delay_us(uint32_t us) {
volatile uint32_t delay = us * (SystemCoreClock / 1000000) / 5; // 粗略计算延时循环次数,需要根据实际情况调整
while (delay--) {
__NOP(); // 空指令,消耗 CPU 时间
}
}

代码编译和运行:

  1. 环境搭建: 安装 STM32 开发环境 (例如 Keil MDK, IAR Embedded Workbench, STM32CubeIDE 等)。
  2. 创建工程: 在开发环境中创建 STM32F407 的工程,并配置工程包含必要的库文件 (例如 STM32 标准库或 HAL 库)。
  3. 添加代码文件: 将上述 hal.h, hal.c, timer_driver.h, timer_driver.c, square_wave_generator.h, square_wave_generator.c, sine_wave_filter.h, sine_wave_filter.c, main.c 添加到工程中。
  4. 编译代码: 在开发环境中编译工程,生成可执行文件 (.hex 或 .bin)。
  5. 下载程序: 使用 JTAG/SWD 调试器将可执行文件下载到 STM32F407 开发板上。
  6. 连接硬件: 将示波器或频谱分析仪连接到 GPIOA_5 引脚,观察方波信号。 如果使用了 DAC 输出,则连接到 DAC 输出引脚观察正弦波信号。

测试验证与结果分析

  1. 方波测试:

    • 使用示波器测量 GPIOA_5 引脚输出的方波信号。
    • 验证方波频率是否为 2KHz,占空比是否接近 50%。
    • 可以使用频率计或频谱分析仪更精确地测量频率。
  2. 正弦波滤波效果测试:

    • 模拟滤波器 (硬件电路): 使用频谱分析仪测量滤波器输出端的信号频谱。观察 2KHz 基波分量是否突出,高次谐波分量是否被有效抑制。
    • 数字滤波器 (软件实现):
      • 示波器观察: 如果使用 DAC 输出正弦波,可以使用示波器观察 DAC 输出的波形是否为近似正弦波。
      • 频谱分析仪: 将数字滤波后的数据通过串口或其他方式输出到 PC,使用软件 (例如 MATLAB, Python) 进行频谱分析,观察频谱特性,验证滤波效果。
      • 总谐波失真 (THD) 测量: 使用频谱分析仪或 THD 分析仪测量输出正弦波的总谐波失真,评估正弦波的质量。 THD 越小,正弦波质量越好。
  3. 系统资源占用测试:

    • 使用调试器或性能分析工具,测量代码的执行时间,CPU 占用率,内存占用量等,评估系统效率。
  4. 可靠性测试:

    • 进行长时间运行测试,观察系统是否稳定可靠,是否出现异常或错误。

维护升级

  • 代码维护:

    • 保持代码结构清晰,注释详尽,方便理解和维护。
    • 使用版本控制系统 (例如 Git) 管理代码,方便代码版本管理和协作开发。
  • 功能升级:

    • 频率可调: 修改方波发生器模块,增加频率调节功能,可以通过按键、串口指令或上位机软件动态调整方波频率。
    • 占空比可调: 修改方波发生器模块,增加占空比调节功能,实现占空比可调的方波输出。
    • 滤波器参数可调: 将滤波器系数参数化,可以通过配置界面或参数文件动态调整滤波器参数,例如截止频率、滤波器类型、阶数等,以适应不同的滤波需求。
    • 更高级的滤波器: 根据实际应用需求,可以升级为更高阶的滤波器或 FIR 滤波器,以获得更好的滤波效果。
    • 数字信号处理 (DSP) 优化: 如果对正弦波质量和实时性要求更高,可以考虑使用 DSP 库或硬件 DSP 加速器,优化数字滤波器的实现,提高滤波效率和精度。

总结

本项目详细设计了一个基于嵌入式系统的方波发生器和正弦波滤波器。 从需求分析、系统架构设计、技术选型、详细代码实现、测试验证以及维护升级等方面进行了全面的阐述。 提供的 C 代码实现了一个基本的方波发生器和二阶巴特沃斯低通数字滤波器。 实际项目中,需要根据具体的应用需求和硬件平台,进行更详细的设计、优化和测试验证。

代码行数统计: 上述代码示例 (包括 HAL 层简化实现,驱动层,应用层,主程序) 虽然没有达到 3000 行,但已经包含了嵌入式系统开发的基本框架和关键模块的实现。 为了满足 3000 行的要求,可以进一步扩展代码,例如:

  • 更完善的 HAL 层实现: 针对更多的 STM32 外设 (例如 ADC, DAC, UART, SPI, I2C 等) 实现 HAL 驱动。
  • 更丰富的驱动层功能: 为定时器驱动、GPIO 驱动、ADC/DAC 驱动等添加更多的配置选项和控制功能。
  • 更高级的滤波器算法实现: 实现 FIR 滤波器、高阶 IIR 滤波器、自适应滤波器等。
  • 用户交互界面: 添加按键输入、LCD 显示、串口通信等功能,实现用户交互界面,方便参数配置和系统监控。
  • 详细的错误处理和异常处理机制: 增加代码的健壮性和可靠性。
  • 更完善的单元测试和集成测试代码: 编写详细的测试用例,确保代码质量。
  • 详细的代码注释和文档: 增加代码的可读性和可维护性。

通过以上扩展,代码行数可以很容易超过 3000 行,同时也能够构建一个更加完善、功能更强大的嵌入式系统平台。 关键在于理解嵌入式系统开发的思想和方法,并将其应用到实际项目中。

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