项目开发背景
随着城市化进程不断加快和居民消费水平持续提升,生活垃圾产生量逐年增加,传统的垃圾分类主要依赖人工判断和分拣,不仅效率低下,还存在分类准确性不高、人力成本较高等问题。在此背景下,利用嵌入式技术和人工智能手段实现智能化的垃圾分类指导,已成为解决城市垃圾处理难题的重要方向。
基于STM32的智能垃圾分类系统正是在这一需求背景下提出的创新方案。该系统通过图像传感器实时采集垃圾的视觉特征,结合本地轻量级算法进行快速识别,能够有效区分可回收物、厨余垃圾、有害垃圾和其他垃圾等常见类别。系统进一步通过语音模块提供实时分类指导,降低居民的学习成本和使用门槛,同时借助物联网技术将识别数据上传至云平台,实现垃圾分类数据的记录与分析。
该系统的开发不仅有助于推动生活垃圾处理的智能化与信息化,还可为城市管理者提供数据支持,优化垃圾收运和处理流程。此外,通过可视化的QT界面展示分类结果和统计信息,进一步增强了系统的实用性和交互性,为环保宣传教育提供了有效工具。
设计实现的功能
(1)使用OV7670摄像头模块采集垃圾图像特征
(2)基于STM32F103C8T6运行本地算法进行垃圾类型识别
(3)通过JL-03语音模块播放分类指导提示
(4)利用ESP8266-01S Wi-Fi模块连接华为云平台,QT上位机显示识别记录和统计数据分析
项目硬件模块组成
(1)STM32F103C8T6最小系统核心板作为主控制器
(2)OV7670摄像头模块采集垃圾图像
(3)JL-03语音模块播放分类提示
(4)RGB LED灯带提供颜色分类指示
(5)ESP8266-01S Wi-Fi模块连接华为云平台
(6)洞洞板焊接图像处理电路,杜邦线连接各传感器
设计意义
基于STM32设计的智能垃圾分类指导系统具有重要的现实意义和应用价值。该系统通过集成图像传感器和本地处理算法,能够自动识别垃圾类型并提供分类指导,有效解决了人工分类效率低、错误率高的痛点。在实际应用中,这有助于提升垃圾分类的准确性和便捷性,推动环保意识的普及和垃圾资源化利用。
系统的硬件组成如STM32主控制器、OV7670摄像头模块等,确保了低成本、高可靠性的实现,使得该设计易于部署在家庭、社区或公共场所。通过本地算法进行垃圾识别,减少了对网络依赖,提高了响应速度,同时语音模块和RGB LED灯带提供直观的提示,增强了用户体验。
连接华为云平台 via ESP8266 Wi-Fi模块,使系统能够上传识别记录和数据,便于通过QT上位机进行统计分析和远程监控。这为垃圾分类管理提供了数据支持,有助于政府和机构制定更有效的环保政策,并实现长期趋势分析。
整体上,该设计不仅促进了垃圾分类的智能化转型,还体现了嵌入式系统在物联网领域的实用创新,为可持续发展贡献了技术解决方案。
设计思路
系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调各模块的工作流程。首先,OV7670摄像头模块采集垃圾图像,通过洞洞板焊接的图像处理电路进行初步信号调理,STM32读取图像数据并提取关键特征,如颜色、形状或纹理,为后续识别做准备。
本地垃圾类型识别算法在STM32上运行,利用提取的图像特征进行简单分类,例如通过阈值分割或模板匹配区分可回收物、厨余垃圾、有害垃圾和其他垃圾。算法设计注重轻量化和实时性,以适应STM32的处理能力限制,确保识别过程快速且准确。
识别结果通过JL-03语音模块播放分类指导提示,例如语音播报垃圾类型和投放建议,同时RGB LED灯带根据垃圾类别显示不同颜色,提供直观的视觉指示,增强用户交互体验。
ESP8266-01S Wi-Fi模块将识别记录和数据上传至华为云平台,实现远程数据存储和管理。QT上位机软件从云端获取数据,显示历史识别记录、分类统计和趋势分析,支持用户查看和导出报告,便于垃圾分类管理的监督和优化。
硬件连接通过洞洞板焊接图像处理电路,并使用杜邦线灵活连接各传感器模块,确保信号传输稳定。整个系统设计注重实用性和可靠性,各模块协同工作,完成从图像采集到数据展示的全流程。
框架图
系统总体设计
系统总体设计基于STM32F103C8T6最小系统核心板作为主控制器,负责协调整个垃圾分类指导系统的运行。该系统通过OV7670摄像头模块采集垃圾图像特征,图像数据经由洞洞板焊接的图像处理电路进行初步处理,然后通过杜邦线连接至STM32核心板。STM32运行本地算法对图像进行垃圾类型识别,识别结果通过JL-03语音模块播放分类指导提示,同时RGB LED灯带根据垃圾类型提供颜色指示,以增强用户交互。
硬件组件包括OV7670摄像头用于图像采集,JL-03语音模块用于音频输出,RGB LED灯带用于视觉反馈,ESP8266-01S Wi-Fi模块用于连接华为云平台,实现数据上传。所有传感器和模块通过杜邦线连接到STM32核心板,确保灵活性和可扩展性。系统设计注重实际应用,图像处理电路在洞洞板上焊接,以降低成本和提高可靠性。
工作流程始于图像采集,OV7670摄像头捕获垃圾图像后,数据被传输到STM32进行本地算法处理,识别垃圾类型如可回收、有害、厨余或其他。识别结果触发语音模块播放相应提示,并控制RGB LED灯带显示对应颜色。同时,ESP8266模块将识别记录上传至华为云平台,QT上位机从云平台获取数据,显示识别历史和统计数据分析,支持用户监控和优化分类行为。
系统集成通过STM32的GPIO、UART和SPI接口实现各模块通信,确保数据流畅传输。本地算法优化了处理效率,减少对云端的依赖,而Wi-Fi模块保障了数据的远程同步。整体设计注重实用性和稳定性,适用于家庭或公共场所的垃圾分类指导场景。
系统功能总结
| 功能名称 | 功能描述 | 使用硬件/模块 |
|---|---|---|
| 图像采集 | 采集垃圾图像特征 | OV7670摄像头模块 |
| 图像处理 | 本地算法进行垃圾类型识别 | STM32F103C8T6核心板 |
| 分类指示 | 提供颜色分类指示 | RGB LED灯带 |
| 语音提示 | 播放分类指导提示 | JL-03语音模块 |
| 数据上传 | 连接华为云平台上传数据 | ESP8266-01S Wi-Fi模块 |
| 数据显示 | 显示识别记录和统计数据分析 | QT上位机软件 |
设计的各个功能模块描述
STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行,包括控制图像采集、执行本地垃圾识别算法、驱动语音模块和LED指示,以及管理Wi-Fi通信,确保各模块协同工作。
OV7670摄像头模块用于采集垃圾的图像数据,通过图像传感器获取视觉特征,为后续的垃圾类型识别提供原始输入,支持系统进行实时图像处理。
JL-03语音模块接收主控制器的指令,播放预录制的分类指导提示语音,例如告知用户垃圾属于可回收、有害或其他类别,增强系统的交互性和实用性。
RGB LED灯带根据识别结果显示不同颜色的光,例如用红色表示有害垃圾、绿色表示可回收垃圾,提供直观的视觉分类指示,辅助用户进行正确投放。
ESP8266-01S Wi-Fi模块实现系统与华为云平台的连接,用于上传识别记录和数据,支持远程监控和统计分析,同时可能接收云端的指令或更新。
洞洞板焊接的图像处理电路包括必要的组件如电阻和电容,用于稳定和预处理OV7670摄像头的图像信号,通过杜邦线可靠连接各传感器,确保数据采集的准确性。
本地算法运行在STM32控制器上,基于采集的图像特征进行垃圾类型识别,采用简单的图像处理或机器学习方法实现分类,无需外部依赖,保证实时性。
QT上位机软件运行在计算机端,通过Wi-Fi接收系统上传的数据,显示识别历史记录和进行统计数据分析,如图表展示分类趋势,提供用户友好的界面。
上位机代码设计
GarbageClassifierUI.pro
QT += core gui charts network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = GarbageClassifierUI
TEMPLATE = app
SOURCES +=
main.cpp
mainwindow.cpp
HEADERS +=
mainwindow.h
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTableWidget>
#include <QtCharts>
#include <QChartView>
#include <QPieSeries>
#include <QVector>
#include <QString>
struct Record {
QString timestamp;
QString type;
double confidence;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void newConnection();
void readData();
void updateChart();
private:
QTcpServer *server;
QTableWidget *table;
QChart *chart;
QChartView *chartView;
QPieSeries *series;
QVector<Record> records;
void setupUI();
void setupServer();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QHeaderView>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QDateTime>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), server(nullptr), table(nullptr), chart(nullptr), chartView(nullptr), series(nullptr)
{
setupUI();
setupServer();
}
MainWindow::~MainWindow()
{
if (server) server->close();
}
void MainWindow::setupUI()
{
QWidget *centralWidget = new QWidget(this);
QHBoxLayout *layout = new QHBoxLayout(centralWidget);
table = new QTableWidget(0, 3, this);
table->setHorizontalHeaderLabels(QStringList() << "Timestamp" << "Type" << "Confidence");
table->horizontalHeader()->setStretchLastSection(true);
chart = new QChart();
chart->setTitle("Garbage Classification Statistics");
series = new QPieSeries();
chart->addSeries(series);
chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
layout->addWidget(table, 2);
layout->addWidget(chartView, 1);
setCentralWidget(centralWidget);
resize(800, 600);
}
void MainWindow::setupServer()
{
server = new QTcpServer(this);
if (!server->listen(QHostAddress::Any, 1234)) {
QMessageBox::critical(this, "Error", "Could not start server: " + server->errorString());
return;
}
connect(server, &QTcpServer::newConnection, this, &MainWindow::newConnection);
}
void MainWindow::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, this, &MainWindow::readData);
connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
}
void MainWindow::readData()
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) return;
while (socket->canReadLine()) {
QByteArray data = socket->readLine().trimmed();
QString line = QString::fromUtf8(data);
QStringList parts = line.split(',');
if (parts.size() != 3) {
qDebug() << "Invalid data format:" << line;
continue;
}
QString timestamp = parts[0];
QString type = parts[1];
double confidence = parts[2].toDouble();
Record record;
record.timestamp = timestamp;
record.type = type;
record.confidence = confidence;
records.append(record);
int row = table->rowCount();
table->insertRow(row);
table->setItem(row, 0, new QTableWidgetItem(timestamp));
table->setItem(row, 1, new QTableWidgetItem(type));
table->setItem(row, 2, new QTableWidgetItem(QString::number(confidence)));
updateChart();
}
}
void MainWindow::updateChart()
{
QMap<QString, int> countMap;
for (const Record &record : records) {
countMap[record.type]++;
}
series->clear();
for (auto it = countMap.begin(); it != countMap.end(); ++it) {
series->append(it.key(), it.value());
}
}
模块代码设计
#include "stm32f10x.h"
// 引脚定义
#define OV7670_SCCB_SCL_PIN GPIO_Pin_6
#define OV7670_SCCB_SDA_PIN GPIO_Pin_7
#define OV7670_SCCB_PORT GPIOB
#define OV7670_VSYNC_PIN GPIO_Pin_8
#define OV7670_href_PIN GPIO_Pin_9
#define OV7670_PCLK_PIN GPIO_Pin_10
#define OV7670_DATA_PORT GPIOC
#define OV7670_DATA_PIN_START GPIO_Pin_0
#define OV7670_DATA_PIN_END GPIO_Pin_7
#define RGB_LED_R_PIN GPIO_Pin_0
#define RGB_LED_G_PIN GPIO_Pin_1
#define RGB_LED_B_PIN GPIO_Pin_2
#define RGB_LED_PORT GPIOA
#define JL03_TX_PIN GPIO_Pin_2
#define JL03_RX_PIN GPIO_Pin_3
#define JL03_USART USART2
#define JL03_USART_PORT GPIOA
#define ESP8266_TX_PIN GPIO_Pin_9
#define ESP8266_RX_PIN GPIO_Pin_10
#define ESP8266_USART USART1
#define ESP8266_USART_PORT GPIOA
// 函数声明
void SystemClock_Config(void);
void GPIO_Config(void);
void I2C_Config(void);
void USART_Config(void);
void OV7670_Init(void);
void OV7670_WriteReg(uint8_t reg, uint8_t data);
uint8_t OV7670_ReadReg(uint8_t reg);
void OV7670_CaptureImage(void);
void Image_Process(void);
void JL03_PlayVoice(uint8_t type);
void RGB_LED_SetColor(uint8_t red, uint8_t green, uint8_t blue);
void ESP8266_SendData(const char* data);
void Delay_ms(uint32_t ms);
// 全局变量
uint8_t image_data[176 * 144]; // QCIF分辨率:176x144
volatile uint8_t capture_done = 0;
volatile uint32_t frame_count = 0;
int main(void) {
SystemClock_Config();
GPIO_Config();
I2C_Config();
USART_Config();
OV7670_Init();
while (1) {
OV7670_CaptureImage();
if (capture_done) {
Image_Process(); // 图像处理并识别类型
capture_done = 0;
}
Delay_ms(1000); // 每秒采集一次
}
}
void SystemClock_Config(void) {
// 启用HSE并设置系统时钟为72MHz
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9;
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
void GPIO_Config(void) {
// 启用GPIO时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
// 配置OV7670 SCCB (I2C)引脚: PB6(SCL), PB7(SDA)
GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7);
GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_1 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7; // 开漏输出,50MHz
// 配置OV7670数据和控制引脚: PC0-PC7为输入, PC8(VSYNC), PC9(href), PC10(PCLK)为输入
GPIOC->CRL &= ~0xFFFFFFFF;
GPIOC->CRL |= GPIO_CRL_CNF0_0 | GPIO_CRL_CNF1_0 | GPIO_CRL_CNF2_0 | GPIO_CRL_CNF3_0 |
GPIO_CRL_CNF4_0 | GPIO_CRL_CNF5_0 | GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0; // 上拉/下拉输入
GPIOC->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_CNF9 | GPIO_CRH_CNF10);
GPIOC->CRH |= GPIO_CRH_CNF8_0 | GPIO_CRH_CNF9_0 | GPIO_CRH_CNF10_0; // 上拉/下拉输入
GPIOC->ODR |= 0x07FF; // 上拉所有引脚
// 配置RGB LED: PA0, PA1, PA2为推挽输出
GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1 | GPIO_CRL_CNF2 | GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | GPIO_CRL_MODE2);
GPIOA->CRL |= GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | GPIO_CRL_MODE2; // 输出模式,50MHz
// 配置JL-03 USART2引脚: PA2(TX), PA3(RX)
GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_CNF3 | GPIO_CRL_MODE2 | GPIO_CRL_MODE3);
GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2 | GPIO_CRL_CNF3_0; // TX: 推挽输出,50MHz; RX: 上拉输入
// 配置ESP8266 USART1引脚: PA9(TX), PA10(RX)
GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_CNF10 | GPIO_CRH_MODE9 | GPIO_CRH_MODE10);
GPIOA->CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9 | GPIO_CRH_CNF10_0; // TX: 推挽输出,50MHz; RX: 上拉输入
}
void I2C_Config(void) {
// 启用I2C1时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 配置I2C1
I2C1->CR1 &= ~I2C_CR1_PE;
I2C1->CR2 |= 36; // APB1时钟为36MHz, 设置频率
I2C1->CCR = 180; // 100kHz标准模式
I2C1->TRISE = 37; // 最大上升时间
I2C1->CR1 |= I2C_CR1_PE;
}
void USART_Config(void) {
// 启用USART1和USART2时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// 配置USART1 for ESP8266: 115200, 8N1
USART1->BRR = 72000000 / 115200; // 系统时钟72MHz
USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
// 配置USART2 for JL-03: 9600, 8N1
USART2->BRR = 36000000 / 9600; // APB1时钟36MHz
USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}
void OV7670_Init(void) {
// 复位OV7670
OV7670_WriteReg(0x12, 0x80);
Delay_ms(100);
// 配置OV7670寄存器
OV7670_WriteReg(0x12, 0x0C); // 设置QCIF格式,RGB输出
OV7670_WriteReg(0x11, 0x80); // 内部时钟分频
OV7670_WriteReg(0x0C, 0x00); // 禁用COM13
OV7670_WriteReg(0x3E, 0x00); // 禁用COM14
OV7670_WriteReg(0x40, 0xD0); // 设置COM15为RGB565
OV7670_WriteReg(0x14, 0x1A); // 设置COM9
// 更多配置可根据需要添加
}
void OV7670_WriteReg(uint8_t reg, uint8_t data) {
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = 0x42; // OV7670写地址
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // 清除ADDR位
while (!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = reg;
while (!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = data;
while (!(I2C1->SR1 & I2C_SR1_BTF));
I2C1->CR1 |= I2C_CR1_STOP;
}
uint8_t OV7670_ReadReg(uint8_t reg) {
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = 0x42; // 写地址
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
while (!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = reg;
while (!(I2C1->SR1 & I2C_SR1_BTF));
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = 0x43; // 读地址
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
I2C1->CR1 &= ~I2C_CR1_ACK;
while (!(I2C1->SR1 & I2C_SR1_RXNE));
uint8_t data = I2C1->DR;
I2C1->CR1 |= I2C_CR1_STOP;
return data;
}
void OV7670_CaptureImage(void) {
// 等待VSYNC下降沿开始一帧
while (GPIOC->IDR & OV7670_VSYNC_PIN);
while (!(GPIOC->IDR & OV7670_VSYNC_PIN));
uint32_t index = 0;
for (int y = 0; y < 144; y++) {
while (!(GPIOC->IDR & OV7670_href_PIN)); // 等待行开始
for (int x = 0; x < 176; x++) {
while (GPIOC->IDR & OV7670_PCLK_PIN); // 等待PCLK低
while (!(GPIOC->IDR & OV7670_PCLK_PIN)); // 等待PCLK高,数据有效
image_data[index++] = OV7670_DATA_PORT->IDR & 0xFF; // 读取数据
}
while (GPIOC->IDR & OV7670_href_PIN); // 等待行结束
}
capture_done = 1;
frame_count++;
}
void Image_Process(void) {
// 简单颜色分类:计算平均RGB值
uint32_t sum_r = 0, sum_g = 0, sum_b = 0;
for (int i = 0; i < 176 * 144; i++) {
uint8_t pixel = image_data[i];
// 假设RGB565格式,但OV7670配置为RGB565,数据为16位,但这里我们只取8位简化
// 实际应根据配置处理
sum_r += (pixel >> 3) & 0x1F;
sum_g += (pixel >> 2) & 0x3F;
sum_b += pixel & 0x1F;
}
uint8_t avg_r = sum_r / (176 * 144);
uint8_t avg_g = sum_g / (176 * 144);
uint8_t avg_b = sum_b / (176 * 144);
// 分类逻辑:示例基于颜色
uint8_t type = 0; // 0: 其他, 1: 可回收, 2: 厨余, 3: 有害, 4: 其他
if (avg_g > avg_r && avg_g > avg_b) {
type = 2; // 绿色为主,厨余
} else if (avg_b > avg_r && avg_b > avg_g) {
type = 1; // 蓝色为主,可回收
} else if (avg_r > avg_g && avg_r > avg_b) {
type = 3; // 红色为主,有害
} else {
type = 4; // 其他
}
// 控制LED和语音
switch (type) {
case 1:
RGB_LED_SetColor(0, 0, 255); // 蓝色
JL03_PlayVoice(1);
break;
case 2:
RGB_LED_SetColor(0, 255, 0); // 绿色
JL03_PlayVoice(2);
break;
case 3:
RGB_LED_SetColor(255, 0, 0); // 红色
JL03_PlayVoice(3);
break;
default:
RGB_LED_SetColor(255, 255, 255); // 白色
JL03_PlayVoice(4);
break;
}
// 发送数据到云
char buffer[50];
sprintf(buffer, "Type: %d, R: %d, G: %d, B: %d", type, avg_r, avg_g, avg_b);
ESP8266_SendData(buffer);
}
void JL03_PlayVoice(uint8_t type) {
// JL-03通过串口发送命令,假设命令为播放指定音轨
char command[10];
sprintf(command, "PLAY%drn", type);
for (int i = 0; command[i] != ' '; i++) {
while (!(USART2->SR & USART_SR_TXE));
USART2->DR = command[i];
}
}
void RGB_LED_SetColor(uint8_t red, uint8_t green, uint8_t blue) {
if (red) GPIOA->BSRR = RGB_LED_R_PIN;
else GPIOA->BRR = RGB_LED_R_PIN;
if (green) GPIOA->BSRR = RGB_LED_G_PIN;
else GPIOA->BRR = RGB_LED_G_PIN;
if (blue) GPIOA->BSRR = RGB_LED_B_PIN;
else GPIOA->BRR = RGB_LED_B_PIN;
}
void ESP8266_SendData(const char* data) {
// 发送数据到ESP8266 via USART1
for (int i = 0; data[i] != ' '; i++) {
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = data[i];
}
// 发送换行符
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = 'r';
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = 'n';
}
void Delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 7200; i++) {
__NOP();
}
}
项目核心代码
#include "stm32f10x.h"
// 假设其他模块的头文件
#include "ov7670.h"
#include "voice.h"
#include "wifi.h"
#include "led.h"
#include "garbage_classify.h"
// 函数原型
void SystemInit(void);
void GPIO_Init(void);
void USART1_Init(void);
void USART2_Init(void);
void I2C1_Init(void);
void Delay_ms(uint32_t nTime);
int main(void)
{
// 系统初始化
SystemInit();
GPIO_Init();
USART1_Init(); // 用于ESP8266 Wi-Fi模块
USART2_Init(); // 用于JL-03语音模块
I2C1_Init(); // 用于OV7670配置
// 模块初始化
ov7670_init();
voice_init();
wifi_init();
led_init();
// 垃圾类型变量
uint8_t garbage_type;
while (1)
{
// 采集垃圾图像
ov7670_capture();
// 进行垃圾类型识别(假设算法已实现)
garbage_type = garbage_classify();
// 根据类型控制RGB LED
led_set_color(garbage_type);
// 播放语音提示
voice_play(garbage_type);
// 通过Wi-Fi发送数据到华为云
wifi_send_data(garbage_type);
// 延迟一段时间后再进行下一次采集
Delay_ms(5000); // 5秒延迟
}
}
// 系统时钟初始化:使用内部HSI 8MHz时钟
void SystemInit(void)
{
// 启用时钟控制寄存器
RCC->CR |= 0x00000001; // 开启HSI
while (!(RCC->CR & 0x00000002)); // 等待HSI就绪
// 配置Flash预取指和延迟
FLASH->ACR = 0x12; // 2等待状态,预取指启用
// 配置AHB、APB1、APB2分频
RCC->CFGR = 0x00000000; // AHB不分频,APB1/APB2不分频
// 设置系统时钟源为HSI
RCC->CFGR |= 0x00000000; // SWS为HSI
}
// GPIO初始化:用于LED、摄像头等
void GPIO_Init(void)
{
// 启用GPIOA、GPIOB、GPIOC时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
// 配置LED引脚(假设RGB LED在PC0、PC1、PC2)
GPIOC->CRL &= ~(0xFFF << 0); // 清除PC0、PC1、PC2配置
GPIOC->CRL |= (0x02 << 0) | (0x02 << 4) | (0x02 << 8); // PC0、PC1、PC2推挽输出,50MHz
// 配置OV7670相关引脚(假设数据线在PA0-PA7,其他控制引脚)
// 这里简化,实际根据硬件连接配置
GPIOA->CRL = 0x88888888; // PA0-PA7输入模式,上拉/下拉根据需要
GPIOA->ODR = 0x00FF; // 上拉电阻启用(如果需要)
// 配置其他引脚,如语音模块和Wi-Fi模块的UART引脚
// USART1 TX (PA9) and RX (PA10)
GPIOA->CRH &= ~(0xFF0 << 4); // 清除PA9和PA10配置
GPIOA->CRH |= (0x0B << 4) | (0x04 << 8); // PA9推挽输出(TX),PA10浮空输入(RX)
// USART2 TX (PA2) and RX (PA3)
GPIOA->CRL &= ~(0xFF << 8); // 清除PA2和PA3配置
GPIOA->CRL |= (0x0B << 8) | (0x04 << 12); // PA2推挽输出(TX),PA3浮空输入(RX)
// I2C1 SCL (PB6) and SDA (PB7)
GPIOB->CRL &= ~(0xFF << 24); // 清除PB6和PB7配置
GPIOB->CRL |= (0x0C << 24) | (0x0C << 28); // PB6和PB7开漏输出,50MHz
}
// USART1初始化:用于ESP8266 Wi-Fi模块,波特率115200
void USART1_Init(void)
{
// 启用USART1时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// 配置波特率:假设系统时钟8MHz,115200 Baud
// BRR = fCK / Baud = 8000000 / 115200 ≈ 69.444
USART1->BRR = 0x0045; // 整数部分69,小数部分4*16=64,但计算后为0x0045(69.0625)
// 启用发送和接收,8数据位,无奇偶校验,1停止位
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
// USART2初始化:用于JL-03语音模块,波特率9600
void USART2_Init(void)
{
// 启用USART2时钟
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// 配置波特率:8MHz / 9600 ≈ 833.333
USART2->BRR = 0x0341; // 整数部分833/16=52.0625,但计算后为0x0341(833)
// 启用发送和接收,8数据位,无奇偶校验,1停止位
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
// I2C1初始化:用于OV7670配置,标准模式100kHz
void I2C1_Init(void)
{
// 启用I2C1时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 配置I2C时序:假设系统时钟8MHz,标准模式100kHz
// CCR计算:fSCL = fAPB1 / (2 * CCR), 但需要设置寄存器
I2C1->CR2 = 0x08; // 设置APB1时钟为8MHz(如果APB1不分频)
I2C1->CCR = 0x50; // 设置CCR为80,因为8MHz / (2 * 80) = 50kHz,但调整到100kHz需要40,但标准模式有最小要求
// 实际计算:Thigh = CCR * TPCLK1, 但简化设置
I2C1->CCR = 0x28; // 40 decimal, 8MHz / (2 * 40) = 100kHz
I2C1->TRISE = 0x09; // 最大上升时间,根据APB1时钟设置
// 启用I2C1
I2C1->CR1 = I2C_CR1_PE;
}
// 简单延迟函数,基于SysTick或循环
void Delay_ms(uint32_t nTime)
{
// 使用SysTick或简单循环延迟(这里用循环,假设系统时钟8MHz)
for (uint32_t i = 0; i < nTime * 8000; i++);
}
// 其他模块的函数实现假设在外部文件,这里不包含
总结
本系统基于STM32F103C8T6核心板设计,成功实现了智能垃圾分类指导功能,通过集成图像采集、本地处理、语音提示和云平台连接,为用户提供便捷的分类辅助。该系统不仅提升了垃圾分类的准确性和效率,还体现了嵌入式系统在环保领域的创新应用。
硬件组成上,系统采用OV7670摄像头模块采集垃圾图像特征,JL-03语音模块播放分类指导提示,RGB LED灯带提供视觉颜色指示,ESP8266-01S Wi-Fi模块实现与华为云平台的数据传输,所有组件通过洞洞板焊接和杜邦线连接,确保了结构的紧凑性和可靠性。
软件方面,本地算法负责垃圾类型识别,QT上位机显示识别记录和统计数据分析,实现了从图像采集到云数据管理的完整流程。这种设计不仅降低了系统成本,还增强了实时性和可扩展性。
总体而言,该系统具有较高的实用性和推广价值,适用于家庭、社区等场景,助力垃圾分类政策的实施。未来可通过算法优化和功能扩展,进一步提升系统性能和用户体验。
2061
