PCA9685是一個 I²C 協定控制的16通道PWM輸出IC,這顆IC原本被設計應用在RGBA彩色背光等LED亮度控制上,因其頻率的可編程範圍為24~1526Hz,含蓋了伺服馬達控制的50Hz,聰明的創客們便用它來擴充伺服的控制數量。
這篇文章主要以"VHDL語言撰寫控制PCA9685的程式碼"介紹為主,首先將全部程式碼顯示於下,接著再來看各程式碼的作用
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity PCA9685 is
port( CLK : in std_logic;
Start : in std_logic;
Addr : in std_logic_vector(5 downto 0);
RegAddr : in std_logic_vector(7 downto 0);
RegData : in std_logic_vector(7 downto 0);
SDA : inout std_logic;
SCL : out std_logic;
NoACK : buffer std_logic;
Complete : buffer std_logic);
end PCA9685;
architecture ARCH of PCA9685 is
constant indexStartCondition : integer := 0;
constant indexActive : integer := 10;
constant indexStopCondition : integer := 20;
signal index : integer;
signal delayCnt : integer;
signal bitN : integer;
signal exIndex : integer;
signal exAddr : std_logic_vector(7 downto 0);
begin
process(Start, CLK, index)
begin
if Start = '0' then
SDA <= '1';
SCL <= '1';
NoACK <= '0';
Complete <= '0';
index <= 0;
delayCnt <= 0;
bitN <= 7;
exIndex <= 0;
exAddr <= '1' & Addr & '0';
elsif CLK'event and CLK = '1' then
if Complete = '0' then
case index is
when (indexStartCondition + 0) =>
if delayCnt >= 29 then
delayCnt <= 0;
index <= index + 1;
else
delayCnt <= delayCnt + 1;
end if;
when (indexStartCondition + 1) =>
SDA <= '0';
index <= index + 1;
when (indexStartCondition + 2) =>
if delayCnt >= 14 then
delayCnt <= 0;
index <= index + 1;
else
delayCnt <= delayCnt + 1;
end if;
when (indexStartCondition + 3) =>
index <= indexActive;
--==============================================================================
when (indexActive + 0) =>
SCL <= '0';
index <= index + 1;
when (indexActive + 1) =>
if bitN < 0 then
SDA <= 'Z';
elsif exIndex = 0 then
SDA <= exAddr(bitN);
elsif exIndex = 1 then
SDA <= RegAddr(bitN);
else
SDA <= RegData(bitN);
end if;
index <= index + 1;
when (indexActive + 2) =>
if delayCnt >= 29 then
delayCnt <= 0;
index <= index + 1;
else
delayCnt <= delayCnt + 1;
end if;
when (indexActive + 3) =>
SCL <= '1';
index <= index + 1;
if bitN < 0 then
NoACK <= SDA;
end if;
when (indexActive + 4) =>
if delayCnt >= 29 then
delayCnt <= 0;
index <= index + 1;
else
delayCnt <= delayCnt + 1;
end if;
when (indexActive + 5) =>
if bitN = -1 then
bitN <= 7;
index <= index + 1;
else
bitN <= bitN - 1;
index <= (indexActive + 0);
end if;
when (indexActive + 6) =>
if exIndex = 2 then
exIndex <= 0;
index <= indexStopCondition;
else
exIndex <= exIndex + 1;
index <= (indexActive + 0);
end if;
--==============================================================================
when (indexStopCondition + 0) =>
SCL <= '0';
SDA <= '0';
index <= index + 1;
when (indexStopCondition + 1) =>
if delayCnt >= 29 then
delayCnt <= 0;
index <= index + 1;
else
delayCnt <= delayCnt + 1;
end if;
when (indexStopCondition + 2) =>
SCL <= '1';
index <= index + 1;
when (indexStopCondition + 3) =>
if delayCnt >= 14 then
delayCnt <= 0;
index <= index + 1;
else
delayCnt <= delayCnt + 1;
end if;
when (indexStopCondition + 4) =>
SDA <= '1';
Complete <= '1';
--==============================================================================
when others => null;
end case;
end if;
end if;
end process;
end ARCH;
接下來分別看程式碼中各部份的作用
這邊是定意一些輸入及輸出腳位,其中只有SDA及SCL會與 PCA9685 連接,其它的腳位則在FPGA內部與別的模組連接(當然亦可連接到外部腳位,但可能要改成串列傳輸會比較省腳位及配線)
宣告一些定值及變數
當未動作時,給予控制腳位及變數預設值
在每次傳輸前,要有一個起手式,就是讓SCL保持在高電位,再讓SDA由高電位轉為低電位
開始傳輸資料啦~ 這邊有一個重點是每傳輸8個bit後,Slave會有一個ACK,我們必須讓SCL產生第9個脈衝來承接這個ACK,若沒有ACK代表沒有將資料正確傳送到Slave
要收尾了,這個動作在告知Slave傳輸結束。做法是讓SCL保持在高電位,再讓SDA由低電位轉為高電位
以上是最陽春的作法,每次僅改變一個暫存器的內容。PCA9685有一個 Auto-Increment 的功能,可以一連串的寫入暫存器,效率會大幅提升,有興趣可以自行參閱手冊
關於I²C通訊協定的詳細規則,請參閱 UM10204 手冊
下一篇再來說明如何應用