MCU單晶片韌體設計

2015年7月30日 星期四

Python 入門學習快速筆記 (一)


Statement :



  • 每行敍述不用分號 ; 做結尾, 除非語法敍述寫在同一行
    print('A')
    print ('B')
    print('A') ;print ('B')    #當寫在同一行時,加分號 ;
  • 用 tab 內縮取代括號, 同一層的內縮表示在同一對括號內
    for n in names:
           print("Hello, %s."%n)  
  • for n in names:print("Hello, %s."%n)



變數: 

  • 變數是有資料型態的但使用前不用事先宣告其資料型態,如int, str, float, set..
  • type () 傳回資料型態
  • 變數是一種label ,可以用來代表所指object的一個名字,
  • del 刪除變數/label 


>>> type(10)
<class 'int'>
>>> type('hello')
<class 'str'>
>>> type(24.3)
<class 'float'>
>>>

>>> num=10
>>> type(num)
<class 'int'>
>>> print(num)
10
>>> del num



>>>i,j=5,10  #指定i=5,=10
>>>print(i) -->卬出5
>>>print(i,j) -->卬出5 10
>>>print(i,j,sep=',') -->卬出5,10
>>>print("i")  --> 卬出i

>>> print('a','b','c','d',sep="")
abcd
>>> print('a','b','c','d',sep="|")

於命令列
> python -c "print('hello')"


變數


#在Python 中所有的data都是object, 凡object 都會有 id, type, value
#可變的資料型態,如 list, ,變數指定時會產生新的id
    a=[1,2,3]
    b=[1,2,3]
   a is b ?

#不可變的資料型態, 如6,變數指定時"可能"會對應相同的id
 判斷id 最準


# is , ==
a=1000
b=10**3
a is b   ==> False
a == b   ==> True
#讀取變數(lable)名稱的id,  a is b ==> True, 表示其ID相同
id(a)
id(b)


2015年7月24日 星期五

什麼是開放認證OAuth ? (一)



很多的網站提供OAuth 的認證服務, 包含Facebook,Google, Yahoo 等. 那到底什麼是OAuth 認證?


OAuth( Open Authorization)是一個開放標準,允許使用者讓第三方應用存取該使用者在某一網站上儲存的私密的資源(如相片,影片,聯絡人清單),而無需將使用者名稱和密碼提供給第三方應用。
  1. OAuth允許使用者只提供一個 Access Token,而不是使用者名稱和密碼來存取他們存放在特定服務提供者(Service Provider)的資料, 如Goolge Picasa 網路相簿 。
  2.  用戶端應用程式(Client Application)利用取得的Access Token (有token 表示已獲得使用者授權), 存取某一個使用者私有且受保護資源。
  3. 用戶端應用程式只要利用 HTTP 協定即可使用 OAuth 服務
  4. Token 會在應用程式每次向服務要求資源時,由用戶端應用程式提交以驗證權限,即每一次送出的在HTTP Request Header Authorization 標頭資料中都必須帶有Access Token。
  5. When using OAuth 2, all requests must be made over HTTPS.
  6. Access Token 是有時效性, 過期後要再重新取得Access Token。
  7. Access Token 除了時效性外,也可以定義Scope , 也就是存取範圍或權限。

Google  APIs 使用OAuth 2.0 協定來作為認證與使用者識別, 針對Web Server (PHP, Java, Python,Ruby, ASP.NET), Android APP程式或Web 劉覽器網路程式 (如Javscrip應用程式)。用戶端應用程式要獲得Client Credentials (用戶程式識別), 然後向
Authorization Server 請求以獲得token, 然後用你用的每一個google API 都要帶有這個token 

使用Google's OAuth 2.0 Authorization 程序:

  1. 先去Google Developers Console 註冊你的程式專案, 獲得用戶程式識別, 即取得2個重要資訊, Client id,Client Secret 該Google 知道你這隻程式。還有其他值,如redirect_URI , 這些會依據申請不同種類的Client 會有所差異。

    去Google Developers Console 建立新的用戶端ID 

  2. 在你的應用程式裡,將使用者Redirect 到Google 登入頁面 (Redirect 含client_id, redirect_uri等資訊)
  3. 你的應用程式獲得使用者認證後, Goolge Authorization Server傳回authorization code
    按下 "接受後"就代表使用者充許你的存取

  4. 你的應用程式再用authorization code向Google Authorization Server 去換取一個Access Token, 此請求也會包含scope 欄位, 因為不同的scope 將決定了所得到的Access Token的使用權限與範圍。(scope用來區分,這個Access Token 可以存取 Google+,Google Docs,Google Fusion Table, Google Map等 )
  5. Google Authorization Server 回傳Access Token 及Refresh Token以及Access Token Lifetime
  6. 之後每次存取Google的資料, Google API都要代入Access Token (放在HTTP Header中)
  7. Access Token過期後,用Refresh Token 重新取得新的Access Token
Your application sends a token request to the Google Authorization Server, receives an authorization code, exchanges the code for a token, and uses the token to call a Google API endpoint.


以上流程你可以試著使用 Google OAuth 2.0 Playground 了解整個流程.


Google OAuth 2.0 Playground



文件參考

  1. https://developers.google.com/identity/protocols/OAuth2InstalledApp
  2. 使用 Google fusion tables 




2015年7月16日 星期四

一個簡單的HTTPS GET 程式



用CURL完成一個簡單的HTTPS GET 程式

CURL 一套  open source 命令列工具與函式庫, 以URL語法格式傳送資料

支援協定: DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMTP, SMTPS, Telnet and TFTP. curl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, HTTP/2, cookies, user+password authentication (Basic, Plain, Digest, CRAM-MD5, NTLM, Negotiate and Kerberos)等協定

安裝:  讓libcurl 支援 https 協定, 必須先安裝 OpenSSL 才會有https的支援
  •  (1) 先安裝及下載OpenSSL 函式庫
下載 wget https://www.openssl.org/source/old/0.9.x/openssl-0.9.8y.tar.gz

再進行OpenSSL 函式庫編譯與安裝

tar zxvf openssl-0.9.8y.tar.gz
cd openssl-0.9.8y
make 
make install

  •  安裝libcurl 函式庫

./configure --with-ssl
make 
make install

執行完configure 後, 顯示,目前支擾的協定狀態

目前curl 工具支援的協定
root@raspberrypi:/home/pi/02_gps/curl-7.43.0/src# ./curl -V
curl 7.43.0 (armv7l-unknown-linux-gnueabihf) libcurl/7.43.0 OpenSSL/0.9.8y zlib/1.2.7
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: Largefile NTLM NTLM_WB SSL libz UnixSockets



一個簡單的HTTPS GET 程式


#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
static char *replace(const char *in, const char *pattern, const char *by);
int main(void)
{
 CURL *curl = curl_easy_init();
 char *new_url;
 char *c_rep=" ";
 char *c_with="+";
 char request[]="https://www.googleapis.com/fusiontables/v2/query?sql=SELECT * from 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU&key=AIzaSyDz_-qvtMk7rVWi6NSJMTSv509Df4wTuOg";
 new_url=replace(request,c_rep,c_with);

  fprintf(stderr,"%s\n\n",request);

  if(curl) {
  CURLcode res;
  curl_easy_setopt(curl, CURLOPT_URL,new_url);
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
}

  free(new_url);
  return 0;
}


//replace 函式主要是將 URL 空白字元轉成 + 字元
//sql=SELECT * from  ==> sql=SELECT+*+from
static char *replace(const char *in, const char *pattern, const char *by)
{
    size_t outsize = strlen(in) + 1;
    // TODO maybe avoid reallocing by counting the non-overlapping occurences of pattern
    char *res = malloc(outsize);
    // use this to iterate over the output
    size_t resoffset = 0;

    char *needle;
    while (needle = strstr(in, pattern)) {
        // copy everything up to the pattern
        memcpy(res + resoffset, in, needle - in);
        resoffset += needle - in;
    
        // skip the pattern in the input-string
        in = needle + strlen(pattern);

        // adjust space for replacement
        outsize = outsize - strlen(pattern) + strlen(by);
        res = realloc(res, outsize);

        // copy the pattern
        memcpy(res + resoffset, by, strlen(by));
        resoffset += strlen(by);
    }

    // copy the remaining input
    strcpy(res + resoffset, in);

    return res;
}

程式執行結果




2015年7月13日 星期一

Raspberry Pi2 (ARM Cortex-A7) 上編譯 Raspbain Kernel (4.0.x) with device tree


=====
前言
=====

為了在Raspberry Pi 2板子上面學習開發 Driver,最近吃飽沒事都在編譯 Kernel。之前更發了一篇文章:

Raspberry Pi2 (ARM Cortex-A7) 上編譯Raspbain Kernel

來記錄編譯核心的過程。但是依照文章裡面所說的方式開發 Driver 還是發生了一些問題。有時候會有 gcc compiler 版本的問題,有時候會出現 "your compiler is too buggy" 之類的訊息,這個是因為編譯核心的compiler 和在Pi2上面編譯 driver 的版本不太一致的關係。還有python 的 GPIO library 失效了....,後來才知道這是因為編譯出來的 kernel 不支援 device tree 的關係。

而這次參考官方網站: 


編譯出來的 kernel 總算是ok了,python GPIO也沒問題了..."compiler too buggy" 的訊息也看不到了~~~~~~

這次編譯的方式依然是跨平台編譯,使用的是vmware 裡面的 32 bit ubuntu 系統。


===================
安裝編譯器 (on ubuntu)
===================

登入 ubuntu 系統之後,會進入家目錄底下,首先去 github 下載跨平台編譯器:

$ git clone https://github.com/raspberrypi/tools.git

然後修改 ~/.bashrc 設定檔,最後一行加入:

export PATH=$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin

如果使用的Debian作業系統是64bit,則在~/.bashrc的在最後一行加入:

export PATH=$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin


重新登入一次,使剛剛修改好的.bashrc設定值生效。成功 的話可以輸入arm之後連按兩下tab鍵看看有沒有顯示一堆 arm-linux-gnueabihf- 開頭的執行檔,有的話表示設定生效了。


===================
Download kernle 4.0.y
===================

在家目錄底下用去github download 新版的kernel ,寫這篇文章的時候是 4.0.y

$ git clone https://github.com/raspberrypi/linux.git


================
準備 .config設定檔
================

$ cd ~/linux
$ KERNLE=kernel7
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig

這樣就會直接產生一個預設的.config。如果沒有其他的 kernel 設定上的需求的話就可以開始進行下一步開始編譯核心了。但是如果有一些要自行更改的kernle 選項的話,記得安裝 libncruses-dev 套件,然後:

$ cd ~/linux
$ KERNEL=kernel7
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

========
編譯核心
========

下面的指令會開始進行核心的編譯、模組的編譯、Device Tree 的設定檔的編譯:

$ cd ~/linux

$ KERNEL=kernel7
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs

這個步驟會花很久的時候,如果 ubuntu 系統是多核心的話,可以在make 後面用 -j 參數指定要使用多少顆CPU核心來進行編譯的工作。使用多核心的話可以省很多時間。下面是使用兩顆CPU核心來進行編譯:

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs - j 2

等到編譯完成之後就可以進行下一步。



========================
製作 kernel 核心檔 & module
========================

4.0.y 版的 linux source 裡面有多了一個 script 檔 mkknlimg ,用來產生 kernel image,而不能像之前的做法,直接把產生的 zImage 拿來用:

$ cd ~/linux
$ ./scripts/mkknlimg ./arch/arm/boot/zImage ../kernel7.img

然後先把 module 安裝到上一層目錄裡:

$ cd ~/linux
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=../modules modules_install

進行到目前為止,我們已經得到了編譯好的 kernel source tree 一份、moudle一份、kernel image 一份 (kernel7.img)

先在要想辦法把這三個東西 copy 到一個已有安裝 raspberry pi 系統的家目錄上。

這邊是用透過網路的方式用 scp 把東西copy 到raspberry pi 系統(假設IP為192.168.1.2)上面去:

$ cd ~/
$ scp -r ./linux/ pi@192.168.1.2:~/
$ scp -r ./modules pi@192.168.1.2:~/
$ scp ./kernel7.img pi@192.168.1.2:~/


======================================
接下來的事情都是在 Raspberry Pi 2 系統上面進行:
======================================


=========
安裝kernel
=========

Raspberry Pi2 開機登入之後,先備份舊的 kernel image 檔:

$ sudo mv /media/boot/kernel7.img /media/boot/kernel7.img.backup

之後安裝新的kernel7.img:

$ sudo mv ~/kernel7.img /boot/

====================
安裝 device tree 設定檔
====================

$ cd ~/linux
$ sudo cp ./arch/arm/boot/dts/*.dtb /boot/ 

$ sudo cp ./arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/ 
$ sudo cp ./arch/arm/boot/dts/overlays/README /boot/overlays/


===========
安裝module:
===========
$ sudo cp -Rf ~/moudles/lib/modules /lib/ 
$ sudo cp -Rf ~/moudles/lib/firmware /lib/

$ sudo rm /lib/modules/4.0.7-v7/build
$ sudo rm /lib/modules/4.0.7-v7/source
$ sudo ln -s /home/pi/linux/ /lib/module/4.0.7-v7/build 
$ sudo ln -s /home/pi/linux/ /lib/module/4.0.7-v7/source



===============
安裝 gcc-4.8
===============

增加檔案 /etc/apt/sources.list.d/jessie.list 內容為:

deb http://mirrordirector.raspbian.org/raspbian/ jessie main contrib non-free rpi

$ sudo apt-get update
$ sudo apt-get install gcc-4.8

設定 gcc4.8 為預設的 gcc

$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 20
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50

===================
make module prepare
===================
This will make sure the kernel contains the information required.to build external module

$ cd ~/linux
$ make modules_prepare




========= 其他 ================
使用這個gcc-4.8可以進行kernel driver的編譯,但是在安裝python module 需要使用到gcc的時候會出問題,這個時候會需要安裝 gcc-4.9 並且用下面的指令改變設設gcc為4.9版:

$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 10
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 50

$ sudo rm /usr/bin/arm-linux-gnueabihf-gcc
$ sudo ln -s /usr/bin/gcc-4.9 /usr/bin/arm-linux-gnueabihf-gcc





2015年7月9日 星期四

艾鍗 Pi 子板 v1.0 for Raspberry Pi 第一代




艾鍗 Pi 子板 v1.0 for Raspberry Pi 第一代 ( pin header : 26)

艾鍗 Pi 子板 v1.0 規格:
  1. I2C Serial EEPROM
  2. SPI Flash 
  3. 2x16 文字型背光LCD顯示器
  4. LEDx2 
  5. 電源指示燈 x1 
  6. 自激磁Buzzer x1 
  7. Buttonx2 
  8. Relay繼電器x1 
  9. 紅外線接收模組 
  10. PL2303 UART to USB 
  11. Switch 切換Pi UART 是否要連接PL2303 
  12. 擴充座 
  13. 電源座 (5V,GND) 
  14. PWM座x1 
  15. UART座for藍芽模組
  16. SPI 座for CC2500通訊模組 RF 
  17. I2C 擴充座 
  18. 26 Pin Header座連接Raspberry Pi




2015年7月7日 星期二

使用SIM5320E抓取GPS 訊號


=====
前言:
=====
  自從1957年在蘇俄射上史上第一顆人造衛星之後,人類就已經宣佈了太空的時代了,而到現在2015年了,各國都有所研發,至今為止全球所發射的人造衛星總數已達到了一萬多顆了,然而隨著科技的進步以及衛星的總數也越來越多,而這些衛星的主要目的便是科學實驗的儀器,而我們的主題只有用到衛星中的導航衛星,而美國的GPS全球定位系統共用了1
科導航衛星,要如何利用衛星呢?要如何去利用這離我們20000多公里的衛星呢?

======
介紹:
======
  在這開源的時代裡,就是要讓Maker可以利用這種快術又容易的學習文章,讓更多人可以做自己想要樹莓派,用以下介紹的主題讓自己加入GPS功能或者是SIM的功能。



SIMCOMM SIM5320E


======
操作方式:
======
利用Widows系統下的USB轉Uart接至SIM5320E。並利用Windows下使用Tera Term軟體操作。


Tera Term軟體下選擇Com Port




進入Com Port 之後要先設定連線埠, Baud Rate 預設9600修改成115200

修改之後再輸入AT確認是可否下達AT指令
at下達之後將會收到OK


確認OK之後直接下AT+CGPSINFOCFG=? 命令, 回傳+CCGPSINFOCFG: <0-255>, <0-31>


下達AT+CGPSINFOCFG=10,31 ,可取得得GPS所有封包格式 GPXXX




AT+CGPSINFOCFG 命令是用來回報NEMA-0183 Sentence


AT+CGPSINFOCFG=<time>[,<config>]

<time>
The range is 0-255, unit is second, after set <time> will report the GPS NMEA sentence every the
seconds.
If <time>=0, module stop reporting the NMEA sentence.

<config>
Range – 0 to 31. Default value is 0.
Each bit enables an NMEA sentence output as follows:
Bit 0 – GPGGA (global positioning system fix data)
Bit 1 – GPRMC (recommended minimum specific GPS/TRANSIT data)
Bit 2 – GPGSV (GPS satellites in view)
Bit 3 – GPGSA (GPS DOP and active satellites)
Bit 4 – GPVTG (track made good and ground speed)
Set the desired NMEA sentence bit(s). If multiple NMEA sentence formats are desired, “OR” the
desired bits together.
For example:
If want to report GPRMC sentence by 10 seconds, should execute AT+CGPSINFOCFG=10,2



在此輸入AT+CGPSINFOCFG=5,2
此範例執行說明:在每隔5秒回傳GPRMC數值

關於GPRMC 資料輸出格式說明


RMC - NMEA has its own version of essential gps pvt (position, velocity, time) data. It is called RMC, The Recommended Minimum, which will look similar to:

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A


Where:
     RMC          Recommended Minimum sentence C
     123519       Fix taken at 12:35:19 UTC
     A            Status A=active or V=Void.
     4807.038,N   Latitude 48 deg 07.038' N
     01131.000,E  Longitude 11 deg 31.000' E
     022.4        Speed over the ground in knots
     084.4        Track angle in degrees True
     230394       Date - 23rd of March 1994
     003.1,W      Magnetic Variation
     *6A          The checksum data, always begins with *

-------------------------------------------------------------------------------------------------------------
也可以把SIM5320E接在Raspberry Pi 上,然後用minicom 終端機來下達AT 命令, 結果也是相同的。在文字模式終端機介面下輸入sudo minicom 即可進入軟體選單畫面。若無法執行minicom , 可透過 apt-get install minicom 進行安裝。

進入Minicom之後,修改Serial port setup設定值


Minicom設定值下修改以下數值
Serial Device        : /dev/ttyAMA0
Bps/Par/Bits      : 115200 8N1 
F - Hardware Flow Control : No 
G - Software Flow Control : No


設定好serial port 之後 就可以使用啦~













https://goo.gl/KjTLJs



【Maker研討會】運用藍芽結合APP與樹莓派基礎入門,報名送開發套組!



活動簡介Theme
相較於過往產品原型機的開發總是曠日廢時,這個情況將因 Open Hardware & Source 而獲得全面改善! 在 Linux 作業系統移植到 Raspberry Pi 的開放硬體架構之後,開發者們得以使用高階語言Python來實作控制GPIO功能並透過溝通 I2C、SPI、UART 通訊介面整合各類功能IC。近幾年來因為移動電子產品的進步,智慧型手機已成為人們最常使用的電子資訊與通訊設備。也因此 Raspberry Pi 與手機之間的藍芽通訊應用開發也成為一個重要的課題。
此次講座我們將分享使用Python程式語言在Raspberry Pi開放硬體平台上利用簡易的藍芽序列通訊開發簡單的應用。利用艾鍗科技開發的 Raspberry Pi Education Kit 子板快速整合液晶字元顯示模組、各類基礎 GPIO 功能、和藍芽-Serial通訊介面, 並利用Google App Inventor 開發簡易的 Android 手機應用程式透過藍芽和 Raspberry Pi 溝通並進行硬體操作。適合欲學習 Python 開發的入門 Maker 族群迅速整合各類元件並專注於創意的發想。


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