PCA9685芯片,每一路LED输出端均可自由调节PWM波的频率 (40~1000Hz) 和占空比 (0%~100%) 。这款芯片主要通过输出不同占空比的PWM脉冲信号来控制舵机转动的角度。是16通道12bit PWM舵机驱动,用2个引脚通过I2C就可以驱动16个舵机。

模块的样子:

使用电路:

这里我使用的是电路,没有买模块;电路已在嘉立创打板验证成功。(嘉立创牛批!)

PCA9685系统框图

既然是IIC,那么IIC的地址是什么?

PCA9685 是一个I2C 从设备,有个设备ID,或者叫从 地址。从地址是如下确定的:

Board 0:  Address = 0×40 Offset = binary 00000 (默认)

Board 1:  Address = 0×41  Offset = binary 00001 (A0接上拉)

Board 2:  Address = 0×42  Offset = binary 00010 (接上A1上拉)

Board 3:  Address = 0×43  Offset = binary 00011 (A0和A1上拉)

Board 4:  Address = 0×44  Offset = binary 00100 (A2上拉)

以此类推;

PCA9685的I2 c总线从地址如下图所示。为了节约电力,硬件可选地址引脚上没有内部上拉电阻,它们必须被拉高或拉低。

地址字节的最后一位定义要执行的操作。当设置为逻辑1时,将选择读操作,而逻辑0则选择写操作

在电路中,

我的地址线全部接0,所以我的slave address是0x40。

对应Fig 4上的位置,则为

固定位 A5 A4 A3 A2 A1 A0 R/W
1 0 0 0 0 0 0 X

则IIC地址是 0x80 ,写入时是0x80,读取时是0x81。

设置PWM频率

舵机控制所需的 PWM 周期为20 ms. 在用 PCA9685 作为多舵机控制器时,需要将 其 PWM 输出周期设定为20 ms,即PWM 波的频率设定为50 Hz,PCA9685 输出频率与振荡器有关,频率的设置值refresh_rate见下面的公式;

其中,EXTCLK是PCA9685的内部时钟频率为25Mhz;prescale是要设置的频率,我们设置为50Hz;

refresh_rate = 25,000,000 /( 4096 * ( 50 + 1 ))

refresh_rate = 25,000,000 / 4096 / (50 + 1)

refresh_rate  = 6,103.52 / (50 + 1)

refresh_rate  = 6,103.52 / 51

refresh_rate = 119.68

所以我们需要设置的值是119.68,取整数就是120。

需要注意的是,频率的更改只能在 PCA9685 芯片处于休眠状态下进行。

以下加粗字体是数据手册内容:

要使用EXTCLK引脚,该位必须按以下顺序设置:
1. 在mode1中设置SLEEP位。这就关闭了内部振荡器,使芯片处于休眠状态。
2. 将逻辑1写入MODE1中的SLEEP和EXTCLK位。这样就转换完成了。外部时钟可以在切换期间处于活动状态,因为设置了SLEEP位。
这个位是一个“粘性位”,也就是说,它不能通过写入逻辑0来清除。EXTCLK位只能通过电源循环或软件重置来清除。

占空比或者脉冲宽度的设定

每个PWM引脚输出的开启时间和PWM的占空比可以通过LEDn_ON和LEDn_OFF寄存器独立控制。

每个PWM引脚输出将有两个12位寄存器。这些寄存器将由用户编程。两个寄存器都将保存从0到4095的值。一个12位寄存器将保存ON时间的值,另一个12位寄存器将保存OFF时间的值。将ON和OFF时间与12位计数器的值进行比较,该计数器将从0000h持续运行到0FFFh(0到4095十进制)。

ON时间是可编程的,它是PWM输出ON的时间,OFF时间也是可编程的,它是PWM输出OFF的时间。这样相移就完全可编程了。相移的分辨率为目标频率的1 / 4096。表7列出了这些寄存器。

以下用一个例子说明如何计算要加载到这些寄存器中的值。

(假设使用LED0输出,(延时时间)+ (PWM占空比)<=100%)

延迟时间 = 10%;PWM占空比= 20% (LEDON电平= 20%;LEDOFF时间= 80%)。延迟时间= 10% = 4096 * 0.1 = 409.6 ~ 410,计数= 410(十进制) = 19Ah(十六进制)

因为计数器从0开始,到4095结束,我们将减去1,所以延迟时间 = 199h 个数。

LED0_ON_H = 1h;LED0_ON_L = 99h (LED开始打开后,这个延迟计数到409)

  • LED开机时间= 20% = 819.2 ~ 819次

  • LED关闭时间= 4CCh(十进制410 + 819-1 = 1228)

  • LED0_OFF_H = 4h;LED0_OFF_L = CCh(此计数到1228后LED开始关闭)

整个周期为4095, LED_ON 和 LED_OFF 2个的设定值确定脉宽,在后面的代码里,LED_ON 设为0, LED_OFF就是脉宽了。 这里都用2位字节来表示。

相关地址表

这里只截图了需要的地址,分别是:

#define PCA_Addr                 0x80    //IIC地址
#define PCA_Model              0x00    
#define LED0_ON_L             0x06
#define LED0_ON_H            0x07
#define LED0_OFF_L           0x08
#define LED0_OFF_H          0x09
#define PCA_Pre                  0xFE    //配置频率地址

 OK!        手册看完,到代码部分了

使用的是立创梁山派的GD32F450RGT6,IIC部分使用的是正点原子的STM32F407VET6的IIC代码。

PCA9685.c

#include "pca9685.h"
#include <math.h>void PCA9685_Init(float hz,u8 angle)
{                  u32 off = 0;IIC_Init();   //在MODE1地址上写0x00PCA9685_Write(PCA_Model,0x00);    //这一步很关键,如果没有这一步PCA9685就不会正常工作。//    pwm.setPWMFreq(SERVO_FREQ)函数主要是设置PCA9685的输出频率,
//  PCA9685的16路PWM输出频率是一致的,所以是不能实现不同引脚不同频率的。
//  下面是setPWMFreq函数的内容,主要是根据频率计算PRE_SCALE的值。PCA9685_setFreq(hz);//计算角度off = (u32)(145+angle*2.4);//控制16个舵机输出off角度PCA9685_setPWM(0,0,off);PCA9685_setPWM(1,0,off);PCA9685_setPWM(2,0,off);PCA9685_setPWM(3,0,off);PCA9685_setPWM(4,0,off);PCA9685_setPWM(5,0,off);PCA9685_setPWM(6,0,off);PCA9685_setPWM(7,0,off);PCA9685_setPWM(8,0,off);PCA9685_setPWM(9,0,off);PCA9685_setPWM(10,0,off);PCA9685_setPWM(11,0,off);PCA9685_setPWM(12,0,off);PCA9685_setPWM(13,0,off);PCA9685_setPWM(14,0,off);PCA9685_setPWM(15,0,off);delay_1ms(100);}void PCA9685_Write(u8 addr,u8 data)
{IIC_Start();IIC_Send_Byte(PCA_Addr);IIC_NAck();IIC_Send_Byte(addr);IIC_NAck();IIC_Send_Byte(data);IIC_NAck();IIC_Stop();}u8 PCA9685_Read(u8 addr)
{u8 data;IIC_Start();IIC_Send_Byte(PCA_Addr);IIC_NAck();IIC_Send_Byte(addr);IIC_NAck();IIC_Stop();delay_us(10);IIC_Start();IIC_Send_Byte(PCA_Addr|0x01);IIC_NAck();data = IIC_Read_Byte(0);IIC_Stop();return data;}
//设置第num个PWM引脚,on默认为0,控制舵机旋转off角度
void PCA9685_setPWM(u8 num,u32 on,u32 off)
{IIC_Start();IIC_Send_Byte(PCA_Addr);IIC_Wait_Ack();IIC_Send_Byte(LED0_ON_L+4*num);IIC_Wait_Ack();IIC_Send_Byte(on&0xFF);IIC_Wait_Ack();IIC_Send_Byte(on>>8);IIC_Wait_Ack();IIC_Send_Byte(off&0xFF);IIC_Wait_Ack();IIC_Send_Byte(off>>8);IIC_Wait_Ack();IIC_Stop();}/*说明:floor语法:
FLOOR(number, significance)Number必需。要舍入的数值。 Significance必需。要舍入到的倍数。 说明将参数 number 向下舍入(沿绝对值减小的方向)为最接近的 significance 的倍数。 如果任一参数为非数值型,则 FLOOR 将返回错误值 #VALUE!。 如果 number 的符号为正,且 significance 的符号为负,则 FLOOR 将返回错误值 #NUM!示例公式                                                                    说明                                                                              结果FLOOR(3.7,2)          将 3.7 沿绝对值减小的方向向下舍入,使其等于最接近的 2 的倍数               2FLOOR(-2.5, -2)        将 -2.5 沿绝对值减小的方向向下舍入,使其等于最接近的 -2 的倍数         -2
*/
void PCA9685_setFreq(float freq)
{u8 prescale,oldmode,newmode;double prescaleval;//  freq *= 0.9;  // Correct for overshoot in the frequency setting (see issue #11).// PCA9685的内部时钟频率是25Mhz
//  公式: presale_Volue = round( 25000000/(4096 * update_rate) ) - 1
//  round = floor();  floor是数学函数,需要导入 math.h 文件
//  update_rate = freq;prescaleval = 25000000;prescaleval /= 4096;prescaleval /= freq;prescaleval -= 1;prescale = floor(prescaleval+0.5f);    //返回MODE1地址上的内容(保护其他内容)oldmode = PCA9685_Read(PCA_Model);//在MODE1中设置SLEEP位newmode = (oldmode&0x7F)|0x10;//将更改的MODE1的值写入MODE1地址,使芯片睡眠PCA9685_Write(PCA_Model,newmode);//写入我们计算的设置频率的值  //PCA_Pre = presale 地址是0xFE,可以数据手册里查找到PCA9685_Write(PCA_Pre,prescale);//重新复位PCA9685_Write(PCA_Model,oldmode);//等待复位完成delay_1ms(5);            //设置MODE1寄存器开启自动递增PCA9685_Write(PCA_Model,oldmode|0xa1);
}//设置角度
void setAngle(u8 num,u8 angle)
{u32 off = 0;off = (u32)(158+angle*2.2);PCA9685_setPWM(num,0,off);
}

PCA9685.h

#ifndef _PCA9685_H_
#define _PCA9685_H_#include "gd32f4xx.h"
#include "sys.h"
#include "systick.h"
#include "myiic.h"#define PCA_Addr        0x80    //IIC地址
#define PCA_Model       0x00
#define LED0_ON_L       0x06
#define LED0_ON_H       0x07
#define LED0_OFF_L      0x08
#define LED0_OFF_H      0x09
#define PCA_Pre         0xFE    //配置频率地址void PCA9685_Init(float hz,u8 angle);
void PCA9685_Write(u8 addr,u8 data);
u8 PCA9685_Read(u8 addr);
void PCA9685_setPWM(u8 num,u32 on,u32 off);
void PCA9685_setFreq(float freq);
void setAngle(u8 num,u8 angle);#endif

myiic.c

#include "myiic.h"
#include "systick.h"
#include "sys.h" //初始化IIC
void IIC_Init(void)
{               //打开SDA与SCL的引脚时钟rcu_periph_clock_enable(RCU_GPIOB);//设置SCL引脚模式为下拉输出gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_8);//设置引脚为推挽模式,翻转速度50MHzgpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);   //设置引脚输出高电平SCL等待信号gpio_bit_write(GPIOB, GPIO_PIN_8, SET);//设置SDA引脚gpio_mode_set(                        GPIOB, GPIO_MODE_OUTPUT,  GPIO_PUPD_PULLUP,     GPIO_PIN_9  );gpio_output_options_set(  GPIOB, GPIO_OTYPE_PP,       GPIO_OSPEED_50MHZ,  GPIO_PIN_9  );  gpio_bit_write(                     GPIOB, GPIO_PIN_9,              RESET);gpio_bit_write(GPIOB, GPIO_PIN_9, SET);}
//产生IIC起始信号
void IIC_Start(void)
{SDA_OUT();     //sda线输出IIC_SDA=1;                           IIC_SCL=1;delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{         u8 ucErrTime=0;SDA_IN();      //SDA设置为输入  IIC_SDA=1;delay_us(1);       IIC_SCL=1;delay_us(1);   while(READ_SDA)        {ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0       return 0;
}
//产生ACK应答
void IIC_Ack(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{                        u8 t;   SDA_OUT();         IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t<8;t++){              IIC_SDA=(txd&0x80)>>7;txd<<=1;      delay_us(2);   //对TEA5767这三个延时都是必须的IIC_SCL=1;delay_us(2); IIC_SCL=0;    delay_us(2);}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){IIC_SCL=0; delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++;   delay_us(1); }                   if (!ack)IIC_NAck();//发送nACKelseIIC_Ack(); //发送ACK   return receive;
}

myiic.h

#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h" //IO方向设置
//#define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}   //PB9输入模式
//#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
#define SDA_IN()  {gpio_mode_set(GPIOB, GPIO_MODE_INPUT,  GPIO_PUPD_PULLUP, GPIO_PIN_9);}   //PB9输入模式
#define SDA_OUT() {gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_9);} //PB9输出模式//IO操作函数
#define IIC_SCL    PBout(8) //SCL
#define IIC_SDA    PBout(9) //SDA
#define READ_SDA   PBin(9)  //输入SDA //IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口
void IIC_Start(void);               //发送IIC开始信号
void IIC_Stop(void);                //发送IIC停止信号
void IIC_Send_Byte(u8 txd);         //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void);              //IIC等待ACK信号
void IIC_Ack(void);                 //IIC发送ACK信号
void IIC_NAck(void);                //IIC不发送ACK信号void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif

main.c

#include "gd32f4xx.h"
#include "systick.h"
#include "led.h"
#include "pca9685.h"int main(void)
{nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);  // 优先级分组systick_config();                                                                    //系统滴答定时器   定时1usPCA9685_Init(60,90);  //PCA9685--16路舵机初始化  频率60Hz -- 180度while(1){}
}

资料参考:(感谢各位前辈大佬)

PCA9685 多舵机控制器的编程_leon_zeng0的博客-CSDN博客_pca9685

http://wiki.sunfounder.cc/index.php?title=PCA9685_16_Channel_12_Bit_PWM_Servo_Driver

代码与PCA9685英文数据手册链接:

链接:https://pan.baidu.com/s/1kRdrESL4OOLy1mgnnN_ohA?pwd=1234
提取码:1234

立创梁山派GD32F450ZGT6--通过PCA9685控制16路舵机相关推荐

  1. 51单片机PCA9685控制16路舵机(代码可直接使用)

    51单片机PCA9685控制16路舵机 /**************************************************************************PCA96 ...

  2. 树莓派python控制舵机_使用树莓派控制16路舵机驱动板(pca9685)

    使用树莓派控制16路舵机驱动板(pca9685) 在树莓派上,可以通过RPI.GPIO方便地输出PWM进行舵机控制. 使用RPI.GPIO 创建一个 PWM 实例: 1 p =GPIO.PWM(cha ...

  3. 树莓派c语言pca9685,使用树莓派控制16路舵机驱动板(pca9685)

    使用RPI.GPIO 创建一个 PWM 实例: 启用 PWM: 更改频率: 更改占空比: 停止 PWM: 但当你同时使用多个舵机时,PWM输出就变得困难了.这时可以借助舵机控制板来进行多路PWM控制. ...

  4. STM32控制16路舵机控制板PCA9685

    介绍 PCA9685 是最新的快速模式 Plus(Fm+)系列中的一员. Fm+器件可以提供更高的频率 (高达 1MHz)和更频繁(densely populated) 的总线操作(高达 4000pF ...

  5. 立创梁山派GD32F450ZGT6--0.96IIC屏移植u8g2

    一.硬件准备 使用的主控是:立创梁山派GD32F450ZGT6 使用的OLED是:0.96寸4针IIC通信的OLED屏 二.u8g2介绍 U8g2 是一个用于嵌入式设备的单色图形库.U8g2支持单色O ...

  6. 立创梁山派学习笔记——GPIO输出控制

    梁山派 前言 开发板简介 GD32F407ZGT6官方资源 数据手册 1.系统框图 2. 引脚复用表 3.命名规则 4.其他 用户手册 固件库与PACK包 开发环境搭建 立创官方的资料包 资料齐活,开 ...

  7. 立创梁山派GD32F450ZGT6--屏幕扩展板LVGL应用

    该文章工程是基于裸机情况下运行的LVGL,通过GUI-Guider-1.4.0进行页面布局配置. 一.介绍 GUI Guider是恩智浦为LVGL开发了一个上位机GUI设计工具,可以通过拖放控件的方式 ...

  8. 树莓派控制16路PWM输出的PCA9685模块

    树莓派控制16路PWM输出的PCA9685模块 1.PCA9685 1.1简介 采用I2C通信,内置了PWM驱动器和一个时钟,不需要不断发送信号占用单片机资源 支持16路PWM输出,每路12位分辨率( ...

  9. Arduino uno使用PCA9685模块实现16路舵机控制

    PCA9685模块 PCA9685是16路12位PWM信号发生器,可用于控制舵机.led.电机等设备,采用I2C通信.主机只需要I2C接口即可实现16路舵机控制. PCA9685的I2C地址默认0x4 ...

最新文章

  1. 【学习】009 NIO编程
  2. 数据处理中的准确性问题
  3. ASP.NET Core中显示自定义错误页面-增强版
  4. C# API强制关机、重启以及注销计算机
  5. 织入业务代码-IModifyService
  6. SVN:This client is too old to work with working copy…解决方法
  7. 新版《Windows Sysinternals实战指南》,读书积赞活动
  8. java try finally connectoin close_Java SocketChannel類代碼示例
  9. 数据库存入表情符报错问题
  10. linux下提示libpng12-0缺失
  11. DevExpress小结(简略)
  12. centos配置网络
  13. 利用python处理dna序列_科学网-简单的Python脚本提取对应位置基因序列(fasta文件)-王彬忠的博文...
  14. mysql 修改字段为主键自增_「MySQL整理」 MySQL语法,如何操作数据表
  15. centos7网络配置总结
  16. 计算机中丢失d3dx11 43.dll,电脑为何玩了游戏都有什么丢失d3dx11_34.dll
  17. 要学习使用 calib3D 模块在图像中创建 3D 效果-姿势估计
  18. 高效笔记法——康奈尔笔记
  19. 计算平均成绩 (10分)
  20. TCP协议--复位报文段

热门文章

  1. 数据类型---C语言变量的定义与初始化
  2. 转:成功的背后!(给所有IT人)
  3. OsgEarth加载DEM(数字高程模型)
  4. 快速傅里叶变换及其实现
  5. FT、DTFT和DFT之间的关系
  6. Kitti数据集简介
  7. 织梦CMS如何搬家并成功连接数据库
  8. python常用接口调用
  9. 回望,勉强及格的大学四年答卷
  10. SQL基本语句应用大全