Il est possible de gérer le matériel d'une carte arduino avec le logiciel octave en utilisant le paquetage arduino de octave. Ce paquetage offre un ensemble de fonctions qui permettent d'utiliser toutes les fonctionnalités de l'arduino.
La procédure d'initialisation de l'ensemble est, à partir d'octave :
Notez que je décline toutes responsabilités quant aux conséquences que pourraient avoir l'utilisation des programmes présentés. Ceux-ci pourraient être erronés ou obsolètes.
Code source arduino_chenillard.m
port=[ 2, 3, 4, 5, 6, 7, 8, 9 ]; valeurs = zeros(1,8); valeurs(1) = 1 ; while true for i=1:8 ledpin=sprintf("d%d",port(i)); writeDigitalPin(ar,ledpin,valeurs(i)); endfor valeurs=shift(valeurs,1); pause(0.5); endwhile
Avant d'exécuter ce code, il ne faut pas oublier de charger le paquetage arduino, programmer l'arduino avec arduinosetup, puis créer l'objet arduino avec ar=arduino
Le vecteur des 8 valeurs est initialisé à 1,0,0,0,0,0,0,0.
La boucle for permet de gérer les 8 broches du port 8 bits. Le vecteur port contient les valeurs des GPIOs utilisés.
Le numéro de port n est transformé en chaîne dn avec sprintf. La fonction writeDigitalPin permet d'écrire la valeur sur la broche.
la fonction pause effectue un délai de x secondes (ici 0.5 s).
On utilise Ctrl-C pour sortir de la boucle infinie, puis on n'oublie pas delete(ar).
Code source bh1745_init.m
function bh1745_init(dev) writeRegister(dev,0x40,0x80); writeRegister(dev,0x40,0); writeRegister(dev,0x41,2); writeRegister(dev,0x42,0x11); writeRegister(dev,0x44,2); endfunction
Code source bh1745_lireID.m
function [v]=bh1745_lireID(dev) v=readRegister (dev, 0x92, 1); endfunction
La fonction writeRegister permet d'écrire la valeur dans le registre du composant I2C.
Code source bh1745_lireReg.m
function [d,lsb,msb]=bh1745_lireReg(dev,reg) lsb=readRegister(dev, reg, 1); msb=readRegister(dev,reg+1,1); d = cast(lsb,"double") + cast(msb,"double") *256; endfunction
La fonction readRegister permet de lire la valeur d'un registre du composant I2C.
La fonction cast permet de convertir les entiers 8 bits retournés par la fonction readRegister en réel pour pouvoir effectuer le calcul correctement.
Code source bh1745_lireRGBL.m
function [r,g,b,l]=bh1745_lireRGBL(dev) r=bh1745_lireReg(dev,0x50); g=bh1745_lireReg(dev,0x52); b=bh1745_lireReg(dev,0x54); l=bh1745_lireReg(dev,0x56); endfunction
Code source arduino_bh1745.m
ar=arduino dev=i2cdev(ar,0x38) bh1745_init(dev); id=bh1745_lireID(dev); printf("%02x\n",id); while true [r,g,b,l]=bh1745_lireRGBL(dev); printf("%d;%d;%d;%d\n",r,g,b,l); pause(0.5); endwhile
La version d'octave utilisée utilise encore i2cdev pour initialiser le périphérique I2C, mais elle peut être remplacée par device suivant la version d'octave. Cette étape permet de créer l'objet I2C.
Ensuite on utilise les fonctions précédentes pour initialiser le circuit puis afficher les valeurs du capteur.
La carte MKR Vidor 4000 comprend une partie arduino avec un SAMD et une partie FPGA avec un cyclone 10LP d'Intel. Ce dernier peut servir d'interface HDMI, MPI ou encore mini-PCI pour le processeur. Cette carte est très intéressante du point vue pédagogique, car elle peut être utilisée pour des applications :
La carte MKRVidor4000 fait partie de la famille MKR.
En plus du processeur SAMD21 Cortex®-M0+ 32bits, cette carte embarque le FPGA d'intel cyclone 10LP. L'utilisation de cette carte nécessite d'utiliser l'IDE arduino et le logiciel Quartus d'Intel présenté dans le guide de démarrage. Le fonctionnement et la mise en oeuvre de cette carte est également décrite sur developpez.com.
L'utilisation de cette carte nécessite d'installer les cartes MKR à partir de l'IDE.
Le FPGA dispose de plus d'entrées sorties que le processeur, le reste des entrées sorties sont partagées par un circuit RAM, la sortie HDMI (voir le paragraphe suivant), l'entrée de la caméra ainsi que le bus mini-PCI.
Ces entrées sorties sont directement liées aux périphériques, ce qui signifie qu'il faut écrire le code VHDL ou verilog de gestion des signaux des périphériques. Par exemple, pour la sortie HDMI, il faut créer tous les signaux du standard HDMI.
Le fait que la carte MKRVidor4000 comporte une FPGA et un processeur implique que les entrées sorties de cette carte sont communes aux deux composants.
La représentation des entres sorties de la carte montre la répartition sur le processeur ainsi que sur le FPGA, en partant de l'intérieur vers l'extérieur, on trouve
Notez que je décline toutes responsabilités quant aux conséquences que pourraient avoir l'utilisation des programmes présentés. Ceux-ci pourraient être erronés ou obsolètes.
Certains codes sources sont insiprés des librairies disponibles sur le site github des librairies vidor.
On va maintenant étudier l'implémentation en VHDL d'un chenillard sur le FPGA. Le développement de cette application nécessite de développer la partie VHDL avec quartus et la partie arduino avec l'IDE arduino.
Les sorties 0 à 7 de la carte MKRVidor4000 sont connectées aux 8 LEDS les plus à droite, la sortie 8 est connectée à la neuvième LED la plus à gauche.
Les sorties 0 à 7 composent le chenillard qui est piloté par le FPGA, la sortie 8 est connectée à une sortie du processeur qui assure son clignotement.
Code du diviseur de fréquence diviseur.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity diviseur is
Generic(N : positive := 50000000);
port (
clk: in STD_LOGIC;
reset: in STD_LOGIC;
tc_out: out STD_LOGIC
);
end diviseur;
architecture arch_diviseur of diviseur is
signal cpt : std_logic_vector(31 downto 0) := x"00000000";
signal tc : std_logic;
constant max : positive := N-1;
begin
tc_out <= tc;
comptage: process(clk,reset)
begin
if reset = '0' then
cpt <= (others => '0');
else
if rising_edge(clk) then
if cpt < max then
cpt <= cpt + 1;
else
cpt <= (others => '0');
end if;
end if;
end if;
end process comptage;
retenue: process(cpt)
begin
if cpt= max then
tc <= '1';
else
tc <='0';
end if;
end process retenue;
end arch_diviseur;
Code du chenillard chenillard.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity chenillard is
port (
iCLK: in STD_LOGIC;
iRESETn: in STD_LOGIC;
bMKR_Q: out STD_LOGIC_VECTOR(0 to 7));
end chenillard;
architecture arch_chenillard of chenillard is
component diviseur is
Generic(N : positive := 50000000);
Port ( clk : in STD_LOGIC;
reset: in STD_LOGIC;
tc_out : out STD_LOGIC);
end component;
signal tc : STD_LOGIC ;
signal qreg : STD_LOGIC_VECTOR(0 to 7);
constant division : positive := 10000000 ;
begin
div1s: diviseur generic map (N => division)
port map(clk => iCLK, reset => iRESETn , tc_out => tc);
bMKR_Q <= qreg;
registres: process(iCLK,tc,iRESETn)
begin
if iRESETn = '0' then
qreg <= ( 0 => '1', others => '0');
elsif tc = '1' then
if rising_edge(iCLK) then
qreg <= qreg(7) & qreg(0 to 6);
end if;
end if;
end process registres;
end arch_chenillard;
Pour comprendre le code VHDL, il faut se reporter au chapitre qui décrit ce langage.
#include <wiring_private.h>
#include "jtag.h"
#define TDI 12
#define TDO 15
#define TCK 13
#define TMS 14
#define MB_INT 28
#define MB_INT_PIN 31
#define SIGNAL_OUT 41 //B5 L16
#define SIGNAL_IN 33 //B2 N2
#define no_data 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, \
0x00, 0x00, 0x00, 0x00 \
#define NO_BOOTLOADER no_data
#define NO_APP no_data
#define NO_USER_DATA no_data
__attribute__ ((used, section(".fpga_bitstream_signature")))
const unsigned char signatures[4096] = {
//#include "signature.ttf"
NO_BOOTLOADER,
0x00, 0x00, 0x08, 0x00,
0xA9, 0x6F, 0x1F, 0x00, // Don't care.
0x20, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x65, 0x73, 0x2d, 0x65, 0x6d, 0x62, 0x61, 0x72, 0x71, 0x75, 0x65, 0x73, 0x2e, 0x66, 0x72, 0x20, 0x00, 0x00, 0xff, 0xf0, 0x0f,
0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, // Force
NO_USER_DATA,
};
__attribute__ ((used, section(".fpga_bitstream")))
const unsigned char bitstream[] = {
#include "app.h"
};
//========== declarations application ==========================
#define DEMI_PERIODE 500
#define CLIGNOTANT 8
//==============================================================
void setup() {
int ret;
uint32_t ptr[1];
//enableFpgaClock();
pinPeripheral(30, PIO_AC_CLK);
clockout(0, 1);
delay(1000);
//Init Jtag Port
ret = jtagInit();
mbPinSet();
// Load FPGA user configuration
ptr[0] = 0 | 3;
mbEveSend(ptr, 1);
// Give it delay
delay(1000);
// Configure onboard LED Pin as output
pinMode(LED_BUILTIN, OUTPUT);
// Disable all JTAG Pins (usefull for USB BLASTER connection)
pinMode(TDO, INPUT);
pinMode(TMS, INPUT);
pinMode(TDI, INPUT);
pinMode(TCK, INPUT);
// Configure other share pins as input too
pinMode(SIGNAL_IN, INPUT); // oSAM_INT
pinMode(MB_INT_PIN, INPUT);
pinMode(MB_INT, INPUT);
//========== setup application =================================
pinMode(CLIGNOTANT,OUTPUT);
//==============================================================
}
//========== loop application =================================
void loop() {
digitalWrite(CLIGNOTANT,HIGH);
delay(DEMI_PERIODE);
digitalWrite(CLIGNOTANT,LOW);
delay(DEMI_PERIODE);
}
//==============================================================
Le code est copié à partir des exemples cités auparavant. Seul la gestion du clignotement d'une LED a été ajouté.
L'interface HDMI (High Definition Multimedia Interface) est composée de trois canaux de transmission de données et d'un canal d'horloge pixel. Les données transmises contiennent la vidéo, les signaux de synchronisation et éventuellement le son ainsi que des données supplémentaires.
Une trame vidéo est composée de la partie affichée suivie de signaux de synchronisation nommés fin de ligne et fin de trame.
En dehors de l'image vidéo qui correspond à la l'image affichée, les données correspondent aux données audio ou bien aux données de synchronisation ligne et trame. Les différentes valeurs horizontales HRES , HSS , HSE , HTotal et verticales VRES , VSS , VSE , VTotal dépendent de la résolution de l'image. Pour une image 640x480 elles valent :
taille | HRES | HSS | HSE | HTotal | VRES | VSS | VSE | VTotal |
---|---|---|---|---|---|---|---|---|
640x480 | 640 | 656 | 752 | 762 | 480 | 489 | 492 | 525 |
Les données HDMI sont encodées en TMDS puis sérialisées pendant une période de l'horloge pixel.
Les données binaires sont transmises en différentiel en mode NRZ (non retour à zéro) où un 1 correspond à +3v et un 0 à -3v.
L'encodage TMDS (Transition Minimized Differantial Signaling) permet de minimiser les transitions ainsi que la valeur moyenne lors de la transmission des données. Une donnée de 8 bits est transmise sur 10 bits, chacun des bits supplémentaires est codé au cours de deux étapes.
Etape 1
Paramètre en entrée : datain (octet) Paramètres en sortie : dataout (octet), bit8 (1bit), nbits1, (4 bits) Début nbits1in ← nombre de bits à 1 de datain; Si (nbits1in > 4) ∨ ((nbits1in = 4) ∧ (dataint(0) = 0)) Alors dataout(0) ← ¬datain(0); bit8 ← 0; Pour i de 1 à 7 faire dataout(i) ← ¬(dataout(i-1) ⊕ datin(i)); Fin pour Else dataout(0) ← datain(0); bit8 ← 1; Pour i de 1 à 7 faire dataout(i) ← dataout(i-1) ⊕ datin(i); Fin pour Fin si nbits1 ← nombre de bits à 1 de dataout; Fin
Le codage utilisé dépend du nombre de bits à 1 ainsi que de la parité de la valeur. Si le nombre de bits est supérieur à 4 ou égal à 4 à condition que ce nombre soit pair, alors le codage est un NON OU exclusif bit par bit entre le bit i de la donnée et le bit i-1 du résultat, dans l'autre cas il s'agit d'un OU exclusif. Le bit de contrôle 8 est positionné en fonction de l'opération effectuée : 0 pour le NON OU exclusif et 1 pour le OU exclusif.
La signification des symboles ∨ , ∧ et ¬ est présentée dans le chapitre sur l'algèbre de Boole.
Etape 2
Paramètre en entrée : datain (octets), nbits1 (4 bits) de l'étape 1, sommecumul (5 bits signés) Paramètres en sortie : datatmds (octet), sommecumul (5 bits signés) Début Si (sommecumul =0) ∨ (nbits1 = 4) Alors Si bit8 = 1 Alors datatmds ← datain + 0x100; sommecumul ← sommecumul + 2*nbits - 8; Else datatmds ← ¬datain + 0x200; sommecumul ← sommecumul - 2*nbits + 8; Fin si Else Si ((sommecumul ≥ 0) ∧ (nbits1 > 4) ∨ (sommecumul < 0) ∧ nbits < 4) Alors datatmds ← ¬datain + 256*bit8 + 0x200; sommecumul ← sommecumul + 2*bit8 - 2*nbits + 8; Else datatmds ← ¬datain + 256*bit8 ; sommecumul ← sommecumul - 2*(¬bit8) + 2*nbits - 8; Fin si Fin si Fin
Cette étape inverse ou non les 8 bits de données en fonction de la somme cumulée précédente. Pour comprendre l'intérêt de ce calcul, il faut penser à raisonner sur le signal NRZ ou un bit à 1 correspond à 3v et un bit à 0 à -3v. Ce qui fait que 4 bits à 1 et 4 bits 0 donnent une valeur moyenne nulle sur le signal HDMI.
datain correspond au paramètre de sortie dataout de l'étape 1
nbits1 correspond au paramètre de sortie nbits1 de l'étape 1
sommecul correspond au calcul de la moyenne cumulée sur tous les pixels.
Tout se passe dans le FPGA, le processeur sert uniquement à programmer le FPGA.
Le programme VHDL est inspiré du programme Verilog su site Système embarqué.
L'ensemble des fichiers VHDL respectent la structure du code Verilog. Ce qui change c'est principalement le code de génération de l'image qui est, ici , une palette de couleur.
Bien sûr cette palette ne contient pas toutes les valeurs de couleurs (224) car ce ne serait pas possible sur un écran 640x480.
Tous ces modules sont groupés dans le module principal hdmi_base.
Le module hdmi_interface intègre le module tmds qui effectue l'encodage TMDS ansi que la sérialisation des données.
Cette palette est crée par le bloc hdmi_image. La palette de couleur est inspirée des palettes des logiciels de dessin. Chaque couleur est calculée en fonction de sa position sur une ligne de l'image. Il n'y a pas de changement en vertical mais uniquement en horizontal.
La largeur de l'image est séparée en 6 zones de 106 pixels, ce qui fait 636 pixels, les 4 derniers pixels restent inchangés. Pour simplifier les calculs, on prend une pente de 2 ce qui fait une valeur maximale pour chaque pixel de 210 (au lieu de 255).
Avec cette simplification on obtient les calculs suivants :
n | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
R | 210 | 210-2(t-np) | 0 | 0 | 2(t-np) | 210 |
V | 2(t-np) | 210 | 210 | 210-2(t-np) | 0 | 0 |
B | 0 | 0 | 2(t-np) | 210 | 210 | 210-2(t-np) |
t représente la position sur la ligne entre 0 et 639, c'est ce tableau de calculs qui sera codé en VHDL avec p=106.
L'image montre le résultat de l'affichage sur un écran. C'est une photo d'un écran, ce qui justifie la qualité de l'image.
Code de l'encodage TMDS tmds.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
entity tmds is
Port ( iclk : in STD_LOGIC;
ictl : in STD_LOGIC_VECTOR(1 downto 0);
iimagevisible : in STD_LOGIC ;
idata : in STD_LOGIC_VECTOR(7 downto 0);
otmds : out STD_LOGIC_VECTOR(9 downto 0));
end tmds;
architecture architecture_tmds of tmds is
signal ouex_data : std_logic_vector(7 downto 0);
signal nouex_data : std_logic_vector(7 downto 0);
signal xdata : std_logic_vector(7 downto 0);
signal nbits_data : std_logic_vector(3 downto 0);
signal nbits_xdata : std_logic_vector(3 downto 0);
signal offset_datas : std_logic_vector(4 downto 0);
signal ctl : std_logic_vector(1 downto 0);
signal imagevisible : std_logic ;
signal bit8 : std_logic ;
begin
nbits_data <= ( "000" & idata(0)) + ( "000" & idata(1)) + ( "000" & idata(2)) + ( "000" & idata(3)) + ( "000" & idata(4)) + ( "000" & idata(5)) + ( "000" & idata(6)) + ( "000" & idata(7));
ouex_data(0) <= idata(0);
xored: for i in 1 to 7 generate
ouex_data(i) <= ouex_data(i-1) xor idata(i);
end generate;
nouex_data(0) <= not idata(0);
xnored: for i in 1 to 7 generate
nouex_data(i) <= nouex_data(i-1) xnor idata(i);
end generate;
calcul: process(iclk,iimagevisible)
begin
-- etape 1
if rising_edge(iclk) then
imagevisible <= iimagevisible ;
ctl <= ictl ;
if ((nbits_data > 4) or ((nbits_data = 4) and (idata(0) = '0'))) then
xdata <= nouex_data ;
bit8 <= '0' ;
nbits_xdata <= ( "000" & nouex_data(0)) + ( "000" & nouex_data(1)) + ( "000" & nouex_data(2)) + ( "000" & nouex_data(3)) + ( "000" & nouex_data(4)) + ( "000" & nouex_data(5)) + ( "000" & nouex_data(6)) + ( "000" & nouex_data(7));
else
xdata <= ouex_data ;
bit8 <= '1' ;
nbits_xdata <= ( "000" & ouex_data(0)) + ( "000" & ouex_data(1)) + ( "000" & ouex_data(2)) + ( "000" & ouex_data(3)) + ( "000" & ouex_data(4)) + ( "000" & ouex_data(5)) + ( "000" & ouex_data(6)) + ( "000" & ouex_data(7));
end if;
-- etape 2
if (imagevisible = '0') then -- controle hors partie visible
offset_datas <= "00000" ;
case ctl is
when "00" => otmds <= "1101010100" ;
when "01" => otmds <= "0010101011" ;
when "10" => otmds <= "0101010100" ;
when "11" => otmds <= "1010101011" ;
when others => otmds <= "1101010100" ;
end case;
else -- data partie visible
if ((offset_datas = 0) or (nbits_xdata = 4)) then
if (bit8 = '1') then
otmds <= "01" & xdata ;
offset_datas <= offset_datas + (nbits_xdata(3 downto 0) & "0") - 8;
else
otmds <= "10" & not xdata;
offset_datas <= offset_datas - (nbits_xdata(3 downto 0) & "0") + 8;
end if;
else
if (((signed(offset_datas) >= 0) and (nbits_xdata > 4)) or ((signed(offset_datas) < 0) and (nbits_xdata < 4))) then
otmds <= '1' & bit8 & not xdata;
offset_datas <= offset_datas + ( "000" & bit8 & '0' ) - (nbits_xdata(3 downto 0) & "0") + 8;
else
otmds<= '0' & bit8 & xdata;
offset_datas <= offset_datas - ( "000" & (not bit8) & '0') + (nbits_xdata(3 downto 0) & "0") - 8;
end if;
end if;
end if;
end if;
end process;
end architecture_tmds;
Code du module hdmi_interface.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity hdmi_interface is
port (
clkpixel : in STD_LOGIC;
clkdata : in STD_LOGIC;
videoenable : in STD_LOGIC ;
imagevisible : in STD_LOGIC;
isynchroH : in STD_LOGIC;
isynchroV : in STD_LOGIC;
irouge : in STD_LOGIC_VECTOR(7 downto 0);
ivert : in STD_LOGIC_VECTOR(7 downto 0);
ibleu : in STD_LOGIC_VECTOR(7 downto 0);
ohdmi_clk : out STD_LOGIC;
ohdmi_datas : out STD_LOGIC_VECTOR(2 downto 0)
);
end hdmi_interface;
architecture arch_hdmi_interface of hdmi_interface is
component tmds is
Port ( iclk : in STD_LOGIC;
ictl : in STD_LOGIC_VECTOR(1 downto 0);
iimagevisible : in STD_LOGIC ;
idata : in STD_LOGIC_VECTOR(7 downto 0);
otmds : out STD_LOGIC_VECTOR(9 downto 0)
);
end component tmds;
component hdmi_sortie is
Port
(
aclr : in STD_LOGIC ;
datain_h : in STD_LOGIC_VECTOR (3 DOWNTO 0);
datain_l : in STD_LOGIC_VECTOR (3 DOWNTO 0);
outclock : in STD_LOGIC ;
dataout : out STD_LOGIC_VECTOR (3 DOWNTO 0)
);
end component hdmi_sortie;
signal tmds_rouge,tmds_vert,tmds_bleu, tmds_clk : std_logic_vector(9 downto 0);
signal hdmi_rouge, hdmi_vert, hdmi_bleu, hdmi_clk : std_logic_vector(9 downto 0);
signal data_rouge,data_vert,data_bleu : std_logic_vector(7 downto 0);
signal ctl_rouge, ctl_vert, ctl_bleu : std_logic_vector(1 downto 0);
signal data_sortie_in_h , data_sortie_in_l : std_logic_vector(3 downto 0);
signal data_sortie_out : std_logic_vector(3 downto 0);
begin
ctl_rouge <= "00" ;
ctl_vert <= "00" ;
ctl_bleu <= isynchroV & isynchroH ;
data_rouge <= irouge ;
data_vert <= ivert ;
data_bleu <= ibleu ;
chiptmdsrouge : tmds port map(iclk => clkpixel, ictl => ctl_rouge, iimagevisible => imagevisible, idata => data_rouge, otmds => tmds_rouge);
chiptmdsvert : tmds port map(iclk => clkpixel, ictl => ctl_vert, iimagevisible => imagevisible, idata => data_vert, otmds => tmds_vert);
chiptmdsbleu : tmds port map(iclk => clkpixel, ictl => ctl_bleu, iimagevisible => imagevisible, idata => data_bleu, otmds => tmds_bleu);
serialisation : process(clkdata,videoenable)
begin
if videoenable = '0' then
hdmi_clk <= "0000011111" ;
else
if rising_edge(clkdata) then
if hdmi_clk = "0000011111" then
hdmi_rouge <= tmds_rouge ;
hdmi_vert <= tmds_vert ;
hdmi_bleu <= tmds_bleu ;
else
hdmi_rouge <= "00" & hdmi_rouge(9 downto 2) ;
hdmi_vert <= "00" & hdmi_vert(9 downto 2) ;
hdmi_bleu <= "00" & hdmi_bleu(9 downto 2) ;
end if;
hdmi_clk <= hdmi_clk(1) & hdmi_clk(0) & hdmi_clk(9 downto 2);
end if ;
end if ;
end process serialisation;
-- sorties sur les fronts montants et descendants
data_sortie_in_h <= hdmi_clk(0) & hdmi_rouge(0) & hdmi_vert(0) & hdmi_bleu(0) ;
data_sortie_in_l <= hdmi_clk(1) & hdmi_rouge(1) & hdmi_vert(1) & hdmi_bleu(1) ;
ohdmi_clk <= data_sortie_out(3) ;
ohdmi_datas <= data_sortie_out(2 downto 0);
chiphdmisortie : hdmi_sortie port map (aclr => '0' , datain_h => data_sortie_in_h , datain_l => data_sortie_in_l ,
outclock => clkdata, dataout => data_sortie_out );
end arch_hdmi_interface;
Code du module hdmi_image.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
entity hdmi_image is
port (
clk : in STD_LOGIC;
ienable : in STD_LOGIC ;
icptH : in STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
icptV : in STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
orouge : out STD_LOGIC_VECTOR(7 downto 0);
overt : out STD_LOGIC_VECTOR(7 downto 0);
obleu : out STD_LOGIC_VECTOR(7 downto 0)
);
end hdmi_image;
architecture arch_hdmi_image of hdmi_image is
constant pasH : integer := 106 ;
constant valmax : integer := 211;
signal rouge, vert, bleu : std_logic_vector(7 downto 0);
begin
orouge <= rouge ;
overt <= vert ;
obleu <= bleu ;
picelsimage: process(clk,ienable)
begin
if falling_edge(clk) and ienable ='1' then
if icptH < pasH then
rouge <= std_logic_vector(to_unsigned(valmax,8)) ;
vert <= icptH(6 downto 0) & "0" ; -- kx
bleu <= x"00" ;
elsif icptH < (2*pasH) then
rouge <= valmax - (icptH(6 downto 0) & "0" - 2*pasH) ; -- valmx -k(x-pasH)
vert <= std_logic_vector(to_unsigned(valmax,8)) ;
bleu <= x"00" ;
elsif icptH < (3*pasH) then
rouge <= x"00" ;
vert <= std_logic_vector(to_unsigned(valmax,8)) ;
bleu <= icptH(6 downto 0) & "0" -4*pasH ; -- k(x-2*pasH)
elsif icptH < (4*pasH) then
rouge <= x"00" ;
vert <= valmax - (icptH(6 downto 0) & "0" -6*pasH) ; -- valmax - k(x-3*pasH)
bleu <= std_logic_vector(to_unsigned(valmax,8)) ;
elsif icptH < (5*pasH) then
rouge <= icptH(6 downto 0) & "0" -8*pasH ; -- k(x-4*pasH)
vert <= x"00" ;
bleu <= std_logic_vector(to_unsigned(valmax,8)) ;
elsif icptH < (6*pasH) then
rouge <= std_logic_vector(to_unsigned(valmax,8)) ;
vert <= x"00" ;
bleu <= valmax - (icptH(6 downto 0) & "0" - 10*pasH) ; -- valmax - k(x-5*pasH)
else
rouge <= std_logic_vector(to_unsigned(valmax,8)) ;
vert <= x"00" ;
bleu <= x"00" ;
end if ;
end if;
end process picelsimage;
end arch_hdmi_image;
Code du module hdmi_trame.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity hdmi_trame is
port (
clk : in STD_LOGIC;
ienable : in STD_LOGIC;
ocptH : out STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
ocptV : out STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
ovisible : out STD_LOGIC;
osynchroH : out STD_LOGIC;
osynchroV : out STD_LOGIC
);
end hdmi_trame;
architecture arch_hdmi_trame of hdmi_trame is
constant HRES : integer := 640 ; -- 640
constant HSS : integer := 656; -- 656
constant HSE : integer := 752; --752
constant Htotal : integer := 762 ; -- 800 ou 762 ??
constant VRES : integer := 480 ; -- 480
constant VSS : integer := 489; -- 489
constant VSE : integer := 492; -- 492
constant Vtotal : integer := 525 ; -- 525
component compteur is
Generic (N : integer := 10);
Port ( clk : in STD_LOGIC;
reset : in STD_LOGIC;
enable : in STD_LOGIC;
tc : out STD_LOGIC;
Q : out STD_LOGIC_VECTOR(11 downto 0));
end component compteur;
signal cptH : std_logic_vector(11 downto 0);
signal cptV : std_logic_vector(11 downto 0);
signal cptHtc : std_logic;
signal cptVenable : std_logic ;
signal Hvisible : std_logic ;
signal Vvisible : std_logic ;
begin
ocptH <= cptH ;
ocptV <= cptV ;
oVisible <= Hvisible and Vvisible ;
chipcompteurH : compteur generic map (N => Htotal)
port map (clk => clk , reset => ienable , enable => '1' , tc => cptVenable , Q => cptH );
chipcompteurV : compteur generic map (N => Vtotal)
port map (clk => clk , reset => ienable , enable => cptVenable , tc => open , Q => cptV );
synchrosH: process(cptH)
begin
if cptH >= HSS and cptH < HSE then
osynchroH <= '1' ;
else
osynchroH <= '0' ;
end if;
if cptH < HRES then
Hvisible <= '1' ;
else
Hvisible <= '0' ;
end if;
end process synchrosH;
synchrosV: process(cptV)
begin
if cptV >= VSS and cptV < VSE then
osynchroV <= '1' ;
else
osynchroV <= '0' ;
end if;
if cptV < VRES then
Vvisible <= '1' ;
else
Vvisible <= '0' ;
end if;
end process synchrosV;
end arch_hdmi_trame;
Code du module hdmi_base.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity hdmi_base is
port (
iCLK : in STD_LOGIC;
iRESETn : in STD_LOGIC;
iHDMI_HPD : in STD_LOGIC ;
oHDMI_TX : out STD_LOGIC_VECTOR(0 to 2);
oHDMI_CLK : out STD_LOGIC
);
end hdmi_base;
architecture arch_hdmi_base of hdmi_base is
component hdmi_horloges is
port
(
areset : in STD_LOGIC := '0';
inclk0 : in STD_LOGIC := '0';
c0 : out STD_LOGIC ;
c1 : out STD_LOGIC ;
locked : out STD_LOGIC
);
end component hdmi_horloges;
component hdmi_trame is
port (
clk : in STD_LOGIC;
ienable : in STD_LOGIC;
ocptH : out STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
ocptV : out STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
ovisible : out STD_LOGIC;
osynchroH : out STD_LOGIC;
osynchroV : out STD_LOGIC
);
end component hdmi_trame;
component hdmi_interface is
port (
clkpixel : in STD_LOGIC;
clkdata : in STD_LOGIC;
videoenable : in STD_LOGIC ;
imagevisible : in STD_LOGIC;
isynchroH : in STD_LOGIC;
isynchroV : in STD_LOGIC;
irouge : in STD_LOGIC_VECTOR(7 downto 0);
ivert : in STD_LOGIC_VECTOR(7 downto 0);
ibleu : in STD_LOGIC_VECTOR(7 downto 0);
ohdmi_clk : out STD_LOGIC;
ohdmi_datas : out STD_LOGIC_VECTOR(2 downto 0) -- inversion des rouge et bleu
);
end component hdmi_interface;
component hdmi_image is
port (
clk : in STD_LOGIC;
ienable : in STD_LOGIC ;
icptH : in STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
icptV : in STD_LOGIC_VECTOR(11 downto 0); -- 12 bits pour toutes les résolutions
orouge : out STD_LOGIC_VECTOR(7 downto 0);
overt : out STD_LOGIC_VECTOR(7 downto 0);
obleu : out STD_LOGIC_VECTOR(7 downto 0)
);
end component hdmi_image;
-- horloges
signal horlogesreset, horlogesclkin, horlogeslocked: std_logic ;
signal clkpixel , clkhdmidatas : std_logic;
-- trames
signal posH : std_logic_vector(11 downto 0);
signal posV : std_logic_vector(11 downto 0);
signal video_enable : std_logic ;
signal ImageVisible : std_logic;
signal ImageSynchroH : std_logic ;
signal ImageSynchroV : std_logic ;
-- couleurs pixel
signal pixrouge, pixvert, pixbleu : std_logic_vector(7 downto 0);
begin
horlogesclkin <= iCLK ;
horlogesreset <= not iRESETn ;
video_enable <= horlogeslocked and iHDMI_HPD ;
chiphorloges : hdmi_horloges port map (areset => horlogesreset , inclk0 => horlogesclkin, locked => horlogeslocked,
c0 => clkpixel , c1 => clkhdmidatas );
chiptrames : hdmi_trame port map (clk => clkpixel , ienable => video_enable ,ocptH => posH,
ocptV => posV , ovisible => ImageVisible ,
osynchroH => ImageSynchroH , osynchroV => ImageSynchroV );
chipinterface : hdmi_interface port map ( clkpixel => clkpixel , clkdata => clkhdmidatas , videoenable => video_enable,
imagevisible => ImageVisible, isynchroH => ImageSynchroH , isynchroV => ImageSynchroV,
irouge => pixrouge, ivert => pixvert , ibleu => pixbleu,
ohdmi_clk => oHDMI_CLK , ohdmi_datas => oHDMI_TX );
-- image
chipimage : hdmi_image port map ( clk => clkpixel , ienable => ImageVisible ,
icptH => posH , icptV => posV ,
orouge => pixrouge , overt => pixvert , obleu => pixbleu );
end arch_hdmi_base;