2015年7月2日 星期四

Raspberry Pi2: 用 Python 透過 I2C 控制 16 頻道 PWM 模組 (PCA9685)


==========
前言:
==========


PWM訊號是一個非常重要的主題,MCU 透過 PWM 訊號來控制馬達轉速、 LED 亮度、伺服馬達的角度控制。在 Maker 的角度來看,幾乎是重中之重的主題。馬達要轉動車輪,螺旋槳要拉起四軸飛行器,伺服機要帶動機械手臂...難怪 Arudino 和 Raspberry Pi 上的PWM模組向來是熱銷項目。

PWM 的輸出,在 Raspberry Pi 上面只有一組,Arduino 上面似乎有六個...不夠不夠不夠~~~~好用的東西怎麼都不夠~~~~。所以這篇文章裡面介紹了一個好東西,PCA9685,它是一個
具有16個PWM頻道12-bit PWM控制器 (I2C界面)。PAC9685 內建oscillator為25 MHz且具有 4096 steps (12-bit PWM)



介紹:
============

Raspberry Pi 的 Linux 系統要使用 Python 去存取 I2C 介面的話要先載入核心模組 i2c-bcm2708 和 i2c-dev 這兩個模組,讓和i2c介面溝通的系統裝置檔 /dev/i2c-1 出現。

> sudo modprobe i2c-bcm2708

> sudo modprobe i2c-dev




--------------
Python & I2C:
--------------


Python 操作 I2C 通訊介面需要用到 smbus 這個模組,所以沒有安裝的話記得用apt-get 指令安裝 python-smbus。

> sudo apt-get install python-smbus

下面的 Python 範例 code 主要是透過使用 


read_i2c_block_data(addr, cmd, len) 

和 

write_i2c_block_data(addr,cmd,[data,...])

這兩個 API 來讀寫 PCA9685 裡面的暫存器數值控制 16 個 PWM。
=============
控制 PCA9685:
=============

從官方 Datasheet 所述,PCA9685 的 I2C 位置預設為 x40。在知道 I2C 位置之後,控制 PCA9685就只剩下三個重點:

  設定PWM訊號頻率 (PWM Frequency)

  進入與離開睡眠模式

  分別設定 16 個 PWM 頻道

----------------
設定PWM訊號頻率:
----------------

PCA9685 的 16 個 PWM 
頻道的 PWM 頻率是一樣的,設定 PWM 頻率(PWM Frequency) 的方法為直接設定 PCA9685 的 PRESCALE 暫存器。 

PRESCALE 暫存器位置為 0xFE

PRESCALE 暫存器設定值和 PWM 頻率 (Hz) 的換算公式為:

PRESCALE_value = 25000000 / ( 4096 * PWM_Frequency - 1.0)

所以這裡我們使用 PWM 頻率為 50Hz (用來控制伺服馬達),換算 PRESCALE 值約為 121。所以利用smbus提供的API的設定方法如下:

bus.write_i2c_block_data(addr , 0xFE, [121])

---------------
進入與離開睡眠模式
---------------

PCA9685 接上電之後的狀態會是處在
睡眠模式(SLEEP)之下。SLEEP模式之下,所有的PWM頻道輸出都會被關掉。所以一開始在設定好PRESCALE的值之後,必順離開SLEEP模式 PCA9685 才會有辦法輸出PWM訊號。

PCA9685 進入睡眠模式的方法為設 MODE1 暫存器(暫存器位置:0x00)的 SLEEP bit (第4個 bit)為 1 ,將其設為 0 的話就醒來。

--code--
def set_sleep(addr): reg_mode1 = 0x00 sleep_bit = 0x01 << 4 old_mode1_val = bus.read_i2c_block_data(addr, reg_mode1, 1) bus.write_i2c_block_data(addr, reg_mode1, [old_mode1_val | sleep_bit]) def unset_sleep(addr): reg_mode1 = 0x00 sleep_bit = 0x01 << 4 old_mode1_val = bus.read_i2c_block_data(addr, reg_mode1, 1) bus.write_i2c_block_data(addr, reg_mode1, [old_mode1_val &~(sleep_bit)]) --code--


-------------------
設定 16 個 PWM 頻道

-------------------

PCA9685 控制每個 PWM 訊號需要設定兩個值: 訊號電壓上升(ON: 由0V變5V)時間點、訊號電壓下降(OFF: 由5V變0V)時間點。



PWM 為一個固定週期的方形電波,而在PCA9685模組裡面透過PRESCALE設定時間週期的長短,而一個週期會被分成 4096 等份。所以 
PWM 訊號電壓上升(ON)和下降(OFF)兩個時間點設定值的範圍均為 0 ~ 4095。然而,要儲存這樣的值需要 12 個 bit 的記憶體空間。

所以一個時間點的設定值需要用到兩個暫存器,一個用來存放值的第 0 ~ 7 bit 稱之為 low byte (L)暫存器,另一個用來存放值的第 8 ~ 11 bit 稱之為 high byte (H)暫存器。而 high byte 暫存器的第0~3 bit 被拿來存放值的第 8 ~ 11 bit,所以high byte 暫存器還剩下第 4 ~ 7 bit ,其中第 4 bit 如果被設為1的話該頻道的PWM輸出會被關掉,剩下的第 5 ~ 7 bit 則棄之不用。

所以每個PWM訊號的暫存器名稱(定義在datasheet中):

LEDx_ON_L : PWM訊號電壓上升時間點的low byte暫存器。x值(0~15)為PWM頻道的號碼。
LEDx_ON_H : PWM訊號電壓上升時間點的high byte暫存器。x值(0~15)為PWM頻道的號碼。
LEDx_OFF_L: PWM訊號電壓下降時間點的low byte 暫存器。x值(0~15)為PWM頻道的號碼。 
LEDx_OFF_H: PWM訊號電壓下降時間點的high byte暫存器。x值(0~15)為PWM頻道的號碼。

這一系列的暫存器是按照 LEDx_ON_L, LEDx_ON_H, LEDx_OFF_L, LEDx_OFF_H 的順序照PWM頻道號碼依 0 ~ 15 的順序排列下去。 而 LED0_ON_L 暫存器位置為 0x06,所以剩下的暫存器位置可以用下面四個簡單的公式算出來:

LEDx_ON_L  = 0x06 + 4 * x
LEDx_ON_H  = 0x07 + 4 * x
LEDx_OFF_L = 0x08 + 4 * x
LEDx_OFF_H = 0x09 + 4 * x

下面寫成兩個 function 來方便設定:
--
def set_PWM_ON(addr , ch, value): low_byte_val = value & 0x00FF high_byte_val = ( value & 0x0F00 ) >> 8 reg_low_byte = 0x06 + 4 * ch bus.write_i2c_block_data(addr, reg_low_byte , [low_byte_val ]) bus.write_i2c_block_data(addr, reg_low_byte + 1, [high_byte_val]) def set_PWM_OFF(addr, ch, value): low_byte_val = value & 0x00FF high_byte_val = ( value & 0x0F00 ) >> 8 reg_low_byte = 0x08 + 4 * ch bus.write_i2c_block_data(addr, reg_low_byte , [low_byte_val ]) bus.write_i2c_block_data(addr, reg_low_byte + 1, [high_byte_val])
--

總之,如果只是想要用PWM訊號控制馬達之類的,那可以不用管所有的 LEDx_ON_L 和 LEDx_ON_H 暫存器,都給它用預設值0就好,也就是每一個週期(20ms)一開就會是ON了,接下來是什麼時間點要OFF而已,也就是只要去控制 LEDx_OFF_L 和 LEDx_OFF_H這兩個暫存器就好了。




所以,PCA9685上電後,設好PRESCALE,離開睡眠模式之後就可以開始使用了。

配合上面的範例code 使用如下:

--code--
import smbus pca9685_addr = 0x40 bus = smbus.SMBus(1) def set_PWM_ON(addr , ch, value): low_byte_val = value & 0x00FF high_byte_val = ( value & 0x0F00 ) >> 8 reg_low_byte = 0x06 + 4 * ch bus.write_i2c_block_data(addr, reg_low_byte , [low_byte_val ]) bus.write_i2c_block_data(addr, reg_low_byte + 1, [high_byte_val]) def set_PWM_OFF(addr, ch, value): low_byte_val = value & 0x00FF high_byte_val = ( value & 0x0F00 ) >> 8 reg_low_byte = 0x08 + 4 * ch bus.write_i2c_block_data(addr, reg_low_byte , [low_byte_val ]) bus.write_i2c_block_data(addr, reg_low_byte + 1, [high_byte_val]) # 設定PRESCALE PWM frequency = 50Hz bus.i2c_write_i2c_block_data(pca9685_addr, 0xFE, [121]) # 離開睡眠模式 bus.i2c_write_i2c_block_data(pca9685_addr, 0x00, [0x01]) # 設定第 0 個 PWM 頻道輸出 dutycycle = 1024/4096 set_PWM_OFF(pca9685_addr, 0, 1024) --------

還是覺得太麻煩的話就下載下面這個 github 的專案。

https://github.com/onionys/python_code

裡面有一個寫好的 PCA9685.py 可以在命令列下這樣使用:

    重設並啟動 PCA9685 (i2c 位置設定為 0x40)
> sudo PCA9685.py reset
    顯示目前所有PWM頻道的狀況
> sudo PCA9685.py info
    設定 PWM 頻道 1 其 duty-cycle 為 996/4096
> sudo PCA9685.py ch 1 996


相關文件:
1) PCA9685 DataSheet

2) 關於伺服馬達控制

0°~180° Servo 
利用PWM控制角度

 Most servo motor will work well on 50 Hz of PWM frequency;
this mean the PWM signal should have a period of 20ms.
Servo Angle Schematic 180 3.8 
This servo can operate 180° when given a pulse signal ranging from 600usec to 2400usec. 
#define MIN_PULSE_WIDTH       600     // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH      2400     // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH  1500     // default pulse width when servo is attached
#define REFRESH_INTERVAL    20000     // minumim time to refresh servos in microseconds
--------------
360° Servo 
利用PWM控制轉的方向與轉速

360° servo 是藉由0.5~2.5ms  HIGH  PULSE
50Hz的脈波訊號做控制霢1.5ms  HIGH  PULSE是
位於停止的狀態;小於1.5ms  時順時霢轉動霢愈
小愈快;大於1.5ms 時霢時霢轉動霢愈大愈快。










7 則留言 :

  1. 請問如何用PI 接上 PCA9685 來控制 2顆以上的MG995? 程式該如何寫? I2C的模組有需要另外更變什麼嗎?

    回覆刪除
  2. Traceback (most recent call last):
    File "/home/pi/Desktop/PAC9685.py", line 20, in
    bus.i2c_write_i2c_block_data(pca9685_addr, 0xFE, [121])
    AttributeError: 'SMBus' object has no attribute 'i2c_write_i2c_block_data'


    您好 我照您上面的代碼貼上 出現了這個
    該怎樣處理呢?

    回覆刪除
  3. We are an independent escorts agency providing Call Girls job in Gurgaon. Call Girls job in hotels, We mainly targeted to the clients satisfaction and fully co-operative service providin you Escorts Girl Job in Gurgaon. We never disclose any identity to any third person. We also provides accommodations,we are looking an independent girl who can work with independently as a Female Escorts Job in Gurgaon.

    The recruitment will be on face to face interview and we need some pics for finalize the girls. The girls should be good looking to do Call Girls job in Gurgaon, good body structure and can work with us independently. Any Girls from any city can join us for Escorts Girl Job in Gurgaon and become a Call girls. We are hiring Female Escorts Job in Gurgaon also.

    回覆刪除

  4. Varanasi Escorts Give Perfections Sexual Substances On Demand
    These facts are important to Varanasi Escorts and they do their best to keep them under control. These sexy babes are suitable for men of all ages. They have exclusive qualities and can offer erotic services 24*7, which is very reasonable.
    Vranasi Escorts
    Varanasi Escorts services
    #varanasiEscorts #escortsinvaranasi #varanasicallgirls

    回覆刪除
  5. Welcome to our website where you will be served with exotic Hauz Khas Escorts who is blessed with immense beauty and erotic figures. Hauz Khas female escorts are regarded as the most sensual ladies in the whole Delhi region. You might easily get have seen prostitutes or hookers roaming in the dark streets of Delhi streets but beware of these unhealthy creatures.

    回覆刪除