369 lines
9.9 KiB
C++
369 lines
9.9 KiB
C++
#include <Arduino.h>
|
|
#include "USBDesc.h"
|
|
#include "Platform.h"
|
|
|
|
int main(void)
|
|
{
|
|
init();
|
|
USBDevice.attach();
|
|
setup();
|
|
for (;;) {
|
|
loop();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// definition des fonction cbi sbi idem assembleur
|
|
#ifndef cbi
|
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
|
#endif
|
|
#ifndef sbi
|
|
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
|
#endif
|
|
|
|
// définitions
|
|
#define TEMPDMXRX 8 // Longueur de trame mini en reception
|
|
#define IOUTDMX_FIN -4
|
|
#define IOUTDMX_WAIT -5 // Attente du PC en mode synchrone
|
|
#define IOUTDMX_IDLE -1
|
|
#define IOUTDMX_BRK -2
|
|
#define IOUTDMX_MAB -3
|
|
#define IINUSB_IDLE 0
|
|
#define IINUSB_ESC 1
|
|
#define IINUSB_DATA 2
|
|
#define IINUSB_PARAM 10
|
|
|
|
#define TX1PIN 1 // pin DMX serial 1
|
|
#define LEDPIN 13 // led interne
|
|
#define RX_TX_LED_TIME 12
|
|
|
|
|
|
// tableaux de données
|
|
byte tab_input_pc[514]; // venant du pc
|
|
byte tab_input_dmx[518]; // données venant de l'extérieur : les 512 premiers DMX; les 4 suivant 4x8 bp ;
|
|
byte tab_temp_dmx[TEMPDMXRX];
|
|
byte inbuffer[7];
|
|
|
|
// Variables d'état et compteurs
|
|
volatile int index_input_pc=0; // entrée USB
|
|
volatile int index_output_pc=0; // sortie USB
|
|
volatile int index_input_dmx=513; // entrée dmx
|
|
volatile int index_output_dmx=0; // sortie dmx
|
|
volatile int etat_input_pc=IINUSB_IDLE; // etat entrée USB
|
|
volatile int emissionPc=0; // flag : emission USB en attente
|
|
unsigned int blkl = 0, blkcpt=0, rxledcpt=0 ,txledcpt=0; // Gestion des leds
|
|
unsigned int blinkon, blinkp, interbrk=0; // idem
|
|
|
|
// Paramètres
|
|
byte brk_timer_end=75; // Duree Break (par 2µs) def : 150us
|
|
byte mab_timer_end=25; // Duree MAB (par 2µs) def : 50us
|
|
int nb_circuits=512; // Nombre de circuits DMX emis
|
|
int dmx_frame_interval=40; // Intervale entre trames DMX emises
|
|
bool flag_merge1=1;
|
|
bool syncflag=false;
|
|
|
|
// ********************************** USB ***************************** //
|
|
// lancé par interruption USB sur reception d'un char <- Cette fonction nécessite une modif de la lib arduino
|
|
// l'interet est de ne pas utiliser le buffer de la classe Serial, trop petit (64o)
|
|
void CDC_accept()
|
|
{
|
|
char c;
|
|
byte *pb, *pe;
|
|
|
|
|
|
switch (etat_input_pc) {
|
|
|
|
case IINUSB_IDLE:
|
|
USB_Recv(CDC_RX,&c,1);
|
|
// on attend un 'esc' pour commencer
|
|
if (c==27) { etat_input_pc=IINUSB_ESC; break;}
|
|
break;
|
|
|
|
case IINUSB_ESC:
|
|
USB_Recv(CDC_RX,&c,1);
|
|
// on attend 'D' pour recevoir
|
|
if (c==68) {
|
|
etat_input_pc=IINUSB_DATA;
|
|
index_input_pc=1;
|
|
tab_input_dmx[517]=68;
|
|
index_output_pc=1; // on init l'index d'emmission vers le pc ( le 0 n'est pas emit => start code)
|
|
emissionPc=1;
|
|
break;
|
|
}
|
|
// Esc 'C' réinit
|
|
if (c==67) {
|
|
etat_input_pc=IINUSB_IDLE;
|
|
|
|
pe=tab_input_pc+514;
|
|
for(pb=tab_input_pc;pb<pe;pb++) *pb=0;
|
|
|
|
pe=tab_input_dmx+518;
|
|
for(pb=tab_input_dmx;pb<pe;pb++) *pb=0;
|
|
|
|
tab_input_dmx[517]=67;
|
|
index_output_pc=517; // on init l'index d'emmission vers le pc ( uniquement etat : 67 'C' )
|
|
emissionPc=1;
|
|
break;
|
|
|
|
}
|
|
// Esc 'B' parametrage
|
|
if (c==66) {
|
|
etat_input_pc=IINUSB_PARAM;
|
|
index_input_pc=0;
|
|
break;
|
|
}
|
|
|
|
// Esc 'A' probe
|
|
if (c==65) {
|
|
etat_input_pc=IINUSB_IDLE;
|
|
tab_input_dmx[517]=65;
|
|
index_output_pc=517; // on init l'index d'emmission vers le pc ( uniquement etat : 65 'A' )
|
|
emissionPc=1;
|
|
break;
|
|
}
|
|
|
|
// si aucune commande n'est trouvée (toujours à 1) on repart à 0
|
|
etat_input_pc=IINUSB_IDLE;
|
|
break;
|
|
|
|
case IINUSB_DATA: // reception trame pc
|
|
// on rempli le tableau
|
|
index_input_pc+=USB_Recv(CDC_RX,tab_input_pc+index_input_pc,513-index_input_pc);
|
|
if(index_input_pc<513) return;
|
|
etat_input_pc=0;
|
|
break;
|
|
|
|
case IINUSB_PARAM:
|
|
index_input_pc+=USB_Recv(CDC_RX,inbuffer+index_input_pc,5-index_input_pc);
|
|
if(index_input_pc<5) return;
|
|
|
|
nb_circuits= ((int)inbuffer[0])*2 + 2;
|
|
brk_timer_end = (inbuffer[1]/2);
|
|
mab_timer_end = (inbuffer[2]/2);
|
|
flag_merge1 = inbuffer[3] & 1;
|
|
syncflag = inbuffer[3] & 2;
|
|
dmx_frame_interval = inbuffer[4];
|
|
|
|
etat_input_pc=0;
|
|
|
|
// on a tout recu, on reponds
|
|
tab_input_dmx[517]=66;
|
|
index_output_pc=517; // on init l'index d'emmission vers le pc ( uniquement etat : 66 'B' )
|
|
emissionPc=1;
|
|
|
|
break;
|
|
default:
|
|
// on fait rien
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void ecritUSB()
|
|
{
|
|
TXLED1; txledcpt=RX_TX_LED_TIME;
|
|
if(index_output_pc > 516){ // Emission d'un seul caractere (code etat)
|
|
USB_Send(CDC_TX,tab_input_dmx+index_output_pc,1);
|
|
emissionPc=0;
|
|
return;
|
|
}
|
|
// Emission du tableau complet
|
|
index_output_pc+= USB_Send(CDC_TX,tab_input_dmx+index_output_pc, 517-index_output_pc);
|
|
if (index_output_pc>516) {
|
|
if(index_output_dmx==IOUTDMX_WAIT)
|
|
index_output_dmx=IOUTDMX_IDLE;
|
|
emissionPc=0;
|
|
}
|
|
}
|
|
|
|
// ********************************** RECEPTION DMX ********************************* //
|
|
|
|
|
|
// vecteur d'intéruption pour reception dmx
|
|
// sur reception d'un caractere / erreur
|
|
ISR(USART1_RX_vect)
|
|
{
|
|
char c,r;
|
|
r = UCSR1A;
|
|
c = UDR1;
|
|
|
|
if (r & (1<<FE1)) {index_input_dmx=0;return; } // Detection du BRK
|
|
|
|
if ( index_input_dmx==0 && c!=0 ) {index_input_dmx=513; } // Start code => doit etre 0
|
|
|
|
if ( index_input_dmx<TEMPDMXRX) { // Les premiers codes sont stockés dans un tableau temporaire
|
|
tab_temp_dmx[index_input_dmx]=c;
|
|
index_input_dmx++;
|
|
return;
|
|
}
|
|
|
|
if ( index_input_dmx==TEMPDMXRX) { // La tramme a depassé le minimum => on copie ce qu'on a déja recu
|
|
for(int i=0;i<TEMPDMXRX;i++)
|
|
tab_input_dmx[i]=tab_temp_dmx[i];
|
|
RXLED1; rxledcpt=RX_TX_LED_TIME;
|
|
}
|
|
|
|
if ( index_input_dmx<=512 )
|
|
{
|
|
tab_input_dmx[index_input_dmx]=c;
|
|
index_input_dmx++;
|
|
}
|
|
}
|
|
|
|
// ********************************** EMISSION DMX ********************************* //
|
|
|
|
|
|
// vecteur d'intéruption pour transmission DMX sur serial 1
|
|
// vecteur : USART 1 transmission buffer vide
|
|
// => Permet d'attendre que tout soit parti avant de déclancher le break
|
|
ISR(USART1_TX_vect)
|
|
{
|
|
// si l'USART 1 est vide => break
|
|
if(index_output_dmx==IOUTDMX_FIN) {
|
|
if(syncflag)
|
|
index_output_dmx=IOUTDMX_WAIT;
|
|
else
|
|
index_output_dmx=IOUTDMX_IDLE;
|
|
}
|
|
}
|
|
|
|
// vecteur : USART 1 transmission registre vide
|
|
ISR(USART1_UDRE_vect)
|
|
{
|
|
if (flag_merge1==1) {
|
|
UDR1 = max(tab_input_pc[index_output_dmx],tab_input_dmx[index_output_dmx]);
|
|
}
|
|
else {
|
|
UDR1 = tab_input_pc[index_output_dmx];
|
|
}
|
|
index_output_dmx++;
|
|
|
|
// si 512 transmits => mise en attente de fin de transmission
|
|
// desactivation de l'interruption sur le registre
|
|
if (index_output_dmx>nb_circuits) {
|
|
index_output_dmx=IOUTDMX_FIN;
|
|
cbi(UCSR1B, UDRIE1);
|
|
}
|
|
}
|
|
|
|
// Timer 4 : gestion des temps de break et mab
|
|
ISR(TIMER4_COMPA_vect) {
|
|
if (index_output_dmx==IOUTDMX_BRK ) { // en cours de break
|
|
index_output_dmx=IOUTDMX_MAB;
|
|
digitalWrite(TX1PIN, HIGH); // on met la broche à 1
|
|
// on relance le timer sur le temps du mab
|
|
OCR4A = mab_timer_end ;
|
|
TCNT4 = 0 ; // RAZ compteur timer
|
|
TIFR4 = 0 ; //Clear Flags timer
|
|
return;
|
|
}
|
|
if (index_output_dmx==IOUTDMX_MAB ) { // en cours de mab
|
|
//TXLED1; txledcpt=RX_TX_LED_TIME;
|
|
sbi(UCSR1B, TXEN1); // on reactive la transmission USART
|
|
index_output_dmx=0; // on se prépare à émmettre à partir du stat code ( 513 octets )
|
|
sbi(UCSR1B, UDRIE1); // on réactive l'intéruption du registre
|
|
|
|
// Arret du timer
|
|
TIMSK4= 0 ; //desactivation intéruption
|
|
TCCR4B = 0; // arret du timer
|
|
TIFR4 = 0 ; //Clear Flags timer
|
|
}
|
|
}
|
|
|
|
// ********************************** INIT ********************************* //
|
|
|
|
|
|
void setup() {
|
|
// Le startcode n'est jamais recu. On l'initialise ici
|
|
tab_input_pc[0]=0;
|
|
|
|
// init pin led interne en sortie
|
|
pinMode(LEDPIN, OUTPUT);
|
|
|
|
// init USART
|
|
// baudrate à 250k
|
|
UCSR1A = 1 << U2X1;
|
|
UBRR1H=0;
|
|
UBRR1L = 7;
|
|
|
|
// 2 bit de stop; pas de parité; 8 bits de données
|
|
UCSR1C = 14;
|
|
|
|
// activation reception et intéruptions serial 1
|
|
sbi(UCSR1B, RXEN1); //Reception
|
|
cbi(UCSR1B, TXEN1); //Pas de transmission avant 1er Break
|
|
sbi(UCSR1B, RXCIE1); //Interruption sur reception
|
|
sbi(UCSR1B, TXCIE1); //Interruption pour fin de transmission
|
|
|
|
// init timer 4 (break)
|
|
TIMSK4 = 0;
|
|
TCCR4C = 0;
|
|
TCCR4B = 0;
|
|
TCCR4A = 0; // 00000000
|
|
TCNT4 = 0; // compteur
|
|
TC4H=0;
|
|
|
|
sei();
|
|
|
|
// préparation du premier break
|
|
index_output_dmx=IOUTDMX_IDLE;
|
|
}
|
|
|
|
// ********************************** BOUCLE ********************************* //
|
|
|
|
void loop() {
|
|
// en mode synchrone, dmx_frame_interval est un maximum
|
|
if(index_output_dmx==IOUTDMX_WAIT && interbrk >=dmx_frame_interval)
|
|
index_output_dmx=IOUTDMX_IDLE;
|
|
|
|
// Lancement du Break : après fin de transmisson et ecoulement de "dmx_frame_interval" ms depuis le dernier break
|
|
// ou immediatement au passage en IDLE si mode synchrone
|
|
if (index_output_dmx==IOUTDMX_IDLE && (interbrk >= dmx_frame_interval||syncflag)) {
|
|
index_output_dmx=IOUTDMX_BRK;
|
|
tab_input_dmx[514]= (byte)interbrk ;
|
|
interbrk=0;
|
|
pinMode(TX1PIN, OUTPUT); // on met la broche en mode sortie
|
|
cbi(UCSR1B, TXEN1); //on stop la transmission
|
|
digitalWrite(TX1PIN, LOW); // on met la broche à 0
|
|
OCR4A = brk_timer_end ; // val 120 µs
|
|
TCNT4 = 0 ; // RAZ compteur timer
|
|
TIFR4 = 0 ; //Clear Flags timer
|
|
TIMSK4 = 64 ; //activation intéruption A &B
|
|
TCCR4B = 6; // 00000110 division par 32 de l'horloge base => 2µs
|
|
}
|
|
|
|
// octets d'état. Pour le moment, tous à 0
|
|
//tab_input_dmx[513]= 0;
|
|
//tab_input_dmx[514]= 0;
|
|
//tab_input_dmx[515]= 0;
|
|
//tab_input_dmx[516]= 0;
|
|
|
|
if(_usbLineInfo.lineState) { // Si port serie USB ouvert
|
|
blinkp=100, blinkon=70; // la led clignote plus vite
|
|
if(emissionPc!=0)
|
|
{
|
|
// en mode synchrone emission uniquement entre les transmissions DMX
|
|
if (!syncflag || index_output_dmx==IOUTDMX_WAIT || index_output_dmx==IOUTDMX_IDLE)
|
|
ecritUSB();
|
|
}
|
|
} else {
|
|
blinkon = 3500, blinkp=4000;
|
|
// dans le doute, on force l'etat d'entree
|
|
etat_input_pc=IINUSB_IDLE;
|
|
}
|
|
|
|
int m = millis();
|
|
if (m-blkl) { // Toutes les ms
|
|
interbrk+=(m-blkl) ;
|
|
blkcpt++; blkl = m;
|
|
if(rxledcpt) rxledcpt--;
|
|
else RXLED0;
|
|
if(txledcpt) txledcpt--;
|
|
else TXLED0;
|
|
if(blkcpt>=blinkon) { digitalWrite(LEDPIN, HIGH); }
|
|
if(blkcpt>=blinkp){ digitalWrite(LEDPIN, LOW); blkcpt=0; }
|
|
}
|
|
}
|
|
|
|
|