/* ********************************************************************** * File GMBCAN.C - Rel. 1.1 con uC/51 V. 1.10.09 * * GRIFO(R) via Dell'Artigiano 8/6 40016 S. Giorgio di Piano (BO) * * Tel. +39 051 892052 Fax. +39 051 893661 * * http://www.grifo.com http://www.grifo.it * * sales@grifo.it tech@grifo.it grifo@grifo.it * * by Angelini Gianluca del 04.07.03 * ********************************************************************** 04/07/03: GMBCAN.C - Rel. 1.1 - By Angelini Gianluca Se il Mini Modulo dispone della linea CAN su CN4, con questo demo e' possibile programmare da console i tipici parametri di comunicazione CAN, quindi ricevere e trasmettere messaggi. Grazie a questa flessibilita' e' possibile comunicare con qualsiasi altro dispositivo dotato di linea CAN. N.B. Per evitare problemi non eseguire operazioni complesse su una singola riga, specialmente all'interno di procedure sui relativi parametri e/o variabili locali. */ /**************************************************************************** Header, costanti, strutture dati, ecc. ****************************************************************************/ #include "canary.h" #include #include #define FALSE 0x00 // Valori booleani #define TRUE 0xFF #define LF 0x0A // Codici ASCII #define CRET 0x0D #define TOUTCAN 10000 // Costante di time out controllore CAN #define FCAN 7372800 // Frequenza del controllore CAN // Variabili globali gestione I2C BUS near unsigned char risi2c; // Variabile errore I2C BUS bit unsigned char SDACAN @ 0xA1; // Pin SDA su CAN GMx=P2.1 bit unsigned char SCLCAN @ 0xA0; // Pin SCL su CAN GMx=P2.0 bit unsigned char SDA5115 @ 0xA0; // Pin SDA su GMM 5115=P2.0 bit unsigned char SCL5115 @ 0xA1; // Pin SCL su GMM 5115=P2.1 // Variabili globali gestione CAN typedef unsigned char canbuf[9]; // Vettore scambio dati CAN controller near unsigned char msgobj; // Message object del controllore CAN near unsigned int id; // Identificatore CAN near unsigned char rtr; // Tipo di frame del messaggio CAN near unsigned char errcan; // Variabile per errori controllore CAN inear canbuf txbufcan,rxbufcan; // Buffer tx/rx CAN // Variabili globali usate dal main e dalle procedure near unsigned char minmod,scelta,dr,dw,hlp; near unsigned int val; inear unsigned char input[9]; // Buffer per input da console /**************************************************************************** Funzioni di utility generale e di gestione sezioni hw della scheda ****************************************************************************/ unsigned char divappr(unsigned long divid,unsigned long divis) /* Procedura di calcolo del quoziente intero a 8 bit, correttamente approssimato tra il dividendo ed il divisore passati nei parametri, con la tecnica delle sottrazioni sucessive. Si usa questa funzione per non avere i 2K di codice delle equivalenti funzioni di libreria. */ { unsigned char d; d=0; // Azzera quoziente while (divid>=divis) { divid=divid-divis; d++; } //endwhile divis=divis>>1; // Dimezza divisore per verifica resto if (divid>=divis) d++; //endif return d; } void init_cpu(void) /* Verifica la CPU montata sulla scheda e lo salva in apposita variabile. Effettua inoltre le apposite inizializzazioni: seriale, wait, ecc. */ { EA=0; // Assicura disabilitazione interrupt CKCON=0x00; // Setta X1 clock mode = standard mode AUXR=0x0C; // Seleziona ERAM su area dati esterna EECON=0x00; // Disabilita EEPROM del micro } void iniser(unsigned long baud) /* Inizializza la linea seriale con: Bit x chr = 8 Stop bit = 1 Parity = None Baud rate = baud usando come baud rate generator il timer 1. */ { SCON=0x052; // Modo 1, abilita ricevitore TMOD&=0x00F; // Timer 1 in modo auto-reload TMOD|=0x020; TR1=0; // Stop al TIMER 1 TH1=(unsigned char)(256-divappr((2*14745600),(384*baud))); // 14.7456 MHz PCON=PCON|0x080; // Setta SMOD=1 per baud rate alti TR1=1; // Start al TIMER 1 TI=1; // Setta bit fine trasmissione per console ottimizzata (SIOTYPE=k) } void clrscr(void) /* Effettua la funzione di clear screen per una generica console */ { unsigned char r; putc(CRET); for (r = 0 ; r < 25 ; r++) { putc(LF); // Trasmette 25 Line Feed } //endfor } void waitkey(void) /* Rappresenta messaggio ed attende pressione tasto */ { printf("Premere tasto per continuare.."); getc(); puts(""); } void nondisp(void) /* Rappresenta messaggio di risorsa non disponibile sul Mini Modulo scelto ed attende la pressione di un tasto */ { printf("Risorsa mancante su Mini Modulo. "); for(;;); } void ritardo(unsigned int rit) /* Effettua un ritardo software di rit millisecondi, calibrato su un Clock di CPU da 14.7456 MHz, a seconda della CPU montata. */ { unsigned int r,rit1ms; rit1ms=100; // Valore sperimentale per ritardo di 1 msec. con 80c32 do { for (r=0 ; r0); } void setP1234inp(void) /* Setta tutte le linee di tutti i port (P1,P2,P3,P4) del modulo CAN GM1 in input. */ { ADCF=0x00; // Setta P1.x come port di I/O P1=0xFF; // Setta Port 1 in INPUT dr=P1; P2=0xFF; // Setta Port 2 in INPUT dr=P2; P3=0xFF; // Setta Port 3 in INPUT dr=P3; P4=0xFF; // Setta Port 4 in INPUT dr=P4; } void riti2c(void) /* Genera ritardo per comunicazione sincrona in I2CBUS. Il ritardo e` sufficiente per un clock X 1, a 22 MHz. */ { #asm nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop #endasm } void starti2c(void) /* Genera sequenza di start per I2C BUS */ { if (minmod=='3') { SCL5115=0; // Genera sequenza di start con GMM 5115 SDA5115=1; riti2c(); SCL5115=1; SDA5115=0; riti2c(); SCL5115=0; } else { SCLCAN=0; // Genera sequenza di start con CAN GMx SDACAN=1; riti2c(); SCLCAN=1; SDACAN=0; riti2c(); SCLCAN=0; } //endif } void stopi2c(void) /* Genera sequenza di stop per I2C BUS */ { if (minmod=='3') { SCL5115=0; // Genera sequenza di stop con GMM 5115 SDA5115=0; riti2c(); SCL5115=1; SDA5115=1; riti2c(); SCL5115=0; } else { SCLCAN=0; // Genera sequenza di stop con CAN GMx SDACAN=0; riti2c(); SCLCAN=1; SDACAN=1; riti2c(); SCLCAN=0; } //endif } void wri2c_bit(unsigned char i2cbit) /* Serializza il bit D0 di i2cbit su I2C BUS */ { if (minmod=='3') { SCL5115=0; // Setta SDA e genera impulso positivo su SCL con GMM 5115 SDA5115=i2cbit; riti2c(); SCL5115=1; riti2c(); SCL5115=0; } else { SCLCAN=0; // Setta SDA e genera impulso positivo su SCL con CAN GMx SDACAN=i2cbit; riti2c(); SCLCAN=1; riti2c(); SCLCAN=0; } //endif } unsigned char rdi2c_bit(void) /* Deserializza un bit da I2C BUS e lo salva in D0 del risultato */ { unsigned char biti2c; if (minmod=='3') { SDA5115=1; // Evita conflitti su acquisizione SDA SCL5115=0; // Assicura stato SCL riti2c(); SCL5115=1; // Genera impulso positivo su SCL e su questo legge SDA biti2c=SDA5115; riti2c(); SCL5115=0; } else { SDACAN=1; // Evita conflitti su acquisizione SDA SCLCAN=0; // Assicura stato SCL riti2c(); SCLCAN=1; // Genera impulso positivo su SCL e su questo legge SDA biti2c=SDACAN; riti2c(); SCLCAN=0; } //endif return biti2c; } void wri2c_byte(unsigned char i2cbyte) /* Serializza il byte i2cbyte su I2C BUS */ { unsigned char b; for (b = 1; b <= 8; b++) { if ((i2cbyte & 0x80) == 0) // Determina e setta bit b wri2c_bit(0); else wri2c_bit(1); i2cbyte = i2cbyte << 1; } } unsigned char peekb(unsigned int addr) /* Legge il byte all'indirizzo addr dell'area dati esterna e lo restituisce nel nome funzione */ { return *(xdata unsigned char *)addr; // Preleva byte dalla locazione } void rd_ee(unsigned int eeaddr,unsigned char *eedato) /* Legge il byte eedato all'indirizzo eeaddr della EEPROM interna */ { AUXR=0x2E; // Deseleziona ERAM ed allunga durata MOVX EECON=0x02; // Seleziona EEPROM del micro su area dati esterna *eedato=peekb(eeaddr); // Effettua lettura EEPROM AUXR=0x0C; // Seleziona ERAM su area dati esterna EECON=0x00; // Disabilita EEPROM del micro } void inican(unsigned long brc,unsigned char sjw,unsigned char smp,unsigned char prs,unsigned char phs1,unsigned char phs2) /* Inizializza il controllore CAN con i parametri passati: brc = bit rate linea CAN (9000..1000000); sjw = tolleranza di durata bit (0..3); smp = numero campionamenti bit (0..1); prs = tempo compensazione bit (0..7); phs1 = tempo di precampionamento bit (0..7); phs2 = tempo di postcampionamento bit (0..7); ed aggiorna variabile globale d'errore. */ { unsigned char mo,nd; P4_0=1; // Setta TxDC alto per consentire comunicazione CAN nd=P4_0; P4_1=1; // Setta RxDC alto per consentire comunicazione CAN nd=P4_1; CANGCON=0x01; // Resetta controllore CAN ECAN=0; // Disabilita interrupt da CAN ETIM=0; CANGSTA=0x00; // Azzera flags errori generali CANIE2=0x00; // Disabilita interrupt da message object do { // Ciclo di attesa scheda pronta dr=P2_1; starti2c(); wri2c_byte(0xA0); dw=rdi2c_bit(); } while ((dr==0) || (dw==1)); for (mo=0; mo<4; mo++) // Inizializza le mailbox { CANPAGE=mo << 4; // Seleziona message object CANCONCH=0x00; // Disabilita message object CANSTCH=0x00; CANIDT1=0x00; CANIDT2=0x00; CANIDT3=0x00; CANIDT4=0x00; CANIDM1=0x00; CANIDM2=0x00; CANIDM3=0x00; CANIDM4=0x00; for (nd=0; nd<8; nd++) // Azzera dati delle mailbox CANMSG=0x00; } nd=4+prs+phs1+phs2; // Calcola numero Tscl per bit nd=divappr(FCAN,(nd*brc)); // Calcola prescaler brp per bit rate nd=nd-1; nd=nd << 1; CANBT1=nd; // Configura registri per tempistiche bit nd=sjw << 5; mo=prs << 1; nd=nd | mo; CANBT2=nd; nd=phs2 << 4; mo=phs1 << 1; mo=mo | smp; nd=nd | mo; CANBT3=nd; CANGCON=0x02; // Abilita CAN controller; no test; no listening mode } void trbasid(unsigned int id,unsigned char *idh,unsigned char *idl) /* Converte identificatore id nei due byte componenti in modalita` BASIC CAN salvati in idl e idh */ { *idh=(unsigned char)(id >> 3); // Determina bit 10..3 dell'identificatore *idl=(unsigned char)(id & 0x07); // Determina bit 2..0 dell'identificatore *idl=*idl << 5; } void txmsgcan(unsigned char *msgcan) /* Trasmette tramite il controllore CAN in modalita` BascicCAN=2.0A il messaggio salvato in msgcan: msgcan[0] = numero byte del messaggio msgcan[1..n] = dati del messaggio inoltre usa ed aggiorna: msgobj = variabile globale con message object da utilizzare id = variabile globale con identificatore rtr = variabile globale con tipo di frame errcan = variabile globale d'errore */ { unsigned char hl,hh; CANPAGE=msgobj << 4; // Seleziona message object CANIE2=0x00; // Disabilita interrupt da message object CANSTCH=0x00; // Azzera status register trbasid(id,&hh,&hl); // Determina e setta identificatore in Basic CAN CANIDT1=hh; CANIDT2=hl; CANIDT3=0x00; // Non usato in Basic CAN if (rtr) // Verifica e setta Remote Trasmission Request hl=0x04; // RTR=1 else hl=0x00; // RTR=0 CANIDT4=hl; for (hl=1; hl<=msgcan[0]; hl++) CANMSG=msgcan[hl]; // Carica byte da trasmettere hl=msgcan[0]; // Preleva n. dati del messaggio hl=hl | 0x40; // Aggiunge abilitazione trasmissione CANCONCH=hl; // Setta n. dati ed abilita trasmissione in Basic CAN CANCONCH=0x88; // Riabilita ricezione in Basic CAN } void setrxcan(unsigned int idrx,unsigned int mskrx) /* Predispone il ricevitore del message object msgobj in modalita` BasicCAN= =2.0A) con i parametri passati: idrx = identificatore accettazione ricezione (0..2047); mskrx = maschera accettazione (0..2047); */ { unsigned char hl,hh; do { // Ciclo di attesa scheda pronta dr=P2_1; starti2c(); wri2c_byte(0xA0); dw=rdi2c_bit(); } while ((dr==0) || (dw==1)); CANPAGE=msgobj << 4; // Seleziona message object trbasid(idrx,&hh,&hl); // Determina e setta identificatore in Basic CAN CANIDT1=hh; CANIDT2=hl; CANIDT3=0x00; // Non usato in Basic CAN CANIDT4=0x00; // Setta per data frame trbasid(mskrx,&hh,&hl); // Determina e setta maschera in Basic CAN CANIDM1=hh; CANIDM2=hl; CANIDM3=0x00; CANIDM4=0x01; // Accetta solo msg in Basic CAN CANCONCH=0x88; // Abilita ricezione in BASIC CAN } unsigned char rxmsgcan(unsigned char *msgcan) /* Controlla se e' stato ricevuto un messaggio dal controllore CAN, in modalita` BasicCAN=2.0A, e lo segnala nel nome funzione. Restituisce eventuale messaggio in msgcan: msgcan[0] = numero byte del messaggio msgcan[1..n] = dati del messaggio inoltre usa ed aggiorna: msgobj = variabile globale con message object da utilizzare id = variabile globale con identificatore rtr = variabile globale con tipo di frame errcan = variabile globale d'errore */ { unsigned char hl,rxok; CANPAGE=msgobj << 4; // Seleziona message object hl=CANSTCH; // Preleva status register rxok=hl & 0x20; // Verifica se message object ha ricevuto messaggio CANSTCH=hl & 0xDF; // Resetta flag RXOK if (rxok) { hl=CANIDT1; // Preleva,determina e salva identificatore in Basic CAN id=hl << 3; hl=CANIDT2; hl=hl & 0xE0; hl=hl >> 5; id=id | hl; hl=CANIDT4; // Verifica e salva Remote Trasmission Request if (hl & 0x04) rtr=0xFF; // RTR=1 else { rtr=0x00; // RTR=0 hl=CANCONCH; // Preleva e determina n. dati ricevuti hl=hl & 0x0F; msgcan[0]=hl; // Salva n. dati ricevuti for (hl=1; hl<=msgcan[0]; hl++) msgcan[hl]=CANMSG; // Preleva byte ricevuti } CANCONCH=0x88; // Riabilita ricezione in Basic CAN } return(rxok); } void wtmsgcan(unsigned char *msgcan) /* Aspetta che arrivi un messaggio sul controllore CAN e lo restituisce in msgcan: msgcan[0] = numero byte del messaggio msgcan[1..n] = dati del messaggio inoltre usa ed aggiorna: msgobj = variabile globale con message object da utilizzare id = variabile globale con identificatore rtr = variabile globale con tipo di frame errcan = variabile globale d'errore */ { unsigned int tmp; tmp=0; // Azzera contatore di time out do { tmp++; } while ((!rxmsgcan(msgcan)) && (tmp=TOUTCAN) errcan=errcan | 0x40; // Setta errore timeout } void checkcan (void) /* Controlla Flags di errore ed overrun del controllore CAN ed eventualmente li resetta. Restituisce la variabile globale d'errore aggiornata con i seguenti bit: errcan.5 -> errore di overflow errcan.4 -> errore di eco sui bit trasmessi errcan.3 -> errore di stuff bit mancante su messaggio ricevuto errcan.2 -> errore di crc su messaggio ricevuto errcan.1 -> errore di frame errcan.0 -> errore di mancato acknowledge */ { unsigned char hl; CANPAGE=msgobj << 4; // Seleziona message object hl=CANSTCH; // Preleva status register del message object CANSTCH=hl & 0xE0; // Azzera flags errori del message object hl=hl & 0x1F; // Maschera flags errori del message object errcan=errcan | hl; // Aggiunge errori message object hl=CANGSTA; // Preleva status register generale CANGSTA=hl & 0xBF; // Azzera flags errori generali hl=hl & 0x40; // Maschera flags errori generali hl=hl >> 1; // Posiziona flags errori generali errcan=errcan | hl; // Aggiunge errori generali } /**************************************************************************** Programma principale ****************************************************************************/ void main(void) { unsigned long bitr; // Variabili per demo CAN unsigned int rxmsk,rxid; unsigned char sjw,smp,prs,phs1,phs2; init_cpu(); // Inizializza tipo di CPU montata iniser(19200); // Inizializza seriale A di console con timer 1 setP1234inp(); // Setta Port 1,2,3,4 in INPUT clrscr(); // Seleziona mini modulo utilizzato puts("1->CAN GM1 , 2->CAN GM2 , 3->GMM 5115"); printf("Selezionare Mini Modulo montato su zoccolo ZC1 (1,2,3):"); do minmod=toupper(getc()); while ((minmod<'1') || (minmod>'3')); clrscr(); // Presenta menu` programma test printf("Demo gestione CAN su GMB HR84 in uC/51 - Rel. 1.1 con "); switch (minmod) { case '1': puts("CAN GM1"); break; case '2': puts("CAN GM2"); break; case '3': puts("GMM 5115"); break; } //endswitch if (minmod=='3') // Ciclo di attesa scheda pronta { // Su GMM 5115 do rd_ee(0x07F8,&dr); while (dr!=0); } else { // Su CAN GM1, CAN GM2 do { dr=P2_1; starti2c(); wri2c_byte(0xA0); dw=rdi2c_bit(); } while ((dr==0) || (dw==1)); } //endif puts(""); if (minmod!='3') { for(;;) { errcan=0; // Resetta variabile globale d'errore msgobj=0; // Seleziona message object 0 printf("Demo linea CAN su CN4."); printf("\r\nBit rate (9000..1000000) [16600]="); inputse(input, 8); // Preleva unsigned long bitr=(unsigned long)atol(input); printf("\r\nTolleranza bit (0..3) [3]="); inputse(input, 8); // Preleva unsigned char sjw=(unsigned char)atoi(input); printf("\r\nNumero campionamenti (0..1) [1]="); inputse(input, 8); // Preleva unsigned char smp=(unsigned char)atoi(input); printf("\r\nTempo compensazione (0..7) [2]="); inputse(input, 8); // Preleva unsigned char prs=(unsigned char)atoi(input); printf("\r\nTempo precampionamento (0..7) [3]="); inputse(input, 8); // Preleva unsigned char phs1=(unsigned char)atoi(input); printf("\r\nTempo postcampionamento (0..7) [3]="); inputse(input, 8); // Preleva unsigned char phs2=(unsigned char)atoi(input); printf("\r\nId. accettazione ric.(0..2047) [255]="); inputse(input, 8); // Preleva unsigned int rxid=(unsigned int)atoi(input); printf("\r\nMaschera accettazione (0..2047) [0]="); inputse(input, 8); // Preleva unsigned int rxmsk=(unsigned int)atoi(input); inican(bitr,sjw,smp,prs,phs1,phs2);// Inizializza tempi controllore CAN setrxcan(rxid,rxmsk); // Inizializza accettazione msg CAN puts("\r\nCAN inizializzato..."); puts("Visualizzazione dei msg ricevuti e degli errori. Premere T per trasmettere"); puts("un msg, F per ultimare."); do { if (rxmsgcan(rxbufcan)) // Se messaggio CAN ricevuto { setrxcan(rxid,rxmsk); // Risetta accettazione msg CAN puts("Msg ricevuto:"); // Rappresenta dati msg rx printf("Identificatore= %d\r\n",id); printf("Tipo msg= "); if (rtr) puts("Remote Frame"); else { puts("Data Frame"); printf("Numero dati = %d\r\n",rxbufcan[0]); for (dr=1; dr <= rxbufcan[0]; dr++) printf("Dato rx n. %d = %d\r\n",dr,rxbufcan[dr]); // endfor } // endif putc(LF); } // endif if (kbhit()) // Se tasto premuto { scelta=getc(); // Lo preleva e lo trasforma in maiuscolo scelta=toupper(scelta); if (scelta == 'T') { puts("Msg da trasmettere:"); // Richiede dati msg da tx printf("Identificatore (0..2047)="); inputse(input, 8); // Preleva unsigned int id=(unsigned int)atoi(input); printf("\r\nTipo msg: Data o Remote frame (D/R)="); do { dr=getc(); dr=toupper(dr); } while ((dr != 'D') && (dr != 'R')); if (dr == 'R') { // Se remote frame setta flag e non ci sono dati puts("Remote Frame"); rtr=0xFF; } else { // Se data frame resetta flag e chiede dati puts("Data Frame"); rtr=0x00; printf("Numero dati (0..8)="); inputse(input, 8); // Preleva unsigned char txbufcan[0]=(unsigned char)atoi(input); for (dr=1; dr <= txbufcan[0]; dr++) { printf("\r\nDato tx n. %d (0..255)=",dr); inputse(input, 8); // Preleva unsigned char txbufcan[dr]=(unsigned char)atoi(input); } // endfor puts(""); } // endif puts(""); txmsgcan(txbufcan); // Trasmette messaggio inserito setrxcan(rxid,rxmsk); // Risetta accettazione msg CAN } // endif } // endif checkcan(); // Controlla e gestisce errori CAN dr=1; // Rappresenta errori CAN do { dw=errcan & dr; // Maschera singolo flag d'errore if (dw) { printf("Errore "); switch (dw) { case 0x80: break; case 0x40: puts("Timeout scaduto"); break; case 0x20: puts("overflow"); break; case 0x10: puts("eco errato"); break; case 0x08: puts("stuff bit mancante"); break; case 0x04: puts("crc"); break; case 0x02: puts("frame"); break; case 0x01: puts("acknowledge"); break; } // endswitch } // endif dr=dr << 1; } while (dr != 0); errcan=0; // Azzera variabile errori CAN ritardo(50); // Ritardo per cosole } while (scelta!='F'); // Attende pressione tasto F scelta=' '; } // endfor (;;) // Fine loop infinito } else nondisp(); // Risorsa non disponibile su Mini Modulo scelto //endif }