FPGAでLEDの調光

FPGAでLEDの調光

 LEDの駆動回路を書きましたので、調光についても記述しておきます。

 LED調光はPWM(Pulse Width Modulation)という方法で点灯している時間を調整します。回路は通常のLED点灯回路と同じです。

 この回路図のように、FPGAの信号が0の時LEDは点灯し、1の時消灯します。これを高速に繰り返す事でLEDの明るさを調整できるのです。

 波形と明るさの様子を示すと次のようになります。

 上の様子は1周期を同じにしてあることに気が付いたでしょうか? 調光回路で重要なのはこのように周期を同じにして、デューティーを変化させる。という所です。

 一般的には周期は10msecぐらいを選びます。しかし、LEDによって明るさの変化や輝度等に違いがありますので、適宜調整は必要です。

 では、10msec周期として、デューティーの変化方法ですが、一般的には一度ノコギリ波を作って、コンパレートします。ブロック図では次のようになります。

 ノコギリ波というのは、ブロック図に記したように三角がノコギリの刃のようになった波形です。この波形の周期を今回の周期と同じにします。

 次にレジスタですが、これはノコギリ波のmin~maxまでの値を設定するDFFです。このようにDFFに一度記憶する回路をレジスタやメモリと呼びます。上のブロック図で赤破線の部分になります。

 コンパレータ回路でレジスタの値とノコギリ波の値を比較して、ノコギリ波の方が高ければ1、低ければ0になるようにします。こうすることで、レジスタの値が低ければ暗く、大きければ明るく設定できるようになります。

 ノコギリ波の生成回路を設計してみましょう。クロックは10MHzとします。

  10msec / 100nsec(10MHz) = 100000(カウント)

 10msecを10MHzで数えるには100000回数える必要があります。16進数では186A0(hec)なので、ビット数が多すぎます。逆に10MHzで100(hex)数えると

 100nsec(10MHz) × 100(hex) = 100nsc × 256(dec) = 25.6usec

この時間を256階調のノコギリ波にすると

 25.6nsec × 256 = 6.5536msec

まだ、10msecになりませんので、10MHzを200(hex)数えることにします。すると

 100nsec(10MHz) × 200(hex) = 100nsec × 512(dec) = 51.2usec

 51.2usec × 256(階調) = 13.1072msec

 これぐらいであれば、問題無さそうです。周期を13.1072msecにしましょう。以上の計算は2進数でキレの良い数値を選んでいます。100(hex)も200(hex)も2進数で0が多い数値です。また、256も8ビットです。

 という事で、10MHzで200(hex)まで数えるカウンタを作って、そのパルスでff(hex)周期のノコギリ波を作ります。この値もかなり適当で良いので設計しやすい値にアレンジします。

 明るさ設定用レジスタは上記の256階調という設定から8ビットになります。しあし、256階調もあっても変化が良くわからないと思いますので、16階調でも良いと思います。案外LEDの輝度はPWMの設定値と比例しないことが多いので、256階調にしておいて、その中で適当な値を使う。という方が良いと思います。

 LEDの調光程度であれば、多少の時間は融通がききますので、今回のように解説は簡単です。しかし、PWMといのは最近のFPGAやワンチップマイコンでは多用される回路で、有名なところではD級アンプとか1ビットオーディオなんかもPWMです。この場合はサンプリングを厳密に行いますので、今回のように適当に時間を決める訳にはいきません。

 時間さえ、きちんと管理すれば今回のPWMと動作は同じですので、FPGAに直接オペアンプを接続して1ビットで音を鳴らす。という事も可能になります。

  ソースコードを掲載いたします。VHDLとVerilogで同じ動作です。

 今回はVHDLのテストベンチで解説が必要です。

--  TITLE:					"TB_LED_pwm.vhd"
--  MODULE NAME:			TB_LED_pwm
--  PROJECT CODE:			
--  AUTHOR:					 (****@nakaharagiken.com)
--  CREATION DATE:			2020.7.18
--  SOURCE:            		
--  LICENSE:           		Copyright (c) 2020 nakaharagiken All rights reserved. 
--  DESCRIPTION:            LED_pwm.vhdのテストベンチ
--  NOTES TO USER:			
--  SOURCE CONTROL:			
--  REVISION HISTORY:  	    v0.1	2020.7.18	First edition
--
--
--
--
--
--
--
--
library IEEE;
library work;							-- integer -> std_logic変換用
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;
use ieee.numeric_std.all;				-- integer -> std_logic変換用

entity TB_LED_pwm is
end TB_LED_pwm;

architecture sim of TB_LED_pwm is


component LED_pwm is
port (
	clock:				in	std_logic;						-- system clock 10MHz
	nreset:				in	std_logic;						-- asynchronous reset 				____reset___|~~~normal~~~
	ledonoff:			in	std_logic;						--	LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
	dimmerdat:			in	std_logic_vector(7 downto 0);	--	明るさデータ			dark = 00h  bright = ffh
	dimmerset:			in	std_logic;						--	明るさデータセット				________|~|______________
	LEDcathode:			out	std_logic						--	LED	駆動信号
);
end component;



constant SYSCLK_PERIOD : time := 100 ns; 					-- 10MHZ

signal	i_clock:		std_logic;						-- system clock
signal	i_nreset:		std_logic;						-- asynchronous reset 				____reset___|~~~normal~~~
signal	i_ledonoff:		std_logic;						-- LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
signal	i_dimmerdat:	std_logic_vector(7 downto 0);	-- 明るさデータ			dark = 00h  bright = ffh
signal	i_dimmerset:	std_logic;						-- 明るさデータセット				________|~|______________
signal	o_output:		std_logic;						-- LED	駆動信号

signal	i: 				integer range 0 to 255 := 0;	-- loopの変数宣言

begin


inst_LED_pwm : LED_pwm
port map(
	clock			=>	i_clock,						--	in	std_logic;						system clock 10MHz
	nreset			=>	i_nreset,						--	in	std_logic;						asynchronous reset 				____reset___|~~~normal~~~
	ledonoff		=>	i_ledonoff,						--	in	std_logic;						LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
	dimmerdat		=>	i_dimmerdat,					--	in	std_logic_vector(7 downto 0);	明るさデータ			dark = 00h  bright = ffh
	dimmerset		=>	i_dimmerset,					--	in	std_logic;						明るさデータセット				________|~|______________
	LEDcathode		=>	o_output						--	out	std_logic						LED	駆動信号
);


------------------------------------
-- Clock Driver
------------------------------------
process begin
	i_clock <= '1';
	wait for SYSCLK_PERIOD / 2;
	i_clock <= '0';
	wait for SYSCLK_PERIOD / 2;
end process;


------------------------------------
-- 信号作成
------------------------------------
process begin

	i_nreset <= '0';									--	初期値
	i_ledonoff <= '0';									-- LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
	i_dimmerdat <= (others =>'0');						-- 明るさデータ			dark = 00h  bright = ffh
	i_dimmerset <= '0';									-- 明るさデータセット				________|~|______________

	wait for (SYSCLK_PERIOD * 50);
	i_nreset <= '1';
	wait for (SYSCLK_PERIOD * 100);
	i_ledonoff <= '1';									-- LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
	wait for (SYSCLK_PERIOD * 100);


	for i in 0 to 255 loop
		i_dimmerdat <= std_logic_vector(to_unsigned(i, i_dimmerdat'length));	-- 明るさデータ			dark = 00h  bright = ffh
		i_ledonoff <= '1';														-- LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
		wait for (SYSCLK_PERIOD * 10);
		i_dimmerset <= '1';														-- 明るさデータセット				________|~|______________
		wait for (SYSCLK_PERIOD * 1);
		i_dimmerset <= '0';
		wait for (SYSCLK_PERIOD * 250000);
		i_ledonoff <= '0';														-- LED ON(1) OFF(0)				___off______|~~~on~~~~~~~
		wait for (SYSCLK_PERIOD * 100000);
	end loop;


end process;

end sim;

  20行目と25行目にライブラリを追加してあります。テストベンチでループを回しながら、ループの数に従ってPWMのセット値を変化させているので、ループ定数のiをi_dimmerdatに代入したいため、型変換を行います。

 Verilogでも同じテストベンチを記載していますが、このような型変換は必要ありません。この部分がVHDLは型に厳密だといわれる所以だと思います。

 55行目でループの変数を宣言し、99行目から109行目までループです。ループはiの数を0~255まで変化させていますが、このiをそのままレジスタに代入するために、100行目で型変換を行っています。