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 //@}