USART 串口透传替代其射频收发功能,保留原数据格式和通信逻辑,仅替换底层硬件实现,适配 Proteus 虚拟仿真。

二、设计步骤(分发送端 + 接收端)

第一步:STM32CubeIDE 工程配置(发送端 / 接收端通用)

  1. 新建工程:选择STM32F103R6Tx,勾选 HAL 库,创建空工程。
  2. 时钟配置:HSE(8MHz)→PLL 倍频 9 倍→系统时钟 72MHz,APB1 时钟 36MHz(USART1 挂载 APB2,时钟 72MHz)。
  3. USART1 配置
    • 模式:异步通信(Asynchronous);
    • 参数:波特率 9600、数据位 8、停止位 1、校验位 None、流控 Disabled;
    • 引脚:PA9(USART1_TX,复用推挽)、PA10(USART1_RX,浮空输入)。
  4. GPIO 配置(可选,用于指示灯)
    • 发送端:PB0→GPIO_Output(发送成功指示灯);
    • 接收端:PB0→GPIO_Output(接收成功指示灯)。
  5. 生成代码:点击 “Project→Generate Code”,生成初始化工程。

第二步:Proteus 仿真电路搭建

  1. 添加元件:两个 STM32F103R6、两个 LED、两个 220Ω 电阻、COMPIM(虚拟串口)、电源和 GND。
  2. 电路连接
    • 电源:STM32 和虚拟串口均接 3.3V/GND;
    • 串口交叉连接:发送端 PA9(TX)→接收端 PA10(RX),接收端 PA9(TX)→发送端 PA10(RX);
    • 指示灯:发送端 PB0→220Ω 电阻→GND,接收端同理。
  3. 虚拟串口配置:双击 COMPIM,波特率设为 9600,其他参数与 CubeIDE 一致,确保两节点串口参数匹配。

第三步:代码编写(发送端 + 接收端)

1. 公共头文件(comm.h,发送端 / 接收端均需添加)

c

运行

#ifndef __COMM_H
#define __COMM_H
#include "stm32f1xx_hal.h"

// 模拟nRF24L01数据帧定义(自定义,保持收发一致)
#define NRF_DATA_LEN 32 // 单次最大数据长度(同nRF24L01)
typedef struct {
    uint8_t header; // 帧头(用于校验)
    uint8_t data[NRF_DATA_LEN]; // 数据内容
    uint8_t checksum; // 校验和(header+data求和取低8位)
} NRF_FrameTypeDef;

// 函数声明
void NRF_Sim_SendData(UART_HandleTypeDef *huart, uint8_t *data, uint8_t len);
uint8_t NRF_Sim_ReceiveData(UART_HandleTypeDef *huart, NRF_FrameTypeDef *frame);
uint8_t Calculate_Checksum(NRF_FrameTypeDef *frame);

#endif

2. 公共源文件(comm.c,发送端 / 接收端均需添加)

c

运行

#include "comm.h"

// 计算校验和
uint8_t Calculate_Checksum(NRF_FrameTypeDef *frame) {
    uint8_t sum = 0;
    sum += frame->header;
    for (uint8_t i = 0; i < NRF_DATA_LEN; i++) {
        sum += frame->data[i];
    }
    return sum;
}

// 模拟nRF24L01发送数据(串口透传)
void NRF_Sim_SendData(UART_HandleTypeDef *huart, uint8_t *data, uint8_t len) {
    NRF_FrameTypeDef frame;
    frame.header = 0xAA; // 固定帧头(可自定义)
    memset(frame.data, 0, NRF_DATA_LEN); // 数据清0
    memcpy(frame.data, data, len > NRF_DATA_LEN ? NRF_DATA_LEN : len); // 填充数据
    frame.checksum = Calculate_Checksum(&frame); // 计算校验和
    
    // 串口发送完整帧
    HAL_UART_Transmit(huart, (uint8_t*)&frame, sizeof(NRF_FrameTypeDef), 100);
}

// 模拟nRF24L01接收数据(串口接收+校验)
uint8_t NRF_Sim_ReceiveData(UART_HandleTypeDef *huart, NRF_FrameTypeDef *frame) {
    // 接收完整帧
    if (HAL_UART_Receive(huart, (uint8_t*)frame, sizeof(NRF_FrameTypeDef), 500) != HAL_OK) {
        return 0; // 接收超时失败
    }
    
    // 校验帧头和校验和
    if (frame->header != 0xAA || frame->checksum != Calculate_Checksum(frame)) {
        return 0; // 校验失败
    }
    
    return 1; // 接收成功
}

3. 发送端 main.c(核心代码)

c

运行

#include "comm.h"
extern UART_HandleTypeDef huart1;
#define LED_TX_Pin GPIO_PIN_0
#define LED_TX_GPIO_Port GPIOB

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    uint8_t send_buf[] = "STM32_NRF_Sim_Tx: Hello!"; // 待发送数据
    while (1) {
        // 模拟nRF24L01发送
        NRF_Sim_SendData(&huart1, send_buf, sizeof(send_buf));
        
        // 发送成功指示灯闪烁
        HAL_GPIO_TogglePin(LED_TX_GPIO_Port, LED_TX_Pin);
        HAL_Delay(1000); // 1秒发送一次
    }
}

// GPIO初始化(CubeIDE自动生成,需确保PB0为输出)
void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = LED_TX_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(LED_TX_GPIO_Port, &GPIO_InitStruct);
    HAL_GPIO_WritePin(LED_TX_GPIO_Port, LED_TX_Pin, GPIO_PIN_RESET);
}

4. 接收端 main.c(核心代码)

c

运行

#include "comm.h"
extern UART_HandleTypeDef huart1;
#define LED_RX_Pin GPIO_PIN_0
#define LED_RX_GPIO_Port GPIOB

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    NRF_FrameTypeDef recv_frame;
    while (1) {
        // 模拟nRF24L01接收
        if (NRF_Sim_ReceiveData(&huart1, &recv_frame) == 1) {
            // 接收成功,指示灯闪烁
            HAL_GPIO_TogglePin(LED_RX_GPIO_Port, LED_RX_Pin);
            // 如需打印接收数据,可添加串口打印:
            // HAL_UART_Transmit(&huart1, recv_frame.data, NRF_DATA_LEN, 100);
        }
        HAL_Delay(10); // 轻微延时,降低CPU占用
    }
}

// GPIO初始化(CubeIDE自动生成,需确保PB0为输出)
void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = LED_RX_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(LED_RX_GPIO_Port, &GPIO_InitStruct);
    HAL_GPIO_WritePin(LED_RX_GPIO_Port, LED_RX_Pin, GPIO_PIN_RESET);
}

三、仿真与验证步骤

  1. 代码编译:分别编译发送端和接收端工程,生成.hex文件。
  2. Proteus 加载程序:双击两个 STM32F103R6,分别导入对应.hex文件。
  3. 启动仿真:点击 Proteus “运行” 按钮,观察现象:
    • 发送端 LED 每 1 秒闪烁一次,说明数据正常发送;
    • 接收端 LED 同步闪烁,说明数据通过串口(模拟无线)接收成功。
  4. 故障排查
    • 若接收失败,检查串口参数是否一致(波特率、数据位等);
    • 确认串口交叉连接(TX→RX),GND 是否共地;
    • 查看帧头和校验和逻辑,确保收发端一致。

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐