#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;
}
}
}
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...
Dans la suite des expérimentations, voici la version sous interruption :
#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 :
/*
SIGNAL(SIG_OVERFLOW0)
{
timer0_overflow_count++;
}
*/
woula !
Alléchant ...
J'essayerai dès que possible, même si ça à l'air difficile à mettre en place !
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 :).
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 ?
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 : NTSC - Wikipedia.
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
[...] 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
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é :"]
Bon j'ai cheché un peu plus loin cette fois. Ca marche en NTSC
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
alors penses tu pouvoir reposter le code avec les indents de NTSC en comments ??
Est ce que sa serais possible avec un setup du genre a . je n ai vraiment pas bcp de trucs d electro chez moi. Je pense que je n ai meme pas de truc pour digital to analog .
Ma config ressemble bcp a ça (J'avais pas les diodes)...
Regarde sur flickr pour plus d'informations (il y a des notes).
J'ai monté le circuit sur un shield de developement, mais ton setup est encore mieux
Ok le code, temporaire, avec les commentaires suivra!
C'est surement mieux formaté sur pastie, mais si tu es paresseux...
(EDIT ou si pastie fini par jetter cette entré)
P.S. Je code dans TextMate plutôt que l'IDE en utilisant le Makefile, raison pour laquelle il y a le include et les prototypes.
#include "WProgram.h"
#define DEBUG 1
//#define USE_SPI 1
// Définition des pins
#define PIN_SYNC 8
#define PIN_VIDEO 9
#define PIN_LED 12
#define PIN_AUDIO 11
#define _SYNC 0x00
#define _BLACK 0x01
#define _GRAY 0x02
#define _WHITE 0x03
// Non modifié, voir mon post! (pas sur de comprendre)
#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 _NB_TV_LIGNES 262 // Modifié pour NTSC
#define _COMPENS_BOUCLE 7
#define _COMPENS_IF_SERIAL 17
#define _BAUD_RATE 19200
// Prototypes
void SPI_MasterInit(void);
void clearScreen(boolean mode);
#ifdef DEBUG
void blinkStatus(void);
#endif
void resetCursor(void);
// Variables definition
byte memVideo[_NB_PIXELS][_NB_LIGNES];
byte index, index2;
int ligne;
char c;
int cx=(_NB_PIXELS / 2), cy=(_NB_LIGNES / 2);
byte couleur = _BLACK;
#ifdef USE_SPI
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);
}
#endif
// Fonction d'effacement de l'écran (avec possibilité de faire un "cadriage" pour les tests)
void clearScreen(boolean mode)
{
for (index2 = 0; index2 < _NB_LIGNES; index2++)
for (index = 0; index < _NB_PIXELS; index++)
if (!mode) {
memVideo[index][index2] = _BLACK;
} else {
memVideo[index][index2] = (index + index2) % 3 + 1;
}
memVideo[0][0] = _WHITE;
resetCursor();
}
// Remet le curseur au milieux
void resetCursor(void)
{
cx=(_NB_PIXELS / 2);
cy=(_NB_LIGNES / 2);
}
#ifdef DEBUG
void blinkStatus(void)
{
int ii;
pinMode(PIN_LED, OUTPUT);
for(ii=0;ii<3;ii++)
{
digitalWrite(PIN_LED, HIGH);
delay(75);
digitalWrite(PIN_LED, LOW);
delay(75);
digitalWrite(PIN_LED, HIGH);
if (ii < 2) delay(75);
}
}
#endif
void setup()
{
pinMode(PIN_SYNC, OUTPUT);
pinMode(PIN_VIDEO, OUTPUT);
pinMode(PIN_AUDIO, OUTPUT);
digitalWrite(PIN_SYNC, HIGH);
digitalWrite(PIN_VIDEO, HIGH);
#ifdef USE_SPI
SPI_MasterInit();
#endif
Serial.begin(_BAUD_RATE);
Serial.println("GO");
clearScreen(true);
#ifdef DEBUG
blinkStatus();
#endif
}
void loop()
{
// SYNC VERT A
/**
* Cette section je suis pas certain de comprendre
**/
// 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 < _NB_TV_LIGNES; ligne++)
{
//** synchro
// HSync
PORTB = _SYNC;
delayMicroseconds(4.7); // Modifier NTSC
// Black
PORTB = _BLACK;
delayMicroseconds(4.7); // Modifier NTSC
//** image ligne 51.5 uS
for (index = 0; index < _NB_PIXELS; index++)
{
PORTB = memVideo[index][ligne>>4];
PORTB = PORTB; // Pas sur de comprendre ???
PORTB = PORTB; // Pas sur de comprendre ???
}
delayMicroseconds(1.4); // Modifier NTSC
/**
* Cette section je suis pas certain de comprendre
**/
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
PORTB = _BLACK;
}
// SYNC VERT B
/**
* Cette section je suis pas certain de comprendre
**/
// 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);
// Fin au prochain poste
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;
case 'c' : clearScreen(false); break; // Clear screen
case 'f' : clearScreen(true); break; // Fill screen
}
}
}
Je pense que je n ai meme pas de truc pour digital to analog .
Tu n'as pas d'Arduino? ;D
AH !!! good good ; D ouais j ai arduino ! j ai commencer a essayer mais je suis vraiment idiot en electronique alors sa ne marche pas en ce moment hehe ..
Voici une pic webcam de mon setup en ce moment.
Bon, j'ai décidé de re-écrire le code a zero, c'est moin mélangeant.
Mais bon, ca marche toujours pas et là.. je comprend vraiment pas pourquoi...
~~Si quelqu'un a une idée... http://pastie.textmate.org/private/34gzhibb7e8gcflkbw~~
P.S. J'ai aussi changer pour une résistance de 900ohm et 350ohm. Ca me semble plus juste (voir ici)
Bon voilà. Ça marche
On peut voir qu'il y a un problème de timing durant les premières lignes. Celle la je capte pas!
Le code pour le pattern de la photo n'est pas dans le code qui suit... mais la version complète ce trouve en pastie. Le code est fait pour être relativement facile a "porter" pour PAL (ou d'autre format).
EDIT Le code ne marchait pas, je vais reposter plus tard!
Bon, j'ai créer un nouveau thread pour le ntsc, en anglais comme ca touche surtout les gens en Amerique et au Japon. S'il y a des francophone qui aimerais une traduction faite moi savoir et je posterais la traduction ici
Dit Ben, peux-tu expliquer un peu comment tu utilises le registre de timer? J'ai regardé un peu la datasheet mais je suis pas certain de capter... Évidemment juste le 256-128 et les while... le reste c'est plus simple encore que sans l'interrupt Aussi, ça serait pas mieux de remettre timer0_overflow_count++
dans la fonction d'overflow?