• 16路PWM输出的pca9685模块


    pca9685模块

    今天要介绍的就是该模块,该模块是16路pwm模块,使用I2C总线可以控制16路舵机(led)。

    接线OE空着就可以,其他VCC是芯片供电+5,SCL时钟线,SDA信号线,GND地线。

    芯片介绍可以看:https://blog.csdn.net/asmallwhite/article/details/83048091 不过“默认情况下,若将A0-A5全部接地,则其器件地址为:0x40。” 错了,地址是 0x80

    51单片机的代码在:http://www.51hei.com/bbs/blog-228471-7372.html    (很感谢,放上就跑,很好的代码,但是ACK函数里有个地方有问题 116行“while((sda=1)&&(i<255)) ” 应该改成"while((sda==1)&&(i<255)) "

    不修改也能跑通,但是觉得应该这样才对。

    再次感谢,代码来自:西瓜太狼 http://www.51hei.com/bbs/blog-228471-7372.html 

     /**************************************************************************
                            PCA9685模块简单应用
                    平台:89C52,晶振:11.0592
    ***************************************************************************/
    #include"stc89.h"           
    #include <intrins.h>  
    #include <stdio.h>
    #include <math.h>
    typedef  unsigned char  uchar;        
    typedef  unsigned int   uint;        
    
    
    sbit scl=P2^1;                        //时钟输入线
    sbit sda=P2^0;                   //数据输入/输出端
    
    
    #define PCA9685_adrr 0x80//  1+A5+A4+A3+A2+A1+A0+w/r 
                            //片选地址,将焊接点置1可改变地址,
                            // 当IIC总 呱嫌 多片PCA9685或相同地址时才需焊接
    #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
    
    
    #define SERVOMIN  90 // this is the 'minimum' pulse length count (out of 4096)
    #define SERVOMAX  700 // this is the 'maximum' pulse length count (out of 4096)
    #define SERVO000  130 //0度对应4096的脉宽计数值
    #define SERVO180  520 //180度对应4096的脉宽计算值,四个值可根据不同舵机修改
    
    
    /**********************函数的声明*********************************/
    /*---------------------------------------------------------------
                      毫秒延时函数
    ----------------------------------------------------------------*/
    void delayms(uint z)
    {
      uint x,y;
      for(x=z;x>0;x--)
          for(y=148;y>0;y--);
    }
    /*---------------------------------------------------------------
                                                                            IIC总线所需的通用函数
    ----------------------------------------------------------------*/
    /*---------------------------------------------------------------
                     微妙级别延时函数 大于4.7us
    ----------------------------------------------------------------*/
    void delayus()
    {
              _nop_();          //在intrins.h文件里
                    _nop_();
                    _nop_();
                    _nop_();
                    _nop_();
    
    }
    /*---------------------------------------------------------------
                     IIC总线初始化函数 
    ----------------------------------------------------------------*/
    void init()
    {
        sda=1;                //sda scl使用前总是被拉高
        delayus();
        scl=1;
        delayus();
    }
    /*---------------------------------------------------------------
                     IIC总线启动信号函数
    ----------------------------------------------------------------*/
    void start()
    {
        sda=1;
        delayus();
        scl=1;                        //scl拉高时 sda突然来个低电平 就启动了IIC总线
        delayus();
        sda=0;
        delayus();
        scl=0;
        delayus();
    }
    /*---------------------------------------------------------------
                     IIC总线停止信号函数
    ----------------------------------------------------------------*/
    void stop()
    {
        sda=0;
        delayus();
        scl=1;                         //scl拉高时 sda突然来个高电平 就停止了IIC总线
        delayus();
        sda=1;                   
        delayus();
    }
    /*---------------------------------------------------------------
                     IIC总线应答信号函数
    ----------------------------------------------------------------*/
    void ACK()
    {
        uchar i;
        scl=1;
        delayus();
        while((sda==1)&&(i<255))         //原来这里是sda=1这里应该等待从设备拉低总线
                    i++;                                        
        scl=0;                                  
        delayus();
    }
    /*---------------------------------------------------------------
                     写一个字节,无返回值,需输入一个字节值
    ----------------------------------------------------------------*/
    void write_byte(uchar byte)
    {
        uchar i,temp;
        temp=byte;
        for(i=0;i<8;i++)
        {
            temp=temp<<1;  
            scl=0;                  
                                    delayus();
                                    sda=CY;                 
                                    delayus();
                                    scl=1;           
                                    delayus();
        }
        scl=0;                  
        delayus();
        sda=1;                 
        delayus();
    }
    /*---------------------------------------------------------------
                     读一个字节函数,有返回值
    ----------------------------------------------------------------*/
    uchar read_byte()
    {
                    uchar i,j,k;
                    scl=0;
                    delayus();
                    sda=1;
                    delayus();
                    for(i=0;i<8;i++)        
                    {
                                    delayus();
                                    scl=1;
                    delayus();
                    if(sda==1)
                    {
                                    j=1;
                    }
                    else j=0;
                    k=(k<< 1)|j;  
                    scl=0;            
                    }
                    delayus();
                    return k;
    }
    /*---------------------------------------------------------------
                    有关PCA9685模块的函数
    ----------------------------------------------------------------*/
    /*---------------------------------------------------------------
                    向PCA9685里写地址,数据
    ----------------------------------------------------------------*/
    void PCA9685_write(uchar address,uchar date)
    {
                    start();
                    write_byte(PCA9685_adrr);        //PCA9685的片选地址
                    ACK();                          
                    write_byte(address);  //写地址控制字节
                    ACK();
                    write_byte(date);          //写数据
                    ACK();
                    stop();
    }
    /*---------------------------------------------------------------
                从PCA9685里的地址值中读数据(有返回值)
    ----------------------------------------------------------------*/
    uchar PCA9685_read(uchar address)
    {
                    uchar date;
                    start();
                    write_byte(PCA9685_adrr); //PCA9685的片选地址
                    ACK();
                    write_byte(address);
                    ACK();
                    start();
                    write_byte(PCA9685_adrr|0x01);        //地址的第八位控制数据流方向,就是写或读
                    ACK();
                    date=read_byte();
                    stop();
                    return date;
    }
    /*---------------------------------------------------------------
                            PCA9685复位
    ----------------------------------------------------------------*/
    void reset(void) 
    {
                    PCA9685_write(PCA9685_MODE1,0x0);
    }
    
    
    void begin(void) 
    {
                    reset();
    }
    /*---------------------------------------------------------------
                                            PCA9685修改频率函数
    ----------------------------------------------------------------*/
    void setPWMFreq(float freq) 
    {
                    uint prescale,oldmode,newmode;
                    float prescaleval;
                    freq *= 0.92;  // Correct for overshoot in the frequency setting 
                    prescaleval = 25000000;
                    prescaleval /= 4096;
                    prescaleval /= freq;
                    prescaleval -= 1;
                    prescale = floor(prescaleval + 0.5);
                    
                    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);
                    delayms(2);
                    PCA9685_write(PCA9685_MODE1, oldmode | 0xa1); 
    }
    /*---------------------------------------------------------------
                                    PCA9685修改角度函数
    num:舵机PWM输出引脚0~15,on:PWM上升计数值0~4096,off:PWM下降计数值0~4096
    一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时
    跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,
    off/4096的值就是PWM的占空比。
    ----------------------------------------------------------------*/
    void setPWM(uint num, uint on, uint 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);
            }
    
    
    /*---------------------------------------------------------------
                          主函数
    ----------------------------------------------------------------*/
    void main()
    {
                    begin();
                    setPWMFreq(50);  
                    //例如要求舵机转到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);;;;当然也可以利用SERVO000和SERVO180计算
                    while(1) 
                    {
                                    setPWM(0, 0, SERVOMIN);//第0路舵机转到最小角度
                                    setPWM(1, 0, SERVO000);//第1路舵机转到0角度
    setPWM(15, 0, 3000);
                                    delayms(1500);
                                    setPWM(0, 0, SERVOMAX);
    //                                setPWM(1, 0, SERVO180);
                                    delayms(1500);
                    }                
    }
    
  • 相关阅读:
    遥感影像数据产品级别概述
    数据传输中的成帧
    帧同步
    I、Q信号是如何产生的,I、Q信号复用的作用
    调制解调技术
    基带信号、载波信号和宽带信号
    浅谈MVP架构及开发模式
    剑指offer代码 vs2013执行
    进程的状态
    死锁的必要条件及避免
  • 原文地址:https://www.cnblogs.com/yjphhw/p/10649940.html
Copyright © 2020-2023  润新知