通过51单片机控制SG90舵机按角度正反转转动

一、前言

本文介绍如何通过51单片机控制SG90舵机实现角度的正反转转动。SG90舵机是一种常用的微型舵机,具有体积小、重量轻、结构简单等特点,被广泛应用于机器人、遥控模型和各种自动控制系统中。

使用51单片机(STC89C52)作为控制器,利用其强大的IO口和定时器功能来实现对SG90舵机的控制。通过编程控制,可以精确地控制舵机按指定的角度进行正转或反转运动。

舵机的控制是通过脉冲宽度调制(PWM)来实现的。在控制舵机时,需要向舵机发送一系列的脉冲信号,脉冲的宽度决定了舵机的角度位置。通常情况下,SG90舵机的控制脉冲周期为20毫秒,脉冲宽度在0.5毫秒到2.5毫秒之间,对应的角度范围为0度到180度。

为了实现舵机的正反转转动,需要控制脉冲的宽度在不同的范围内,以达到不同的角度位置。通过调整脉冲的宽度和周期,我们可以控制舵机按照我们的要求进行旋转。

下面将介绍如何通过51单片机的IO口和定时器来生成适用于SG90舵机的PWM信号。编写相应的程序,通过调整脉冲宽度来实现舵机的正反转转动,并提供示例代码。

二、SG90电机介绍

SG90电机是一种微型舵机,常用于模型、机器人和其他小型机械装置中。

1. 工作原理: SG90电机基于直流电机的原理,通过PWM(脉宽调制)信号控制舵机转动角度。它由一个电机、减速齿轮组和一个位置反馈电路组成。该反馈电路使用了一个电位器来检测舵机的当前位置并将其反馈给控制电路。

2. 特点:

  • 尺寸小巧:SG90电机非常小巧,体积轻盈,适合于空间有限的应用。
  • 转动角度范围广:通常情况下,SG90电机可以转动约180度左右,但具体转动范围可以通过控制信号调整。
  • 高精度:SG90电机具有较高的转动精度和稳定性,适用于需要精确控制的应用。
  • 低功耗:SG90电机功耗较低,可以在低电压下工作。
  • 相对经济:相比大型舵机或步进电机,SG90电机价格相对较低,适合在预算有限的项目中使用。

3. 控制方式: 控制SG90电机需要提供PWM信号。以下是控制SG90电机的基本步骤:

  • 将SG90电机的VCC引脚连接到正电源(通常为5V),将GND引脚连接到地。
  • 将信号线(例如,控制舵机角度的引脚)连接到微控制器或其他控制设备的数字输出引脚。
  • 在控制设备上设置指定的PWM输出引脚,并使用相应的编程语言或库发送PWM信号。PWM的工作周期通常为20ms,并且脉宽的范围可以在0.5ms到2.5ms之间调整。
  • 根据所发送的PWM信号,SG90电机会转动到相应的角度位置。一般来说,0.5ms的脉宽对应最左端角度,2.5ms的脉宽对应最右端角度,1.5ms的脉宽对应中间位置。具体的脉宽范围和对应的角度可以根据电机型号和要求进行调整。

SG90电机的额定工作电压为4.8V-6V,超过这个范围可能会损坏电机。舵机在运行时会产生一定的电流峰值,在使用时应确保电源能够提供足够的电流。

三、实现代码

3.1 正反转实现-模拟延时

以下是通过51单片机控制SG90舵机按角度正反转转动的实现代码,封装子函数调用:

cpp 复制代码
#include <reg51.h>
​
// 定义IO口连接舵机的引脚
sbit servoPin = P1^0;
​
// 延时函数
void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++) {
        for(j = 0; j < 1000; j++);
    }
}
​
// 控制舵机按指定角度进行正转
void rotateClockwise(unsigned int angle) {
    unsigned int pulseWidth = 500 + angle * 11.11;
    unsigned int i;
    for(i = 0; i < 50; i++) {
        servoPin = 1; // 输出高电平
        delay(pulseWidth);
        servoPin = 0; // 输出低电平
        delay(20000 - pulseWidth);
    }
}
​
// 控制舵机按指定角度进行反转
void rotateCounterclockwise(unsigned int angle) {
    unsigned int pulseWidth = 2500 - angle * 11.11;
    unsigned int i;
    for(i = 0; i < 50; i++) {
        servoPin = 1; // 输出高电平
        delay(pulseWidth);
        servoPin = 0; // 输出低电平
        delay(20000 - pulseWidth);
    }
}
​
void main() {
    while(1) {
        // 正转90度
        rotateClockwise(90);
        delay(2000); // 停留2秒
​
        // 反转90度
        rotateCounterclockwise(90);
        delay(2000); // 停留2秒
    }
}

3.2 正反转角度控制-PWM控制

下面是使用STC89C52的定时器0和GPIO口来模拟产生PWM信号的实现代码:

cpp 复制代码
#include <reg52.h>
​
#define FREQ_OSC 11059200UL   // 单片机工作频率
#define PWM_FREQ 50           // PWM信号频率
#define PWM_RESOLUTION 100    // PWM信号分辨率
​
sbit Servo = P1^0;   // SG90舵机控制引脚
​
unsigned int pwmWidth = 0;   // PWM脉宽
​
// 定时器0初始化函数
void Timer0Init() {
    EA = 0;   // 关闭总中断
    TMOD &= 0xF0;   // 清除T0控制位
    TMOD |= 0x01;   // 设置T0为工作方式1(16位定时器)
    TH0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) / 256;   // 计算并设置初始计数值高8位
    TL0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) % 256;   // 计算并设置初始计数值低8位
    TR0 = 1;   // 启动定时器0
    ET0 = 1;   // 允许定时器0中断
    EA = 1;   // 开启总中断
}
​
// 定时器0中断服务函数
void Timer0Interrupt() interrupt 1 {
    if (pwmWidth > PWM_RESOLUTION) {
        Servo = 0;   // 舵机复位
    } else {
        Servo = 1;   // 舵机置位
    }
    TH0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) / 256;   // 重新设置计数值高8位
    TL0 = (65536 - (FREQ_OSC / 12 / PWM_FREQ)) % 256;   // 重新设置计数值低8位
    pwmWidth++;   // 每次中断增加PWM脉宽
}
​
// 主函数
void main() {
    Timer0Init();   // 初始化定时器0
​
    while (1) {
        if (pwmWidth > PWM_RESOLUTION) {
            pwmWidth = 0;
        }
    }
}

代码中,使用P1^0引脚作为SG90舵机的控制引脚,并通过定时器0来产生PWM信号。

在Timer0Init函数中,设置定时器0为16位定时器工作方式1,计算并设置初始计数值,启动定时器0,并允许定时器0中断。

在Timer0Interrupt函数中,每次定时器0中断时调整舵机控制引脚的电平状态,并更新定时器0的计数值。

在主函数中,循环检测PWM脉宽是否达到设定的分辨率,如果超过则重新从0开始计数。

相关推荐
程序员爱钓鱼3 分钟前
Python编程实战 - 面向对象与进阶语法 - 封装与私有属性
后端·python·ipython
IT_陈寒8 分钟前
Spring Boot 3.2性能翻倍!我仅用5个技巧就让接口响应时间从200ms降到50ms
前端·人工智能·后端
风象南19 分钟前
Spring Boot 手撸一个自助报表系统
后端
donotshow22 分钟前
Spring Boot 整合 ShedLock 处理定时任务重复
java·后端
木土雨成小小测试员22 分钟前
简单创建一个flask项目
后端·python·flask
Victor35634 分钟前
Redis(100)如何防止Redis的数据丢失?
后端
Victor35635 分钟前
Redis(101)Redis为什么是单线程的?
后端
程序员三明治2 小时前
选 Redis Stream 还是传统 MQ?队列选型全攻略(适用场景、优缺点与实践建议)
java·redis·后端·缓存·rocketmq·stream·队列
cj6341181506 小时前
【MySQL】mysqldump使用方法
java·后端
JIngJaneIL7 小时前
停车场管理|停车预约管理|基于Springboot的停车场管理系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·停车场管理系统