target/avr/asuro_avr.c

Go to the documentation of this file.
00001 /** @file 
00002  * 
00003  * AVR dependent part of interface to ASURO's devices
00004  * 
00005  * @author Denis Martin
00006  * 
00007  * This program is free software; you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation; either version 2 of the License, or (at your option) any later
00010  * version.
00011  * This program is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
00014  * details. You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation, Inc.,
00016  * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include "asuro.h"
00020 #include "asuro_irrc5.h"
00021 
00022 /**
00023  * User ISR for INT1 (switches).
00024  */
00025 volatile AS_UserIsr AS_UserIsrInterrupt1 = 0;
00026 
00027 /**
00028  * Intermediate counter for system clock ticks. Don't read it directly, but 
00029  * use AS_GetClockTicks() or AS_GetSysTime() instead.
00030  */
00031 volatile unsigned char AS_count36kHz = 0;
00032 
00033 /**
00034  * Counter for system clock ticks. Don't read it directly, but use 
00035  * AS_GetClockTicks() or AS_GetSysTime() instead.
00036  */
00037 volatile unsigned long AS_clockTicks = 0;
00038 
00039 /**
00040  * Enable/disable IR-RC5 decoding in ISR. Will be disabled during read/write on
00041  * IR serial interface.
00042  */
00043 volatile unsigned char AS_irrc5DecodeEnabled = 1;
00044 
00045 /**
00046  * Set to true if the interrupt for switches is enabled. If it is not enabled,
00047  * you may activate it by using AS_SensSwitchesInterruptOn().
00048  */
00049 volatile char AS_sensSwitchesOn = 0;
00050 
00051 /**
00052  * Set to true, when one or more of the switches are pressed. Note that you
00053  * have to turn on the concerned interrupt using AS_SensSwitchesInterruptOn().
00054  */
00055 volatile char AS_sensSwitched = 0;
00056 
00057 /** @name Interrupt handlers **************************************************/
00058 //@{
00059 
00060 /**
00061  * ISR for timer2 overflow. timer2 is used as a 72kHz counter for IR 
00062  * communication. While the signal SIG_OUTPUT_COMPARE2 would be triggered every 
00063  * 1/72k seconds, this ISR is called only every 1/36k seconds.
00064  */
00065 SIGNAL(SIG_OVERFLOW2)
00066 {
00067     TCNT2 += 0x25;  // next compare match in (0x91 - 0x25) = 0x6C 8MHz ticks
00068                     // this causes the compare match to occur every 1/72kHz seconds
00069                     // (needed for IR modulation)
00070     
00071     AS_count36kHz++;
00072     if (!AS_count36kHz) AS_clockTicks++; // count overflows
00073     
00074     if (AS_irrc5DecodeEnabled && !(AS_count36kHz % 8)) 
00075         AS_isr_irrc5(); // called about every 222.2us
00076     
00077 }
00078 
00079 /**
00080  * ISR for switches. The interrupt is disabled here, so you have to reactivate
00081  * it manually using AS_SensSwitchesInterruptOn().
00082  */
00083 SIGNAL(SIG_INTERRUPT1)
00084 {
00085     AS_sensSwitched = 1;
00086     AS_SensSwitchesInterruptOff();
00087     
00088     // call user ISR
00089     if (AS_UserIsrInterrupt1 != 0) AS_UserIsrInterrupt1();
00090 }
00091 
00092 //@}
00093 
00094 /** @name Functions ***********************************************************/
00095 //@{
00096 
00097 /**
00098  * Halt execution for a given time of 36kHz ticks.
00099  * 
00100  * @param ticks Time to sleep in 36kHz ticks (0..255)
00101  */
00102 inline void AS_Avr_Sleep(unsigned char ticks)
00103 {
00104     unsigned char tick = AS_count36kHz + ticks;
00105     while (tick != AS_count36kHz);
00106 }
00107 
00108 /**
00109  * Do some initialization stuff.
00110  */
00111 inline void AS_Init()
00112 {
00113     // serial interface programmed in boot routine and already running
00114     
00115     // prepare 72kHz oscillator for IR communication
00116     TCCR2 = (1 << WGM20) | (1 << WGM21) | (1 << COM20) | (1 << COM21) | (1 << CS20);
00117     OCR2  = 0x91; // (0xFF - 0x91) = 0x6E = 8MHz/72kHz (oscillator freq. divided by 2x IR modulation freq.)
00118     TIMSK |= (1 << TOIE2); // unmask timer2/overflow interrupt
00119     
00120     // set RS232 communication parameters
00121     UCSRA = 0x00;
00122     UCSRB = 0x00;
00123     UCSRC = 0x86; // no parity | 1 stop bit | 8 data bits
00124     UBRRL = 0xCF; // 2400bps @ 8.00MHz
00125     
00126     // set output ports
00127     DDRB = AS_DIR_FWD | AS_DIR_RWD | AS_PB_MTR_PWM | AS_PB_LED_IRTX | AS_PB_LED_GREEN;
00128     DDRD = AS_DIR_FWD | AS_DIR_RWD | AS_PD_LED_FRONT | AS_PD_LED_ODO | AS_PD_LED_RED;
00129     
00130     // for PWM (8-Bit PWM) on OC1A & OC1B
00131     TCCR1A = (1 << WGM10) | (1 << COM1A1) | (1 << COM1B1);
00132     // tmr1 running on MCU clock/8 
00133     TCCR1B = (1 << CS11);
00134     
00135     // A/D Conversion
00136     ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // clk/64 
00137     
00138     AS_BACK_LED_MODE;
00139     AS_LED_LBACK_ON; AS_LED_RBACK_ON;
00140     AS_LED_LBACK_OFF; AS_LED_RBACK_OFF; 
00141     AS_LED_ODO_OFF;
00142     AS_LED_FRONT_OFF;
00143     AS_LED_GREEN_OFF;
00144     AS_LED_RED_OFF;
00145     
00146     AS_MotorDir(AS_DIR_FWD, AS_DIR_FWD);
00147     AS_MotorSpeed(0,0);
00148     AS_LED_GREEN_ON;
00149     sei();
00150 }
00151 
00152 /**
00153  * Return the number of system clock ticks elapsed since system start. Remember
00154  * that it might overflow.
00155  * 
00156  * @return Number of system clock ticks
00157  */
00158 inline unsigned long AS_GetClockTicks()
00159 {
00160     return (AS_clockTicks<<8) + AS_count36kHz;
00161 }
00162 
00163 /**
00164  * Return the time in milliseconds elapsed since system start. Remember that it
00165  * might overflow.
00166  * 
00167  * @return System time in milliseconds
00168  */
00169 inline unsigned long AS_GetSysTime()
00170 {
00171     return ((AS_clockTicks/36)<<8) + AS_count36kHz/36;
00172 }
00173 
00174 /**
00175  * Halt execution for a given time. Note: a value of zero will not return 
00176  * immediately as you might expect. Instead, the execution is halted for
00177  * over a minute!
00178  * 
00179  * @param time  Time to sleep in milliseconds
00180  */
00181 void AS_Sleep(unsigned int time)
00182 {
00183     do {
00184         unsigned char tick = AS_count36kHz + 36;
00185         while (tick != AS_count36kHz);
00186         time--;
00187     } while (time);
00188 }
00189 
00190 /**
00191  * Set status LED to the specified color. Note that you can also call 
00192  * AS_LED_GREEN_ON, AS_LED_GREEN_OFF, AS_LED_RED_ON, AS_LED_RED_OFF, 
00193  * AS_LED_YELLOW_ON and AS_LED_YELLOW_OFF directly.
00194  * 
00195  * @params color    Color (possible values: AS_GREEN, AS_RED, AS_YELLOW)
00196  */
00197 inline void AS_StatusLED(unsigned char color)
00198 {
00199     if (color & AS_GREEN) AS_LED_GREEN_ON; else AS_LED_GREEN_OFF;
00200     if (color & AS_RED)   AS_LED_RED_ON;   else AS_LED_RED_OFF;
00201 }
00202 
00203 /**
00204  * Set front LED on or off. Note that you can also call AS_LED_FRONT_ON and 
00205  * AS_LED_FRONT_OFF directly.
00206  * 
00207  * @param status    AS_ON or AS_OFF
00208  */
00209 inline void AS_FrontLED(unsigned char status)
00210 {
00211     if (status) AS_LED_FRONT_ON; else AS_LED_FRONT_OFF;
00212 }
00213 
00214 /**
00215  * Set back LEDs on or off. Note that you can also call AS_LED_RBACK_ON, 
00216  * AS_LED_RBACK_OFF, AS_LED_LBACK_ON and AS_LED_LBACK_OFF directly, but you have
00217  * to switch to AS_BACK_LED_MODE before.
00218  * 
00219  * @params left     Status for left back LED (AS_ON or AS_OFF)
00220  * @params right    Status for right back LED (AS_ON or AS_OFF)
00221  */
00222 inline void AS_BackLED(unsigned char left, unsigned char right)
00223 {
00224     if (left || right) {
00225         AS_LED_ODO_OFF;
00226         AS_BACK_LED_MODE;
00227         
00228     }
00229     
00230     if (left)  AS_LED_LBACK_ON; else AS_LED_LBACK_OFF;
00231     if (right) AS_LED_RBACK_ON; else AS_LED_RBACK_OFF;
00232 }
00233 
00234 /**
00235  * Set speed of left and right motor. Use AS_MotorDir() before to specifiy the
00236  * direction.
00237  * 
00238  * @param left_speed    Speed of left motor (0..255)
00239  * @param right_speed   Speed of right motor (0..255)
00240  */
00241 inline void AS_MotorSpeed(unsigned char left_speed, unsigned char right_speed)
00242 {
00243     OCR1A = left_speed;
00244     OCR1B = right_speed;
00245 }
00246 
00247 /**
00248  * Set rotation direction of left and right motor. Possible values are
00249  * AS_DIR_FWD (forward), AS_DIR_RWD (backward), AS_DIR_BREAK (stop) and
00250  * AS_DIR_FREE (no change to direction).
00251  * 
00252  * @param left_dir  Direction for left motor.
00253  * @param right_dir Direction for right motor.
00254  */
00255 inline void AS_MotorDir(unsigned char left_dir, unsigned char right_dir)
00256 {
00257     AS_DIR_LEFT(left_dir);
00258     AS_DIR_RIGHT(right_dir);
00259 }
00260 
00261 /**
00262  * Send data via IR serial interface. IR-RC5 decoding will be disabled during
00263  * write.
00264  * 
00265  * @param data      Pointer to data buffer to send.
00266  * @param length    Size of data in bytes.
00267  */
00268 void AS_SerWrite(unsigned char *data, unsigned char length)
00269 {
00270     unsigned char i = 0;
00271     unsigned char irrc5Decode = AS_irrc5DecodeEnabled;
00272     AS_irrc5DecodeEnabled = 0;
00273     
00274     UCSRB = 0x08; // enable transmitter
00275     while (length > 0) {
00276         if (UCSRA & 0x20) { // wait for empty transmit buffer
00277             UDR = data[i++];
00278             length --;
00279         }
00280     }
00281     while (!(UCSRA & 0x40)); 
00282     for (i = 0; i < 0xFE; i++)
00283         for(length = 0; length < 0xFE; length++);
00284         
00285     AS_irrc5DecodeEnabled = irrc5Decode;
00286 }
00287 
00288 /**
00289  * Turn on interrupt for activity on switches. The user ISR assigned to 
00290  * AS_UserIsrInterrupt1 will be called if any of the switches was pushed.
00291  */
00292 void AS_SensSwitchesInterruptOn(void)
00293 {
00294     AS_sensSwitched = 0;
00295     AS_SENS_SWITCHES_OFF;
00296     DDRD &= ~AS_PD_SWITCHES; // switches as input
00297     MCUCR &= ~((1 << ISC11) | (1 << ISC10)); // low level interrupt generation
00298     GICR |= (1 << INT1); // enable external interrupt 1
00299 }
00300 
00301 /**
00302  * Disable interrupt for activity on switches.
00303  */
00304 inline void AS_SensSwitchesInterruptOff(void)
00305 {
00306     GICR &= ~(1 << INT1); // disable external interrupt 1
00307 }
00308 
00309 /**
00310  * Read status of switches. The bits of the returned value represent the 
00311  * switches currently pressed. The mapping is the following:
00312  * 
00313  * bit0 = K6,
00314  * bit1 = K5,
00315  * bit2 = K4,
00316  * bit3 = K3,
00317  * bit4 = K2,
00318  * bit5 = K1
00319  * 
00320  * @return  A value whose bits represent the switches currently pressed (see 
00321  *          above)
00322  */
00323 unsigned char AS_SensSwitchesRead(void)
00324 {
00325     unsigned int value;
00326     
00327     AS_SENS_SWITCHES_MODE;  // output for switches
00328     AS_SENS_SWITCHES_ON;    // output 'high' for measurement
00329     
00330     ADMUX = (1 << REFS0) | AS_MUX_SWITCHES; // AVCC reference with external capacitor
00331     AS_Avr_Sleep(10);
00332     
00333     AS_ADC_START;               // start A/D conversion
00334     while (!AS_ADC_FINISHED);   // wait for ADC to finish
00335     AS_ADC_STOP;                // stop A/D conversion (clear finished flag)
00336     value = AS_ADC_VALUE;
00337     
00338     AS_SENS_SWITCHES_OFF;
00339     
00340     // if no switch is pressed, the ADC value will be around 1024, and if all 
00341     // are pressed it is around 512
00342     return (char) (((1024000000UL/((unsigned long) value) - 1000000UL)*63UL + 500000UL) / 1000000UL);
00343 }
00344 
00345 //@}

Generated on Fri May 12 10:11:15 2006 for simsuro by  doxygen 1.4.6