介绍
朋友们,美好的一天!最近在工作中,我们从莱迪思半导体获得了全新的iCE40 UltraPlus移动开发平台板。根据iCE40 UltraPlus官方网站上的开发人员的说法,MDP是一块包含4个iCE40 UltraPlus FPGA的电路板,每个FPGA都控制自己的一组外设。该集合包括:
- 带有MIPI DSI接口的分辨率为240x240的移动显示器;
- 分辨率为640x480(OVM7692)的图像传感器;
- 4支低功率麦克风;
- BLE模块用于无线数据传输;
- 可编程SPI闪存;
- 一整套各种传感器(压力,指南针,陀螺仪和加速度计);
- 好吧,各种各样的按钮和灯泡。
关于这头鲸的最酷的事情是,借助特殊的软件包,您可以部署神经网络来处理视频和声音。更不用说莱迪思FPGA低功耗,小尺寸且相当便宜的事实。
UltraPlus MDP

作为测试用例,请使RGB LED指示灯闪烁(图中的D13,在左侧图片中以红色突出显示)。阅读文档后,我们得出结论,该LED由FPGA编号U3控制(在右图中以红色突出显示)。我们还从文档中了解到,LED由内置PWM调制器和电流驱动器控制。
我们注意到此信息。
设置板并编写程序
板上有一组跳线,您可以使用这些跳线选择需要刷新的FPGA才能与选定的一组外围设备一起工作。我们对三组跳线感兴趣,这些跳线负责为LED供电并为所需的FPGA编程。

步骤如下:
- 将开关SW5设置在ON / OFF位置
- 水平J19上的两个跳线
- J26 , 1-2 3-4 ( . , )
- J17, J25, J27 9-10 ( )
是的,我知道,这很无聊,但是没有它就无法工作。
另外,为了连接时钟信号发生器,必须将跳线J23设置在位置2-3(编号从顶部开始)。

现在的程序。要为iCE40 UltraPlus MDP固件创建一个位文件,您需要Lattice iCE cube 2开发环境(链接到产品页面)并刷新Programmer and Deployment Tool板本身。该产品已获得许可,但是在注册后,可以从以下位置获取许可:www.latticesemi.com/Support/Licensing/DiamondAndiCEcube2SoftwareLicensing/iceCube2
IDE中的编辑器非常不便,因此我以Sublime Text编写,但每个人都有自己的名字。
这是一个通用的方案,可以让您了解做什么和在哪里做:

因此,我前面提到的PWM调制器和电流驱动器已经浮出水面。这两个设备是内部模块。有必要编写一个逻辑控制设备并发送数据,以使整个厨房正常工作。让我们从顺序开始,描述“黑匣子”:
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-电源模式(完全或一半)。

是的,很酷。有地址,有数据。好吧,要寄给他们什么?总的来说,莱迪思的开发人员在其文档中给出了相当详细的描述,但是数量很多。为了清楚起见,我将尝试将所有内容压缩为几行描述和代码。
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寄存器时,将添加频率值的两个最高有效位。
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时钟信号频率的两个最高有效位:
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上的代码和所有必要文档的链接。
将来,我计划测试其他包子,如果可能的话,将它们放在这里进行检查。
评估板用户指南
iCE40 LED驱动器使用指南
代码