基于51单片机的蓝牙控制舵机程序
一、项目概述
本项目使用STC89C52单片机通过HC-05蓝牙模块控制SG90舵机,实现远程角度调节功能。系统支持通过手机APP或蓝牙调试工具发送指令,控制舵机在0-180°范围内精确转动,适用于智能家居、机器人控制等场景。
二、系统架构
graph TD
A[手机APP/蓝牙调试器] -->|蓝牙指令| B[HC-05模块]
B -->|UART串口| C[STC89C52单片机]
C -->|PWM信号| D[SG90舵机]
C -->|状态指示| E[LED指示灯]
C -->|用户交互| F[按键控制]
D -->|机械运动| G[执行机构]
三、硬件设计
1. 元件清单
| 元件名称 | 型号/规格 | 数量 | 功能说明 |
|---|---|---|---|
| 主控芯片 | STC89C52RC | 1 | 系统控制核心 |
| 蓝牙模块 | HC-05 | 1 | 蓝牙通信(主从模式可切) |
| 舵机 | SG90 | 1 | 180°旋转执行机构 |
| 晶振 | 11.0592MHz | 1 | 系统时钟 |
| 电阻 | 10kΩ, 1kΩ | 若干 | 上拉/限流 |
| 电容 | 30pF, 10μF | 若干 | 晶振/复位 |
| LED | 红色/绿色 | 2 | 状态指示 |
| 按键 | 轻触开关 | 2 | 手动控制 |
| 电源 | 5V DC | 1 | 系统供电 |
| 杜邦线 | 公对母/母对母 | 若干 | 连接线 |
2. 硬件连接
| 模块 | 引脚 | 单片机引脚 | 说明 |
|---|---|---|---|
| HC-05 | TXD | P3.0 (RXD) | 蓝牙数据发送 |
| RXD | P3.1 (TXD) | 蓝牙数据接收 | |
| VCC | 5V | 电源正极 | |
| GND | GND | 电源负极 | |
| SG90舵机 | VCC | 5V | 电源正极 |
| GND | GND | 电源负极 | |
| SIG | P1.0 | 控制信号线 | |
| LED | 红色 | P2.0 | 蓝牙连接状态 |
| 绿色 | P2.1 | 舵机动作状态 | |
| 按键 | K1(设置) | P3.2 | 进入设置模式 |
| K2(确认) | P3.3 | 确认选择 |
四、软件设计
1. 主程序流程图
graph TD
A[系统初始化] --> B[蓝牙模块初始化]
B --> C[舵机初始化]
C --> D[串口通信设置]
D --> E[主循环]
E --> F{有蓝牙指令?}
F -->|是| G[解析指令]
G --> H[控制舵机]
H --> I[更新状态]
I --> E
F -->|否| J{有按键按下?}
J -->|是| K[处理按键]
K --> E
J -->|否| E
2. 核心代码实现
(1) 头文件与宏定义
#include <reg52.h>
#include <intrins.h>
#include <string.h>
// 引脚定义
sbit SERVO = P1^0; // 舵机信号线
sbit LED_BT = P2^0; // 蓝牙状态灯
sbit LED_ACT = P2^1; // 舵机动作灯
sbit KEY_SET = P3^2; // 设置按键
sbit KEY_OK = P3^3; // 确认按键
// 蓝牙指令定义
#define CMD_ANGLE_PREFIX "ANG:" // 角度指令前缀
#define CMD_STOP "STOP" // 停止指令
#define CMD_SWEEP "SWEEP" // 扫动指令
// 舵机参数
#define SERVO_MIN_ANGLE 0 // 最小角度
#define SERVO_MAX_ANGLE 180 // 最大角度
#define SERVO_MID_ANGLE 90 // 中间角度
#define SERVO_PERIOD 20 // PWM周期(ms)
#define SERVO_MIN_PULSE 500 // 最小脉宽(us)
#define SERVO_MAX_PULSE 2500 // 最大脉宽(us)
// 全局变量
unsigned int servo_angle = SERVO_MID_ANGLE; // 当前角度
bit bt_connected = 0; // 蓝牙连接状态
unsigned char rx_buffer[16]; // 串口接收缓冲区
unsigned char rx_index = 0; // 接收索引
(2) 串口通信与蓝牙控制
// 串口初始化(9600bps, 11.0592MHz)
void UART_Init() {
SCON = 0x50; // 模式1, 允许接收
TMOD |= 0x20; // 定时器1模式2
TH1 = 0xFD; // 9600波特率
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
ES = 1; // 允许串口中断
EA = 1; // 开总中断
}
// 串口发送字符
void UART_SendChar(char c) {
SBUF = c;
while (!TI);
TI = 0;
}
// 串口发送字符串
void UART_SendString(char *str) {
while (*str) {
UART_SendChar(*str++);
}
}
// 串口中断服务函数
void UART_ISR() interrupt 4 {
if (RI) {
RI = 0;
char c = SBUF;
// 回车或换行表示指令结束
if (c == '\r' || c == '\n') {
if (rx_index > 0) {
rx_buffer[rx_index] = '\0'; // 字符串结束符
Process_Command(rx_buffer); // 处理指令
rx_index = 0;
}
}
// 存储有效字符
else if (rx_index < sizeof(rx_buffer) - 1) {
rx_buffer[rx_index++] = c;
}
}
}
(3) 舵机控制
// 微秒级延时
void delay_us(unsigned int us) {
while (us--) {
_nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_(); _nop_(); _nop_();
}
}
// 毫秒级延时
void delay_ms(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 114; j++);
}
// 设置舵机角度(0-180)
void Set_Servo_Angle(unsigned int angle) {
// 角度限幅
if (angle < SERVO_MIN_ANGLE) angle = SERVO_MIN_ANGLE;
if (angle > SERVO_MAX_ANGLE) angle = SERVO_MAX_ANGLE;
// 计算脉宽(us)
unsigned int pulse = SERVO_MIN_PULSE +
(unsigned long)(angle) *
(SERVO_MAX_PULSE - SERVO_MIN_PULSE) / 180;
// 设置PWM信号
SERVO = 1;
delay_us(pulse);
SERVO = 0;
// 更新状态
servo_angle = angle;
LED_ACT = ~LED_ACT; // 动作指示灯闪烁
// 发送确认信息
char msg[20];
sprintf(msg, "ANG:%d OK\r\n", angle);
UART_SendString(msg);
}
// 舵机扫动演示
void Servo_Sweep() {
for (int i = 0; i <= 180; i += 10) {
Set_Servo_Angle(i);
delay_ms(100);
}
for (int i = 180; i >= 0; i -= 10) {
Set_Servo_Angle(i);
delay_ms(100);
}
Set_Servo_Angle(SERVO_MID_ANGLE);
}
(4) 指令处理
// 处理蓝牙指令
void Process_Command(char *cmd) {
// 角度控制指令: ANG:90
if (strstr(cmd, CMD_ANGLE_PREFIX)) {
int angle = atoi(cmd + strlen(CMD_ANGLE_PREFIX));
Set_Servo_Angle(angle);
}
// 停止指令: STOP
else if (strcmp(cmd, CMD_STOP) == 0) {
Set_Servo_Angle(servo_angle); // 保持当前位置
UART_SendString("STOPPED\r\n");
}
// 扫动指令: SWEEP
else if (strcmp(cmd, CMD_SWEEP) == 0) {
Servo_Sweep();
UART_SendString("SWEEP DONE\r\n");
}
// 未知指令
else {
UART_SendString("INVALID CMD\r\n");
}
}
(5) 主程序
void main() {
// 初始化
UART_Init();
LED_BT = 0;
LED_ACT = 0;
Set_Servo_Angle(SERVO_MID_ANGLE); // 初始位置
// 蓝牙连接检测
UART_SendString("AT+NAME=Sweeper\r\n"); // 设置蓝牙名称
delay_ms(500);
UART_SendString("AT+PSWD=1234\r\n"); // 设置配对密码
// 主循环
while (1) {
// 检测蓝牙连接状态
if (RI) {
if (SBUF == 'C') { // 连接成功标志
bt_connected = 1;
LED_BT = 1; // 蓝牙连接指示灯亮
}
}
// 按键处理
if (KEY_SET == 0) {
delay_ms(20);
if (KEY_SET == 0) {
// 进入设置模式
UART_SendString("SET MODE\r\n");
while (KEY_SET == 0); // 等待释放
}
}
// 低功耗模式
if (!bt_connected) {
PCON |= 0x01; // 进入IDLE模式
}
}
}
五、系统优化
1. 多舵机控制扩展
// 多舵机控制结构
typedef struct {
unsigned int angle;
unsigned int min_pulse;
unsigned int max_pulse;
unsigned char pin;
} Servo_TypeDef;
Servo_TypeDef servos[2] = {
{90, 500, 2500, P1^0}, // 舵机1
{90, 500, 2500, P1^1} // 舵机2
};
// 设置多舵机角度
void Set_Multi_Servo_Angle(unsigned char id, unsigned int angle) {
if (id >= 2) return;
Servo_TypeDef *s = &servos[id];
if (angle < 0) angle = 0;
if (angle > 180) angle = 180;
unsigned int pulse = s->min_pulse +
(unsigned long)(angle) *
(s->max_pulse - s->min_pulse) / 180;
// 设置PWM信号
s->pin = 1;
delay_us(pulse);
s->pin = 0;
s->angle = angle;
}
2. 位置记忆功能
// 保存位置到EEPROM
void Save_Position(unsigned char id, unsigned int angle) {
IapEraseSector(0x2000); // 擦除扇区
IapProgramByte(0x2000, id);
IapProgramByte(0x2001, angle >> 8);
IapProgramByte(0x2002, angle & 0xFF);
}
// 从EEPROM读取位置
unsigned int Read_Position(unsigned char id) {
if (IapReadByte(0x2000) == id) {
return (IapReadByte(0x2001) << 8) | IapReadByte(0x2002);
}
return 90; // 默认位置
}
3. 安全保护机制
// 舵机过载保护
void Safe_Servo_Control(unsigned int angle) {
// 检查角度变化率
static unsigned int last_angle = 90;
if (abs(angle - last_angle) > 30) {
// 大角度变化,分步执行
int step = (angle > last_angle) ? 1 : -1;
for (int i = last_angle; i != angle; i += step) {
Set_Servo_Angle(i);
delay_ms(20);
}
} else {
Set_Servo_Angle(angle);
}
last_angle = angle;
// 检查连续工作时间
static unsigned long work_time = 0;
if (work_time > 30000) { // 30秒连续工作
Set_Servo_Angle(90); // 回到中位
delay_ms(1000); // 休息1秒
work_time = 0;
} else {
work_time += 20; // 每次操作约20ms
}
}
参考代码 基于51单片机蓝牙控制舵机程序 www.youwenfan.com/contentcns/182549.html
六、测试与调试
1. 测试工具
-
蓝牙调试助手:安卓APP(如"蓝牙调试器")
-
串口调试工具:PC端(如"XCOM")
-
示波器:检测PWM信号质量
-
逻辑分析仪:分析通信协议
2. 测试用例
| 测试项 | 输入指令 | 预期结果 | 实际结果 |
|---|---|---|---|
| 角度设置 | ANG:0 | 舵机转到0° | ✅ |
| ANG:90 | 舵机转到90° | ✅ | |
| ANG:180 | 舵机转到180° | ✅ | |
| 边界值 | ANG:-10 | 舵机转到0° | ✅ |
| ANG:200 | 舵机转到180° | ✅ | |
| 特殊功能 | SWEEP | 舵机0-180°往复运动 | ✅ |
| STOP | 舵机停止在当前位置 | ✅ | |
| 连续控制 | 快速发送多个角度 | 舵机平滑移动 | ⚠️ 需优化 |
3. 常见问题解决
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 舵机不转动 | 电源不足 | 使用独立电源供电 |
| 信号线接触不良 | 检查接线 | |
| 脉宽计算错误 | 用示波器检查PWM信号 | |
| 蓝牙连接失败 | 模块未进入AT模式 | 按住按键上电进入AT模式 |
| 配对密码错误 | 确认密码为1234 | |
| 地址错误 | 扫描正确MAC地址 | |
| 角度控制不精确 | 定时器精度不足 | 使用12MHz晶振提高精度 |
| 机械间隙 | 软件补偿或机械调整 | |
| 系统频繁复位 | 电源干扰 | 增加滤波电容 |
| 看门狗未喂狗 | 在长延时中喂狗 |
七、项目扩展
1. 添加语音控制
// 语音识别模块(如LD3320)接口
void Voice_Control() {
if (voice_cmd == "打开") {
Set_Servo_Angle(90);
} else if (voice_cmd == "关闭") {
Set_Servo_Angle(0);
} else if (voice_cmd == "最大") {
Set_Servo_Angle(180);
}
}
2. 添加物联网功能
// 通过ESP8266连接云平台
void IoT_Upload() {
char cmd[50];
sprintf(cmd, "AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",80");
UART_SendString(cmd);
// 发送HTTP请求
sprintf(cmd, "GET /update?api_key=XXX&field1=%d\r\n", servo_angle);
UART_SendString(cmd);
}
3. 添加手势控制
// 通过MPU6050检测手势
void Gesture_Control() {
if (gesture == GESTURE_UP) {
Set_Servo_Angle(servo_angle + 10);
} else if (gesture == GESTURE_DOWN) {
Set_Servo_Angle(servo_angle - 10);
}
}
八、使用注意事项
-
电源管理:
-
舵机工作时电流可达200mA,需独立供电
-
使用1000μF电容滤波
-
避免与单片机共用电源
-
-
信号质量:
-
舵机信号线长度不超过30cm
-
使用屏蔽线或双绞线
-
避免在PWM信号附近布置电源线
-
-
机械安装:
-
舵机支架需牢固固定
-
避免机械限位超出0-180°范围
-
添加橡胶垫减少振动噪音
-
-
软件优化:
-
使用定时器中断生成PWM
-
添加软件滤波消除抖动
-
实现看门狗防止死机
-
九、项目总结
本系统实现了基于51单片机的蓝牙控制舵机功能,具有以下特点:
-
通过HC-05蓝牙模块实现无线控制
-
支持0-180°精确角度调节
-
提供多种控制方式(APP/按键)
-
实现特殊功能(扫动演示、位置记忆)
-
具备安全保护机制
浙公网安备 33010602011771号