基于STM32的无线抢答器设计

简介: 基于STM32的无线抢答器以“无线信号传输-抢答优先级判断-声光结果反馈-多终端协同”为核心,实现多人抢答场景下的快速响应、公平判罚与实时显示,支持3-8个选手终端、1个主机(裁判端),无线传输距离≥10米,响应延迟≤50ms,适用于知识竞赛、课堂互动、企业团建等场景。

一、系统概述与核心功能

1. 系统定位

基于STM32的无线抢答器以“无线信号传输-抢答优先级判断-声光结果反馈-多终端协同”为核心,实现多人抢答场景下的快速响应、公平判罚与实时显示,支持3-8个选手终端、1个主机(裁判端),无线传输距离≥10米,响应延迟≤50ms,适用于知识竞赛、课堂互动、企业团建等场景。

2. 核心功能模块

模块 功能描述
无线传输 选手终端→主机:抢答信号(含终端编号);主机→终端:抢答结果(成功/失败)
抢答判断 主机接收多终端信号,按信号到达顺序判定优先级(先到先得),支持“犯规检测”(提前抢答)
结果显示 主机LCD显示抢答成功选手编号、倒计时(可选),终端LED指示自身状态(待机/抢答中/成功)
声光反馈 抢答成功:主机蜂鸣器鸣响+LED闪烁;犯规:短促蜂鸣+红灯提示
终端控制 选手按键抢答,终端本地显示“已抢答”状态,支持复位(裁判端控制)
低功耗设计 待机时终端关闭无线模块,主机进入低功耗模式(仅保留无线接收)

二、硬件设计方案

1. 核心硬件选型

模块 型号 关键参数 接口方式
主控MCU STM32F103C8T6 72MHz Cortex-M3,64KB Flash,20KB RAM,3个UART,2个SPI,支持外部中断 核心控制器(主机/终端共用)
无线模块 NRF24L01+ 2.4GHz ISM频段,最大传输速率2Mbps,多发一收(1主多从),传输距离10-20米 SPI(SCK/MISO/MOSI/CS/CE)
显示模块 LCD1602(主机) 16×2字符型LCD,HD44780控制器,5V供电,支持自定义字符 并行接口(GPIO模拟)
输入模块 轻触按键(终端) 6×6mm贴片按键,寿命10万次,终端每人1个(编号1-N) GPIO(外部中断)
声光模块 有源蜂鸣器+LED 蜂鸣器(5V,85dB),LED(红/绿双色),终端用绿色(待机)、红色(抢答成功) GPIO(PB0-PB1)
电源模块 5V/2A适配器+AMS1117 输入220V AC,输出5V/2A(主机),3.3V给STM32/NRF24L01(终端用锂电池3.7V) 供电一体化

2. 系统架构与硬件连接

2.1 主机(裁判端)硬件设计

  • 核心电路:STM32F103C8T6最小系统(8MHz晶振+复位电路)+ NRF24L01+(SPI接口:SCK=PA5,MISO=PA6,MOSI=PA7,CS=PA4,CE=PA3)+ LCD1602(RS=PB10,RW=PB11,E=PB12,D4-D7=PB13-PB16)+ 蜂鸣器(PB0)+ 状态LED(PB1,红/绿双色)。
  • 功能:接收终端抢答信号→判断优先级→显示结果→声光反馈。

2.2 终端(选手端)硬件设计

  • 核心电路:STM32F103C8T6最小系统(简化版,无晶振用内部RC)+ NRF24L01+(SPI接口:SCK=PA5,MISO=PA6,MOSI=PA7,CS=PA4,CE=PA3)+ 抢答按键(PA0,外部中断)+ 状态LED(PB0,绿色待机/红色抢答成功)+ 复位按键(PB1)。
  • 功能:按键触发→发送抢答信号(含终端编号)→本地LED指示状态。

三、软件设计与核心代码

1. 系统架构(主机/终端双模式)

  • 主机模式:初始化NRF24L01+为接收模式(PRX),循环检测RX FIFO,解析数据包(终端编号+抢答时间戳),记录首个有效信号,更新LCD显示并触发声光反馈。
  • 终端模式:初始化NRF24L01+为发送模式(PTX),按键按下时(外部中断)发送数据包(含终端编号+当前时间戳),本地LED变红提示“已抢答”。

2. 核心代码实现(基于HAL库)

2.1 NRF24L01+驱动(主机/终端共用,nrf24l01.c

#include "nrf24l01.h"
#include "spi.h"
#include "gpio.h"

// NRF24L01+寄存器地址
#define CONFIG      0x00
#define EN_AA       0x01
#define EN_RXADDR   0x02
#define SETUP_AW    0x03
#define SETUP_RETR  0x04
#define RF_CH       0x05
#define RF_SETUP    0x06
#define STATUS      0x07
#define RX_ADDR_P0  0x0A
#define TX_ADDR     0x10
#define RX_PW_P0    0x11
#define FIFO_STATUS 0x17

// 全局变量
uint8_t tx_addr[5] = {
   0x34,0x43,0x10,0x10,0x01};  // 主机接收地址(终端发送目标)
uint8_t rx_addr[5] = {
   0x34,0x43,0x10,0x10,0x01};  // 终端接收地址(主机发送目标)

// NRF24L01+初始化(主机:PRX模式;终端:PTX模式)
void NRF24L01_Init(uint8_t mode) {
   
  HAL_GPIO_WritePin(NRF_CE_GPIO_Port, NRF_CE_Pin, GPIO_PIN_RESET);  // CE=0,待机模式
  NRF24L01_WriteReg(CONFIG, 0x0F);  // 使能CRC(2字节),上电,PTX模式(默认)
  if (mode == MODE_PRX) {
     // 主机:PRX模式
    NRF24L01_WriteReg(CONFIG, 0x0F | (1<<0));  // PRX模式(PWR_UP=1,PRIM_RX=1)
    NRF24L01_WriteBuf(RX_ADDR_P0, rx_addr, 5);  // 设置接收地址
    NRF24L01_WriteReg(RX_PW_P0, 32);  // 接收数据长度32字节
    NRF24L01_WriteReg(EN_RXADDR, 0x01);  // 使能P0通道
  } else {
     // 终端:PTX模式
    NRF24L01_WriteBuf(TX_ADDR, tx_addr, 5);  // 设置发送地址
    NRF24L01_WriteBuf(RX_ADDR_P0, rx_addr, 5);  // 设置接收地址(主机反馈用)
    NRF24L01_WriteReg(EN_AA, 0x01);  // 使能自动应答(P0通道)
  }
  NRF24L01_WriteReg(RF_CH, 40);  // 设置频道40(2.440GHz)
  NRF24L01_WriteReg(RF_SETUP, 0x07);  // 发射功率0dBm,传输速率2Mbps
  NRF24L01_WriteReg(SETUP_RETR, 0x1A);  // 自动重发10次,间隔500us
  HAL_GPIO_WritePin(NRF_CE_GPIO_Port, NRF_CE_Pin, GPIO_PIN_SET);  // CE=1,启动
}

// 发送数据(终端用)
void NRF24L01_SendData(uint8_t *data, uint8_t len) {
   
  HAL_GPIO_WritePin(NRF_CE_GPIO_Port, NRF_CE_Pin, GPIO_PIN_RESET);
  NRF24L01_WriteReg(CONFIG, 0x0E);  // PTX模式,PWR_UP=1,PRIM_RX=0
  NRF24L01_WriteBuf(0xA0, data, len);  // 写入TX FIFO
  HAL_GPIO_WritePin(NRF_CE_GPIO_Port, NRF_CE_Pin, GPIO_PIN_SET);  // 启动发送
  while (!(NRF24L01_ReadReg(STATUS) & (1<<5)));  // 等待发送完成(TX_DS=1)
  NRF24L01_WriteReg(STATUS, 0x20);  // 清除TX_DS标志
}

// 接收数据(主机用)
uint8_t NRF24L01_ReceiveData(uint8_t *data) {
   
  if (NRF24L01_ReadReg(STATUS) & (1<<6)) {
     // RX_DR=1,接收完成
    NRF24L01_ReadBuf(0x61, data, 32);  // 读取RX FIFO
    NRF24L01_WriteReg(STATUS, 0x40);  // 清除RX_DR标志
    return 1;  // 接收成功
  }
  return 0;  // 无数据
}

2.2 主机主程序(抢答判断与显示,main_host.c

#include "stm32f1xx_hal.h"
#include "nrf24l01.h"
#include "lcd1602.h"
#include "buzzer.h"

// 抢答数据包结构(32字节)
typedef struct {
   
  uint8_t dev_id;      // 终端编号(1-8)
  uint32_t timestamp;  // 抢答时间戳(ms,基于SysTick)
  uint8_t reserved[27];// 保留字段
} BidData_t;

BidData_t first_bid = {
   0};  // 首个有效抢答数据
uint8_t bid_success = 0;    // 抢答成功标志(0:无,1:成功,2:犯规)

int main(void) {
   
  HAL_Init();
  SystemClock_Config();  // 72MHz
  MX_GPIO_Init();        // 初始化GPIO(LCD、蜂鸣器、LED)
  MX_SPI1_Init();        // 初始化SPI1(NRF24L01+)
  LCD1602_Init();        // 初始化LCD1602
  Buzzer_Init();         // 初始化蜂鸣器
  NRF24L01_Init(MODE_PRX);  // 主机:PRX模式

  LCD1602_ShowString(0, 0, "Waiting...", 16);  // 初始显示“等待抢答”

  while (1) {
   
    uint8_t rx_buf[32];
    if (NRF24L01_ReceiveData(rx_buf)) {
     // 接收终端数据
      BidData_t *bid = (BidData_t *)rx_buf;

      if (!bid_success) {
     // 首次抢答
        first_bid = *bid;
        bid_success = 1;
        LCD1602_ShowString(0, 0, "Winner: ", 16);
        LCD1602_ShowChar(8, 0, '0' + bid->dev_id, 16);  // 显示终端编号
        Buzzer_Beep(1000);  // 长鸣1秒(成功)
        HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET);  // 绿灯亮
      } else {
     // 二次抢答(犯规)
        bid_success = 2;
        LCD1602_ShowString(0, 1, "Foul! Dev:", 16);
        LCD1602_ShowChar(10, 1, '0' + bid->dev_id, 16);
        Buzzer_Beep(200);  // 短鸣0.2秒(犯规)
        HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_SET);  // 红灯亮
      }
    }

    // 裁判复位(按键PA1)
    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET) {
   
      bid_success = 0;
      first_bid.dev_id = 0;
      LCD1602_ShowString(0, 0, "Waiting...    ", 16);
      LCD1602_ShowString(0, 1, "            ", 16);
      Buzzer_Off();
      HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_RESET);
      HAL_Delay(200);  // 消抖
    }

    HAL_Delay(10);  // 10ms轮询一次
  }
}

2.3 终端主程序(抢答发送,main_terminal.c

#include "stm32f1xx_hal.h"
#include "nrf24l01.h"
#include "led.h"

// 终端编号(1-8,通过拨码开关或代码定义)
#define DEV_ID 1  

// 抢答数据包
BidData_t bid_data = {
   DEV_ID, 0, {
   0}};

int main(void) {
   
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();  // 初始化GPIO(按键、LED、NRF24L01+)
  MX_SPI1_Init();
  NRF24L01_Init(MODE_PTX);  // 终端:PTX模式
  LED_Init();  // 初始化LED(绿色待机,红色抢答成功)
  LED_Green_On();  // 初始绿灯(待机)

  // 按键中断配置(PA0,下降沿触发)
  GPIO_InitTypeDef GPIO_InitStruct = {
   0};
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);

  while (1) {
   
    // 低功耗待机(仅保留中断)
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
  }
}

// 按键中断服务函数(发送抢答信号)
void EXTI0_IRQHandler(void) {
   
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
   
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
    if (LED_GetState() != RED) {
     // 未抢答过
      bid_data.timestamp = HAL_GetTick();  // 记录当前时间戳
      NRF24L01_SendData((uint8_t *)&bid_data, sizeof(bid_data));  // 发送数据
      LED_Red_On();  // 红灯亮(已抢答)
      LED_Green_Off();
    }
  }
}

参考代码 基于STM32的无线抢答器设计 www.youwenfan.com/contentalh/133735.html

四、关键技术与优化

1. 无线传输可靠性

  • 抗干扰设计:NRF24L01+工作在2.4GHz ISM频段,通过RF_CH设置频道(如40),避开Wi-Fi(2.4GHz)干扰;启用自动重发(SETUP_RETR=0x1A,10次重发)。
  • 数据校验:数据包含dev_idtimestamp,主机通过timestamp判断信号到达顺序(精确到1ms),避免多终端同时发送的冲突。

2. 抢答优先级判断

  • 先到先得逻辑:主机维护first_bid变量,仅记录首个有效信号,后续信号判定为“犯规”。
  • 防误触设计:终端按键需长按50ms(软件消抖)才发送信号,避免误碰触发。

3. 低功耗优化

  • 终端待机:无抢答时,STM32进入SLEEP模式,关闭NRF24L01+电源(通过MOS管控制VCC),仅保留按键中断唤醒。
  • 主机低功耗:无信号时,NRF24L01+进入接收待机模式(功耗<1mA),STM32通过HAL_PWR_EnterSLEEPMode降低功耗。

五、系统调试与扩展

1. 调试步骤

阶段 操作 工具
硬件调试 测量NRF24L01+供电(3.3V),示波器检查SPI时序 万用表、示波器(SCK/MOSI)
无线测试 终端发送“test”数据,主机用串口打印接收内容 串口调试助手(SSCOM)
抢答逻辑 2个终端同时按键,观察主机是否只记录首个 逻辑分析仪(NRF24L01+信号)
声光反馈 抢答成功后,检查蜂鸣器频率(1kHz)和LED状态 示波器(蜂鸣器驱动信号)

2. 扩展功能

  • 倒计时显示:主机增加DS1302实时时钟,支持设置抢答倒计时(如30秒),LCD显示剩余时间。
  • 多轮抢答:主机记录每轮胜者,支持“重新开始”按钮,自动清空历史数据。
  • 无线扩展:增加蓝牙模块(HC-05),手机APP显示抢答结果、导出数据。

六、总结

基于STM32的无线抢答器通过NRF24L01+无线传输实现多终端与主机通信,时间戳优先级判断确保公平性,声光反馈+LCD显示提升交互体验,硬件成本约80元/套(含3个终端+1个主机),软件可扩展性强,适合作为嵌入式通信、实时系统设计的实践项目。

相关文章
|
9月前
|
算法 API 数据安全/隐私保护
电商 API 双平台实战:淘宝 item.get + 京东 item_detail 对接指南(附可复用代码 + 问题排查)
本文详细解析了淘宝和京东双平台API对接的核心流程,涵盖资质申请、凭证获取、签名生成、高频接口调用及常见问题解决方案,助力开发者高效实现商品数据同步与管理。
|
3月前
|
机器学习/深度学习 传感器 算法
无线通信系统信道估计算法详解
信道估计是无线通信系统的核心技术之一,其目的是通过接收信号推断信道的冲激响应或频率响应,为相干解调、波束赋形、资源分配等功能提供信道状态信息(CSI)。在4G/5G/6G系统中,信道估计的精度直接影响通信质量(如误码率、吞吐量),而随着大规模MIMO、毫米波、超密集组网等技术的普及,信道估计的复杂度与实时性要求也日益提高。
|
3月前
|
网络协议 物联网 编译器
STM32 MQTT客户端实现方案(基于二次开发包)
STM32 MQTT客户端实现方案(基于二次开发包)
|
3月前
|
编解码 运维 测试技术
基于DSP F28335的AD2S1210旋变数字转换器串行通信程序
AD2S1210是一款高精度的旋变数字转换器(RDC),用于将旋转变压器输出的模拟信号转换为数字角度和速度值。本程序基于TI DSP F28335实现与AD2S1210的串行通信(SPI接口),完成角度和速度的读取与解析。
|
9月前
|
数据可视化 5G
Turbo码与卷积码误码率性能对比分析
Turbo码与卷积码误码率性能对比分析
|
4月前
|
缓存 安全 测试技术
基于C#实现Modbus RTU通信
基于C#实现Modbus RTU通信
|
9月前
|
算法 自动驾驶 机器人
MATLAB中实现LSD直线检测
MATLAB中实现LSD直线检测
|
C++
基于 C++ 的 IEC60870-5-104 规约的主从站模拟数据通信
基于 C++ 的 IEC60870-5-104 规约的主从站模拟数据通信
507 0
|
9月前
|
移动开发 监控 网络协议
C#通过TCP/IP控制康奈视读码枪实现方案
C#通过TCP/IP控制康奈视读码枪实现方案
|
10月前
正则化方法、L曲线和奇异值分解
正则化方法、L曲线和奇异值分解

热门文章

最新文章