IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 树莓派使用PCA9685扩展PWM驱动舵机入坑指南 -> 正文阅读

[嵌入式]树莓派使用PCA9685扩展PWM驱动舵机入坑指南

? ? ? ? ?从床底下拖出这个老版的树莓派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管脚才行。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:27:28  更:2022-04-04 12:30:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:54:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码