Linux Driver

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 時霢時霢轉動霢愈大愈快。




沒有留言 :

張貼留言