introduction
Bonne journée, mes amis! Récemment, au travail, nous avons obtenu une toute nouvelle carte de plate-forme de développement mobile iCE40 UltraPlus de Lattice Semiconductor. Selon les développeurs sur le site officiel de l'iCE40 UltraPlus MDP est une carte avec 4 FPGA iCE40 UltraPlus, chacun contrôlant son propre ensemble de périphériques. L'ensemble comprend:
- écran mobile avec une résolution de 240x240 avec interface MIPI DSI;
- capteur d'image avec une résolution de 640x480 (OVM7692);
- microphones de faible puissance au nombre de 4 pièces;
- Module BLE pour la transmission de données sans fil;
- mémoire flash SPI programmable;
- pack de divers capteurs (pression, boussole, gyroscope et accéléromètre);
- eh bien, toutes sortes de boutons et d'ampoules.
Ce qui est cool avec cette baleine, c'est qu'avec l'aide de progiciels spéciaux, vous pouvez déployer des réseaux de neurones pour travailler avec la vidéo et le son. Et cela sans parler du fait que les FPGA Lattice sont de faible puissance, de petite taille et assez bon marché.
UltraPlus MDP
Comme cas de test, faites clignoter la LED RVB (D13 dans le diagramme, surligné en rouge sur l'image de gauche). Après avoir examiné la documentation, nous concluons que la LED est contrôlée par le numéro FPGA U3 (surligné en rouge sur l'image de droite). Nous apprenons également de la documentation que la LED est contrôlée par un modulateur PWM intégré et un pilote actuel.
Nous prenons note de ces informations.
Mise en place du tableau et rédaction d'un programme
Il y a un groupe de cavaliers sur la carte, avec lesquels vous pouvez sélectionner le FPGA qui doit être flashé pour fonctionner avec le groupe sélectionné de périphériques. Nous nous intéressons à trois groupes de cavaliers chargés de tirer l'alimentation de la LED et de programmer le FPGA souhaité.
La procédure est la suivante:
- Mettre le commutateur SW5 en position ON / OFF
- Deux cavaliers sur J19 horizontalement
- J26 , 1-2 3-4 ( . , )
- J17, J25, J27 9-10 ( )
Oui, je comprends, tout est ennuyeux, mais sans cela, cela ne fonctionnera pas.
De plus, pour connecter le générateur de signal d'horloge, il est nécessaire de régler le cavalier J23 sur la position 2-3 (la numérotation part du haut).
Maintenant le programme. Pour créer un fichier bit pour le micrologiciel iCE40 UltraPlus MDP, vous avez besoin de l'environnement de développement Lattice iCE cube 2 ( lien vers la page produit ) et de flasher la carte de l' outil de programmation et de déploiement elle-même . Le produit est sous licence, mais après enregistrement, la licence peut être obtenue ici www.latticesemi.com/Support/Licensing/DiamondAndiCEcube2SoftwareLicensing/iceCube2
L'éditeur de l'IDE est très gênant, j'ai donc écrit en Sublime Text, mais à chacun son propre.
Voici un schéma général qui a permis de comprendre quoi et où faire:
Ainsi, le modulateur PWM et le pilote actuel, que j'ai mentionnés plus tôt, ont fait surface. Ces deux appareils sont des modules internes. Il est nécessaire d'écrire un dispositif de contrôle logique et d'envoyer des données pour que toute cette cuisine fonctionne correctement. Commençons dans l'ordre, décrivons la "boîte noire":
entity DriverRGB is
port (
-- RGB Led:
LED0 : out std_logic;
LED1 : out std_logic;
LED2 : out std_logic );
end DriverRGB;
L'initialisation de la synchronisation est manquante dans la boîte noire. Pour cela, un module interne est utilisé, qui se déclare comme suit:
-- 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;
Ce module peut générer un signal avec des fréquences de 48MHz, 24MHz, 12MHz et 6MHz. Le paramètre CLKHF_DIV est responsable du facteur de division de fréquence ("0b00", "0b01", "0b10", "0b11", respectivement). CLKHFPU et CLKHFEN permettent au module de fonctionner. CLKHF - signal d'horloge.
Ensuite, nous déclarons le modulateur PWM et le pilote actuel:
-- 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;
Le modulateur PWM doit fournir l'adresse et les données responsables des modes de fonctionnement des LED et de certains signaux de commande. Les sorties sont ensuite traitées par un pilote de courant RVB, qui allume déjà la LED.
Le pilote actuel traite les données du modulateur PWM et ajuste la valeur actuelle fournie à la LED. Les paramètres RGB0_CURRENT, RGB1_CURRENT, RGB2_CURRENT définissent la quantité de courant pour chaque couleur. CURRENT_MODE - mode d'alimentation (plein ou moitié).
Ouais cool. Il y a des adresses, il y a des données. Eh bien, que leur envoyer? En général, les développeurs de Lattice donnent une description assez détaillée dans leur documentation, mais elle est assez volumineuse. J'essaierai de tout compresser en quelques lignes de description et de code pour plus de clarté.
Le modulateur PWM attend 9 adresses. Chacun d'eux est responsable d'une fonction spécifique pour maintenir le fonctionnement de la LED. Ci-dessous, un tableau montrant les valeurs et les noms des adresses:
Pour envoyer des données, nous implémentons la machine à états finis:
type LED_Driver is (IDLE, LEDDBR, LEDDONR, LEDDOFR, LEDDBCRR, LEDDBCFR, LEDDPWRR, LEDDPWRG, LEDDPWRB, LEDDCR0, DONE);
La première étape consiste à écrire des données dans le registre LEDDBR. Il stocke la valeur de la fréquence d'horloge PWM. Il est considéré comme suit:
Register Value N = Fsys/64kHz-1
La structure de l'enregistrement de données ressemble à ceci: Les
deux bits les plus significatifs pour la valeur de fréquence seront ajoutés lorsque nous passons au registre 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;
Le registre LEDDONR enregistre le temps pendant lequel la LED est active. La documentation contient une table de correspondance à laquelle l'ensemble de bits appartient à un certain temps de combustion des LED.
when LEDDONR =>
led_en <= '1';
led_cs <= '1';
led_exe <= '0';
LEDD_ADR <= "1010";
DAT_Bits(7 downto 0) <= "00010001"; -- (0.5 c)
Le registre LEDDOFR contient des données pendant combien de temps la LED est inactive. Exactement les mêmes valeurs que dans 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 - données sur la durée de la LED soft-on.
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 - données sur la durée de l'extinction progressive de la 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;
Les registres LEDDPWRR, LEDDPWRG et LEDDPWRB enregistrent les données sur la luminosité des LED rouge, bleue et verte, respectivement. La valeur de luminosité est calculée en pourcentage par la formule suivante:
ADC(%) = PulseWidth/256
Par conséquent, différentes valeurs de luminosité donnent un mélange de couleurs, ce qui vous permet de jouer et d'atteindre votre idéal.
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;
Eh bien, le dernier registre LEDDCR0 enregistre les informations d'activation et les deux bits les plus significatifs de la fréquence du signal d'horloge 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;
Exemples de mise en œuvre
RVB
Violet / Blanc
Résumer
Eh bien voilà tout. En modifiant les paramètres, vous pouvez obtenir un bel effet de respiration LED avec différentes couleurs et luminosité en modifiant les valeurs dans les registres LEDDPWRR, LEDDPWRG, LEDDPWRB ou la valeur actuelle du pilote RVB. Vous trouverez ci-dessous des liens vers le code sur GitHub et toute la documentation nécessaire.
À l'avenir, je prévois de tester d'autres petits pains et, si possible, de les mettre ici pour examen.
Guide de l'utilisateur de la
carte d' évaluation Code du guide d'utilisation du pilote LED iCE40