PCA9685 16路12位pwm信號發生器


原文地址:http://nicekwell.net/blog/20161213/pca9685-16lu-12wei-pwmxin-hao-fa-sheng-qi.html



16路12位PWM信號發生器,可用於控制舵機、led、電機等設備,i2c通信,節省主機資源。

pca9685

一、概述和硬件

1、概述

很常見的模塊板子是這個樣子,這個板子也比較便宜,十幾塊錢一個。
i2c通信,只需要幾根i2c線就可以控制16路pwm,週期和佔空比都可控。
可以多個模塊級聯。
可控制16路通道的四種工作模式:關、開、pwm、可變pwm。
精度是12位:

工作頻率 時間分辨率 通常舵機500~2500us可分成份數 通常舵機500~2500us,旋轉角180°的角度分辨率
50Hz 4.88us 410份 0.439°
60Hz 4us 492份 0.366°

驅動方式可以選擇開漏輸出或推輓輸出。

2、硬件

1、電壓

數字電路電壓範圍可接受3.3和5v電平。
此外還有一個v+引腳,這個引腳是給舵機供電用的,可以接稍微高一點的電壓。

2、i2c地址

有6個地址控制腳,通過這些引腳可以控制設備的i2c地址。
7位的I2C地址爲:0x40 + A5:A0,A5到A0如果不做任何處理的話是0,想要把哪一位置1就把那個引腳焊到一起。
另外用i2cdetect檢測出還有一個0x70地址一直存在,這是一個通用地址,可以給所有從機下達指令。

3、使能腳

模塊有一個OE反使能腳,這個引腳低電平使能,不接的話模塊內部默認已經接地使能了,所以正常使用可以不接。

二、寄存器功能

:-:|:-:|:-:

內部地址(hex) 名稱 功能
00 MODE1 設置寄存器1
01 MODE2 設置寄存器2
02 SUBADR1 i2c-bus subaddress1
03 SUBADR2 i2c-bus subaddress2
04 SUBADR3 i2c-bus subaddress3
05 ALLCALLADR  
06 LED0_ON_L  
07 LED0_ON_H  
08 LED0_OFF_L  
09 LED0_OFF_H  
0x06 + 4*X LEDX_ON_L  
0x06 + 4*X + 1 LEDX_ON_H  
0x06 + 4*X + 2 LEDX_OFF_L  
0x06 + 4*X + 3 LEDX_OFF_H  
… 上面共16路通道
FA ALL_LED_ON_L  
FB ALL_LED_ON_H  
FC ALL_LED_OFF_L  
FD ALL_LED_OFF_H  
FE PRE_SCALE 控制週期的寄存器
FF TestMode  

MODE1寄存器

名稱 功能
D7 RESTART 寫1復位,寫完後此位自動清除。一定要在SLEEP位寫0後至少500us後才能對此位寫1進行復位。
D6 EXTCLOCK 0-使用內部時鐘(25MHz)。1-使用外部時鐘引腳的時鐘。修改此位前,一定要先SLEEP,再修改此位(此時SLEEP位仍然寫1),再退出SLEEP。
D5 AI 0-內部地址讀寫後不自動增加。1-內部地址讀寫後自動增加。一般i2c設備在對從機讀寫後內部地址都會自動增加,這個芯片可以手動設置是否自動增加,我們一般都會設成自動增加。
D4 SLEEP 0-退出SLEEP模式。1-進入SLEEP模式。注:1、寫0退出sleep模式後,最多等500us後即可產生穩定的時鐘信號。2、寫1進入sleep模式後,時鐘會關閉。此時可以修改時鐘源寄存器EXTCLOCK和週期寄存器PRE_SCALE,修改這兩個寄存器之前必須先進入sleep模式。
D3 SUB1  
D2 SUB2  
D1 SUB3  
D0 ALLCALL 0-不響應0x70通用i2c地址。1-響應0x70通用i2c地址。這個芯片除了可以通過A5:A0自定義i2c地址外,還有一個通用i2c地址0x70,此寄存器可以控制是否響應這個通用地址。注意啊:這個寄存器的設置好像掉電會保存的!

各個通道的ON和OFF寄存器

總共16個通道,每個通道都有 LEDX_ON_L、LEDX_ON_H、LEDX_OFF_L、LEDX_OFF_H 四個寄存器。
系統中有一個12位的計數ACK,ACK根據PRE_SCALE寄存器設置的週期進行增加,沒增加一次就會和上述四個寄存器對比:
當發現 ACK == LEDX_ON_H[3:0]:LEDX_ON_L 時,X通道輸出高電平;
當發現 ACK == LEDX_OFF_H[3:0]:LEDX_OFF_L 時,X通道輸出低電平。

PRE_SCALE寄存器

這個寄存器是用來設置週期的,具體原理可以不用管,只要記住這個公式: prescale 其中osc_clock是時鐘,根據上面的寄存器設置選擇是內部25MHz時鐘還是外部時鐘; update_rate是頻率,比如週期是20ms,那麼頻率就是50。 注意:實際應用中發現有誤差,需要加入校準,要把udpate_rate乘以0.915。 包括從網上下載的arduino驅動中也加入了此校準。

三、驅動

樹莓派wiringPi平臺

這裏是基於樹莓派wiringPi提供的i2c通信接口基礎上實現的驅動,在其他平臺上的驅動方法類似,只要把這裏的i2c接口換成其他平臺的通信接口即可。
本驅動週期固定爲20ms不可變,如需修改也非常容易。

pca9685_wiringpi.h文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
  這個驅動是在樹莓派的wiringPi基礎上的,基於wiringPi對i2c的接口函數。
  此驅動的使用方法是:
  1、先用 pca9685_init(從機地址) 初始化,得到一個設備描述符(int型),這個設備描述符代表這個pca9685芯片,因爲可能多個pca9685級聯,通過這個設備描述符來區分它們。
    它的
  2、調用 pca9685_setmk
 */

#ifndef PCA9685_WIRINGPI_H
#define PCA9685_WIRINGPI_H
#include <wiringPi.h>

int pca9685_init(unsigned char addr);	// addr是7位的i2c從機地址,返回的是linux標準的設備描述符,調用它的地方視作pca9685的設備描述符
					//因爲可以多個pca9685級聯,通過設備描述符區別它們
					//此驅動僅作爲驅動舵機使用,週期固定死位20ms,不允許外部設置
void pca9685_setmk(int fd, int num, int mk);	//設置指定通道的脈寬。fd是在pca9685_init時獲得的設備描述符,num是通道號(從0開始),mk是脈寬單位是us。週期已經固定爲20ms了

#endif

pca9685_wiringpi.c文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "pca9685_wiringpi.h"

#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

int pca9685_init(unsigned char addr)	// addr是7位的i2c從機地址,返回的是linux標準的設備描述符,調用它的地方視作pca9685的設備描述符
					//因爲可以多個pca9685級聯,通過設備描述符區別它們
					//此驅動僅作爲驅動舵機使用,週期固定死位20ms,不允許外部設置
{
    int pca9685;
    pca9685 = wiringPiI2CSetup(addr);

    {	//初始化pca9685芯片
	double T = 20000;	//週期,單位是us
	unsigned char prescale;
	double osc_clock = 25000000;
	unsigned char oldmode, newmode;
	T /= 0.915;	//不知道爲什麼,會有所偏差,這裏校準一下就ok了,從網上找的arduino代碼也進行了校準。
	T /= 1000000;	//把T轉換成秒
	prescale = (unsigned char)(osc_clock/4096*T - 1);
//	printf("prescale = 0x%x", prescale);
	oldmode = wiringPiI2CReadReg8(pca9685, PCA9685_MODE1);
	newmode = (oldmode&0x7f) | 0x10;	//準備進入sleep,設置時鐘前必須先進入sleep模式
	wiringPiI2CWriteReg8(pca9685, PCA9685_MODE1, newmode);
	wiringPiI2CWriteReg8(pca9685, PCA9685_PRESCALE, prescale);
	oldmode &= 0xef;	//清除sleep位
	wiringPiI2CWriteReg8(pca9685, PCA9685_MODE1, oldmode);
	delay(0.005);
	wiringPiI2CWriteReg8(pca9685, PCA9685_MODE1, oldmode | 0xa1);
    }
    
    return pca9685;
}

void pca9685_setmk(int fd, int num, int mk)	//設置指定通道的脈寬。fd是在pca9685_init時獲得的設備描述符,num是通道號(從0開始),mk是脈寬單位是us。週期已經固定爲20ms了
{
    unsigned int ON, OFF;
    ON = 0;	//每次週期一開始就輸出高電平
    OFF = (unsigned int)((((double)mk)/20000 * 4096)*1.0067114);	//最後的1.0067114是校準用的
//    printf("off = 0x%x", OFF);

    wiringPiI2CWriteReg16(fd, LED0_ON_L+4*num, ON);
    wiringPiI2CWriteReg16(fd, LED0_OFF_L+4*num, OFF);
}

關於驅動在樹莓派上的速度:

樹莓派設置的i2c波特率 設置16路通道所用時間
100000  
1000000(1M) 2067us
2000000(2M) 1300us

四、使用流程

1、確定i2c地址
通過焊接A5~A0確定模塊的i2c地址,如果不做任何焊接,默認地址是0x40。
2、連接數字電路電源。
3、連接兩根i2c線。
4、連接v+引腳,給舵機供電電源。
5、把驅動合入到工程,即可使用。


更多內容,歡迎訪問作者博客:http://nicekwell.net/



發佈了81 篇原創文章 · 獲贊 66 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章