基于STM32的2FSK调制解调器源程序(标准库3.5)
一、系统概述
2FSK(二进制频移键控) 是一种常用的数字调制方式,通过两个不同频率的载波信号分别表示二进制“0”和“1”。本设计基于STM32F103C8T6(Cortex-M3,72MHz),实现2FSK调制(数字信号→两频率方波)与解调(方波→数字信号),核心包括:
- 调制:用定时器PWM生成f1(1kHz,对应“0”)和f2(2kHz,对应“1”);
- 解调:通过外部中断+过零检测,根据信号频率恢复“0”/“1”;
- 应用:简单无线传输(如315MHz/433MHz模块)、低速数据通信。
二、硬件设计
2.1 核心组件
| 模块 | 型号/参数 | 功能说明 |
|---|---|---|
| 主控 | STM32F103C8T6 | 2FSK调制(PWM生成)、解调(过零检测) |
| 调制输出 | PA0(TIM2_CH1,PWM) | 输出2FSK方波(f1=1kHz,f2=2kHz) |
| 解调输入 | PA1(外部中断EXTI1) | 接收2FSK方波,过零检测 |
| 调试接口 | USART1(PA9-TX,PA10-RX) | 打印解调结果(0/1) |
2.2 硬件连接
| 模块 | 引脚(STM32F103C8T6) | 说明 |
|---|---|---|
| 调制输出 | PA0(TIM2_CH1) | 2FSK方波输出(接解调输入) |
| 解调输入 | PA1(EXTI1) | 2FSK方波输入(外部信号) |
| UART1 | PA9(TX)→ USB-TTL RX | 调试信息输出 |
三、软件设计(标准库3.5)
3.1 核心原理
-
调制:用TIM2_CH1生成PWM,通过改变ARR(自动重装载值)切换频率:
- f1=1kHz:ARR1 = (72MHz / 1kHz) – 1 = 71999(预分频=0);
- f2=2kHz:ARR2 = (72MHz / 2kHz) – 1 = 35999。
-
解调:PA1配置为外部中断(下降沿触发),记录两次中断的时间间隔Δt,计算频率f=1/(2Δt)(方波周期=2Δt),若f≈1kHz判为“0”,f≈2kHz判为“1”。
3.2 源程序实现
3.2.1 头文件 2fsk_modem.h
#ifndef __2FSK_MODEM_H
#define __2FSK_MODEM_H
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
// ==================== 宏定义 ====================
#define F1 1000 // 0对应频率1kHz
#define F2 2000 // 1对应频率2kHz
#define SYS_CLK 72000000 // 系统时钟72MHz
#define TIM_ARR1 (SYS_CLK/F1 - 1) // 1kHz时ARR(预分频=0)
#define TIM_ARR2 (SYS_CLK/F2 - 1) // 2kHz时ARR
#define THRESHOLD 500 // 过零检测时间阈值(μs,1.5kHz中间值)
// ==================== 函数声明 ====================
void RCC_Config(void); // 时钟配置
void GPIO_Config(void); // GPIO配置
void TIM2_PWM_Init(void); // TIM2 PWM初始化(调制)
void EXTI1_Init(void); // 外部中断1初始化(解调)
void FSK_Modulate(uint8_t bit); // 2FSK调制(bit=0/1)
void FSK_Demodulate(void); // 2FSK解调(主逻辑)
void USART1_Init(uint32_t baud);// USART1初始化(调试)
#endif
3.2.2 源文件 2fsk_modem.c
#include "2fsk_modem.h"
// 全局变量
volatile uint32_t last_time = 0; // 上次过零时间(μs)
volatile uint8_t rx_bit = 0; // 解调结果(0/1)
volatile uint8_t new_data = 0; // 新数据标志
// ==================== 时钟配置 ====================
void RCC_Config(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
// ==================== GPIO配置 ====================
void GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStruct;
// PA0(TIM2_CH1,调制输出)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// PA1(EXTI1,解调输入)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// ==================== TIM2 PWM初始化(调制) ====================
void TIM2_PWM_Init(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
// 时基配置(初始频率1kHz)
TIM_TimeBaseStruct.TIM_Period = TIM_ARR1; // ARR=71999(1kHz)
TIM_TimeBaseStruct.TIM_Prescaler = 0; // 预分频0
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
// PWM模式配置(CH1,PA0)
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = TIM_ARR1/2; // 50%占空比
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
TIM_Cmd(TIM2, ENABLE); // 使能TIM2
}
// ==================== 外部中断1初始化(解调) ====================
void EXTI1_Init(void) {
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 配置PA1为EXTI1
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
// EXTI1配置(下降沿触发)
EXTI_InitStruct.EXTI_Line = EXTI_Line1;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
// NVIC配置(中断优先级)
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
// ==================== 2FSK调制(bit=0/1) ====================
void FSK_Modulate(uint8_t bit) {
if (bit == 0) {
TIM2->ARR = TIM_ARR1; // 1kHz(0)
} else {
TIM2->ARR = TIM_ARR2; // 2kHz(1)
}
TIM2->CCR1 = TIM2->ARR / 2; // 50%占空比
}
// ==================== 外部中断1服务函数(过零检测) ====================
void EXTI1_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line1) != RESET) {
uint32_t current_time = TIM_GetCounter(TIM2); // 用TIM2计数(需配置TIM2为时基)
uint32_t delta_t = current_time - last_time; // 时间差(μs,需校准)
// 计算频率f=1/(2Δt)(方波周期=2Δt)
if (delta_t > THRESHOLD*1.5) { // 2kHz时Δt=250μs,1kHz时Δt=500μs
rx_bit = 0; // 低频(1kHz)→0
} else {
rx_bit = 1; // 高频(2kHz)→1
}
new_data = 1; // 置新数据标志
last_time = current_time;
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
// ==================== 2FSK解调主逻辑 ====================
void FSK_Demodulate(void) {
if (new_data) {
new_data = 0;
USART1_SendByte(rx_bit + '0'); // 串口打印解调结果(0/1)
}
}
// ==================== USART1初始化(调试) ====================
void USART1_Init(uint32_t baud) {
USART_InitTypeDef USART_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
// PA9(TX)复用推挽
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// USART1配置
USART_InitStruct.USART_BaudRate = baud;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
}
// ==================== 主函数 ====================
int main(void) {
uint8_t test_data[] = {0, 1, 0, 1, 1, 0}; // 测试数据
uint8_t i;
RCC_Config();
GPIO_Config();
TIM2_PWM_Init();
EXTI1_Init();
USART1_Init(115200);
delay_init();
// 测试:调制+解调循环
while (1) {
for (i=0; i<sizeof(test_data); i++) {
FSK_Modulate(test_data[i]); // 调制发送0/1
delay_ms(500); // 每个符号持续500ms
FSK_Demodulate(); // 解调(实际中调制和解调可分开)
}
}
}
参考代码 基于stm32的2FSK调制解调器源程序 www.youwenfan.com/contentcss/56456.html
四、关键函数说明
-
FSK_Modulate():
- 根据输入bit(0/1),设置TIM2的ARR为TIM_ARR1(1kHz)或TIM_ARR2(2kHz),生成对应频率的PWM方波。
-
EXTI1_IRQHandler():
-
外部中断下降沿触发(方波过零点),计算两次中断的时间差Δt,根据Δt判断频率:
- Δt≈500μs(1kHz,周期1ms=2×500μs)→ 判为“0”;
- Δt≈250μs(2kHz,周期0.5ms=2×250μs)→ 判为“1”。
-
-
FSK_Demodulate():
- 检测到新数据(new_data=1)时,通过串口打印解调结果(0/1)。
五、测试与优化
-
硬件测试:
- 用示波器观察PA0输出,确认0→1kHz方波、1→2kHz方波;
- 用信号发生器输入1kHz/2kHz方波至PA1,观察串口输出是否为0/1。
-
抗干扰优化:
- 增加软件滤波:连续3次检测到相同频率才判为有效数据;
- 调整阈值THRESHOLD:根据实测Δt微调(如1.5kHz对应Δt=333μs,取300~400μs为阈值)。
-
性能提升:
- 用定时器输入捕获替代外部中断,提高频率测量精度;
- 增加曼彻斯特编码,增强抗干扰能力。
六、总结
本程序基于STM32F103实现了2FSK调制解调,核心是PWM频率切换(调制) 和过零检测(解调)。代码简洁,可直接用于低速无线传输(如配合315MHz模块),扩展后可实现多进制FSK(4FSK/8FSK)或QAM调制。