前書き
良い一日、友達!最近、LatticeSemiconductorから新しいiCE40UltraPlusモバイル開発プラットフォームボードを入手しました。iCE40 UltraPlusの公式ウェブサイトの開発者によると、MDPは4つのiCE40 UltraPlus FPGAを備えたボードであり、それぞれが独自の周辺機器のセットを制御します。セットに含まれるもの:
- MIPIDSIインターフェイスを備えた240x240の解像度のモバイルディスプレイ。
- 解像度640x480のイメージセンサー(OVM7692);
- 4個の低電力マイク。
- ワイヤレスデータ送信用のBLEモジュール。
- プログラム可能なSPIフラッシュメモリ。
- さまざまなセンサー(圧力、コンパス、ジャイロスコープ、加速度計)のパック。
- まあ、あらゆる種類のボタンと電球。
このクジラのすごいところは、特別なソフトウェアパッケージの助けを借りて、ニューラルネットワークを展開してビデオとサウンドを操作できることです。そしてこれは、LatticeFPGAが低電力で小型で非常に安価であるという事実は言うまでもありません。
UltraPlus MDP

テストケースとして、RGB LEDを点滅させます(図のD13、左の写真で赤で強調表示されています)。ドキュメントを確認した結果、LEDはFPGA番号U3(右の図で赤で強調表示されている)によって制御されていると結論付けました。また、ドキュメントから、LEDは内蔵のPWM変調器と電流ドライバーによって制御されることがわかります。
この情報に注意します。
ボードのセットアップとプログラムの作成
ボードにはジャンパーのグループがあり、選択した周辺デバイスのグループと連携するためにフラッシュする必要のあるFPGAを選択できます。LEDへの電源供給を引き上げ、目的のFPGAをプログラミングする3つのグループのジャンパーに関心があります。

手順は次のとおりです。
- スイッチSW5をON / OFFの位置に設定します
- J19の2つのジャンパーを水平に
- J26 , 1-2 3-4 ( . , )
- J17, J25, J27 9-10 ( )
はい、私は理解しています、それはすべて退屈ですが、それなしでは機能しません。
また、クロック信号発生器を接続するためには、ジャンパージャンパーJ23を2〜3の位置に設定する必要があります(番号は上から順になります)。

今プログラム。 iCE40 UltraPlus MDPファームウェアのビットファイルを作成するには、Lattice iCEキューブ2開発環境(製品ページへのリンク)が必要であり、Programmer and DeploymentToolボード自体をフラッシュする必要があります。製品はライセンスされていますが、登録後、ここでライセンスを取得できますwww.latticesemi.com/Support/Licensing/DiamondAndiCEcube2SoftwareLicensing/iceCube2
IDEのエディターは非常に不便なので、Sublime Textで書きましたが、それぞれ独自のものです。
これは、何をどこで行うかを理解するための一般的なスキームです。

そのため、前述したPWM変調器と電流ドライバが登場しました。これら2つのデバイスは内部モジュールです。このキッチン全体が正しく機能するためには、ロジック制御デバイスを作成し、データを送信する必要があります。「ブラックボックス」について説明しながら、順番に始めましょう。
entity DriverRGB is
port (
-- RGB Led:
LED0 : out std_logic;
LED1 : out std_logic;
LED2 : out std_logic );
end DriverRGB;
ブラックボックスに同期の初期化がありません。このために、次のように宣言されている内部モジュールが使用されます。
-- Generator clock:
component SB_HFOSC is
generic (
CLKHF_DIV : string := "0b00" );
port (
CLKHFPU : in std_logic;
CLKHFEN : in std_logic;
CLKHF : out std_logic );
end component;

次に、PWM変調器と現在のドライバを宣言します。
-- Embedded PWM IP:
component SB_LEDDA_IP is
port (
LEDDCS : in std_logic;
LEDDCLK : in std_logic;
LEDDDAT7 : in std_logic;
LEDDDAT6 : in std_logic;
LEDDDAT5 : in std_logic;
LEDDDAT4 : in std_logic;
LEDDDAT3 : in std_logic;
LEDDDAT2 : in std_logic;
LEDDDAT1 : in std_logic;
LEDDDAT0 : in std_logic;
LEDDADDR3 : in std_logic;
LEDDADDR2 : in std_logic;
LEDDADDR1 : in std_logic;
LEDDADDR0 : in std_logic;
LEDDDEN : in std_logic;
LEDDEXE : in std_logic;
LEDDRST : in std_logic;
PWMOUT0 : out std_logic;
PWMOUT1 : out std_logic;
PWMOUT2 : out std_logic;
LEDDON : out std_logic );
end component;
-- RGB Driver:
component SB_RGBA_DRV is
generic (
CURRENT_MODE : string := "0b0";
RGB0_CURRENT : string := "0b000000";
RGB1_CURRENT : string := "0b000000";
RGB2_CURRENT : string := "0b000000" );
port (
CURREN : in std_logic;
RGBLEDEN : in std_logic;
RGB0PWM : in std_logic;
RGB1PWM : in std_logic;
RGB2PWM : in std_logic;
RGB0 : out std_logic;
RGB1 : out std_logic;
RGB2 : out std_logic );
end component;

電流ドライバは、PWM変調器からのデータを処理し、LEDに供給される電流を調整します。パラメータRGB0_CURRENT、RGB1_CURRENT、RGB2_CURRENTは、各色の電流量を設定します。 CURRENT_MODE-電源モード(フルまたはハーフ)。

うん、かっこいい。アドレスがあり、データがあります。さて、彼らに何を送るべきですか?一般に、Latticeの開発者はドキュメントでかなり詳細な説明をしますが、それは非常に膨大です。わかりやすくするために、すべてを数行の説明とコードに要約します。
PWM変調器は9つのアドレスを期待します。それらのそれぞれは、LEDを機能させ続けるための特定の機能を担当します。以下は、アドレスの値と名前を示す表です:

データを送信するために、有限状態マシンを実装します:
type LED_Driver is (IDLE, LEDDBR, LEDDONR, LEDDOFR, LEDDBCRR, LEDDBCFR, LEDDPWRR, LEDDPWRG, LEDDPWRB, LEDDCR0, DONE);
最初のステップは、LEDDBRレジスタにデータを書き込むことです。PWMクロック周波数の値を格納します。それは次のように考えられます:
Register Value N = Fsys/64kHz-1
データレコードの構造は次のように

なります。LEDDCR0レジスタに目を向けると、周波数値の最上位2ビットが追加されます。
when LEDDBR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "1001";
DAT_Bits(7 downto 0) <= "11101101"; -- ( )
PWM_state_next <= LEDDONR;
LEDDONRレジスタは、LEDがアクティブである時間を記録します。ドキュメントには、ビットのセットが特定のLED書き込み時間に属する対応の表が含まれています。
when LEDDONR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "1010";
DAT_Bits(7 downto 0) <= "00010001"; -- (0.5 c)
LEDDOFRレジスタには、LEDが非アクティブである時間のデータが含まれています。LEDDONRとまったく同じ値。
when LEDDOFR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "1011";
DAT_Bits(7 downto 0) <= "00010001"; -- (0.5 c)
PWM_state_next <= LEDDBCRR;
LEDDBCRR-LEDソフトオンの期間に関するデータ。
when LEDDBCRR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "0101";
DAT_Bits(7) <= '1'; -- ()
DAT_Bits(6) <= '1'; --
DAT_Bits(5) <= '1'; --
DAT_Bits(4) <= '0'; -- RESERVED
DAT_Bits(3 downto 0) <= "0011"; -- (0.5 )
PWM_state_next <= LEDDBCFR;
LEDDBCRR-LEDのソフトオフの持続時間に関するデータ。
when LEDDBCFR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "0110";
DAT_Bits(7) <= '1'; -- () (disable/enable)
DAT_Bits(6) <= '0'; -- PWM Range Extend
DAT_Bits(5) <= '1'; --
DAT_Bits(4) <= '0'; -- RESERVED
DAT_Bits(3 downto 0) <= "0011"; -- (0.5 )
PWM_state_next <= LEDDPWRR;
レジスタLEDDPWRR、LEDDPWRG、およびLEDDPWRBは、それぞれ赤、青、および緑のLEDの輝度に関するデータを記録します。輝度値は、次の式によってパーセンテージとして計算されます。
ADC(%) = PulseWidth/256
したがって、明るさの値が異なると色が混ざり合うので、遊んで理想を達成することができます。
when LEDDPWRR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "0001";
DAT_Bits(7 downto 0) <= "00000001"; -- RED Pulse Width
PWM_state_next <= LEDDPWRG;
when LEDDPWRG =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "0010";
DAT_Bits(7 downto 0) <= "11111111"; -- GREEN Pulse Width
PWM_state_next <= LEDDPWRB;
when LEDDPWRB =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "0011";
DAT_Bits(7 downto 0) <= "00011111"; -- BLUE Pulse Width
PWM_state_next <= LEDDCR0;
さて、最後のレジスタLEDDCR0には、イネーブル情報とPWMクロック信号周波数の最上位2ビットが書き込まれます。
when LEDDCR0 =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "1000";
DAT_Bits(7) <= '1'; -- ()
DAT_Bits(6) <= '1'; -- Flick Rate Select Bit (125/250 Hz)
DAT_Bits(5) <= '0'; -- (1/0)
DAT_Bits(4) <= '0'; --
DAT_Bits(3) <= '1'; -- Blinking Sequence Quick Stop Enable Bit
DAT_Bits(2) <= '0'; -- PWM Mode Selection Bit
DAT_Bits(1 downto 0) <= "10"; --
PWM_state_next <= DONE;
実装例
RGB

パープル/ホワイト

要約
さて、それがすべてです。パラメータを変更することにより、レジスタLEDDPWRR、LEDDPWRG、LEDDPWRBの値、またはRGBドライバの現在の値を変更することにより、さまざまな色と明るさのLEDの美しい呼吸効果を実現できます。以下は、GitHubのコードと必要なすべてのドキュメントへのリンクです。
将来的には、他のパンをテストし、可能であれば、レビューのためにここに置く予定です。
評価ボードユーザーガイド
iCE40LEDドライバー使用ガイド
コード