モータのPWM駆動やラジコンサーボの信号生成などでは 様々な周波数、デューティ比のパルスを生成する必要があります。 ところがPICやH8といったマイコンに内蔵されたパルスジェネレータでは すぐチャンネル数が足りなくなってしまいます。
CPLDを使用すれば、多チャンネルのパルスジェネレータを手軽かつ小さな回路規模で 作成することができます。 本稿では、XilinxのCPLD,XC95108を使用して Hブリッジ駆動用PWM信号3ch、サーボ用信号4chを生成する場合を例にとって説明します。
| ピン名 | 型 | 説明 |
|---|---|---|
| rst | in std_logic | リセット入力 |
| clk | in std_logic | ベースクロック |
| trig | in std_logic | 読み込みトリガ |
| addr_data | in std_logic | アドレス/データ切替 |
| data | in std_logic_vector(7 downto 0) | 8ビットバス |
| srv_a〜srv_d | out std_logic | サーボ信号出力 |
| pwm_a〜pwm_c | out std_logic_vector(3 downto 0) | Hブリッジ駆動出力 |
| 信号名 | 型 | 説明 |
|---|---|---|
| addr | std_logic_vector(2 downto 0) | アドレス |
| cnt | std_logic_vector(7 downto 0) | 8ビットカウンタ |
| phase_cnt | std_logic_vector(2 downto 0) | 周期カウンタ |
| srv_cmp_a〜srv_cmp_d | std_logic_vector(7 downto 0) | サーボ信号パルス幅 |
| pwm_cmp_a〜pwm_cmp_c | std_logic_vector(3 downto 0) | PWM信号パルス幅 |
| pwm_drv_a〜pwm_drv_c | std_logic | HブリッジOn/Off |
| pwm_dir_a〜pwm_dir_c | std_logic | Hブリッジ正逆転 |
process(clk, rst) begin
if(rst = '0') then
-- リセット処理(省略)
elsif(clk'event and clk = '1') then
-- メインカウンタのインクリメント
cnt <= cnt + 1;
if(cnt = "11111111") then
phase_cnt <= phase_cnt + 1;
end if;
if(cnt = "00000000") then
-- サーボ信号の立ち上げ
case phase_cnt is
when "000" => if(srv_cmp_a /= "00000000") then srv_a <= '1'; end if;
when "001" => if(srv_cmp_b /= "00000000") then srv_b <= '1'; end if;
when "010" => if(srv_cmp_c /= "00000000") then srv_c <= '1'; end if;
when "011" => if(srv_cmp_d /= "00000000") then srv_d <= '1'; end if;
when others =>
end case;
-- PWM信号の立ち上げ
if(pwm_cmp_a /= "0000") then pwm_drv_a <= '1'; end if;
if(pwm_cmp_b /= "0000") then pwm_drv_b <= '1'; end if;
if(pwm_cmp_c /= "0000") then pwm_drv_c <= '1'; end if;
end if;
-- サーボ信号の立ち下げ
case phase_cnt is
when "000" => if(srv_cmp_a = cnt) then srv_a <= '0'; end if;
when "001" => if(srv_cmp_b = cnt) then srv_b <= '0'; end if;
when "010" => if(srv_cmp_c = cnt) then srv_c <= '0'; end if;
when "011" => if(srv_cmp_d = cnt) then srv_d <= '0'; end if;
when others =>
end case;
-- PWM信号の立ち下げ
if(pwm_cmp_a = cnt(7 downto 4)) then pwm_drv_a <= '0'; end if;
if(pwm_cmp_b = cnt(7 downto 4)) then pwm_drv_b <= '0'; end if;
if(pwm_cmp_c = cnt(7 downto 4)) then pwm_drv_c <= '0'; end if;
end if;
end process;
|
![]() |
| Fig.1 パルス生成の信号チャート |
信号チャートをFig.1に示します。 ベースクロックclkには外部から100kHzを入力します。マイコンのクロック出力機能を使えばよいでしょう。 cntはclkによってカウントアップする8ビットアップカウンタです。
初めにHブリッジ信号から説明しましょう。 cntがオーバフローして0になった瞬間にpwm_xを立ち上げ、 cntがデュティ比指定信号pwm_cmp_xに一致した瞬間に対応するpwm_drv_xを立ち下げます。 clkが100[kHz]ですので、PWM信号の周波数は100[kHz] / 256 = 400[Hz]となります。
次にサーボ信号ですが、srv_xの各信号は一周期ずつずれてパルスを発生します
(これは、多数のサーボ信号を同時に立ち上げると、電圧降下によりサーボの制御回路が誤動作するという話を耳にしたからです。
アナログサーボは関係ないかも知れません)。
cntがオーバフローする度にphasecntがカウントアップします。
phasecntが0, 1, 2, 3の時に、それぞれsrv_a, srv_b, srv_c, srv_dがパルスを発生します。
phasecntが3ビットカウンタですので、サーボ信号の周期は100[kHz] / 256 / 8 = 50[Hz]となります。
また、生成されるパルスの幅は(srv_cmp_x / 100)[μs]となります。
srv_cmp_xは0〜255の値をとれますので、0.8[μs]や2.2[μs]といった「規格外の」パルス幅の信号を生成することも可能です。
pwm_a(0) <= '1' when (pwm_drv_a = '1' and pwm_dir_a = '0') else '0'; pwm_a(1) <= '1' when (pwm_drv_a = '1' and pwm_dir_a = '1') else '0'; pwm_a(2) <= '1' when (pwm_drv_a = '1' and pwm_dir_a = '1') else '0'; pwm_a(3) <= '1' when (pwm_drv_a = '1' and pwm_dir_a = '0') else '0'; |
従来はロジックICにさせていた論理変換もCPLDで行います。 これでCPLDの出力をHブリッジの各FETにつなぐだけでOK。 省スペース化にも貢献します。
process(rst, trig) begin
if(rst = '0') then
-- リセット処理(省略)
elsif(trig'event and trig = '1') then
if(addr_data = '1') then
-- アドレスを記憶
addr <= data(2 downto 0);
else
case addr is
when "000" => srv_cmp_a <= data;
when "001" => srv_cmp_b <= data;
when "010" => srv_cmp_c <= data;
when "011" => srv_cmp_d <= data;
when "100" => pwm_dir_a <= data(4); pwm_cmp_a <= data(3 downto 0);
when "101" => pwm_dir_b <= data(4); pwm_cmp_b <= data(3 downto 0);
when others => pwm_dir_c <= data(4); pwm_cmp_c <= data(3 downto 0);
end case;
end if;
end if;
end process;
|
![]() |
| Fig.2 通信プロセスの信号チャート |
外部マイコンからの指令によってサーボ信号のパルス幅やHブリッジ信号のPWMデュティ比、正逆転を更新するためのプロセスです。 信号チャートをFig.2に示します。 まず8ビット単方向バス(命名がよくありませんね;;)に更新したい信号のアドレスが入力され、 トリガ信号trigの立ち上がりと共に内部信号addrに格納されます。 続いてdataに新しい値が入力されtrigの2度目の立ち上がりによってaddrに対応する信号が更新されます。 アドレスと対応する信号、データ構成を以下にまとめます。
| アドレス | 信号 | データ構成 |
| 0 | サーボ信号A | パルス幅[μs] * 100 |
| 1 | サーボ信号B | 同上 |
| 2 | サーボ信号C | 同上 |
| 3 | サーボ信号D | 同上 |
| 4 | Hブリッジ信号A | 第0〜3ビット→4ビット(16段階)PWMデュティ比 第4ビット→正逆転 |
| 5 | Hブリッジ信号B | 同上 |
| 6 | Hブリッジ信号C | 同上 |
今回解説したVHDLソースコードをXC95108にインプリメントしたところ、使用マクロセル数は108中84となりました。 あと数チャンネル分追加することができそうです。 これより大幅にチャンネル数を増やしたい場合はより大規模なCPLDかFPGAを採用するべきでしょう。
今回XC95108を採用したのは、規模的に必要十分であったということに加えて、 PLCCパッケージのためソケットを使用することで容易にユニバーサル基板上に実装できたということもあります。 より大規模なCPLDやFPGAになると実装性や保守性においてリスクが増加してくることを気に留めておくべきでしょう。