树莓派使用PCA9685扩展(一)之PWM驱动舵机入坑指南

8 篇文章 0 订阅
3 篇文章 0 订阅

         从床底下拖出这个老版的树莓派B+,仅支持1路PWM,正好最近有空,就研究下之前买的PWM扩展板,这次使用bcm2835-1.71的I2C和PCA9685通讯(之前的MPU6500则使用SPI通讯)。

        bcm2835-1.71默认关闭对老版本Pi的I2C支持,需要修改驱动源码,在bcm2835.c中找到如下行取消注释编译即可:

#define I2C_V1

        好了,剩下的就看代码了,是仿着别人的python代码修改位C/C++的。我的Pi上的I2C设备地址是0x40。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <bcm2835.h>


#define PCA9685_ADDRESS 0x40

#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4

#define PCA9685_MODE1 0x00
#define PCA9685_PRESCALE 0xFE

#define LED0_ON_L 0x06
#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 BAUDRATE 100000

#define SERVOMIN 115 // this is the ‘minimum’ pulse length count (out of 4096)
#define SERVOMAX 590 // this is the ‘maximum’ pulse length count (out of 4096)
#define SERVO000 130 //0度
#define SERVO180 520 //180度
#define SERVO80 284 //80度
#define SERVO110 340//110度


void setPWM(uint16_t ch, uint16_t on, uint16_t off)
{

  printf("channel: %d  LED_ON: %d LED_OFF: %d\n" ,ch,on,off);
  char buf[2];
  uint8_t ret;
  buf[0]=LED0_ON_L+4*ch;
  buf[1]=on;
  ret = bcm2835_i2c_write(buf,2);
printf("ret = 0x%02X, val = 0x%02X\n", buf[0],buf[1]);

  buf[0]=LED0_ON_H+4*ch;
  buf[1]=on>>8;
  ret = bcm2835_i2c_write(buf, 2);
printf("ret = 0x%02X, val = 0x%02X\n", buf[0],buf[1]);

  buf[0]=LED0_OFF_L+4*ch;
  buf[1]=off;
  ret = bcm2835_i2c_write(buf,2);
printf("ret = 0x%02X, val = 0x%02X\n", buf[0],buf[1]);

  buf[0]=LED0_OFF_H+4*ch;
  buf[1]=off>>8;
  ret = bcm2835_i2c_write(buf,2);
printf("ret = 0x%02X, val = 0x%02X\n", buf[0],buf[1]);

}

/**
 * 设置脉宽,需要mode1设置为sleep模式,对PCA9685_PRESCALE进行设置
 */
void setFreq(float freq){
  char buf[2]={0,0};
  char reg[1]={0};
  uint8_t ret;
        // Constrain the frequency
        float prescaleval = 25000000.0;
        prescaleval /= 4096.0;
        prescaleval /= freq;
        prescaleval -= 1.0;
        printf("Estimated pre-scale: %d\n", (int)prescaleval);
        float prescale = floor((int)prescaleval + 0.5);
        printf("Final prescale = %d\n", (int)floor(prescale));

        buf[0]=PCA9685_MODE1;
        buf[1]=0x00;

        ret=bcm2835_i2c_write(buf, 2);
        printf("Write 0x%02X to register 0x%02X\n", buf[1],buf[0]);

        ret=bcm2835_i2c_write(reg, 1);
        ret=bcm2835_i2c_read(buf, 1);
        printf("Device 0x%02X returned 0x%02X from reg 0x%02X\n",
        PCA9685_ADDRESS, buf[0],reg[0]);


        uint8_t oldmode = buf[0];
        uint8_t newmode = (oldmode & 0x7F) | 0x10; // sleep
        buf[0]=PCA9685_MODE1;
        buf[1]=newmode;

        ret=bcm2835_i2c_write(buf, 2);
        printf("Write 0x%02X to register 0x%02X\n", buf[1], buf[0]);


        buf[0]=PCA9685_PRESCALE;
        buf[1]=(int)floor(prescale);

        ret = bcm2835_i2c_write(buf,2); // go to sleep
        printf("Write 0x%02X to register 0x%02X\n", buf[1],buf[0]);

        buf[0]=PCA9685_MODE1;
        buf[1]=oldmode;
        ret=bcm2835_i2c_write(buf, 2);
        printf("Write 0x%02X to register 0x%02X\n", buf[1],buf[0]);

        bcm2835_delay(5);

        buf[0]=PCA9685_MODE1;
        buf[1]=oldmode|0x80;
        ret=bcm2835_i2c_write(buf, 2);
        printf("Write 0x%02X to register 0x%02X\n", buf[1],buf[0]);



}

void setServoPulse(uint16_t channel, int pulse){
    //"Sets the Servo Pulse,The PWM frequency must be 50HZ"
    printf("setServoPulse = %d\n", pulse);
    pulse = (int)(pulse*4096/20000);        //PWM frequency is 50HZ,the period is 20000us
    printf("setServoPulse 2 = %d\n", pulse);
    setPWM(channel, 0, pulse);
}


int main()
{        
    char buf[2]; 
    uint8_t data;
    char  reg;
    int i,ret; 
    if (!bcm2835_init())
    {
      printf("bcm2835_init failed. Are you running as root??\n");
      return -1;
    }

    if(!bcm2835_i2c_begin())
    {
      printf("bcm2835_i2c_begin failedg. Are you running as root??\n");
      return -1;
    }
    bcm2835_i2c_setSlaveAddress(PCA9685_ADDRESS);
    //bcm2835_i2c_setClockDivider(BCM2835_I2C_CLOCK_DIVIDER_2500); 


    setFreq(50.0);

    int a[3]={500,2500,10};
    int b[3] ={2500,500,-10};
    int k,j;
    while(1) 
    {
      for(k=500;k<2500;k+=10){
        setServoPulse(0, k);
        bcm2835_delay(20);
      }
      for(j=2500;j>500;j-=10){
        setServoPulse(0, j);
        bcm2835_delay(20);
      }
    }

    bcm2835_delay(1000);
    bcm2835_i2c_end();
    bcm2835_close();
    return 0;
}


        完整的示例代码,就不注释了。本文章仅供初学者参考,如有任何问题可以随时交流。

        bcm2835开发文档:

        bcm2835: Examples

坑0

        上面讲了,bcm2835的pi上驱动认版本,写完代码调试,发现都返回NACK。怀疑代码问题半天,最后感谢谷歌。。。

坑1

        PCA9685单独使用3.3V供电VCC貌似不工作,还需要将V+管脚接到Pi上的5V管脚才行。

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值