精华内容
下载资源
问答
  •  PCA9685芯片被包裹在小板的中央  电源输入端子  绿色电源指示灯  在4组3针连接器中方便你一次插入16个伺服电机(伺服电机的插头稍宽于0.1“,所以你可以放4对0.1”的接头)  接线板上输入的反向极性保护 ...
  • Arduino使用PC9685控制板,通过串口输入角度来控制舵机。
  • Arduino uno使用PCA9685模块实现16路舵机控制

    万次阅读 多人点赞 2019-08-18 09:47:05
    PCA9685模块 PCA9685是16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,采用I2C通信。主机只需要I2C接口即可实现16路舵机控制。 PCA9685的I2C地址默认0x40,如果需要改变地址,则需要将板上A0-A5焊通即可...

    PCA9685模块

    PCA9685是16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,采用I2C通信。主机只需要I2C接口即可实现16路舵机控制。
    PCA9685的I2C地址默认0x40,如果需要改变地址,则需要将板上A0-A5焊通即可对应的bit置1,此时地址为:0x40+A5:A0。这也意味着主机可以通过I2C地址控制64个PCA9685模块,从而实现最大16*64路舵机控制。
    PCA9685模块如下图:
    在这里插入图片描述

    Arduino uno与模块连线

    AIN4 ------- SDA
    AIN5 ------- SCL
    5V -------- VCC
    3.3V -------- V+
    GND -------- GND
    舵机线按照颜色对应接模块0控制口。
    V+是舵机电源,试验采用的是9g小舵机,所以也可以用3.3V带动,但是mg995这种大舵机,则需要5V以上才能带动。
    VCC是模块的电源,用于PCA9685芯片。
    在这里插入图片描述

    Adafruit库安装

    使用Arduino的好处之一是有丰富的库支持。PC9685模块也有对应的库可以使用,这是一个外部库,由Adafruit提供。

    选择工具菜单下的管理库选项

    在这里插入图片描述

    搜索adafruit pwm

    在这里插入图片描述

    点击安装

    在这里插入图片描述

    安装完后,即可在文件下示例里面找到对应的示例

    在这里插入图片描述

    控制程序

    舵机的控制一般需要一个20ms的时基脉冲,该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度舵机为例,那么对应的控制关系是这样的:
    0.5ms————–0度;
    1.0ms————45度;
    1.5ms————90度;
    2.0ms———–135度;
    2.5ms———–180度;

    PCA9685可以设置更新频率,时基脉冲周期20ms相当于50HZ更新频率。PCA9685采用12位的寄存器来控制PWM占比,对于0.5ms,相当于0.5/204096=102的寄存器值。以此类推如下:
    0.5ms————–0度:0.5/20
    4096 = 102
    1.0ms————45度:1/204096 = 204
    1.5ms————90度:1.5/20
    4096 = 306
    2.0ms———–135度:2/204096 = 408
    2.5ms———–180度:2.5/20
    4096 =510

    但是实际使用的时候,还是有偏差,除了0度以及180度,其他需要乘以0.915系数。最后的寄存器值如下:
    0.5ms————–0度:0.5/204096 = 102
    1.0ms————45度:1/20
    4096 = 204 * 0.915 = 187
    1.5ms————90度:1.5/204096 = 306 * 0.915 = 280
    2.0ms———–135度:2/20
    4096 = 408 * 0.915 = 373
    2.5ms———–180度:2.5/20*4096 =510

    控制程序使用串口通讯接受指令,实现0/45/90/135/180度,总共5种角度的控制。

    #include <Wire.h>
    #include <Adafruit_PWMServoDriver.h>
    
    // 默认地址 0x40
    Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
    
    #define SERVO_0  102 
    #define SERVO_45  187 
    #define SERVO_90  280 
    #define SERVO_135  373 
    #define SERVO_180  510 
    
    // our servo # counter
    uint8_t servonum = 0;
    char comchar;
    
    void setup() {
      Serial.begin(9600);
      Serial.println("8 channel Servo test!");
    
      pwm.begin();
      pwm.setPWMFreq(50);  // 50HZ更新频率,相当于20ms的周期
    
      delay(10);
    }
    
    void loop() {
        while(Serial.available()>0){
        comchar = Serial.read();//读串口第一个字节
        switch(comchar)
        {
          case '0':
          pwm.setPWM(0, 0, SERVO_0);
          Serial.write(comchar);
          break;
          case '1':
          pwm.setPWM(0, 0, SERVO_45);
          Serial.write(comchar);
          break;
          case '2':
          pwm.setPWM(0, 0, SERVO_90);
          Serial.write(comchar);
          break;
          case '3':
          pwm.setPWM(0, 0, SERVO_135);
          Serial.write(comchar);
          break;       
          case '4':
          pwm.setPWM(0, 0, SERVO_180);
          Serial.write(comchar);
          break;
          default:
          Serial.write(comchar);
          break;                  
        }
      }
    }
    

    效果视频如下:
    http://v.douyin.com/y1VBvX/

    展开全文
  • 本教程将向您展示如何使用Arduino Nano和易于使用的伺服电机驱动器来控制伺服电机。
  • 12_DOF舵机狗arduino uno加PCA 9685...2、最好锂离子电池给舵机供电,也就是接在PCA9685板子的供电口上,arduino再另外接电池供电,那么此时上图接线的arduino的5v和PCA9685的vcc口就不要连接,但是GND要连。这样做的好

    12_DOF舵机狗arduino uno加PCA 9685控制代码

    硬件接线

    arduino和舵机硬件接线
    参考:https://blog.csdn.net/qq_42807924/article/details/82229997
    注意:
    1、舵机数量多,所以电池要用供电电流大的锂离子电池,普通电池供电电流不够,舵机会不转或者乱转;
    2、最好锂离子电池给舵机供电,也就是接在PCA9685板子的供电口上,arduino再另外接电池供电,那么此时上图接线的arduino的5v和PCA9685的vcc口就不要连接,但是GND要连。这样做的好处是:舵机运行的电流变化不会影响arduino板子,并且烧录程序的时候舵机不会因为arduino连接USB就乱转。

    控制代码

    写了一个特别简单的代码,能实现对角的两条腿同时动,前进的足端轨迹是先水平往后,再抬腿走一个抛物线迈回到起点。

    #include <Wire.h>
    #include <Adafruit_PWMServoDriver.h>
    #include <math.h>

    //这是四足狗走直线的程序
    #define pi 3.1415
    #define L0 15.5 //髋关节长度
    #define L1 88.5 //大腿长度
    #define L2 115 //小腿长度
    #define STEP 150 //定义一个行走周期的脉冲总数为100

    Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

    //右前腿舵机通道,1–髋关节舵机,2-大腿舵机,3-小腿舵机;
    #define R_f_1 0
    #define R_f_2 1
    #define R_f_3 2
    #define R_b_1 3
    #define R_b_2 4
    #define R_b_3 5
    #define L_f_1 6
    #define L_f_2 7
    #define L_f_3 8
    #define L_b_1 9
    #define L_b_2 10
    #define L_b_3 11

    int ledPin1 = 3;
    int ledPin2 = 4;
    int ledPin3 = 5;

    float x0=-25.0;
    float y0=145.0;
    float x1=10.0;
    float y1=115.0;
    float h=105.0; //h是腿抬起的最高点坐标
    float Px[STEP];
    float Py[STEP];

    void make_path()
    {
    //着地相,分配P/2个脉冲
    int j=0;
    for(float i=x0;i<x1;i=i+(x1-x0)/(STEP/2))
    {
    Px[j] = i;
    Py[j] = y0;

    // Serial.print(“j= “);
    // Serial.print(j);
    // Serial.print(” Px[j]=”);
    // Serial.print(Px[j]);
    // Serial.print(" Py[j]=");
    // Serial.println(Py[j]);
    j++;
    if(j>=STEP/2) break;
    }
    //腾空相,分配P/2个脉冲
    j=STEP/2;
    for(float i=x1;i>x0;i=i-(x1-x0)/(STEP/2) )
    {
    Px[j] = i;
    Py[j]=4*(y0-h)/pow(x1-x0,2)*pow(i-(x0+x1)/2,2)+h;

    // Serial.print(“j= “);
    // Serial.print(j);
    // Serial.print(” Px[j]=”);
    // Serial.print(Px[j]);
    // Serial.print(" Py[j]=");
    // Serial.println(Py[j]);
    j++;
    if(j>=STEP) break;
    }
    }

    void setup()
    {
    Serial.begin(9600);
    Serial.println(“12 channel Servo test!”);
    pwm.begin();
    pwm.setPWMFreq(50); // Analog servos run at ~60 Hz updates
    //模拟伺服在50赫兹更新下运行

    make_path();
    }

    //存下500个路径点

    //0度:0.5/204096 = 102
    //45度:1/20
    4096 = 1/204096 = 204 * 0.915 = 187
    //90度:1.5/20
    4096 = 1.s5/204096 = 306 * 0.915 = 280
    //135度:2/20
    4096 = 2/204096 = 408 * 0.915 = 373
    //180度:2.5/20
    4096 = 2.5/20*4096 =510

    //把角度值转化为4096对应的脉冲值
    int angle_to_pluse(float angle)
    {
    float pluse;
    pluse = 187.392 * (0.5 + 2*angle/pi);
    return int(pluse);
    }

    //已以大腿舵机转动中心为原点建立直角坐标系,x轴向后,y轴向下,把足底坐标值转化为关节角度
    //angle_2是小腿关节角度,angle_1是大腿关节角度
    //右边两条腿逆解
    float R_x_y_to_angle_1(float x ,float y)
    {
    float angle_1;
    float angle_2;
    angle_2 = acos((xx + yy - L1L1 - L2L2)/(2L1L2));
    double judge_expression = 4 * pow(x*(L1 + L2cos(angle_2)), 2)-4 * (xx+yy)( pow(x, 2) - pow(L2sin(angle_2), 2) );
    angle_1 = acos( ((x
    (L1 + L2cos(angle_2))) + 0.5sqrt(judge_expression))/(xx+yy) ) ;
    return angle_1+pi/6;
    }
    float R_x_y_to_angle_2(float x ,float y)
    {
    float angle_1;
    float angle_2;
    angle_2 = acos((xx + yy - L1L1 - L2L2)/(2L1L2));
    return angle_2;
    }

    //左边两条腿逆解
    float L_x_y_to_angle_1(float x ,float y)
    {
    float angle_1;
    float angle_2;
    angle_2 = acos((L1L1 + L2L2-xx - yy)/(2L1L2));
    double judge_expression = 4pow(x(L2cos(angle_2) - L1),2) - 4(2L1L1 + 2L2L2 - xx -yy)(xx - pow(L2sin(angle_2),2));
    angle_1 = acos( (x
    (L2cos(angle_2) - L1) - 0.5sqrt(judge_expression)) / (2L1L1 + 2L2L2-xx - yy ) );
    return angle_1;
    }
    float L_x_y_to_angle_2(float x ,float y)
    {
    float angle_1;
    float angle_2;
    angle_2 = acos((L1L1 + L2L2-xx - yy)/(2L1L2));
    return angle_2;
    }

    //x0和y0是脚落地瞬间的位置,作为一个周期的起始位置。x1和y2是脚抬起瞬间的位置,h是腿抬起的最高点坐标

    //对角步态
    void loop()
    {
    //创建一条路径

    //对角的腿动作相同,且腾空相和着地相交替轮换
    for(int i=0;i<STEP/2;i++)
    {
    pwm.setPWM(R_f_1, 0, angle_to_pluse(pi/2-pi/20) );
    pwm.setPWM(R_f_2, 0, angle_to_pluse( R_x_y_to_angle_1(Px[i] , Py[i]) ) );//着地
    pwm.setPWM(R_f_3, 0, angle_to_pluse( R_x_y_to_angle_2(Px[i] , Py[i]) ) );//着地
    pwm.setPWM(R_b_1, 0, angle_to_pluse(pi/2-pi/20) );
    pwm.setPWM(R_b_2, 0, angle_to_pluse( R_x_y_to_angle_1(Px[i+STEP/2] , Py[i+STEP/2]) ) );//腾空
    pwm.setPWM(R_b_3, 0, angle_to_pluse( R_x_y_to_angle_2(Px[i+STEP/2] , Py[i+STEP/2]) ) );//腾空
    // Serial.print("i= “);
    // Serial.print(i);
    // Serial.print(” Px[i+STEP/2]= “);
    // Serial.print(Px[i+STEP/2]);
    // Serial.print(” Py[i+STEP/2]= “);
    // Serial.print(Py[i+STEP/2]);
    // Serial.print(” R_x_y_to_angle_1= “);
    // Serial.print(R_x_y_to_angle_1(Px[i+STEP/2] , Py[i+STEP/2]) );
    // Serial.print(” R_x_y_to_angle_2= ");
    // Serial.println(R_x_y_to_angle_2(Px[i+STEP/2] , Py[i+STEP/2]) );

    pwm.setPWM(L_f_1, 0, angle_to_pluse(pi/2+pi/20) );
    pwm.setPWM(L_f_2, 0, angle_to_pluse( L_x_y_to_angle_1(Px[i+STEP/2] , Py[i+STEP/2]) ) );//腾空
    pwm.setPWM(L_f_3, 0, angle_to_pluse( L_x_y_to_angle_2(Px[i+STEP/2] , Py[i+STEP/2]) ) );//腾空
    pwm.setPWM(L_b_1, 0, angle_to_pluse(pi/2+pi/20) );
    pwm.setPWM(L_b_2, 0, angle_to_pluse( L_x_y_to_angle_1(Px[i] , Py[i]) ) );//着地
    pwm.setPWM(L_b_3, 0, angle_to_pluse( L_x_y_to_angle_2(Px[i] , Py[i]) ) );//着地
    delay(4);
    

    }

    for(int i=STEP/2;i<STEP;i++)
    {
    pwm.setPWM(R_f_1, 0, angle_to_pluse(pi/2-pi/20));
    pwm.setPWM(R_f_2, 0, angle_to_pluse( R_x_y_to_angle_1(Px[i] , Py[i]) ) );//腾空
    pwm.setPWM(R_f_3, 0, angle_to_pluse( R_x_y_to_angle_2(Px[i] , Py[i]) ) );//腾空
    pwm.setPWM(R_b_1, 0, angle_to_pluse(pi/2-pi/20));
    pwm.setPWM(R_b_2, 0, angle_to_pluse( R_x_y_to_angle_1(Px[i-STEP/2] , Py[i-STEP/2]) ) );//着地
    pwm.setPWM(R_b_3, 0, angle_to_pluse( R_x_y_to_angle_2(Px[i-STEP/2] , Py[i-STEP/2]) ) );//着地
    // Serial.print("i= “);
    // Serial.print(i);
    // Serial.print(” Px[i-STEP/2]= “);
    // Serial.print(Px[i-STEP/2]);
    // Serial.print(” Py[i-STEP/2]= “);
    // Serial.print(Py[i-STEP/2]);
    // Serial.print(” R_x_y_to_angle_1= “);
    // Serial.print(R_x_y_to_angle_1(Px[i-STEP/2] , Py[i-STEP/2]) );
    // Serial.print(” R_x_y_to_angle_2= ");
    // Serial.println(R_x_y_to_angle_2(Px[i-STEP/2] , Py[i-STEP/2]) );

    pwm.setPWM(L_f_1, 0, angle_to_pluse(pi/2+pi/20));
    pwm.setPWM(L_f_2, 0, angle_to_pluse( L_x_y_to_angle_1(Px[i-STEP/2] , Py[i-STEP/2]) ) );//着地
    pwm.setPWM(L_f_3, 0, angle_to_pluse( L_x_y_to_angle_2(Px[i-STEP/2] , Py[i-STEP/2]) ) );//着地
    pwm.setPWM(L_b_1, 0, angle_to_pluse(pi/2+pi/20));
    pwm.setPWM(L_b_2, 0, angle_to_pluse( L_x_y_to_angle_1(Px[i] , Py[i]) ) );//腾空
    pwm.setPWM(L_b_3, 0, angle_to_pluse( L_x_y_to_angle_2(Px[i] , Py[i]) ) );//腾空
    delay(4);
    

    }
    }

    需要这个Adafruit_PWMServoDriver.h库,没有的话在网上找,下载存在arduino的安装路径下,具体找教程。

    展开全文
  • 当初急用但直接在网上翻遍了也没找到PCA9685版的...【3】连接图:https://steemit.com/utopian-io/@drencolha/adafruit-pca9685-pwm-servo-driver-setup-arduino-library-use-shown-with-an-example-project-tutorial
  • PCA9685 多舵机控制器的编程

    千次阅读 2021-01-08 15:06:25
    本文介绍通过写入寄存器的方法实现对PCA9685 的控制。 PCA9685 模块 制作机器人,舵机就是让它动起来的关键器件。而又由于通常设计中为了不影响处理器供电或者主控功率不够,都会另外制作一块板子用作舵机驱动,也...

    本文介绍通过写入寄存器的方法实现对PCA9685 的控制。

     PCA9685 模块

    制作机器人,舵机就是让它动起来的关键器件。而又由于通常设计中为了不影响处理器供电或者主控功率不够,都会另外制作一块板子用作舵机驱动,也叫舵机控制板。
    PCA9685是一款用于产生16路PWM信号的LED控制芯片, 采用I2C总线与主控芯片进行通信。PCA9685具有可以产生16路PWM脉冲、控制独立精准、编程简单灵活等特点, 以其为基础实现的舵机控制能够有限减少硬件和软件设计的复杂度, 具有高可靠性
    PCA9685的用途

    PCA9685是一款基于I2C总线控制的16路LED背光调节控制芯片。每一路LED输出端均可自由调节PWM波的频率 (40~1000Hz) 和占空比 (0%~100%) 。这款芯片主要通过输出不同占空比的PWM脉冲信号来控制舵机转动的角度。
    PCA9685的特点

    PCA9685可编程调节16路PMW脉冲的占空比以及高电平到来的时刻, 分辨率为12位 (4096) 。

    地址问题

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

    A5-A0 缺省是开放的, 地址是0x40

     

    I2C 时钟最高可以1M 

    频率

    16路PWM 的频率都是一样的, 由prescale 

     

    PWM 波长设定. 舵机控制所需的 PWM 周期为20 ms. 在用 PCA9685 作为多舵机控制器时,需要将 其 PWM 输出周期设定为20 ms,即PWM 波的频率设定为50 Hz,PCA9685 输出频率与振荡器有关,频率的 设置值
    在这里插入图片描述

    内部时钟频率为25M ,经计算,当需要输出50 Hz 的 PWM 频率时,设定值为121. 将该值下载到频率设定寄存器就可以改变芯片 工作频率. 需要注意的是,频率的更改只能在 PCA9685 芯片处于休眠状态下进行。

    presale 地址是0xFE,可以datasheet 里查找到。下面是 开源库中设定频率的函数代码:

    void Adafruit_PWMServoDriver::setPWMFreq(float freq) {
      //Serial.print("Attempting to set freq ");
      //Serial.println(freq);
      freq *= 0.9;  // Correct for overshoot in the frequency setting (see issue #11).
      float prescaleval = 25000000;
      prescaleval /= 4096;
      prescaleval /= freq;
      prescaleval -= 1;
      if (ENABLE_DEBUG_OUTPUT) {
        Serial.print("Estimated pre-scale: "); Serial.println(prescaleval);
      }
      uint8_t prescale = floor(prescaleval + 0.5);
      if (ENABLE_DEBUG_OUTPUT) {
        Serial.print("Final pre-scale: "); Serial.println(prescale);
      }
      
      uint8_t oldmode = read8(PCA9685_MODE1);
      uint8_t newmode = (oldmode&0x7F) | 0x10; // sleep
      write8(PCA9685_MODE1, newmode); // go to sleep
      write8(PCA9685_PRESCALE, prescale); // set the prescaler
      write8(PCA9685_MODE1, oldmode);
      delay(5);
      write8(PCA9685_MODE1, oldmode | 0xa1);  //  This sets the MODE1 register to turn on auto increment.
                                              // This is why the beginTransmission below was not working.
      //  Serial.print("Mode now 0x"); Serial.println(read8(PCA9685_MODE1), HEX);
    }

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

    下图是设定脉冲宽度的示意图。整个周期为4095, LED_ON 和 LED_OFF 2个的设定值确定脉宽,我的设定中LED_ON 设为0, LED_OFF就是脉宽了。 这里都用2位字节来表示。

    比如6,7 是LED0_ON,而8,9是 LEN0_OFF ,接下来都是4个字节控制一路pwm的占空比。

    地址定义表

    在datasheet 文件中可以看到地址定义信息表,这里剪一页到这里作为参考 

    实际例子

    我的实现是在zynq 7000上完成的,zynq 7000 的 iic 外设编程

    本文并没介绍I2C 的编程,各种情况都可以完成,强调的是写哪些寄存器,如何控制。

    下面是我控制的序列,(左边是寄存器地址,右边是写入值)

    //测试配置列表
    u8 configList[11][2]={
    		{0x00,0x10},
    		{0xfe,120},
    		{0x00,0x00},
    		{6,0x0},
    		{7,0x0},
    		{8,200},
    		{9,0},
    		{0x0A,0},
    		{0x0B,0},
    		{0x0c,150},
    		{0x0d,1}
    };

    那我解说一下是,控制休眠,设定频率,取消休眠,pwm0 的LED0_ON 为 (0x00_00) ,LED0_OFF 为(0x00_d8或者200)  pwm1 的LED1_ON 为 (0x00_00) ,LED1_OFF 为(0x01_96或者406) 

    0xd8 =200, 0x96=150 算得不一定对。

    Arduino Uno 系统可以参看 http://wiki.sunfounder.cc/index.php?title=PCA9685_16_Channel_12_Bit_PWM_Servo_Driver 都有程序库。

    展开全文
  • STM32F407配置pca9685驱动

    千次阅读 多人点赞 2018-04-07 15:32:24
    STM32F407配置pca9685驱动 pca9685是16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,i2c通信,节省主机资源。在淘宝上随处可见,Arduino用它非常方便,不过STM32要想使用它必须要写好驱动才行,本文...

    STM32F407配置pca9685驱动

    这里写图片描述
    pca9685是16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,i2c通信,节省主机资源。在淘宝上随处可见,Arduino用它非常方便,不过STM32要想使用它必须要写好驱动才行,本文简述如何配置其驱动以及一些需要注意的地方。

    pca9685简介

    当然对于老手,看datasheet是最好的选择pca9685datasheet
    对于我这种新手,自然是看高手的文章比较容易上手了~

    我在网上看到的对pca9685总结的最好的一篇文章推荐给大家:生命不息折腾不止
    博主写得非常详细,本文某些片段也是参考博主的这篇文章,在此致谢。

    pca9685用的是IIC通信,如果有同学对IIC不熟悉,要先去了解一下~

    这里写图片描述

    pca9685驱动

    网上有不少写pca9685的文章,写驱动的也有,不过我试过的大多有错误,我详细地进行了修改,并且调试成功,走了不少弯路。

    pcf8574.h:

    头文件的话主要就是寄存器地址和IIC通信的函数声明,比较简单。

    内部地址(hex)名称功能
    Harry PotterGryffindor90
    Hermione GrangerGryffindor100
    Draco MalfoySlytherin90
    00MODE1设置寄存器1
    01MODE2设置寄存器2
    02SUBADR1i2c-bus subaddress1
    03SUBADR2i2c-bus subaddress2
    04SUBADR3i2c-bus subaddress3
    05ALLCALLADR
    06LED0_ON_L
    07LED0_ON_H
    08LED0_OFF_L
    09LED0_OFF_H
    0x06 + 4*XLEDX_ON_L
    0x06 + 4*X + 1LEDX_ON_H
    0x06 + 4*X + 2LEDX_OFF_L
    0x06 + 4*X + 3LEDX_OFF_H
    … 上面共16路通道
    FAALL_LED_ON_L
    FBALL_LED_ON_H
    FCALL_LED_OFF_L
    FDALL_LED_OFF_H
    FEPRE_SCALE 控制周期的寄存器
    FFTestMode

    照着上面的表格写头文件就好了 - -

    #ifndef __PCF8574_H
    #define __PCF8574_H
    #include "sys.h"
    #include "myiic.h"
    
    #define PCA9685_adrr 0x80
    #define PCA9685_SUBADR1 0x2
    #define PCA9685_SUBADR2 0x3
    #define PCA9685_SUBADR3 0x4
    
    #define PCA9685_MODE1 0x0
    #define PCA9685_PRESCALE 0xFE
    
    
    #define LED0_ON_L 0x6
    #define LED0_ON_H 0x7
    #define LED0_OFF_L 0x8
    #define LED0_OFF_H 0x9
    
    #define ALLLED_ON_L 0xFA
    #define ALLLED_ON_H 0xFB
    #define ALLLED_OFF_L 0xFC
    #define ALLLED_OFF_H 0xFD
    
    void PCA9685_write(unsigned char reg,unsigned char data);
    u8 PCA9685_read(unsigned char reg);
    void setPWMFreq(u8 freq);
    void setPWM(u8 num, u16 on, u16 off);
    void down();
    void up();
    
    #endif
    

    pcf8574.c:

    PCA9685的读写操作没什么好说的,直接调用原子哥写好的IIC相关函数即可:

    void PCA9685_write(unsigned char reg,unsigned char data)
    {
        IIC_Start();
        IIC_Send_Byte(PCA9685_adrr);
        IIC_Wait_Ack();
        IIC_Send_Byte(reg);
        IIC_Wait_Ack();
        IIC_Send_Byte(data);
        IIC_Wait_Ack();
        IIC_Stop();
    }
    
    u8 PCA9685_read(unsigned char reg)
    {
        u8 res;
        IIC_Start();
        IIC_Send_Byte(PCA9685_adrr);
        IIC_Wait_Ack();
        IIC_Send_Byte(reg);
        IIC_Wait_Ack();    
            IIC_Start();                
        IIC_Send_Byte(PCA9685_adrr|0X01);
        IIC_Wait_Ack();
        res=IIC_Read_Byte(0);       
        IIC_Stop();             
        return res;  
    }

    设置PWM波的频率,看下面这个公式即可:
    setfreq
    其中osc_clock是时钟,根据上面的寄存器设置选择是内部25MHz时钟还是外部时钟。
    update_rate是频率,比如周期是20ms,那么频率就是50。
    注意:实际应用中发现有误差,需要加入校准,要把udpate_rate乘以0.915。
    包括从网上下载的arduino驱动中也加入了此校准。
    注意要使用浮点型变量作为中间量,否则会计算错误,另外不要忘记四舍五入。

    void setPWMFreq(u8 freq)
    {
       u8 prescale,oldmode,newmode;
       double prescaleval;
       prescaleval = 25000000.0/(4096*freq*0.915);
       prescale = (u8)floor(prescaleval+0.5)-1;
    
       oldmode = PCA9685_read(PCA9685_MODE1);
       newmode = (oldmode&0x7F) | 0x10; // sleep
       PCA9685_write(PCA9685_MODE1, newmode); // go to sleep
       PCA9685_write(PCA9685_PRESCALE, prescale); // set the prescaler
       PCA9685_write(PCA9685_MODE1, oldmode);
       delay_ms(5);
       PCA9685_write(PCA9685_MODE1, oldmode | 0xa1); 
    }

    setPWM函数,向相应的寄存器(LED0_ON_L、LED0_ON_H、LED0_OFF_L、LED0_OFF_H)写值即可。
    其中num为舵机PWM输出引脚0~15,on是PWM上升计数值0~4096,off是PWM下降计数值0~4096。

    他的PWM发生原理是这样的:

    一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,off/4096的值就是PWM的占空比。

    void setPWM(u8 num, u16 on, u16 off) 
    {
        PCA9685_write(LED0_ON_L+4*num,on);
        PCA9685_write(LED0_ON_H+4*num,on>>8);
        PCA9685_write(LED0_OFF_L+4*num,off);
        PCA9685_write(LED0_OFF_H+4*num,off>>8);
    }

    为了方便使用,我写了一个输入角度输出相应PWM值(off值)的函数。
    这个要针对舵机进行修改,我用的是乐幻索尔的LDX218双轴舵机:
    这里写图片描述
    周期20ms,所以在设置频率的时候要设为50;占空比0.5ms~2.5ms分别对应0°~180°,且成线性关系,所以计算公式为:

    PWM=4096×0.5+(2.50.5)×angle/18020 P W M = 4096 × 0.5 + ( 2.5 − 0.5 ) × a n g l e / 180 20

    u16 calculate_PWM(u8 angle){
        return (int)(204.8*(0.5+angle*1.0/90));
    }

    然后就是一些动作组了,这个就随便写一个8个舵机全部置零位和全部置90°:
    注意IIC通信间隔的问题,每调用一次setPWM()函数就要延迟1ms,否则会出现莫名其妙的BUG,这个很重要~

    void down(){
        u16 pwm = calculate_PWM(0);
        setPWM(0x0,0,pwm);
        delay_ms(1);
        setPWM(0x1,0,pwm);
        delay_ms(1);
        setPWM(0x2,0,pwm);
        delay_ms(1);
        setPWM(0x3,0,pwm);
        delay_ms(1);
        setPWM(0x4,0,pwm);
        delay_ms(1);
        setPWM(0x5,0,pwm);
        delay_ms(1);
        setPWM(0x6,0,pwm);
        delay_ms(1);
        setPWM(0x7,0,pwm);
    }
    
    void up(){
        u16 pwm = calculate_PWM(90);
        setPWM(0x0,0,pwm);
        delay_ms(1);
        setPWM(0x1,0,pwm);
        delay_ms(1);
        setPWM(0x2,0,pwm);
        delay_ms(1);
        setPWM(0x3,0,pwm);
        delay_ms(1);
        setPWM(0x4,0,pwm);
        delay_ms(1);
        setPWM(0x5,0,pwm);
        delay_ms(1);
        setPWM(0x6,0,pwm);
        delay_ms(1);
        setPWM(0x7,0,pwm);
    }
    
    

    应用与调试

    我是想做一个用蓝牙控制一个四足机器人的,机器人有8个舵机,完整的工程代码已经跑通,感兴趣的可以下载看看:
    蓝牙控制8个舵机(STM32F407+pca9685+HC06)

    展开全文
  • 【学习笔记】——16路PWM舵机驱动板(PCA 9685) + Arduino

    万次阅读 多人点赞 2018-08-31 02:42:01
    此舵机驱动板使用PCA9685芯片,是16通道12bit PWM舵机驱动,用2个引脚通过I2C就可以驱动16个舵机。 强大如斯! 先对单个舵机尝试一下,了解一下PWM 20ms周期 = 频率为50Hz 单个舵机例程1: 非常...
  • PCA9685PWM调灯

    2019-04-23 20:34:17
    stm32f103c8t6;通过芯片PCA9685产生PWM控制LED的亮度;
  • PCA9685资料

    万次阅读 多人点赞 2018-05-16 09:37:01
    原文地址:http://nicekwell.net/blog/20161213/pca9685-16lu-12wei-pwmxin-hao-fa-sheng-qi.html一、概述和硬件1、概述2、硬件1、电压2、i2c地址3、使能脚二、寄存器功能MODE1寄存器各个通道的ON和OFF寄存器PRE_...
  • 目录前言1,关于arduino esp8266 舵机2,lib库安装2,代码使用3,总结 前言 相关arduino 全部分类: https://blog.csdn.net/freewebsys/category_8799254.html 本文的原文连接是: ...未经博主允许不得转载...
  • 程序的作用是控制 PCA9685 来实现控制舵机的功能 这个程序的底层逻辑 改编自 Arduino版本的代码 现在改变成51代码 使用的时候不要忘了 设置好自己的控制器地址 默认地址 0x80!!!
  • stm32+pca9685控制舵机机械臂,正点原子板子上完美通过。
  • 之前的【PCA 9685学习笔记初级版】https://blog.csdn.net/qq_42807924/article/details/82229997 /*************************************************** 这是16通道PWM和伺服驱动器的一个例子,驱动16个伺服...
  • micropython ESP32+PCA9685 舵机转速控制

    千次阅读 2018-08-19 00:08:45
    esp32烧录micropython固件,将用micropython来编写代码,esp32连接数据线到电脑,电脑通过串口工具编写代码,esp32接收后通过i2c给pca9685pca9685控制舵机运动。 接线图: 我写的代...
  • 最近买了块16路PWM舵机驱动板,测试后做个总结。 舵机原理网上资料很多就不详细介绍了,一般以9g舵机为例,一个20ms的周期内通过0.5ms到2.5ms的脉冲宽度控制舵机角度。 板子为16通道12bit PWM舵机驱动,用2个引脚...
  • PCA9685版OTTO开源跳舞机器人正文关于OTTO开源跳舞机器人PCA9685库文件Oscillater.cpp文件:Oscillator.h文件主程序:nanoPca9685ODance.ino参考文献 当初急用但直接在网上翻遍了也没找到PCA9685版的OTTO机器人,库...
  • 树莓派Adafruit_PCA9685,舵机驱动自定义通道,正反转,角度,输入通道,选择正转或者反转,输入角度
  • 树莓派4B使用 Adafruit_PCA9685 报错IOError: [Errno 121] Remote I/O error解决办法 首先,确保已经下载了python2或python3的Adafruit_PCA9685库,然后要明白,报该错原因是:Adafruit_PCA9685无法找到外部的硬件,...
  • PCA9685:I2C转16路PWM,助力你的系统

    万次阅读 多人点赞 2018-10-15 01:44:13
    PCA9685:I2C转16路PWM,助力你的系统 1 基本介绍 1.1 该IC主要参数特征如下: I2C接口,支持高达16路PWM输出,每路12位分辨率(4096级) 内置25MHz晶振,可不连接外部晶振,也可以连接外部晶振,最大50MHz 支持...
  • STM32F407通过IIC与pca9685(16路12位PWM发生器)通信),通过USART2串口与HC06(蓝牙模块)通信,实现蓝牙控制8路舵机,keil MDK工程

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 189
精华内容 75
关键字:

arduinopca9685