【SG90模拟舵机控制及PCA9685模块的使用】[通俗易懂]

【SG90模拟舵机控制及PCA9685模块的使用】[通俗易懂]一.模拟舵机控制1.简介9g模拟舵机在市面上十分常见,价格也比较便宜。可用于航模,机器人或智能小车等。如上图所示,一个舵机有三条线:VCC、GND和信号线。只要通过信号线给予规定的控制信号即可实现舵机码盘的转动。2.控制信号对于此种模拟舵机的控制是通过发送PWM。…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

一.模拟舵机控制

在这里插入图片描述
在这里插入图片描述
网上不乏对此种舵机的介绍,比如下面这篇文章:
浅谈用单片机控制SG90舵机(原理+编程)

1.简介

SG90模拟舵机在市面上十分常见,价格也比较便宜。常用于航模,机器人或智能小车等。
如上图所示,一个舵机有三条线:VCC、GND和信号线。只要通过信号线给予规定的控制信号即可实现舵机码盘的转动。

2.控制信号

对于此种模拟舵机的控制是通过向信号线持续发送PWM信号,直到舵机转到指定位置(对于数字舵机只需发送一次目标角度的信号)。透过蓝色的舵机外壳可以看到里面有一块很小的电路板,它便是用来将PWM信号转换成舵机的实际转动。

一般PWM信号的周期为20ms(50Hz的频率),想要控制角度只需控制一个周期中高电平持续的时间。以180°舵机为例,对应关系如下:

一个周期中高电平持续时间 舵机保持的角度
0.5ms
1ms 45°
1.5ms 90°
2ms 135°
2.5ms 180°

其他在0°~180°之间的角度通过上表类推即可。(但需要注意舵机的死区时间
需要理解注意的是:对于舵机而言,所有提到的角度均为绝对角度,而非相对角度。即每个角度所对应的是一个固定的位置,并而不是像步进电机那样相对当前位置转动一定角度。

二.PCA9685模块

也可参考下面这篇博客:
16路12位PWM控制器 PCA9685
后续的介绍借鉴了此篇文章。

1.简介

通过之前对舵机的介绍会发现,控制一个舵机需要占用三个引脚,其中一个为单片机的I/O脚。当所需使用的舵机数量较多时,十分占用引脚资源。此时PCA9685模块便可以发挥作用。

PCA9685芯片设计之初是用来控制多路LED灯,后来发现在控制多路舵机上也可发挥很大作用。因此网上所购买的PCA9685模块大都是以控制多路舵机而设计的。如下图所示:
在这里插入图片描述
PCA9685芯片内置了25MHz的晶振,同时也提供外部晶振输入引脚(但是模块中一般不引出此脚,只能使用内部晶振)

2.模块接口介绍:

★1.PCA9685模块的通信使用的是IIC协议,如上图模块左右两侧的SDA数据线和SCL时钟线

★2.OE是芯片的使能引脚,低电平时使能芯片。模块中已经下拉保持低电平,因此使用时可不连接。

★3. 上图中红色线连接的V+引脚是舵机的驱动电压,与PCA9685芯片本身无关。可通过左右两侧的排针接到最小系统板的标准3.3V/5V。当发现驱动电压不够,舵机无法转动或者扭力不够时,可通过上方的接线端子接入外接电源,如锂电池。但需要注意,接入的最大电压不能大于左上角电解电容的耐压值,一般为10V

★4.绿色方框便是舵机的接口,一块PCA9685芯片最多可以控制16路舵机

3.模块器件地址

在这里插入图片描述
模块的器件地址构成如上。其中最高位固定为1,最低位为读/写控制位A0~A5决定了其硬件地址,当采用多个此模块时可借此用于分别的控制。模块中对于A0~A5的设置可见上图的右上角紫色方框。6个引脚通过下拉电阻接地,因此默认全为低电平(即器件地址默认0x80。有些地方说是0x40是因为不考虑读写位,也是正确的),如果焊上框中上方的连接点,便会接到VCC,从而变为高电平。

4.相关寄存器

PCA9685的寄存器较多,使用也相对复杂,此处仅介绍控制舵机需要用到的。

①寄存器总览
寄存器地址 名称
00H MODE1
01H MODE2
02H SUBADR1
03H SUBADR2
04H SUBADR3
05H ALLCALLADR
06H LED0_ON_L
07H LED0_ON_H
08H LED0_OFF_L
09H LED0_OFF_H
··· ···
06H+4*X LEDX_ON_L
06H+4*X+1 LEDX_ON_H
06H+4*X+2 LEDX_OFF_L
06H+4*X+3 LEDX_OFF_H
FA ALL_LED_ON_L
FB ALL_LED_ON_H
FC ALL_LED_OFF_L
FD ALL_LED_OFF_H
FE PRE_SCALE
FF TestMode
②MODE1模式配置寄存器1
SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
MODE1 00H name RESTART EXTCLK AI SLEEP SUB1 SUB2 SUB3 ALLCALL

RESTART:0—不复位,1—复位,完成复位后会自动清零;要在SLEEP置0后至少500us以后才能进行复位。

EXTCLK:0—使用内部时钟,1—使用外部时钟;修改此位时,需要先将SLEEP位置1

AI:0—读写后寄存器地址不自动递增,1—读写后寄存器地址自动递增;一般设置自动递增

SLEEP:0—退出SLEEP模式,1—进入SLEEP模式。设置EXTCLK位和PRE_SCALE寄存器前都需先进入SLEEP模式

ALLCALL:0—不响应0x70通用IIC地址,1—相应0x70通用IIC地址。

③PRE_SCALE寄存器

p r e s c a l e   v a l u e = r o u n d ( o s c _ c l o c k 4096 ∗ u p d a t e _ r a t e ) − 1 prescale ~value=round(\frac{osc\_clock}{4096*update\_rate})-1 prescale value=round(4096update_rateosc_clock)1

osc_clock为选用的时钟频率,如使用内部25MHz时钟,即为25 000 000。
update_rate为PWM的频率,如前面说舵机PWM周期为20ms,则update_rate=50Hz。
4096是因为计数器是12位。

经过上式计算可发现,PRE_SCALE中所存的值实际是计数器ACK每自加1,需要的时钟脉冲个数。其实就是时钟分频后计数。和51或stm32的定时器原理类似。

④每个通道的四个寄存器

由之前的寄存器总览表中可看出:16个通道中,每个都有LEDX_ON_L、LEDX_ON_H、LEDX_OFF_L、LEDX_OFF_H 四个寄存器

芯片中12位的计数器ACK,会根据PRE_SCALE设置的值进行计数。
当LEDX_ON_H[3:0]:LEDX_ON_L < ACK < LEDX_OFF_H[3:0]:LEDX_OFF_L时,输出高电平;
当LEDX_OFF_H[3:0]:LEDX_OFF_L < ACK < 4096时,输出低电平;

实际应用中有误差,需要校准,把update_rate乘0.915

5.以stc15单片机为例代码

以上提到的一些要点均会体现在下方的代码中。

★需要注意的是:此模块同一时刻只能改变一个PWM输出,因此控制多个舵机时,只能依次控制,并不能实现多个同步控制。当然,如果使用上面寄存器表中给出的ALL_LED_ON/OFF_L/H四个寄存器,也可以实现所有舵机一起转动,但是转动角度只能是全部相同。

#include <stc15.h> 
#include <intrins.h> 
#include <stdio.h>
#include <math.h>

typedef  unsigned char  u8;        
typedef  unsigned int   u16;        

sbit SCL=P2^0;                   //时钟线
sbit SDA=P2^1;                   //数据线

#define DELAY_TIME 5 //延时时间
#define PCA9685_adrr 0x80 //1+A5+A4+A3+A2+A1+A0+w/r 

#define PCA9685_SUBADR1 0x02
#define PCA9685_SUBADR2 0x03
#define PCA9685_SUBADR3 0x04

#define PCA9685_MODE1 0x00 //MODE1寄存器地址
#define PCA9685_PRESCALE 0xFE //PRE_SCALE寄存器地址

#define LED0_ON_L 0x06 //通道0的四个控制寄存器
#define LED0_ON_H 0x07
#define LED0_OFF_L 0x08
#define LED0_OFF_H 0x09

#define ALLLED_ON_L 0xFA //全部通道的四个控制寄存器
#define ALLLED_ON_H 0xFB //舵机控制时一般不用
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD

#define SERVO000 130 //0度对应4096的脉宽计数值
#define SERVO180 520 //180度对应4096的脉宽计算值
								//每个舵机都会有一定差异,需要实际测试。

/*-----------------------IIC协议-------------------------*/

/********************************************************* 函数功能:毫秒延时函数 *********************************************************/
void delayms(u16 z)
{ 
   
  u16 x,y;
  for(x=z;x>0;x--)
      for(y=148;y>0;y--);
}

/********************************************************* 函数功能:IIC微秒延时函数,晶振12M,指令周期1T *********************************************************/
void IIC_Delay(unsigned char i)
{ 
   
    do
	{ 
   
		_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
	}while(i--);      
}

/********************************************************* 函数功能:IIC启动 *********************************************************/
void IIC_Start(void)
{ 
   
	SDA = 1;
	SCL = 1;				
	IIC_Delay(DELAY_TIME);
	SDA = 0;
	IIC_Delay(DELAY_TIME);
	SCL = 0;	
}

/********************************************************* 函数功能:IIC停止 *********************************************************/
void IIC_Stop(void)
{ 
   
	SDA = 0;
	SCL = 1;				
	IIC_Delay(DELAY_TIME);
	SDA = 1;
}

/********************************************************* 函数功能:等待从机应答 *********************************************************/
bit IIC_WaitAck(void)
{ 
    
	SDA = 1;
	IIC_Delay(DELAY_TIME);
	SCL = 1;
	IIC_Delay(DELAY_TIME);
	if(SDA)    
	{ 
      
		SCL = 0;
		IIC_Stop();
		return 0;
	}
	else  
	{ 
    
		SCL = 0;
		return 1;
	}
}

/********************************************************* 函数功能:IIC发送一个字节 *********************************************************/
void IIC_SendByte(unsigned char byt)
{ 
   
	unsigned char i;
	for(i=0;i<8;i++)
	{ 
      
		if(byt&0x80) 
		{ 
   	
			SDA = 1;
		}
		else 
		{ 
   
			SDA = 0;
		}
		IIC_Delay(DELAY_TIME);
		SCL = 1;
		byt <<= 1;
		IIC_Delay(DELAY_TIME);
		SCL = 0;
	}
}

/********************************************************* 函数功能:IIC接收一个字节 *********************************************************/
unsigned char IIC_RecByte(void)
{ 
   
	unsigned char da;
	unsigned char i;
	
	for(i=0;i<8;i++)
	{ 
      
		SCL = 1;
		IIC_Delay(DELAY_TIME);
		da <<= 1;
		if(SDA) 
		da |= 0x01;
		SCL = 0;
		IIC_Delay(DELAY_TIME);
	}
	return da;
}

/*-----------------------PCA9685模块相关函数-------------------------*/

/********************************************************* 函数功能:向PCA9685的一个地址写数据 *********************************************************/
void PCA9685_write(u8 address,u8 date)
{ 
   
	IIC_Start();
	IIC_SendByte(PCA9685_adrr);   //PCA9685的片选地址
	IIC_WaitAck();                          
	IIC_SendByte(address);  	  //写地址控制字节
	IIC_WaitAck();
	IIC_SendByte(date);           //写数据
	IIC_WaitAck();
	IIC_Stop();
}

/********************************************************* 函数功能:从PCA9685的一个地址读数据 *********************************************************/
u8 PCA9685_read(u8 address)
{ 
   
	u8 date;
	IIC_Start();
	IIC_SendByte(PCA9685_adrr); //PCA9685的片选地址
	IIC_WaitAck();
	IIC_SendByte(address);
	IIC_WaitAck();

	IIC_Start();
	IIC_SendByte(PCA9685_adrr|0x01);        //地址的第八位控制数据流方向,就是写或读
	IIC_WaitAck();
	date=IIC_RecByte();
	IIC_Stop();
	return date;
}

/********************************************************* 函数功能:PCA9685的MODE1寄存器清零 *********************************************************/
void reset(void) 
{ 
   
	PCA9685_write(PCA9685_MODE1,0x00);
}

/********************************************************* 函数功能:PCA9685频率修改 入口参数:freq-输出PWM频率 *********************************************************/
void setPWMFreq(float freq) 
{ 
   
	u16 prescale,oldmode,newmode;
	float prescaleval;
	
	freq *= 0.92;   							//纠正频率设置中的过冲,进行校准 
	prescaleval = 25000000;						//根据公式计算prescale的值
	prescaleval /= 4096;						//prescaleval=round(osc_cloc/4096/freq)-1;
	prescaleval /= freq;
	prescaleval -= 1;
	prescale = floor(prescaleval + 0.5);		
                
	oldmode = PCA9685_read(PCA9685_MODE1);		  //获得MODE1寄存器值
	newmode = (oldmode&0xEF) | 0x10; 			  //SLEEP位 置1
	PCA9685_write(PCA9685_MODE1, newmode);  	  //进入SLEEP模式
	PCA9685_write(PCA9685_PRESCALE, prescale);    //设置频率
	PCA9685_write(PCA9685_MODE1, oldmode);		  //退出SLEEP模式
	delayms(2);
	PCA9685_write(PCA9685_MODE1, oldmode | 0xa1); //RESTART、AI、ALLCALL三个位 置1
}

/********************************************************* 函数功能:改变通道PWM占空比 输入参数:num-使用的通道0~15 on-高电平开始时计数器ACK值 off-高电平结束时计数器ACK值 *********************************************************/
void setPWM(u16 num, u16 on, u16 off) 
{ 
   
	PCA9685_write(LED0_ON_L+4*num,on);		//LED0_ON_L保存on低8位
	PCA9685_write(LED0_ON_H+4*num,on>>8);	//LED0_ON_H保存on高4位
	PCA9685_write(LED0_OFF_L+4*num,off);
	PCA9685_write(LED0_OFF_H+4*num,off>>8);
 }
 
/*-----------------------主函数-------------------------*/
void main()
{ 
   
	reset();
	setPWMFreq(50);  //设置频率50Hz
	//以转动到60°位置为例:
	//60度对应的脉宽=0.5ms+(60/180)*(2.5ms-0.5ms)=1.1666ms
	//利用占空比=1.1666ms/20ms=off/4096,off=239,50hz对应周期20ms
	//setPWM(num,0,239);
	while(1) 
	{ 
   
		setPWM(0, 0, 239);
		setPWM(1, 0, SERVO000);
	}                
}

Jetbrains全家桶1年46,售后保障稳定

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203716.html原文链接:https://javaforall.net

(0)
上一篇 2025年8月10日 上午11:15
下一篇 2025年8月10日 上午11:43


相关推荐

  • 关系数据库设计理论中,起核心作用的是_关系数据库设计理论主要包括

    关系数据库设计理论中,起核心作用的是_关系数据库设计理论主要包括关系数据库设计理论设计一个好的关系数据库系统,关键是要设计一个好的数据库模式(数据库逻辑设计问题)数据库逻辑设计主要解决的问题关系数据库应该组织成几个关系模式关系模式中包括哪些属性“不好”的数据库设计举例:为学校设计一个关系数据库关系模式:UN(Sno,Cno,G,Sdept,MN)Sno:描述学生Sdept:描述系名MN:描述系…

    2022年10月16日
    5
  • ASP.NET中进行消息处理(MSMQ) 一 (转)

    ASP.NET中进行消息处理(MSMQ) 一 (转)

    2021年7月28日
    59
  • erlang lists操作

    erlang lists操作转自 http blog csdn net cntway article details nbsp 一 带函数 Pred1 all Pred List boolean 如果 List 中的每个元素作为 Pred 函数的参数执行 结果都返回 true 那么 all 函数返回 true 否则返回 false 例子 lists all fun E trueend

    2026年2月24日
    20
  • 【WPS自动签到】利用云函数实现WPS自动签到获得WPS免费会员

    【WPS自动签到】利用云函数实现WPS自动签到获得WPS免费会员前言 WPS 是一款经常用的软件 但是会员又不想买 有些功能还必须会员使用 本来 WPS 是有一个 打卡签到 可以免费领取会员的 但是每天总是忘 因此想到利用 云函数 Server 酱实现基于 Python 的自动打卡签到 这样每天就会自动获得会员了 效果还不错 由于内置了一些小号来接受打卡邀请 因此我们的账号还可获得邀请奖励 2020 07 17 日亲测是每天最少获得 11 天的会员 下面是这几天的实测效果 2020 07 17 亲测有效 2020 07 18 又获得 11 天会员一 需要的一些配

    2026年3月19日
    3
  • Kotlin学习之路(3)控制语句

    Kotlin学习之路(3)控制语句

    2021年3月12日
    155
  • 苹果新旧手机数据转移_换机必备知识:如何将数据转移到Oppo手机上

    苹果新旧手机数据转移_换机必备知识:如何将数据转移到Oppo手机上现在的智能手机越来越便宜了,换手机是经常的事情。但唯一的缺点是更换手机时新旧手机的数据备份很麻烦。许多人会选择将数据传输到计算机,然后再传输到新手机。或者,用户将可以备份的内容备份到microSD卡上。但这些方法都比较老土。如今,智能手机制造商拥有专用的应用程序,可以使此过程变得轻松,高效和无缝。本指南将教您如何将所有个人数据(SMS,电话,应用程序,照片等)从旧手机转移到Oppo品牌的手机上。…

    2022年5月25日
    228

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号