Presentamos la plataforma de desarrollo móvil FPGA iCE40 UltraPlus de Lattice Semiconductor

Introducción



¡Buen dia amigos! Recientemente, en el trabajo, obtuvimos una nueva placa de plataforma de desarrollo móvil iCE40 UltraPlus de Lattice Semiconductor. Según los desarrolladores en el sitio web oficial del iCE40 UltraPlus, MDP es una placa con 4 FPGA iCE40 UltraPlus, cada uno de los cuales controla su propio conjunto de periféricos. El set incluye:



  • pantalla móvil con una resolución de 240x240 con interfaz MIPI DSI;
  • sensor de imagen con una resolución de 640x480 (OVM7692);
  • micrófonos de baja potencia en la cantidad de 4 piezas;
  • Módulo BLE para transmisión inalámbrica de datos;
  • memoria flash SPI programable;
  • paquete de varios sensores (presión, brújula, giroscopio y acelerómetro);
  • bueno, todo tipo de botones y bombillas.


Lo bueno de esta ballena es que, con la ayuda de paquetes de software especiales, puede implementar redes neuronales para trabajar con video y sonido. Y esto sin mencionar el hecho de que los FPGA Lattice son de bajo consumo, de tamaño pequeño y bastante baratos.



UltraPlus MDP







Como caso de prueba, haga parpadear el LED RGB (D13 en el diagrama, resaltado en rojo en la imagen de la izquierda). Después de revisar la documentación, llegamos a la conclusión de que el LED está controlado por la FPGA número U3 (resaltada en rojo en la imagen de la derecha). También aprendemos de la documentación que el LED está controlado por un modulador PWM incorporado y un controlador de corriente.



Tomamos nota de esta información.



Configurar el tablero y escribir un programa



Hay un grupo de puentes en la placa, con los cuales puede seleccionar el FPGA que necesita ser flasheado para trabajar con el grupo seleccionado de dispositivos periféricos. Nos interesan tres grupos de jumpers encargados de tirar de la fuente de alimentación al LED y programar la FPGA deseada.







El procedimiento es el siguiente:



  1. Coloque el interruptor SW5 en la posición ON / OFF
  2. Dos puentes en J19 horizontalmente
  3. J26 , 1-2 3-4 ( . , )
  4. J17, J25, J27 9-10 ( )


Sí, lo entiendo, todo es aburrido, pero sin él no funcionará.



Además, para conectar el generador de señales de reloj, es necesario colocar el jumper jumper J23 en la posición 2-3 (la numeración va desde arriba).







Ahora el programa. Para crear un archivo de bits para el firmware iCE40 UltraPlus MDP, necesita el entorno de desarrollo Lattice iCE cube 2 ( enlace a la página del producto ) y flashear la propia placa de herramientas de implementación y programación . El producto tiene licencia, pero después del registro, la licencia se puede obtener aquí www.latticesemi.com/Support/Licensing/DiamondAndiCEcube2SoftwareLicensing/iceCube2



El editor en el IDE es muy inconveniente, así que escribí en Sublime Text, pero para cada uno lo suyo.



A continuación, se muestra un esquema general que dio una idea de qué y dónde hacer:







Entonces, el modulador PWM y el controlador actual, que mencioné anteriormente, han aparecido. Estos dos dispositivos son módulos internos. Es necesario escribir un dispositivo de control lógico y enviar datos para que toda esta cocina funcione correctamente. Comencemos en orden, describiendo la "caja negra":



entity DriverRGB is
	port (
		-- RGB Led:
		LED0 	: out std_logic;
		LED1 	: out std_logic;
		LED2 	: out std_logic );
end DriverRGB;


Falta la inicialización de sincronización en el cuadro negro. Para ello, se utiliza un módulo interno, que se declara de la siguiente manera:



-- 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;


imagenEste módulo puede generar una señal con frecuencias de 48MHz, 24MHz, 12MHz y 6MHz. El parámetro CLKHF_DIV es responsable del factor de división de frecuencia ("0b00", "0b01", "0b10", "0b11", respectivamente). CLKHFPU y CLKHFEN permiten que el módulo funcione. CLKHF - señal de reloj.



A continuación, declaramos el modulador PWM y el controlador actual:



-- 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;


imagenEl modulador PWM necesita alimentar la dirección y los datos que son responsables de los modos de funcionamiento del LED y algunas señales de control. Las salidas luego son procesadas por el controlador de corriente RGB, que ya enciende el LED.


El controlador actual procesa los datos del modulador PWM y ajusta la corriente suministrada al LED. Los parámetros RGB0_CURRENT, RGB1_CURRENT, RGB2_CURRENT establecen la cantidad de corriente para cada color. CURRENT_MODE - modo de energía (completo o medio).



imagen



Sí, genial. Hay direcciones, hay datos. Bueno, ¿qué enviarles? En general, los desarrolladores de Lattice en su documentación dan una descripción bastante detallada, pero es bastante voluminosa. Intentaré condensar todo en unas pocas líneas de descripción y código para mayor claridad.



El modulador PWM espera 9 direcciones. Cada uno de ellos es responsable de una función específica para mantener el LED funcionando. A continuación se muestra una tabla que muestra los valores y nombres de las direcciones:







Para enviar datos, implementamos la máquina de estados finitos:



type LED_Driver is (IDLE, LEDDBR, LEDDONR, LEDDOFR, LEDDBCRR, LEDDBCFR, LEDDPWRR, LEDDPWRG, LEDDPWRB, LEDDCR0, DONE);


El primer paso es escribir datos en el registro LEDDBR. Almacena el valor de la frecuencia de reloj PWM. Se considera lo siguiente:



Register Value N = Fsys/64kHz-1


La estructura del registro de datos se ve así: Los







dos bits más significativos para el valor de frecuencia se agregarán cuando pasamos al registro 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;


El registro LEDDONR registra el tiempo en el que el LED está activo. La documentación contiene una tabla de correspondencia a qué conjunto de bits pertenece un determinado tiempo de encendido de LED.



when LEDDONR =>
		led_en			<= '1';
		led_cs			<= '1';
		led_exe			<= '0';
		LEDD_ADR		<= "1010";
		DAT_Bits(7 downto 0)	<= "00010001"; --    (0.5 c)


El registro LEDDOFR contiene datos sobre el tiempo que el LED está inactivo. Exactamente los mismos valores que en 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: datos sobre la duración del encendido suave del 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 - datos sobre la duración del LED apagado suave.



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;


Los registros LEDDPWRR, LEDDPWRG y LEDDPWRB registran los datos sobre el brillo de los LED rojo, azul y verde, respectivamente. El valor de brillo se calcula como un porcentaje mediante la siguiente fórmula:



ADC(%) = PulseWidth/256


Por lo tanto, diferentes valores de brillo dan una mezcla de colores, para que puedas jugar y lograr tu ideal.



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;


Pues bien, en el último registro LEDDCR0, se escribe la información de habilitación y los dos bits más significativos de la frecuencia de la señal del reloj 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;


Ejemplos de implementación



RGB







Morado / Blanco







Resumiendo



Bueno eso es todo. Al cambiar los parámetros, puede lograr un hermoso efecto de respiración del LED con diferentes colores y brillo cambiando los valores en los registros LEDDPWRR, LEDDPWRG, LEDDPWRB o el valor actual del controlador RGB. A continuación se muestran los enlaces al código en GitHub y toda la documentación necesaria.



En el futuro, planeo probar otros bollos y, si es posible, ponerlos aquí para su revisión.



Guía del usuario de la placa de evaluación Código de la

guía de uso del controlador LED iCE40




All Articles