零知开源——基于STM32F4的HC-12无线通信系统及ST7789显示应用

发布于:2025-08-16 ⋅ 阅读:(13) ⋅ 点赞:(0)

 ✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、硬件系统设计

1.1 硬件清单

1.2 接线方案

1.3 连接硬件图

1.4 接线实物图

二、软件系统设计

2.1 HC-12模块模式切换机制

2.2 ST7789显示优化技术

2.3 无线数据处理状态机

2.4 信道切换处理核心

2.5 发送端完整代码

2.6 接收端完整代码

三、数据传输展示

3.1 发送端操作

3.2 无线传输

3.3 接收端处理

3.4 视频演示

四、关键技术详解

4.1 HC-12模块工作原理

4.2 软件串口实现

五、常见问题解答

Q1: 为什么HC-12模块无法通信?

Q2: 如何增加通信距离?

六、结论


(1)项目概述

        本项目构建了一个基于STM32F407VET6微控制器的无线通信系统,通过HC-12模块实现两个零知增强板之间的长距离无线通信。发送端通过物理按键切换信道,接收端使用ST7789显示屏实时显示当前信道状态。系统实现了HC-12模块的无线通信控制、AT指令的无线传输与解析、接收端可视化界面设计、状态信息的实时更新与显示

(2)项目亮点

        >提供清晰直观的UI界面
        >HC-12模块支持1公里以上通信距离(空旷环境)
        >功能模块分离,便于维护和扩展
        >优化了通信协议,减少能耗

一、硬件系统设计

1.1 硬件清单

组件 数量 说明
零知增强板(STM32F407VET6) 2 主控制器
HC-12无线模块 2 433MHz无线通信
ST7789 TFT显示屏 1 240×320分辨率,SPI接口
轻触按键 2 发送端信道切换
杜邦线 若干 模块连接

1.2 接线方案

(1)发送端接线

HC-12模块 按键 零知增强板1
VCC 按键的一端 3.3V
GND 通过10k下拉电阻接到按键另一端 GND
/ 按键1 4
/ 按键2 3
TX / 10
RX / 11
SET / 6

(2)接收端接线

HC-12模块 ST7789 零知增强板2
VCC VCC 3.3V
GND GND GND
/ SCL 52
/ SDA 51
/ RES 47
/ DC 49
/ CS 53
TX / 10
RX / 11
SET / 6

1.3 连接硬件图

(1)发送端接线图

(2)接收端接线图

1.4 接线实物图

二、软件系统设计

2.1 HC-12模块模式切换机制

// 发送信道切换命令
void sendChannelCommand(String channel) {
  HC12.print("AT+C" + channel); // 发送指令到另一个模块
  delay(100);
  
  // 进入AT指令模式
  digitalWrite(setPin, LOW);    // 设置HC-12为AT指令模式
  delay(100);                   // 等待进入AT模式
  HC12.print("AT+C" + channel); // 发送AT指令到本地HC-12
  delay(200);
  
  // 读取并显示响应
  while (HC12.available()) {
    Serial.write(HC12.read());
  }
  Serial.println("信道切换成功");
  
  // 退出AT指令模式
  digitalWrite(setPin, HIGH);
}

双模切换机制:

        首先以透明模式发送指令到接收端(digitalWrite(setPin, HIGH))
        然后进入AT模式(digitalWrite(setPin, LOW))配置本地模块
        确保发送端和接收端同时切换到相同信道

2.2 ST7789显示优化技术

// 更新信道显示
void updateChannel() {
  // 清除旧信道显示区域
  tft.fillRect(20, 130, 200, 30, ST77XX_BLACK);
  
  // 绘制新信道信息
  tft.setCursor(20, 130);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setTextSize(3);
  tft.println(currentChannel);
}

// 更新消息显示
void updateMessage() {
  // 清除旧消息显示区域
  tft.fillRect(20, 210, 200, 30, ST77XX_BLACK);
  
  // 绘制新消息
  tft.setCursor(20, 210);
  
  // 根据消息类型设置颜色
  if (lastMessage == "Success!") {
    tft.setTextColor(ST77XX_GREEN); // 成功消息为绿色
  } else {
    tft.setTextColor(ST77XX_WHITE); // 普通消息为白色
  }
  
  tft.setTextSize(3);
  tft.println(lastMessage);
}

局部刷新技术:

        使用fillRect()仅刷新特定区域,而非整个屏幕
        显著提高刷新效率(全屏刷新需300ms,局部刷新仅需20ms)
        避免屏幕闪烁现象

2.3 无线数据处理状态机

void loop() {
  // 接收HC-12数据并存储到缓冲区
  while (HC12.available()) {
    incomingByte = HC12.read();
    readBuffer += char(incomingByte);
  }
  delay(100);
  
  // 处理信道切换指令
  if (readBuffer == "AT+C001") {
    processChannelChange("01");
  }
  else if (readBuffer == "AT+C002") {
    processChannelChange("02");
  }
  
  // 检查并处理AT指令
  checkATCommand();
  
  // 消息超时处理
  if (millis() - messageTime > MESSAGE_TIMEOUT && lastMessage != "Waiting...") {
    lastMessage = "Waiting...";
    updateMessage();
  }
  
  readBuffer = ""; // 清空缓冲区
}

数据接收状态机:

        使用while (HC12.available())确保完整接收数据包
        delay(100)提供数据接收缓冲时间
        字符串比较readBuffer == "AT+C001"实现指令识别        

2.4 信道切换处理核心

void processChannelChange(String channel) {
  // 进入AT指令模式
  digitalWrite(setPin, LOW);    // 设置HC-12为AT指令模式
  delay(100);                   
  HC12.print("AT+C" + channel); // 发送完整AT指令
  delay(200);
  
  // 读取响应
  String response = "";
  while (HC12.available()) {
    response += char(HC12.read());
  }
  
  // 退出AT指令模式
  digitalWrite(setPin, HIGH);   
  
  // 更新显示信息
  currentChannel = "Channel: " + channel;
  lastMessage = "Success!";
  messageTime = millis();       // 重置消息计时器
  
  // 更新显示屏
  updateChannel();
  updateMessage();
}

信道切换协议:

严格遵循AT指令格式"AT+C" + channel
确保与发送端指令格式完全一致
支持扩展更多信道(如"003"、"004"等)

响应处理机制:

构建response字符串收集模块响应
可用于错误检测和调试(当前版本未使用)

显示更新联动:

同时更新currentChannel和lastMessage
调用updateChannel()和updateMessage()刷新界面
重置messageTime启动超时计时器

2.5 发送端完整代码

/* 
 * 基于STM32F407VET6的HC-12无线通信发送端
 * 功能:通过按键切换信道并发送AT指令
 */
#include <SoftwareSerial.h>

// 引脚定义
#define setPin 6    // HC-12设置引脚(PD6)
#define button1 4   // 按钮1引脚(PD4)
#define button2 3   // 按钮2引脚(PD3)

// 初始化软件串口 (RX, TX)
SoftwareSerial HC12(10, 11); // PA10(RX), PA9(TX)

// 全局变量
byte incomingByte;
String readBuffer = "";
int button1State = 0;
int button1Pressed = 0;
int button2State = 0;
int button2Pressed = 0;

void setup() {
  Serial.begin(9600);          // 初始化串口监视器
  HC12.begin(9600);            // 初始化HC-12通信
  pinMode(setPin, OUTPUT);     // 配置SET引脚为输出
  pinMode(button1, INPUT);     // 配置按键1为输入
  pinMode(button2, INPUT);     // 配置按键2为输入
  digitalWrite(setPin, HIGH);  // 设置HC-12为正常透明模式
}

void loop() {
  // 接收HC-12数据并存储到缓冲区
  while (HC12.available()) {
    incomingByte = HC12.read();
    readBuffer += char(incomingByte);
  }
  delay(100);
  
  // 串口转发功能:将串口数据转发到HC-12
  while (Serial.available()) {
    HC12.write(Serial.read());
  }
  
  // 按钮1处理:切换到信道01
  button1State = digitalRead(button1);
  if (button1State == HIGH && button1Pressed == LOW) {
    button1Pressed = HIGH;
    delay(20); // 按键消抖
  }
  
  if (button1Pressed == HIGH) {
    sendChannelCommand("001"); // 发送信道切换指令
    button1Pressed = LOW;     // 重置按键状态
  }
  
  // 按钮2处理:切换到信道02
  button2State = digitalRead(button2);
  if (button2State == HIGH && button2Pressed == LOW) {
    button2Pressed = HIGH;
    delay(100); // 按键消抖
  }
  
  if (button2Pressed == HIGH) {
    sendChannelCommand("002"); // 发送信道切换指令
    button2Pressed = LOW;     // 重置按键状态
  }
  
  // 检查并处理AT指令
  checkATCommand();
  readBuffer = ""; // 清空缓冲区
}

// 发送信道切换命令
void sendChannelCommand(String channel) {
  HC12.print("AT+C" + channel); // 发送指令到另一个模块
  delay(100);
  
  // 进入AT指令模式
  digitalWrite(setPin, LOW);    // 设置HC-12为AT指令模式
  delay(100);                   // 等待进入AT模式
  HC12.print("AT+C" + channel); // 发送AT指令到本地HC-12
  delay(200);
  
  // 读取并显示响应
  while (HC12.available()) {
    Serial.write(HC12.read());
  }
  Serial.println("信道切换成功");
  
  // 退出AT指令模式
  digitalWrite(setPin, HIGH);
}

// 检查并处理串口接收的AT指令
void checkATCommand() {
  if (readBuffer.startsWith("AT")) {
    digitalWrite(setPin, LOW);  // 进入AT模式
    delay(200);
    HC12.print(readBuffer);     // 发送AT指令
    delay(200);
    
    // 读取并显示响应
    while (HC12.available()) {
      Serial.write(HC12.read());
    }
    
    digitalWrite(setPin, HIGH); // 退出AT模式
  }
}

2.6 接收端完整代码

/*
 * 基于STM32F407VET6的HC-12无线通信接收端
 * 功能:接收信道切换指令并在ST7789显示屏上显示状态
 */
#include <SoftwareSerial.h>
#include <Adafruit_GFX.h>       // 核心图形库
#include <Adafruit_ST7789.h>    // ST7789显示驱动

// 引脚定义
#define setPin 6                // HC-12设置引脚(PD6)
#define TFT_CS   53             // 显示屏片选(PG9)
#define TFT_DC   49             // 数据/命令选择(PG8)
#define TFT_RST  47             // 显示屏复位(PG7)

// 初始化软件串口和显示屏
SoftwareSerial HC12(10, 11);    // PA10(RX), PA9(TX)
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

// 全局变量
byte incomingByte;
String readBuffer = "";
String currentChannel = "Channel: --"; // 当前信道显示
String lastMessage = "Waiting...";     // 最后消息
unsigned long messageTime = 0;         // 消息时间戳
const unsigned long MESSAGE_TIMEOUT = 2000; // 消息显示超时(2秒)

void setup() {
  Serial.begin(9600);          // 初始化串口监视器
  HC12.begin(9600);            // 初始化HC-12通信
  pinMode(setPin, OUTPUT);     // 配置SET引脚为输出
  digitalWrite(setPin, HIGH);  // 设置HC-12为正常模式
  
  // 初始化显示屏
  tft.init(240, 320);          // 初始化240x320显示屏
  tft.setRotation(3);           // 设置显示方向(0-3)
  tft.invertDisplay(false);     // 禁用显示反转
  tft.fillScreen(ST77XX_BLACK); // 清屏
  tft.setTextWrap(false);       // 禁用文本换行
  
  // 绘制静态UI元素
  drawUI();
}

void loop() {
  // 接收HC-12数据并存储到缓冲区
  while (HC12.available()) {
    incomingByte = HC12.read();
    readBuffer += char(incomingByte);
  }
  delay(100);
  
  // 串口转发功能:将串口数据转发到HC-12
  while (Serial.available()) {
    HC12.write(Serial.read());
  }
  
  // 处理信道切换指令
  if (readBuffer == "AT+C001") {
    processChannelChange("01");
  }
  else if (readBuffer == "AT+C002") {
    processChannelChange("02");
  }
  
  // 检查并处理AT指令
  checkATCommand();
  
  // 消息超时处理
  if (millis() - messageTime > MESSAGE_TIMEOUT && lastMessage != "Waiting...") {
    lastMessage = "Waiting...";
    updateMessage();
  }
  
  readBuffer = ""; // 清空缓冲区
}

// 处理信道切换
void processChannelChange(String channel) {
  // 进入AT指令模式
  digitalWrite(setPin, LOW);    // 设置HC-12为AT指令模式
  delay(100);                   
  HC12.print("AT+C" + channel); // 发送完整AT指令
  delay(200);
  
  // 读取响应
  String response = "";
  while (HC12.available()) {
    response += char(HC12.read());
  }
  
  // 退出AT指令模式
  digitalWrite(setPin, HIGH);   
  
  // 更新显示信息
  currentChannel = "Channel: " + channel;
  lastMessage = "Success!";
  messageTime = millis();       // 重置消息计时器
  
  // 串口输出
  Serial.print("Channel ");
  Serial.print(channel);
  Serial.println(" set successfully");
  
  // 更新显示屏
  updateChannel();
  updateMessage();
}

// 检查并处理AT指令
void checkATCommand() {
  if (readBuffer.startsWith("AT")) {
    digitalWrite(setPin, LOW);  // 进入AT模式
    delay(100);                 
    HC12.print(readBuffer);     // 发送AT指令
    delay(200);
    
    // 读取并显示响应
    while (HC12.available()) {
      Serial.write(HC12.read());
    }
    
    digitalWrite(setPin, HIGH); // 退出AT模式
  }
}

// 绘制静态UI元素
void drawUI() {
  // 绘制标题
  tft.setCursor(20, 20);
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(3);
  tft.println("HC-12 SYSTEM");
  
  // 绘制分隔线
  tft.drawFastHLine(10, 60, 220, ST77XX_BLUE);
  
  // 绘制信道标签
  tft.setCursor(20, 100);
  tft.setTextColor(ST77XX_CYAN);
  tft.setTextSize(2);
  tft.println("Current Channel:");
  
  // 绘制状态标签
  tft.setCursor(20, 180);
  tft.setTextColor(ST77XX_CYAN);
  tft.setTextSize(2);
  tft.println("Status:");
  
  // 绘制边框
  tft.drawRect(5, 5, 240, 235, ST77XX_WHITE);
  
  // 初始化动态内容
  updateChannel();
  updateMessage();
}

// 更新信道显示
void updateChannel() {
  // 清除旧信道显示区域
  tft.fillRect(20, 130, 200, 30, ST77XX_BLACK);
  
  // 绘制新信道信息
  tft.setCursor(20, 130);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setTextSize(3);
  tft.println(currentChannel);
}

// 更新消息显示
void updateMessage() {
  // 清除旧消息显示区域
  tft.fillRect(20, 210, 200, 30, ST77XX_BLACK);
  
  // 绘制新消息
  tft.setCursor(20, 210);
  
  // 根据消息类型设置颜色
  if (lastMessage == "Success!") {
    tft.setTextColor(ST77XX_GREEN); // 成功消息为绿色
  } else {
    tft.setTextColor(ST77XX_WHITE); // 普通消息为白色
  }
  
  tft.setTextSize(3);
  tft.println(lastMessage);
}

错误预防机制:

        指令格式严格校验
        超时管理系统
        缓冲区定期清理

三、数据传输展示

系统工作时,数据传输流程如下:

3.1 发送端操作

        用户按下按键1或按键2
        发送端通过软件串口发送"AT+C001"或"AT+C002"指令
        同时设置本地HC-12模块的信道

3.2 无线传输

        两个HC-12模块通过433MHz无线频段传输数据
        传输距离可达1000米(开阔地带)

3.3 接收端处理

        接收端HC-12模块收到指令
        解析指令内容并设置本地信道
        更新ST7789显示屏上的信道信息
        显示"Success!"状态信息2秒后恢复等待状态

3.4 视频演示

基于零知增强板和HC-12传输无线指令

发送端(零知增强板1)按下按键同时将接收端(零知增强板2)的信道切换成信道01和信道02

四、关键技术详解

4.1 HC-12模块工作原理

HC-12是基于Si4463射频芯片的无线串口通信模块,特点包括:

        工作频率:433.4-473.0MHz
        传输距离:100-1000米(取决于环境和功率设置)
        支持多种波特率(1200bps-115200bps)
        提供4个通信频道

AT指令关键操作:

        >AT+C001:切换到信道001
        >AT+RPx:设置发射功率(x=0-8,8为最大功率20dBm)
        >AT+FBx:设置工作模式(x=0-2,0为FU1模式)

4.2 软件串口实现

在STM32F407VET6上使用软件串口的优势:

        不占用硬件串口资源
        可灵活配置任意GPIO作为TX/RX
        支持多串口并发通信

SoftwareSerial库注意事项:

        >波特率不宜过高(建议≤38400bps)
        >避免在中断服务程序中调用
        >接收缓冲区大小可调整以优化性能

五、常见问题解答

Q1: 为什么HC-12模块无法通信?

A:以下步骤进行检查:

        检查电源:确保3.3V稳定供电
        验证接线:TX-RX交叉连接
        检查波特率:双方必须使用相同波特率
        确认信道:双方必须处于相同信道

Q2: 如何增加通信距离?

A:发送端进行指令配置:

        设置更高发射功率:AT+RP8(最大功率)
        使用外置天线:替换模块上的弹簧天线
        调整工作模式:AT+FB2(FU2模式,低速率但更远距离)
        优化天线位置:避免金属屏蔽

六、结论

        项目充分展示了STM32F407VET6的强大性能和HC-12模块的通信能力,为物联网应用开发提供了实用参考。系统可进一步扩展为环境监测、远程控制等多种应用场景。通过本项目的开发,我们实现了:

        基于HC-12的可靠无线通信系统
        ST7789显示屏的GUI界面设计与实现
        硬件按键与无线指令的协同控制
        实时状态反馈与显示

项目资源:

(1)所需库

        ST7789显示屏: Adafruit ST7789 Library

        GFX图形库: Adafruit GFX Library

        软件串口库: SoftwareSerial Library

(2)技术文档

        HC-12无线通信模块: HC-12用户手册

        主控芯片数据手册: STM32F407VET6 Datasheet

本文详细介绍了基于STM32F407VET6零知增强板的HC-12无线通信系统设计与实现!点击了解更多零知开发教程:
https://www.lingzhilab.com/freesources.html


网站公告

今日签到

点亮在社区的每一天
去签到