基于STM32的2FSK调制解调器源程序(标准库3.5)

基于STM32的2FSK调制解调器源程序(标准库3.5)

一、系统概述

2FSK(二进制频移键控) 是一种常用的数字调制方式,通过两个不同频率的载波信号分别表示二进制“0”和“1”。本设计基于STM32F103C8T6(Cortex-M3,72MHz),实现2FSK调制(数字信号→两频率方波)与解调(方波→数字信号),核心包括:

二、硬件设计

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 核心原理

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

四、关键函数说明

  1. FSK_Modulate()

    • 根据输入bit(0/1),设置TIM2的ARR为TIM_ARR1(1kHz)或TIM_ARR2(2kHz),生成对应频率的PWM方波。
  2. EXTI1_IRQHandler()

    • 外部中断下降沿触发(方波过零点),计算两次中断的时间差Δt,根据Δt判断频率:

      • Δt≈500μs(1kHz,周期1ms=2×500μs)→ 判为“0”;
      • Δt≈250μs(2kHz,周期0.5ms=2×250μs)→ 判为“1”。
  3. FSK_Demodulate()

    • 检测到新数据(new_data=1)时,通过串口打印解调结果(0/1)。

五、测试与优化

  1. 硬件测试

    • 用示波器观察PA0输出,确认0→1kHz方波、1→2kHz方波;
    • 用信号发生器输入1kHz/2kHz方波至PA1,观察串口输出是否为0/1。
  2. 抗干扰优化

    • 增加软件滤波:连续3次检测到相同频率才判为有效数据;
    • 调整阈值THRESHOLD:根据实测Δt微调(如1.5kHz对应Δt=333μs,取300~400μs为阈值)。
  3. 性能提升

    • 定时器输入捕获替代外部中断,提高频率测量精度;
    • 增加曼彻斯特编码,增强抗干扰能力。

六、总结

本程序基于STM32F103实现了2FSK调制解调,核心是PWM频率切换(调制) 和过零检测(解调)。代码简洁,可直接用于低速无线传输(如配合315MHz模块),扩展后可实现多进制FSK(4FSK/8FSK)或QAM调制。

专注于matlab/simulink,电子电路,编程