//----------------------------------------------------------------------------- // Propellor Clock // dsPIC 30F3010 // L. BAGHLI 03/02/2007 // version actuelle : beta Quartz=10Mhz, // IC2 repère un aimant tournant et calcule la Ts (Timer2) // Timer1 ttes les 100 us allume le diodes en fonction de la position // v0.11 09/02/2007 prise en compte sur IC2/RD0 de l'entrée IR du 1838 // decodage de la trame RC5 en cours // MAX3233E pour envoyer les données de deboggage à un terminal RS232 via UART1 //----------------------------------------------------------------------------- #include "p30F3010.h" //Configuration bits _FOSC(CSW_FSCM_OFF & XT_PLL8); //10Mhz *8 = 80 MHz /4 = 20 MIPS maxi pour ce pic _FWDT(WDT_OFF); _FBORPOR(PBOR_OFF & BORV_27 & PWRT_16 & MCLR_EN); _FGS(CODE_PROT_OFF); //----------------------------------------------------------------------------- //Program Specific Constants #define FCY 20000000 //Instruction cycle rate (Osc x PLL / 4) = 20 MIPS #define T1Period 2000 // pour 100us à 20 MHz = T1Period = 2000=FCY*100us #define tcntPRD 10000 // combien de fois pour ariver en 1s avec des pas de 100us : 10000 #define MILLISEC FCY/20000 // 1 mSec delay constant // 11 LED differentes = 1 (en fait 6 en même tps) + 8 + 2 + 1 blue donc 12 Outputs // LED bleue #define LEDBlue PORTEbits.RE8 // la derniere (pour les secondes) #define LED_E PORTE // 6 Leds #define LED_B PORTB // 6 Leds // IC1/RD0 et IC2/RD1 sont pour la mesure de l IR 1838 en codage RC5 et du top index du changement d etat du hall sensor SS40A #define IR_receive PORTDbits.RD0 // InputIR module #define TopIndex PORTDbits.RD1 // Input sends top index hall SS041 + aimant fixe. void setup_ports(void); void DelayNmSec(unsigned int N); struct { unsigned Running : 1; unsigned CheckRX : 1; unsigned SendTX : 1; unsigned SendIR : 1; unsigned Sendtheta : 1; unsigned SendLED : 1; unsigned IR_bit : 1; // bit value unsigned IR_toggle : 1; // toggle on short pulse unsigned IR_busy : 1; // receive in progress unsigned IR_done : 1; // receive done unsigned charinv : 1; // receive done unsigned unused : 5; } Flags; // l angle theta est codé sur 16 bits. unsigned int theta, dth, thcentre; // angle actuel et increment angulaire, centre du caractere à afficher int Ts; // Periode de rotation (en unités Timer2) unsigned int heure, heure1, heure2, min, min1, min2, sec, sec1, sec2, tcnt; unsigned int Ref1, Ref2; unsigned int thAigsec, thAigmin, thAigheure; // position des aiguilles unsigned char Char2Disp, kp, EtatLEDchar; // etat des led pour les caracteres unsigned int EtatLED; // etat des led sur 16 bits, LSB centre, MSB péripherie int itmp; // 65536/60 et 65536/12 #define c65536_60 1092 #define c65536_12 5461 // thc=M_PI/8; // kth=5/M_PI; // thsec1=M_PI*0.5/8; // thsec2=M_PI*15.5/8; // thmin1=M_PI*6/8+thc*0; // thmin2=M_PI*6/8+thc*1; // thcolon=M_PI; // thh1=M_PI*6/8+thc*3; // thh2=M_PI*6/8+thc*4; #define thc 0x1000 #define thc_2 0x0800 #define kth 0.000152587890625 #define thsec1 0x0800 #define thsec2 0xF800 #define thmin1 0x6000 #define thmin2 0x7000 #define thcolon 0x8000 #define thheure1 0x9000 #define thheure2 0xA000 // angle de 2° pour la largeur des aiguilles #define th2deg 0x016C // code des caractères inversés unsigned char invNumber[]={ 0, 0, 0, 126, 129, 129, 129, 126, // 0 inversé encore une fois 0, 0, 0, 128, 130, 130, 253, 128, // 1 0, 0, 0, 194, 161, 145, 137, 134, // 2 0, 0, 0, 66, 129, 137, 137, 118, // 3 0, 32, 48, 40, 36, 34, 255, 32, // 4 0, 0, 0, 79, 129, 137, 137, 113, // 5 0, 0, 0, 124, 138, 137, 137, 112, // 6 0, 0, 0, 1, 193, 49, 13, 3, // 7 0, 0, 0, 118, 137, 137, 137, 118, // 8 0, 0, 0, 14, 145, 145, 81, 62, // 9 0, 0, 0, 0, 0, 102, 0, 0}; // : // IR code ------------------------------------------- unsigned char IR_time; // count bit time unsigned int IR_tmp; // shift bits in unsigned int IR_CodeFull; // store result unsigned char IR_CodeAddy, IR_CodeData; #define PULSE1_2 (unsigned char)(8 * 1.5 + 1) #define PULSE2MAX (unsigned char)(8 * 2.5 + 1) // RS232 ------------------------------------------- unsigned char *TXPtr; unsigned char *RXPtr; void InitUART(void); void SendMsg(void); #define CR 0x0D #define LF 0x0A #define BAUD 19200 #define OffsetIRCodeData 5 // offset in OutData : position de la val de IRCodeData #define OffsetIRCodeAddy 13 // offset in OutData : position de la val de IRCodeAddy unsigned char InData[] = {"000000"}; unsigned char OutData[] = {"IRD= 00 IRA= 00\r\n"}; #define Offsettheta 7 // offset in OutData : position de la val de theta #define OffsetTime 18 // offset in OutData : position de la val de Time unsigned char OutTheta[] = {"theta= 0000 Time= 00:00:00\r\n"}; // offset in OutEtatLed #define OffsetLED 5 #define OffsetEtatLED 34 #define OffsetChar 45 #define Offsetkp 51 unsigned char OutEtatLed[] = {"LED=[0123456789ABCDEF[ EtatLED=0xXXXX Char=XX kp=X\r\n"}; int SeqComm; // ttes les 0.5 s #define SeqCommMax 5000 //debug bad #define SeqCommMax 2000 // ------------------------------------------- //----------------------------------------------------------------------------- // Initialise variables //----------------------------------------------------------------------------- void InitVar(void) { Flags.Running=0; tcnt=0; heure=0; heure1=0; heure2=0; min=0; min1=0; min2=0; sec=0; sec1=0; sec2=0; Flags.IR_done=0; Flags.IR_busy=0; Flags.IR_bit=1; Flags.SendIR = 0; // clear 3 flags RS232 Flags.Sendtheta = 0; Flags.SendLED = 0; } //----------------------------------------------------------------------------- // Setup ports //----------------------------------------------------------------------------- void setup_ports(void) { // Clear All Ports Prior to defining I/O // ADPCFG = 0xFFFF; // all PORTB = Digital(1), no analog PORTB=0; //Initialize LED pin data to off state PORTC=0; PORTD=0; PORTE=0; // Now set pin direction registers //debug_____________________________________________________________________ // temporaire pour lire la valeur de theta à l aide des potars AN4 et AN5 sur ch3 et ch4 // TRISB = 0xFF00; // RB0-5 output LED TRISB = 0xFFF0; // RB0-3 output LED, RB4, 5 input ADC AN4 et AN5 TRISC = 0xDFFF; // C13 TX out, C14 RX in, le reste : input / Unused TRISD = 0xFFFF; // RD0 input : Topindex, RD1 input : IR_receive TRISE = 0x0000; // RE0-8 Output LED TRISF = 0xFFFF; // RFx not used (input) //debug_____________________________________________________________________ // temporaire pour lire la valeur de theta à l aide des potars AN4 et AN5 sur ch3 et ch4 ADPCFG = 0xFFCF; // all PORTB = Digital(1) ;RB4 et RB5 = analog(0) ie 1100 1111 ADCON1 = 0x000F; // Clearing SAMP bit SOC, 4 ch simultanés, ADC Off for configuring ADCON2 = 0x0200; // simulataneous sample 4 channels, ADC INTerrupt à chaque EOC=100 us ADCHS = 0x0020; // AN4/RB4 Ch2 Ref1, AN5/RB5 Ch3 Ref2 ADCON3 = 0x0080; // Tad = internal RC (4uS) ADCON1bits.ADON = 1; // turn ADC ON // fin debug temporaire_____________________________________________________ } //----------------------------------------------------------------------------- // intitialise timer 1 et l interruption //----------------------------------------------------------------------------- void initTimer_CNx(void) { // Timer1 pour l ISR des 100 us T1CON = 0; // ensure Timer 1 is in reset state, internal timer clock Fosc/4, no prescale TMR1 = 0; // RAZ Timer1 IFS0bits.T1IF = 0; // reset Timer 1 interrupt flag IPC0bits.T1IP = 4; // set Timer1 interrupt priority level to 4 IEC0bits.T1IE = 1; // enable Timer 1 interrupt PR1 = T1Period; // set Timer 1 period register // Timer2 pour compter Ts entre 2 index T2CON = 0x0010; // ensure Timer 1 is in reset state, internal timer clock Fosc/4, prescale 1:8 // ie 100 tr/s donne Ts=20e6*0.01/8 = 25000 = 0x61A8 TMR2 = 0; // RAZ Timer2 PR2 = 0xFFFF; // set Timer 2 period register maximum, RAZ sur index // ICN2 enclenché sur Timer2, on rising edge pour la sonde Hall d index IC2CON = 0x0083; IFS0bits.IC2IF = 0; // Clear IF bit IPC1bits.IC2IP = 3; // assigning Interrupt Priority to IPC Register IEC0bits.IC2IE = 1; // assiging Interrupt Enable T2CONbits.TON = 1; // enable Timer 2 and start the count T1CONbits.TON = 1; // enable Timer 1 and start the count } //--------------------------------------------------------------------- // Check si on est sur une aiguille int InAiguille(unsigned int th1, unsigned int th2) { if (th1-th2<=th2deg && th1-th2>=0) return 1; //2 degrés else return 0; } //--------------------------------------------------------------------------- // Check si on est sur un caractere int InCaractere(unsigned int th1, unsigned int th2) { unsigned int res; res=th1-th2; if (th1 depasse 2pi) alors on fait quoi ? //debug_____________________________________________________________________ // temporaire pour lire la valeur de theta à l aide des potars AN4 et AN5 sur ch3 et ch4 ADCON1bits.SAMP = 0; // start conversion while(!ADCON1bits.DONE) {} // attend la fin Ref1=ADCBUF2; Ref2=ADCBUF3; // debug // PORTE=Ref1>>4; // if (Ref1>512) PORTEbits.RE8=1; // else PORTEbits.RE8=0; //___ // dummy read adcbuf 0 et 1 ? // debug // theta=Ref1<<6; theta=(Ref1<<6) + (Ref2<<2); // donne une position fictive des diodes theta=(Ref1<<6) + (Ref2<<2); // theta=Ref1; // donne une position fictive des diodes if (++tcnt>=tcntPRD) { tcnt=0; sec++; if (++sec1==10) { sec1=0; if (++sec2==6) { sec=0; sec2=0; min++; if (++min1==10) { min1=0; if (++min2==6) { min=0; min2=0; heure1++; if (++heure==24) { heure1=0; heure2=0; } else if (heure1==10) // n atteintt pas plus de 23 h ! { heure1=0; heure2++; } } } } } // comme l'heure hh:mm:ss a changé => calcule la nouvelle position des aiguilles thAigsec=c65536_60*sec; thAigmin=c65536_60*min; itmp=heure-12; if (itmp<0) thAigheure=c65536_12*heure; else thAigheure=c65536_12*itmp; } // fin debug temporaire_____________________________________________________ // utilisation du theta et de l heure ! Char2Disp=22; // ie pas ds un caractere Flags.charinv=0; if (InCaractere(theta, thsec1)) {Char2Disp=sec1; thcentre=thsec1; Flags.charinv=1;} if (InCaractere(theta, thsec2)) {Char2Disp=sec2; thcentre=thsec2; Flags.charinv=1;} if (InCaractere(theta, thmin1)) {Char2Disp=min1; thcentre=thmin1; } if (InCaractere(theta, thmin2)) {Char2Disp=min2; thcentre=thmin2; } if (InCaractere(theta, thheure1)) {Char2Disp=heure1; thcentre=thheure1; } if (InCaractere(theta, thheure2)) {Char2Disp=heure2; thcentre=thheure2; } if (InCaractere(theta, thcolon)) {Char2Disp=10; thcentre=thcolon; } // : // calcule l'etat des LED EtatLED=0; if (InAiguille(theta,thAigheure)) EtatLED |=0x001F; if (InAiguille(theta,thAigmin)) EtatLED |=0x007F; if (InAiguille(theta,thAigsec)) EtatLED |=0x0080; EtatLEDchar=0; if (Char2Disp!=22) { kp=(theta-thcentre+thc_2)>>9; // de theta vers pixel index : 0x1000 -> 8 ie divisé par 0x200 if (Flags.charinv) kp=7-kp; if (kp>7) kp=7; EtatLEDchar=invNumber[(Char2Disp<<3)+kp]; EtatLED |= EtatLEDchar<<8; // affiche le caractère vers l'extrémité, loin du centre de rotation } // allume les LED // debug // ne touche pas à RE8 unsigned int etatR8=PORTE & 0x100; PORTE=(EtatLED>>8 )& 0x3F | etatR8; // 6 LED actuellement + 1 de controle/oscillo // decode trame IR suivant RC5 if (!IR_receive==Flags.IR_bit){ // change detect Flags.IR_bit = ~Flags.IR_bit; // store new state if( Flags.IR_busy == 0 ) { if( Flags.IR_bit == 1 ){ // start pulse detect Flags.IR_busy = 1; IR_tmp = 1; // == 0x2000 after 13 shift Flags.IR_toggle = 1; } } else { Flags.IR_toggle = ~Flags.IR_toggle; // skip every second change if( Flags.IR_toggle || IR_time < PULSE1_2 ){ PORTEbits.RE8=1; Flags.IR_toggle = 1; // false on next pulse IR_tmp <<= 1; // shift if( Flags.IR_bit ) IR_tmp |= 1; // add new bit if( IR_tmp & 0x2000 ){ // after shifted 13 times IR_CodeFull = IR_tmp; Flags.IR_busy = 0; Flags.IR_done = 1; IR_CodeAddy=IR_CodeFull>>6 & 0x1F; IR_CodeData=IR_CodeFull & 0x3F; } PORTEbits.RE8=0; } } IR_time = PULSE2MAX; } else { if (--IR_time==0 ) // if no change, count down Flags.IR_busy = 0; // timeout } // communication dsPIC -> PC if (!--SeqComm) { SeqComm=SeqCommMax; // uniquement si nouvelle donnée : if (IR_done) Flags.SendIR=1; Flags.SendIR=1; Flags.Sendtheta=1; Flags.SendLED=1; } //PORTEbits.RE8=0; // InterruptLED=0; } //--------------------------------------------------------------------- // ICN2 interrupt : passage devant l index (aimant) alors mesure du chrono et RAZ //--------------------------------------------------------------------- void __attribute__((__interrupt__)) _IC2Interrupt( void ) { IFS0bits.IC2IF = 0; TMR2 = 0; // RAZ Timer2 theta=0; // la position aussi if (IC1CONbits.ICBNE) Ts= IC2BUF; // reads the input capture buffer // calcule l incrément dth correspondant à 100 us // il y a N increment en 0.01s : N = 0.01s/100us = Ts*8/(100e-6*20e6) = 65536/dth // dth=65536*2000/8/Ts //debug // dth=8192*T1Period/Ts; } //--------------------------------------------------------------------- // Below are the interrupt vectors for the serial receive and transmit //--------------------------------------------------------------------- void __attribute__((__interrupt__)) _U1TXInterrupt(void) { IFS0bits.U1TXIF = 0; // clear interrupt flag } //--------------------------------------------------------------------- void __attribute__((__interrupt__)) _U1RXInterrupt(void) { IFS0bits.U1RXIF = 0; // clear interrupt flag *RXPtr = U1RXREG; if (*RXPtr == CR) {Flags.CheckRX = 1; RXPtr = &InData[0];} else *RXPtr++; } //------------------------------------------------------------------------ // Transmission over serial void InitUART(void) { // Initialize the UART1 for BAUD = 19,200 U1MODE = 0x8400; // enable + alternate pins // U1MODE = 0x8000; // enable + normal pins U1STA = 0x0000; U1BRG = ((FCY/16)/BAUD) - 1; // set baud to 19200 IEC0bits.U1RXIE = 1; // enable RX interrupt RXPtr = &InData[0]; // point to first char in receive buffer Flags.CheckRX = 0; // clear rx and tx flags Flags.SendTX = 0; U1STAbits.UTXEN = 1; // Initiate transmission SeqComm=SeqCommMax; } //------------------------------------------------------------------------ // Sendfs sends the IRcode (Data & Addy) information on the uart at 19200 baud void SendIRcode() { unsigned int k; unsigned char c; // Codage ASCII de la donnée k = IR_CodeData; c = k/10; if (c > 0) k = k - c*10; OutData[OffsetIRCodeData] = (c + 0x30); OutData[OffsetIRCodeData+1] = (char)(k + 0x30); k = IR_CodeAddy; //format à revoir .... c = k/10; if (c > 0) k = k - c*10; OutData[OffsetIRCodeAddy] = (c + 0x30); OutData[OffsetIRCodeAddy+1] = (char)(k + 0x30); TXPtr = &OutData[0]; SendMsg(); } //------------------------------------------------------------------------ // Sendfs sends the theta information on the uart at 19200 baud void Sendthetacode() { unsigned int k; unsigned char c; // Codage ASCII de la donnée hexa k = theta; c=k & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutTheta[Offsettheta+3]=c; c=k>>4 & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutTheta[Offsettheta+2]=c; c=k>>8 & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutTheta[Offsettheta+1]=c; c=k>>12 & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutTheta[Offsettheta]=c; // codage ASCII de l heure OutTheta[OffsetTime]= (heure2 + 0x30); OutTheta[OffsetTime+1]=(heure1 + 0x30); OutTheta[OffsetTime+3]=(min2 + 0x30); OutTheta[OffsetTime+4]=(min1 + 0x30); OutTheta[OffsetTime+6]=(sec2 + 0x30); OutTheta[OffsetTime+7]=(sec1 + 0x30); TXPtr = &OutTheta[0]; SendMsg(); }//------------------------------------------------------------------------ // Sendfs sends the LED information on the uart at 19200 baud void SendLEDcode() { unsigned int k; unsigned char c; // Codage ASCII de la donnée hexa for (k=0; k<16; k++) if ((EtatLED>>k)&1 == 1) OutEtatLed[OffsetLED+k]=0x2A; else OutEtatLed[OffsetLED+k]=0x20; k = EtatLED; c=k & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutEtatLed[OffsetEtatLED+3]=c; c=k>>4 & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutEtatLed[OffsetEtatLED+2]=c; c=k>>8 & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutEtatLed[OffsetEtatLED+1]=c; c=k>>12 & 0x000F; if (c<=9) c+=0x30; else c+=0x37; OutEtatLed[OffsetEtatLED]=c; // Char k = Char2Disp; c = k/10; if (c > 0) k = k - c*10; OutEtatLed[OffsetChar] = (c + 0x30); OutEtatLed[OffsetChar+1] = (char)(k + 0x30); // kp OutEtatLed[Offsetkp] = (kp + 0x30); TXPtr = &OutEtatLed[0]; SendMsg(); } //----------------------------------------------------------------------------- void SendMsg(void) { while (*TXPtr) { while (U1STAbits.UTXBF); U1TXREG = *TXPtr++; } } //----------------------------------------------------------------------------- //Main routine int main(void) { setup_ports(); InitVar(); InitUART(); initTimer_CNx(); while(1) { if (Flags.SendIR) { SendIRcode(); // send present fs serially Flags.SendIR = 0; // clear flag } if (Flags.Sendtheta) { Sendthetacode(); // send present fs serially Flags.Sendtheta = 0; // clear flag } if (Flags.SendLED) { SendLEDcode(); // send present fs serially Flags.SendLED = 0; // clear flag } } // end of while (1) } // end of main //============================================================================= //Error traps //----------------------------------------------------------------------------- //Oscillator Fail Error trap routine void _ISR _OscillatorFail(void) { while(1); //Wait forever } //----------------------------------------------------------------------------- //Address Error trap routine void _ISR _AddressError(void) { while(1); //Wait forever } //----------------------------------------------------------------------------- //Stack Error trap routine void _ISR _StackError(void) { while(1); //Wait forever } //----------------------------------------------------------------------------- //Math (Arithmetic) Error trap routine void _ISR _MathError(void) { while(1); //Wait forever } //--------------------------------------------------------------------- // This is a generic 1ms delay routine to give a 1mS to 65.5 Seconds delay // For N = 1 the delay is 1 mS, for N = 65535 the delay is 65,535 mS. // Note that FCY is used in the computation. Please make the necessary // Changes(PLLx4 or PLLx8 etc) to compute the right FCY as in the define // statement above. //--------------------------------------------------------------------- void DelayNmSec(unsigned int N) { unsigned int j; while(N--) for(j=0;j < MILLISEC;j++); } //---------------------------------------------------------------------