Pages: [1] 2   Go Down
Author Topic: [APL] Vidéo avec Arduino, afficher sur moniteur  (Read 10216 times)
0 Members and 1 Guest are viewing this topic.
Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonsoir à tous,

  Cela faisait longtemps, je vois que le forum français tourne et ça me fait vraiment plaisir smiley.

  Pour ce retour je vous amène un expérimentation qui je pense va susciter des programmes, exalté les amoureux du "lowtech", ...

  Une carte vidéo !

  Oh biensur elle est rudimentaire et plein de choses restent à faire car se n'est qu'un début expérimentation... Il va falloir passer la routine d'afichage sous interruption, faire reluire quelques recoins de code, ... mais ça fonctionne et c'est un bon point de départ.

  Dans le prochain billet, une ou deux captures d'écran pour vous mettre l'eau à la bouche, dans le suivant la partie physique (harware) et enfin le code (software). Wooooh, c'est partit...
« Last Edit: April 16, 2007, 04:03:13 pm by Benoit » Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

L'image complète


Détail pour voir LE niveau de gris smiley
Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Alors, passons aux choses sérieuses...

La partie électronique est accessible à n'importe qui !

Il faut générer quelques tensions distinctes pour génrérer un signal vidéo composite avec un niveau de gris : 0V, 0,3V, 0,77 et 1V. Pour cela il faut réaliser un convertisseur numérique analogique 2 bits !

je me suis insipré du schéma donné ici : (c) le propriétaire


On peux faire plus simple mais j'aime bien l'idée des diodes "anti retour".

Pour ma part j'ai câblé ça à la gros porc sur un bout de ciruit récupéré dans mon bazar. Je n'avais pas de résistanec de 330 j'ai donc mis un potar réglé sur 330 Ohms.



La résistance de 1K est relièe à la broche D8 (via diode) de l'Arduino et la résistance de 330 est reliée à la broche D9 (via diode). Biensur, la masse est reliée à l'une des broches GND de l'Arduino.

Il faut ensuite connecté une prise (RCA, CINCH, ..) à la sortie. La "patte" centrale du connecteur au point où toutes les résistances se rejoignent ("to tv" sur le schéma) et la partie externe ronde du connecteur à la masse.

Difficile de faire plus simple !
« Last Edit: April 16, 2007, 06:09:23 pm by Benoit » Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Il nous faut maintenant connaitre quel est le signal à générer pour obtenir une image.

Je vous invite à consulter pour cela cette page. Elle est en anglais (aïe !) mais c''est de loin la plus complète que j'ai trouvé. Il y a l'essentiel : http://www.retroleum.co.uk/PALTVtimingandvoltages.html.

Cherchez sur un moteur de recherche, il existe peut être des pages en français... Si vous trouvez, postez le lien, cela interessera les anglophobes. Merci pour eux.
Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Pas d'explications cela serait trop long smiley-sad !

Si vous avez des questions, j'y repondrai...

----

MAIS ATTENTION !

AFIN QUE TOUT FONCTIONNE BIEN, IL VOUS FAUDRA BRICOLER UN PEU !

LES INTERUPTIONS INDESIRABLES DOIVENT ËTRE DESACTIVEES. EN PARTICULIER L'INTERUPTION DE DEBORDEMENT DU TIMER 0 DESTINE A GERER LES FONCTIONS Delay() ET Millis().

Pour cela, recherchez dans votre répertoire d'installation d'Arduino le fichier "wiring.c". Il se trouve normalement dans le chemin "\arduino-0007\lib\targets\arduino". Faites en une copie, renomez la en "wiring.bak" puis ouvrez le fichier "wiring.c" en édition (avec le bloc note de Windows par exemple).

Dans le fichier, recherchez les lignes :

Code:
// enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
      sbi(TIMSK0, TOIE0);
#else
      sbi(TIMSK, TOIE0);
#endif

qui se trouvent dans la fonction "main ()".

Mettez en commentaire cette partie de code avec "/*" et "*/" comme ceci :
Code:
/*
// enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
      sbi(TIMSK0, TOIE0);
#else
      sbi(TIMSK, TOIE0);
#endif
*/

et, sauvegardez le fichier.

Tant que ces lignes seront en commentaires, ous ne pouvez plus utiliser millis() et delay() dans vos programmes ! Pour d'autres projets, vous devrez donc réouvrir "wiring.c" et dé-commenter ces lignes. Ou effacer "wiring.c" et renommer "wiring.bak" en "wiring.c".

Le code qui suit à été testé avec sur un ATMEGA8 inséré sur une Arduino NG. Mais il n'y a aucune raison qu'il ne fonctionne pas sur n'importe quelle Arduino.



Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:

#define _SYNC 0x00
#define _BLACK  0x01
#define _GRAY  0x02
#define _WHITE  0x03

#define _LONG_SYNC 19
#define _SHORT_SYNC 2
#define _LONG_SYNC_DELAI 2
#define _SHORT_SYNC_DELAI 30

#define _NB_PIXELS 29
#define _NB_LIGNES 19

#define _COMPENS_BOUCLE 7
#define _COMPENS_IF_SERIAL 17

byte memVideo[_NB_PIXELS][_NB_LIGNES];
byte index, index2;
byte shift;

void SPI_MasterInit(void)
{
  /* Set MOSI and SCK output, all others input */
  DDRD = (1<<DDB0)|(1<<DDB1);
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}


void setup()
{
  pinMode (8, OUTPUT);
  pinMode (9, OUTPUT);
  digitalWrite (8, HIGH);
  digitalWrite (9, HIGH);
//  SPI_MasterInit();
  Serial.begin(19200);
  Serial.print ("GO");
  Serial.print (13, BYTE);
  
  for (index2 = 0; index2 < _NB_LIGNES; index2++)
     for (index = 0; index < _NB_PIXELS; index++)
       memVideo[index][index2] = _BLACK;
       /*memVideo[index][index2] = (index + index2) % 3 + 1;*/
      
  memVideo[0][0] = _WHITE;
}

int ligne;
char c;
int cx=0, cy=0;
byte couleur = _BLACK;

void loop()
{
  // SYNC VERT A
  
  // ligne 1 LONG SYNC
  PORTB = _SYNC; delayMicroseconds(_LONG_SYNC);
  PORTB = _BLACK; delayMicroseconds(_LONG_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_LONG_SYNC);
  PORTB = _BLACK; delayMicroseconds(_LONG_SYNC_DELAI);
  
  // ligne 2 LONG SYNC
  PORTB = _SYNC; delayMicroseconds(_LONG_SYNC);
  PORTB = _BLACK; delayMicroseconds(_LONG_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_LONG_SYNC);
  PORTB = _BLACK; delayMicroseconds(_LONG_SYNC_DELAI);
  
  // ligne 3 MIXTE SYNC
  PORTB = _SYNC; delayMicroseconds(_LONG_SYNC);
  PORTB = _BLACK; delayMicroseconds(_LONG_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  // ligne 4 SHORT SYNC
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);

  // ligne 5 SHORT SYNC
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI-_COMPENS_BOUCLE);
  
  // IMAGE
  
  for (ligne = 0; ligne < 304; ligne++)
  {
    //** synchro

    // HSync
    PORTB = _SYNC;
    delayMicroseconds(4);
    
    // Black
    PORTB = _BLACK;
    delayMicroseconds(4);
    
    //** image ligne 52 uS
    
    for (index = 0; index < _NB_PIXELS; index++)
    {
      PORTB = memVideo[index][ligne>>4];
      PORTB = PORTB;
      PORTB = PORTB;
    }
        
    delayMicroseconds(2); // 4 uS
    
    PORTB = _BLACK;
    PORTB = _BLACK;
    PORTB = _BLACK;
    PORTB = _BLACK;
    PORTB = _BLACK;
    
    PORTB = _BLACK;
    PORTB = _BLACK;
    PORTB = _BLACK;
    PORTB = _BLACK;

  }

  // SYNC VERT B
  
  // ligne 310 LONG SYNC
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);

  // ligne 311 SHORT SYNC
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  // ligne 312 SHORT SYNC
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI);
  
  PORTB = _SYNC; delayMicroseconds(_SHORT_SYNC);
  PORTB = _BLACK; delayMicroseconds(_SHORT_SYNC_DELAI-_COMPENS_BOUCLE-_COMPENS_IF_SERIAL);
  
  if (Serial.available())
  {
    c = Serial.read ();
    switch (c)
    {
      case '4' : if (cx>0) cx--; break;
      case '6' : if (cx<29) cx++; break;
      case '8' : if (cy>0) cy--; break;
      case '2' : if (cy<18) cy++; break;
      case '5' : memVideo[cx][cy] = couleur; break;
      case 'n' : couleur = _BLACK; break;
      case 'g' : couleur = _GRAY; break;
      case 'b' : couleur = _WHITE; break;
    }
  }


}

Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Pour tester, le petit programme accepte quelques commandes via la prise usb.

Ouvrez votre terminal série préféré (HyperTerminal sous Windows, par exemple) et dessinez à l'aide des commandes qui suivent. Pensez à brancher le montage au moniteur (téléviseur) et à l'allumer.

Les touches 4, 6, 8 et 2 servent à diriger un curseur invisible qui au départ est en haut à gauche de l'écran. Remarquez au passage que la première ligne n'est pas visible, il vous faudra donc descendre de deux crans pour commenr à voir vos actions en écriture.

Les touches b, n et g servent à choisir la couleur du crayon Blanc, Gris, Noir.

La touche 5 (cinq) écrit un point de la couleur du crayon.

En vous servant du pavé numérique c'est plus intuitif...

Bonne bourre...
Logged

Cordialement,
Benoît ROUSSEAU

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Dans la suite des expérimentations, voici la version sous interruption :

Code:
#define _SYNC 0x00
#define _BLACK  0x01
#define _GRAY  0x02
#define _WHITE  0x03

// compensation chargement us de 1 incluse
/*
#define _NORMAL_SYNC 32
#define _LONG_SYNC 240
#define _SHORT_SYNC 16
#define _LONG_SYNC_DELAI 16
#define _SHORT_SYNC_DELAI 240
*/

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

byte memVideo[_NB_PIXELS][_NB_LIGNES];
byte index, index2;
int noligne = 0;
byte diviseur = 0;

SIGNAL(SIG_OVERFLOW0)
{
  TCNT0 = 256 - 128;
  PORTB = _SYNC;
      
  if (noligne == 3 || noligne == 4)
  {
    // SYNCHRO
    while (TCNT0 < 128 + 4); // 2 uS
        
    PORTB = _BLACK;
    while (TCNT0 < 128 + 60 + 4); // 32 us
    
    PORTB = _SYNC;
    while (TCNT0 < 128 + 60 + 4 + 4);
    
    PORTB = _BLACK;
  }
    
  // fin
  if (noligne >= 309)
  {
    // SHORT
    while (TCNT0 < 128 + 4); // 2 uS
    
    PORTB = _BLACK;
    while (TCNT0 < 128 + 60 + 4); // 32 us
    
    // SHORT
    PORTB = _SYNC;
    while (TCNT0 < 128 + 60 + 4 + 4);
    
    PORTB = _BLACK;
  }

  // image
  if (noligne >= 5 && noligne < 309)
  {
    // SYNC
    while (TCNT0 < 128 + 8);
    
    PORTB = _BLACK; // porch
    while (TCNT0 < 128 + 8 + 8);
    
    //** image ligne 52 uS
//    if (noligne >= 16 && noligne < 304)
      for (index = 0; index < _NB_PIXELS; index++)
      {
        PORTB = memVideo[index][(noligne>>4)];
        PORTB = PORTB;
        PORTB = PORTB;
      }
    
    PORTB = _BLACK;
  }
  
  if (noligne < 2)
  {
    // SYNCHRO
    while (TCNT0 < 128 + 60);
    
    PORTB = _BLACK;
    while (TCNT0 < 128 + 60 + 4);

    PORTB = _SYNC;
    while (TCNT0 < 128 + 60 + 4 + 60);

    PORTB = _BLACK;
  }
      
  // COMPTEUR LIGNE
  if (noligne == 311)
    noligne = 0;
  else
    noligne++;
}

void InitTimer()
{
  #if defined(__AVR_ATmega168__)
      sbi(TCCR0A, WGM01);
      sbi(TCCR0A, WGM00);
  #endif  
      // set timer 0 prescale factor to 8
  #if defined(__AVR_ATmega168__)
        cbi(TCCR0B, CS02);
      sbi(TCCR0B, CS01);
      cbi(TCCR0B, CS00);
  #else
        cbi(TCCR0, CS02);
      sbi(TCCR0, CS01);
      cbi(TCCR0, CS00);
  #endif

      // enable timer 0 overflow interrupt
  #if defined(__AVR_ATmega168__)
        sbi(TIMSK0, TOIE0);
  #else
        sbi(TIMSK, TOIE0);
  #endif
}

void setup()
{
  pinMode (8, OUTPUT);
  pinMode (9, OUTPUT);
  digitalWrite (8, HIGH);
  digitalWrite (9, HIGH);

  Serial.begin(19200);
  Serial.print ("GO");
  Serial.print (13, BYTE);
  
  for (index2 = 0; index2 < _NB_LIGNES; index2++)
     for (index = 0; index < _NB_PIXELS; index++)
     {
       /*memVideo[index][index2] = _BLACK;*/
       memVideo[index][index2] = (index + index2) % 3 + 1;
     }

  memVideo[2][2] = _WHITE;

  InitTimer();
}

int ligne;
char car;
int cx=0, cy=0;
byte couleur = _BLACK;

void loop()
{

}

Cette version fonctionne et tente de s'affranchir des problème de temporisation. Il y a encore quelques comportements que je ne comprends pas mais je vous la livre pour que vous puissiez aussi y regarder...

Il vous faudra mettre en commentaire la fonction "SIGNAL(SIG_OVERFLOW0)" dans le fichier wiring.c comme ceci :
Code:
/*
SIGNAL(SIG_OVERFLOW0)
{
      timer0_overflow_count++;
}
*/

woula !
Logged

Cordialement,
Benoît ROUSSEAU

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Alléchant ...  smiley-grin
J'essayerai dès que possible, même si ça à l'air difficile à mettre en place !
Logged

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour Vchahum,

   Cela à l'air plus compliqué que ça ne l'est réellement.

   Tiens nous au courant de tes essais car un anglais à essayé et il est victime des stries à l'afichage. J'aimerai donc savoir si d'autres que moi affichent correctement l'image  smiley.
Logged

Cordialement,
Benoît ROUSSEAU

0
Offline Offline
Newbie
*
Karma: 0
Posts: 18
M0OOPHONE
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hey benoit ! Merci tellement pour ton tutorial. Je suis sur le point de commencer ce truc, mais je me demandais si sa marcherais sur NTSC ?
Logged

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour Ikaruga,

  Oui, cela fonctionnera, mais il te faudra adapter de nombreuses valeurs, les fréquences (lignes, trames, ...) ne sont pas identiques en NTSC. Pour le détail, voir par exemple : http://en.wikipedia.org/wiki/NTSC#Lines_and_refresh_rate.

Logged

Cordialement,
Benoît ROUSSEAU

0
Offline Offline
Newbie
*
Karma: 0
Posts: 18
M0OOPHONE
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ah !
merci bien benoit. Je ne m y connais plus ou moin en electronique et je n ai pas vraiment les outils pour confectionner le board, alors ton "On peux faire plus simple mais j'a..." a piquer ma curiositee. Est ce que tu pourrais me sortir une petite liste d epicerie des trucs necessaire pour connecter les fils RCA au board arduino. : ) Je suis sur que sa benificiera les autre membres aussi. J espere que je ne t en demande pas trop. je suis super content que d autre persone parlent francais ici !!!

mici
Logged

Montréal, Qc
Offline Offline
Full Member
***
Karma: 1
Posts: 185
Practice safe hex!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
[...] il te faudra adapter de nombreuses valeurs, les fréquences (lignes, trames, ...) ne sont pas identiques en NTSC. [...]

Salut Benoit! En effet ça fait un baille smiley-wink

Est-ce possible de mettre des commentaires du genre MODIF NTSC où il faut modifier des valeurs pour adapter le code? J'suis plutôt rouillé  :"]
Logged

"Pilots believe in a clean living... they never drink wisky from a dirty glass."

Montréal, Qc
Offline Offline
Full Member
***
Karma: 1
Posts: 185
Practice safe hex!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bon j'ai cheché un peu plus loin cette fois. Ca marche en NTSC smiley-grin
Par contre j'ai tjr un peu mal a comprendre. Premièrement a quoi servent les lignes 1 a 5 et 310 a 312? Sans ca marche pas, mais en meme temps je capte pas ne pas avoir eu a changer quoi que ce soit dans cette partie (incluant ses timings). Deuxièmement, PORTB = PORTB eu..... pas capté :/ et dernièrement, pourquoi les PORTB = _BLACK après l'écriture?

(P.S. ya beaucoup d'info sur http://www.stanford.edu/class/ee281/handouts/lab4.pdf)

P.P.S. J'aurais aimé avoir un Diecimila, c'est chiant courrir a coté de la tv, reseter le arduino, revenir a l'ordi 3~4m plus loin pour faire upload :p
« Last Edit: August 24, 2007, 01:38:38 pm by xSmurf » Logged

"Pilots believe in a clean living... they never drink wisky from a dirty glass."

Pages: [1] 2   Go Up
Jump to: