文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 应用场景](#1.2 应用场景)
- [1.3 读者收获](#1.3 读者收获)
- [1.4 技术栈](#1.4 技术栈)
- 二、环境准备
-
- [2.1 硬件准备](#2.1 硬件准备)
- [2.2 软件安装](#2.2 软件安装)
- 三、系统架构设计
-
- [3.1 系统总体架构](#3.1 系统总体架构)
- [3.2 机柜硬件结构](#3.2 机柜硬件结构)
- [3.3 硬件连接](#3.3 硬件连接)
- 四、核心代码实现
-
- [4.1 系统配置文件](#4.1 系统配置文件)
- [4.2 电磁锁控制模块](#4.2 电磁锁控制模块)
- [4.3 仓位检测模块](#4.3 仓位检测模块)
- [4.4 租借流程控制](#4.4 租借流程控制)
- [4.5 主程序](#4.5 主程序)
- 五、故障排查与问题解决
-
- [5.1 电磁锁问题](#5.1 电磁锁问题)
- 六、测试验证
-
- [6.1 功能测试](#6.1 功能测试)
- 七、总结
-
- [7.1 核心知识点回顾](#7.1 核心知识点回顾)
- [7.2 扩展方向](#7.2 扩展方向)
一、前言
1.1 技术背景
共享充电宝作为移动互联网时代的新兴服务,已广泛应用于商场、餐厅、车站等公共场所。传统充电宝租借设备存在管理效率低、计费不精准、用户体验差等问题。基于STM32的智能充电宝管理系统通过集成多种传感器、实现精准计费、提供友好的用户交互,能够大幅提升运营效率和用户体验。
STM32F103微控制器凭借其丰富的外设接口、可靠的性能和低成本优势,非常适合用于共享充电宝机柜的控制核心。
1.2 应用场景
本系统适用于以下场景:
- 商场/购物中心充电宝租借点
- 餐厅/咖啡厅桌面充电站
- 车站/机场候机楼充电柜
- 景区/公园便民充电点
1.3 读者收获
完成本教程后,你将能够:
- 掌握电磁锁控制与状态检测
- 实现充电宝在位检测与电量监测
- 设计扫码租借与归还流程
- 掌握OLED显示和语音播报控制
- 实现4G/WiFi联网与云端通信
1.4 技术栈
硬件平台:
- 主控芯片:STM32F103C8T6
- 锁控模块:电磁锁 × 6(6仓位机柜)
- 检测模块:霍尔传感器(仓位检测)
- 电量检测:ADC分压电路(充电宝电量)
- 显示模块:OLED 128x64
- 语音模块:JQ8400语音播报模块
- 通信模块:ESP8266 WiFi / Air724 4G模块
- 扫码模块:二维码扫描头(可选)
软件工具:
- 开发环境:Keil MDK-ARM 5
- 固件库:STM32标准外设库
- 编程语言:C语言
二、环境准备
2.1 硬件准备
核心模块:
- STM32F103C8T6最小系统板 × 1
- ST-Link V2下载器 × 1
锁控与检测:
- 电磁锁(5V/2A)× 6
- 霍尔传感器(A3144)× 6
- MOSFET驱动电路(IRF540N)× 6
交互模块:
- 0.96寸OLED显示屏 × 1
- JQ8400语音模块 × 1
- 按键 × 2(租借/归还)
- LED指示灯 × 6(仓位状态)
通信模块:
- ESP8266-01S WiFi模块 × 1
- 或 Air724 4G模块 × 1
2.2 软件安装
Keil MDK-ARM 安装:
- 下载并安装Keil MDK-ARM 5.38
- 安装STM32F1系列Device Family Pack
三、系统架构设计
3.1 系统总体架构
通信层
设备层
云平台
用户层
手机APP/小程序
扫码租借
在线支付
订单管理
计费系统
设备监控
数据分析
STM32F103主控
电磁锁控制
仓位检测
电量检测
OLED显示
语音播报
LED指示
4G/WiFi模块
仓位1
仓位2
仓位6
3.2 机柜硬件结构
┌─────────────────────────────────────┐
│ 共享充电宝机柜 │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 1 │ │ 2 │ │ 3 │ ← 仓位 │
│ │ 🔋 │ │ 🔋 │ │ 🔒 │ │
│ └─────┘ └─────┘ └─────┘ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 4 │ │ 5 │ │ 6 │ │
│ │ 🔋 │ │ 🔒 │ │ 🔋 │ │
│ └─────┘ └─────┘ └─────┘ │
│ │
│ [OLED显示屏] [租借] [归还] │
│ │
└─────────────────────────────────────┘
🔋 = 有充电宝 🔒 = 空仓
3.3 硬件连接
STM32F103引脚分配:
| 功能模块 | 引脚分配 | 说明 |
|---|---|---|
| 电磁锁1-6 | PB0-PB5 | 锁控输出 |
| 霍尔传感器1-6 | PA0-PA5 | 仓位检测 |
| 电量检测1-6 | ADC1_IN6-IN11 | 电量采集 |
| OLED_SDA | PB7 | I2C数据 |
| OLED_SCL | PB6 | I2C时钟 |
| 语音模块TX | PA9 | USART1 |
| 语音模块RX | PA10 | USART1 |
| WiFi_TX | PA2 | USART2 |
| WiFi_RX | PA3 | USART2 |
| 按键租借 | PB8 | 输入 |
| 按键归还 | PB9 | 输入 |
| LED1-6 | PC0-PC5 | 状态指示 |
四、核心代码实现
4.1 系统配置文件
📄 创建文件:
system_config.h
c
/**
* @file system_config.h
* @brief 共享充电宝系统配置文件
*/
#ifndef __SYSTEM_CONFIG_H
#define __SYSTEM_CONFIG_H
#include "stm32f10x.h"
/* ==================== 硬件参数 ==================== */
#define SLOT_COUNT 6 // 仓位数量
#define LOCK_OPEN_TIME 3000 // 开锁保持时间(ms)
#define LOCK_PWM_FREQ 1000 // 锁控PWM频率
/* ==================== 引脚定义 ==================== */
// 电磁锁控制引脚
#define LOCK1_PIN GPIO_Pin_0
#define LOCK2_PIN GPIO_Pin_1
#define LOCK3_PIN GPIO_Pin_2
#define LOCK4_PIN GPIO_Pin_3
#define LOCK5_PIN GPIO_Pin_4
#define LOCK6_PIN GPIO_Pin_5
#define LOCK_PORT GPIOB
// 霍尔传感器引脚
#define HALL1_PIN GPIO_Pin_0
#define HALL2_PIN GPIO_Pin_1
#define HALL3_PIN GPIO_Pin_2
#define HALL4_PIN GPIO_Pin_3
#define HALL5_PIN GPIO_Pin_4
#define HALL6_PIN GPIO_Pin_5
#define HALL_PORT GPIOA
// ADC通道(电量检测)
#define ADC_BAT1 ADC_Channel_6
#define ADC_BAT2 ADC_Channel_7
#define ADC_BAT3 ADC_Channel_8
#define ADC_BAT4 ADC_Channel_9
#define ADC_BAT5 ADC_Channel_10
#define ADC_BAT6 ADC_Channel_11
/* ==================== 数据结构 ==================== */
/**
* @brief 仓位状态枚举
*/
typedef enum {
SLOT_EMPTY = 0, // 空仓
SLOT_OCCUPIED, // 有充电宝
SLOT_LOCKED, // 锁定中
SLOT_FAULT // 故障
} SlotState_t;
/**
* @brief 仓位信息结构体
*/
typedef struct {
uint8_t slot_id; // 仓位编号
SlotState_t state; // 当前状态
uint16_t battery_voltage; // 充电宝电压(mV)
uint8_t battery_percent; // 电量百分比
uint8_t is_rented; // 是否已租借
uint32_t lock_open_time; // 开锁时间
char order_id[32]; // 订单号
} SlotInfo_t;
/**
* @brief 系统状态结构体
*/
typedef struct {
SlotInfo_t slots[SLOT_COUNT];
uint8_t available_count; // 可用仓位数
uint8_t total_count; // 总仓位数
uint8_t system_status; // 系统状态
uint32_t total_rentals; // 总租借次数
} PowerBankSystem_t;
/* ==================== 全局变量 ==================== */
extern PowerBankSystem_t g_system;
extern volatile uint32_t g_tick_ms;
#endif /* __SYSTEM_CONFIG_H */
4.2 电磁锁控制模块
📄 创建文件:
modules/lock_control.c
c
/**
* @file lock_control.c
* @brief 电磁锁控制模块
*/
#include "lock_control.h"
// 锁控引脚表
static const uint16_t s_lock_pins[SLOT_COUNT] = {
LOCK1_PIN, LOCK2_PIN, LOCK3_PIN,
LOCK4_PIN, LOCK5_PIN, LOCK6_PIN
};
/**
* @brief 电磁锁初始化
*/
void Lock_Control_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置PB0-PB5为推挽输出
GPIO_InitStruct.GPIO_Pin = LOCK1_PIN | LOCK2_PIN | LOCK3_PIN |
LOCK4_PIN | LOCK5_PIN | LOCK6_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LOCK_PORT, &GPIO_InitStruct);
// 初始状态:全部锁定(低电平)
for (int i = 0; i < SLOT_COUNT; i++) {
GPIO_ResetBits(LOCK_PORT, s_lock_pins[i]);
}
}
/**
* @brief 打开指定仓位锁
* @param slot_id 仓位编号(0-5)
* @return 0=成功,-1=失败
*/
int Lock_Control_Open(uint8_t slot_id)
{
if (slot_id >= SLOT_COUNT) return -1;
// 高电平开锁
GPIO_SetBits(LOCK_PORT, s_lock_pins[slot_id]);
// 记录开锁时间
g_system.slots[slot_id].lock_open_time = g_tick_ms;
g_system.slots[slot_id].state = SLOT_LOCKED;
return 0;
}
/**
* @brief 关闭指定仓位锁
* @param slot_id 仓位编号(0-5)
*/
void Lock_Control_Close(uint8_t slot_id)
{
if (slot_id >= SLOT_COUNT) return;
// 低电平锁定
GPIO_ResetBits(LOCK_PORT, s_lock_pins[slot_id]);
}
/**
* @brief 自动锁管理(超时自动锁定)
*/
void Lock_Control_Auto_Manage(void)
{
for (int i = 0; i < SLOT_COUNT; i++) {
if (g_system.slots[i].state == SLOT_LOCKED) {
uint32_t elapsed = g_tick_ms - g_system.slots[i].lock_open_time;
// 超过开锁时间自动锁定
if (elapsed >= LOCK_OPEN_TIME) {
Lock_Control_Close(i);
// 根据霍尔传感器状态更新仓位状态
// TODO: 读取霍尔传感器
}
}
}
}
4.3 仓位检测模块
📄 创建文件:
modules/slot_detector.c
c
/**
* @file slot_detector.c
* @brief 仓位检测模块
*/
#include "slot_detector.h"
// 霍尔传感器引脚表
static const uint16_t s_hall_pins[SLOT_COUNT] = {
HALL1_PIN, HALL2_PIN, HALL3_PIN,
HALL4_PIN, HALL5_PIN, HALL6_PIN
};
// ADC通道表
static const uint8_t s_adc_channels[SLOT_COUNT] = {
ADC_BAT1, ADC_BAT2, ADC_BAT3,
ADC_BAT4, ADC_BAT5, ADC_BAT6
};
/**
* @brief 仓位检测初始化
*/
void Slot_Detector_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
// 初始化霍尔传感器GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStruct.GPIO_Pin = HALL1_PIN | HALL2_PIN | HALL3_PIN |
HALL4_PIN | HALL5_PIN | HALL6_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(HALL_PORT, &GPIO_InitStruct);
// 初始化ADC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = ENABLE;
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = SLOT_COUNT;
ADC_Init(ADC1, &ADC_InitStruct);
// 配置ADC通道
for (int i = 0; i < SLOT_COUNT; i++) {
ADC_RegularChannelConfig(ADC1, s_adc_channels[i], i + 1,
ADC_SampleTime_239Cycles5);
}
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
/**
* @brief 检测仓位是否有充电宝
* @param slot_id 仓位编号
* @return 1=有充电宝,0=空仓
*/
uint8_t Slot_Detector_Check_Occupied(uint8_t slot_id)
{
if (slot_id >= SLOT_COUNT) return 0;
// 读取霍尔传感器(低电平表示有磁铁,即充电宝在位)
return (GPIO_ReadInputDataBit(HALL_PORT, s_hall_pins[slot_id]) == Bit_RESET);
}
/**
* @brief 读取充电宝电量
* @param slot_id 仓位编号
* @return 电压值(mV)
*/
uint16_t Slot_Detector_Read_Voltage(uint8_t slot_id)
{
// 读取ADC值并转换为电压
// 假设使用分压电阻,3.7V锂电池分压到ADC量程内
// 转换公式:Voltage = ADC * 3300 / 4095 * 分压比
// 这里简化处理,实际需要根据硬件电路计算
uint16_t adc_value = ADC_GetConversionValue(ADC1);
uint16_t voltage = (uint16_t)((uint32_t)adc_value * 3300 / 4095 * 2);
return voltage;
}
/**
* @brief 计算电量百分比
* @param voltage 电压(mV)
* @return 电量百分比(0-100)
*/
uint8_t Slot_Detector_Calc_Percent(uint16_t voltage)
{
// 锂电池电压范围:3.0V-4.2V
// 3.0V = 0%, 4.2V = 100%
if (voltage <= 3000) return 0;
if (voltage >= 4200) return 100;
return (uint8_t)((voltage - 3000) * 100 / 1200);
}
/**
* @brief 更新所有仓位状态
*/
void Slot_Detector_Update_All(void)
{
uint8_t available = 0;
for (int i = 0; i < SLOT_COUNT; i++) {
uint8_t occupied = Slot_Detector_Check_Occupied(i);
if (occupied) {
g_system.slots[i].state = SLOT_OCCUPIED;
g_system.slots[i].battery_voltage = Slot_Detector_Read_Voltage(i);
g_system.slots[i].battery_percent =
Slot_Detector_Calc_Percent(g_system.slots[i].battery_voltage);
} else {
g_system.slots[i].state = SLOT_EMPTY;
g_system.slots[i].battery_percent = 0;
}
if (g_system.slots[i].state == SLOT_EMPTY) {
available++;
}
}
g_system.available_count = available;
}
4.4 租借流程控制
📄 创建文件:
modules/rental_manager.c
c
/**
* @file rental_manager.c
* @brief 租借管理模块
*/
#include "rental_manager.h"
/**
* @brief 处理租借请求
* @param order_id 订单号
* @return 分配的仓位编号,-1表示失败
*/
int Rental_Manager_Process_Rent(const char* order_id)
{
// 查找可用仓位
int slot_id = -1;
for (int i = 0; i < SLOT_COUNT; i++) {
if (g_system.slots[i].state == SLOT_EMPTY) {
slot_id = i;
break;
}
}
if (slot_id < 0) {
// 无可用仓位
Voice_Play(VOICE_NO_AVAILABLE);
return -1;
}
// 保存订单信息
strncpy(g_system.slots[slot_id].order_id, order_id, 31);
g_system.slots[slot_id].order_id[31] = '\0';
g_system.slots[slot_id].is_rented = 1;
// 打开电磁锁
Lock_Control_Open(slot_id);
// 语音播报
Voice_Play(VOICE_PLEASE_TAKE);
// 上报云端
// TODO: 发送租借记录到服务器
g_system.total_rentals++;
return slot_id;
}
/**
* @brief 处理归还请求
* @return 归还的仓位编号,-1表示失败
*/
int Rental_Manager_Process_Return(void)
{
// 等待用户放入充电宝
// 检测哪个仓位从空变为有充电宝
for (int i = 0; i < SLOT_COUNT; i++) {
if (g_system.slots[i].state == SLOT_EMPTY) {
// 检测该仓位是否有充电宝放入
if (Slot_Detector_Check_Occupied(i)) {
// 锁定仓位
Lock_Control_Close(i);
// 清除租借状态
g_system.slots[i].is_rented = 0;
memset(g_system.slots[i].order_id, 0, 32);
// 语音播报
Voice_Play(VOICE_RETURN_SUCCESS);
// 上报云端
// TODO: 发送归还记录到服务器
return i;
}
}
}
return -1;
}
4.5 主程序
📄 创建文件:
main.c
c
/**
* @file main.c
* @brief 共享充电宝主程序
*/
#include "system_config.h"
#include "lock_control.h"
#include "slot_detector.h"
#include "rental_manager.h"
PowerBankSystem_t g_system = {0};
volatile uint32_t g_tick_ms = 0;
void SysTick_Handler(void) { g_tick_ms++; }
uint32_t Get_Tick(void) { return g_tick_ms; }
void Delay_ms(uint32_t ms) {
uint32_t start = g_tick_ms;
while ((g_tick_ms - start) < ms);
}
void System_Init(void)
{
SysTick_Config(SystemCoreClock / 1000);
Lock_Control_Init();
Slot_Detector_Init();
g_system.total_count = SLOT_COUNT;
printf("Power Bank System Started!\r\n");
}
int main(void)
{
System_Init();
while (1) {
// 更新仓位状态
Slot_Detector_Update_All();
// 自动锁管理
Lock_Control_Auto_Manage();
// TODO: 处理扫码租借/归还
// TODO: 更新显示
// TODO: 与云端通信
Delay_ms(100);
}
}
五、故障排查与问题解决
5.1 电磁锁问题
问题1:电磁锁无法打开
原因分析:
- 电源功率不足
- MOSFET驱动电路故障
- 电磁锁机械卡死
解决方案:
电源要求:
- 电磁锁启动电流:2-3A
- 需要独立5V/10A电源供电
- STM32只输出控制信号,不直接驱动
驱动电路:
STM32 GPIO → 光耦隔离 → MOSFET驱动 → 电磁锁
六、测试验证
6.1 功能测试
测试1:仓位检测测试
c
void Test_Slot_Detection(void) {
printf("Slot Detection Test\r\n");
for (int i = 0; i < SLOT_COUNT; i++) {
uint8_t occupied = Slot_Detector_Check_Occupied(i);
uint16_t voltage = Slot_Detector_Read_Voltage(i);
printf("Slot %d: %s, Voltage: %dmV\r\n",
i + 1, occupied ? "Occupied" : "Empty", voltage);
}
}
七、总结
7.1 核心知识点回顾
通过本教程,我们学习了:
- 电磁锁控制:大功率负载的驱动方法
- 传感器应用:霍尔传感器和ADC电量检测
- 租借流程设计:完整的借还业务逻辑
- 物联网通信:设备与云端的交互
- 商业产品设计:可靠性、安全性考虑
7.2 扩展方向
功能扩展:
- 添加广告屏显示
- 集成POS机支付
- 会员积分系统
- 故障自动上报
学习资源: