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