Systèmes informatiques
Fermer ×

Arduino et plus

Arduino et octave

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 :

  1. pkg load arduino pour charger le paquetage arduino
  2. arduinosetup('arduinobinary','/usr/local/arduino/arduino'); (sous linux avec arduino installé dans /usr/local/arduino) qui exécute le programme arduino avec le programme ino de création d'une interface arduino pour octave. Il suffit de téléverser le programme, puis de fermer l'application arduino.
  3. ar=arduino qui crée un objet arduino qui sert de base à toutes les fonctions octave de gestion de l'arduino.
  4. Ecrire l'application octave de gestion de l'arduino et l'exécuter.
  5. delete(ar) libère l'objet arduino.

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.

Voir l'exemple du codage du chenillard de l'arduino avec octave

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).

Voir l'exemple du codage des fonctions de gestion du capteur BH1745 avec octave.

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						
					
Voir l'exemple du codage du programme de test de gestion du capteur

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.

Arduino et FPGA

Présentation de la carte MKRVidor4000

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 :

Image issue du site arduino.cc

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

  • En jaune, les identifiants utilisés par les fonctions de gestions des GPIOs de l'arduino
  • En vert, les identifiants des broches du processeur
  • En bleu, les identifiants des broches réelles du FPGA
  • En rouge, les identifiants des contraintes utilisées dans le fichier .qsf du projet quartus.

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.

Etude d'un exemple

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.

Voir les codes VHDL du FPGA

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.

Voir le code de la partie processeur

#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é.

Utilisation de la sortie HDMI

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.

Format de l'image

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 :

tailleHRESHSSHSEHTotalVRESVSSVSEVTotal
640x480640656752762480489492525

Données HDMI

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.

Encodage TMDS

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.

Exemple avec la palette de couleurs

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.

Structure des fichiers VHDL

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.

Calcul des couleurs de la palette

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 :

n012345
R210210-2(t-np)002(t-np)210
V2(t-np)210210210-2(t-np)00
B002(t-np)210210210-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.

Voir le code VHDL de l'encodage TMDS

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;
			
Voir le code VHDL du module hdmi_interface

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;
			
Voir le code VHDL du module hdmi_image

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;
			
Voir le code VHDL du module hdmi_trame

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;
			
Voir le code VHDL du module hdmi_base

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;