Go to most recent revision | Details | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 682 | eqlazer | 1 | /** |
| 638 | eqlazer | 2 | * AntennaControl |
| 3 | * |
||
| 731 | eqlazer | 4 | * Build for use at ETA, http://www.eta.chalmers.se/ |
| 5 | * controlling their 2 meter yagi antenna. |
||
| 6 | * For the moment only support for azimuth, |
||
| 7 | * elevation is yet to be implemented. |
||
| 638 | eqlazer | 8 | * |
| 731 | eqlazer | 9 | * @date 2007-12-10 |
| 638 | eqlazer | 10 | * @author Erik Larsson |
| 11 | * |
||
| 12 | */ |
||
| 13 | |||
| 731 | eqlazer | 14 | /* |
| 15 | * This version uses a NodeEssential |
||
| 16 | * and the pins used are: |
||
| 17 | * 0: PD2 azimuth plus |
||
| 18 | * 1: PD1 azimuth minus |
||
| 19 | * 8: PC4 ADC4 azimuth feedback |
||
| 20 | */ |
||
| 21 | |||
| 22 | /*----------------------------------------------------------------------------- |
||
| 23 | * Includes |
||
| 24 | *---------------------------------------------------------------------------*/ |
||
| 638 | eqlazer | 25 | #include <inttypes.h> |
| 26 | #include <avr/interrupt.h> |
||
| 27 | #include <stdio.h> |
||
| 28 | #include <string.h> |
||
| 29 | #include <avr/eeprom.h> |
||
| 30 | #include <config.h> // All configuration parameters |
||
| 31 | #include <bios.h> // BIOS interface declarations, including CAN structure and ID defines. |
||
| 32 | #include <drivers/timer/timer.h> |
||
| 33 | |||
| 731 | eqlazer | 34 | /*----------------------------------------------------------------------------- |
| 35 | * Defines |
||
| 36 | *---------------------------------------------------------------------------*/ |
||
| 638 | eqlazer | 37 | #define APP_TYPE 0xf0a0 |
| 731 | eqlazer | 38 | #define APP_VERSION 0x0002 |
| 638 | eqlazer | 39 | |
| 40 | #define AZIMUTH 0 |
||
| 41 | |||
| 42 | #define ROTATE_STOP 0 |
||
| 43 | #define ROTATE_PLUS 1 |
||
| 44 | #define ROTATE_MINUS 2 |
||
| 45 | |||
| 46 | #define SET 0 |
||
| 47 | #define GET 1 |
||
| 48 | |||
| 682 | eqlazer | 49 | #define CALIBRATE_AZIMUTH 0 // EEPROM adress |
| 50 | |||
| 638 | eqlazer | 51 | // Make sure this is a power of 2 |
| 52 | #define AVERAGE_SIZE 16 |
||
| 53 | #define AVERAGE_SIZE_SHIFT 4 |
||
| 54 | |||
| 731 | eqlazer | 55 | //FIXME move these message id defines |
| 56 | #define MSG_CAL_SET 0x11100001UL |
||
| 57 | #define MSG_CAL_GET 0x11100002UL |
||
| 58 | #define MSG_ABS 0x111000031UL |
||
| 59 | #define MSG_REL 0x11100004UL |
||
| 60 | #define MSG_START 0x11100005UL |
||
| 61 | #define MSG_STOP 0x11100006UL |
||
| 62 | #define MSG_STATUS 0x11100007UL |
||
| 63 | #define MSG_CAL_RET 0x111000f2UL |
||
| 682 | eqlazer | 64 | |
| 65 | |||
| 638 | eqlazer | 66 | // A simple message "queue", with space for one message only. |
| 67 | // These are declared volatile to tell the compiler not to optimize away accesses. |
||
| 68 | volatile Can_Message_t rxMsg; // Message storage |
||
| 69 | volatile uint8_t rxMsgFull; // Synchronization flag |
||
| 70 | |||
| 682 | eqlazer | 71 | // For calculating average feedback measurement |
| 638 | eqlazer | 72 | uint16_t azimuthReadout[ AVERAGE_SIZE ]; |
| 73 | |||
| 682 | eqlazer | 74 | uint16_t azimuthCalibration; |
| 75 | |||
| 731 | eqlazer | 76 | /*----------------------------------------------------------------------------- |
| 77 | * Declarations |
||
| 78 | *---------------------------------------------------------------------------*/ |
||
| 79 | |||
| 80 | /** |
||
| 81 | * calibration |
||
| 82 | * Sets and gets calibration data from EEPROM |
||
| 83 | * |
||
| 84 | * @param mode: set /get |
||
| 85 | * @param value |
||
| 86 | * @return value |
||
| 87 | */ |
||
| 682 | eqlazer | 88 | int16_t calibration( uint8_t mode, uint16_t value ); |
| 638 | eqlazer | 89 | |
| 682 | eqlazer | 90 | // Turn rotor |
| 638 | eqlazer | 91 | // position: 0-1024 |
| 731 | eqlazer | 92 | /** |
| 93 | * turn |
||
| 94 | * Turns rotor to given position |
||
| 95 | * FIXME implement this function |
||
| 96 | * |
||
| 97 | * @param position |
||
| 98 | * @return |
||
| 99 | */ |
||
| 682 | eqlazer | 100 | uint8_t turn( uint16_t position ); |
| 638 | eqlazer | 101 | |
| 731 | eqlazer | 102 | /** |
| 103 | * getPosition |
||
| 104 | * Calculates the antenna position |
||
| 105 | * Uses a number of the latest adc readings |
||
| 106 | * and gets the avarage value |
||
| 107 | * |
||
| 108 | * @param void |
||
| 109 | * @return position |
||
| 110 | */ |
||
| 682 | eqlazer | 111 | uint16_t getPosition( void ); |
| 638 | eqlazer | 112 | |
| 731 | eqlazer | 113 | /** |
| 114 | * initAdcFeedback |
||
| 115 | * Starts the ADC |
||
| 116 | * |
||
| 117 | * @param void |
||
| 118 | * @return void |
||
| 119 | */ |
||
| 638 | eqlazer | 120 | void initAdcFeedback( void ); |
| 121 | |||
| 731 | eqlazer | 122 | /** |
| 123 | * readFeedback |
||
| 124 | * Gets antenna position from ADC and |
||
| 125 | * puts in arrary for later calculations |
||
| 126 | * |
||
| 127 | * @param void |
||
| 128 | * @return void |
||
| 129 | */ |
||
| 682 | eqlazer | 130 | void readFeedback( void ); |
| 638 | eqlazer | 131 | |
| 731 | eqlazer | 132 | /** |
| 133 | * controlRelay |
||
| 134 | * Control the relay that turns the rotor |
||
| 135 | * |
||
| 136 | * @param direction |
||
| 137 | * @return void |
||
| 138 | */ |
||
| 682 | eqlazer | 139 | void controlRelay( uint8_t direction ); |
| 638 | eqlazer | 140 | |
| 731 | eqlazer | 141 | /** |
| 142 | * sendStatus |
||
| 143 | * Sends antennas position on CAN bus |
||
| 144 | * |
||
| 145 | * @param void |
||
| 146 | * @return void |
||
| 147 | */ |
||
| 682 | eqlazer | 148 | void sendStatus( void ); |
| 638 | eqlazer | 149 | |
| 731 | eqlazer | 150 | // CAN message reception callback. |
| 151 | // This function runs with interrupts disabled, keep it as short as possible. |
||
| 152 | void can_receive( Can_Message_t *msg ) { |
||
| 153 | if (!rxMsgFull) { |
||
| 154 | memcpy((void*)&rxMsg, msg, sizeof(rxMsg)); |
||
| 155 | rxMsgFull = 1; |
||
| 156 | } |
||
| 157 | } |
||
| 638 | eqlazer | 158 | |
| 731 | eqlazer | 159 | /*----------------------------------------------------------------------------- |
| 160 | * Main Program |
||
| 161 | *---------------------------------------------------------------------------*/ |
||
| 638 | eqlazer | 162 | int main( void ) |
| 163 | { |
||
| 164 | // Enable interrupts as early as possible |
||
| 165 | sei(); |
||
| 166 | |||
| 167 | Timer_Init(); |
||
| 731 | eqlazer | 168 | |
| 169 | DDRD |= (1 << PD1)|(1 << PD2); |
||
| 170 | PORTD |= (1 << PD1)|(1 << PD2); |
||
| 171 | |||
| 682 | eqlazer | 172 | // Setup ADC |
| 173 | initAdcFeedback(); |
||
| 638 | eqlazer | 174 | |
| 175 | Can_Message_t txMsg; |
||
| 176 | txMsg.Id = (CAN_NMT_APP_START << CAN_SHIFT_NMT_TYPE) | (NODE_ID << CAN_SHIFT_NMT_SID); |
||
| 177 | txMsg.DataLength = 4; |
||
| 178 | txMsg.RemoteFlag = 0; |
||
| 179 | txMsg.ExtendedFlag = 1; |
||
| 180 | txMsg.Data.words[0] = APP_TYPE; |
||
| 181 | txMsg.Data.words[1] = APP_VERSION; |
||
| 182 | |||
| 183 | // Set up callback for CAN reception, this is optional if only sending is required. |
||
| 184 | BIOS_CanCallback = &can_receive; |
||
| 185 | // Send CAN_NMT_APP_START |
||
| 186 | BIOS_CanSend(&txMsg); |
||
| 187 | |||
| 682 | eqlazer | 188 | // Read calibration value from eeprom |
| 189 | azimuthCalibration = eeprom_read_word( CALIBRATE_AZIMUTH ); |
||
| 638 | eqlazer | 190 | |
| 682 | eqlazer | 191 | // Timer for reading position feedback |
| 192 | Timer_SetTimeout(0, 50, TimerTypeFreeRunning, 0); |
||
| 731 | eqlazer | 193 | Timer_SetTimeout(1, 1000, TimerTypeFreeRunning, 0); |
| 638 | eqlazer | 194 | |
| 731 | eqlazer | 195 | sendStatus(); |
| 196 | |||
| 638 | eqlazer | 197 | while (1) { |
| 198 | if (Timer_Expired(0)) { |
||
| 682 | eqlazer | 199 | // Periodicly read antennas position |
| 200 | readFeedback(); |
||
| 638 | eqlazer | 201 | } |
| 731 | eqlazer | 202 | if (Timer_Expired(1)) { |
| 203 | // Send antennas position |
||
| 204 | sendStatus(); |
||
| 205 | } |
||
| 638 | eqlazer | 206 | |
| 731 | eqlazer | 207 | /* If any messages is received */ |
| 638 | eqlazer | 208 | if (rxMsgFull) { |
| 682 | eqlazer | 209 | |
| 731 | eqlazer | 210 | switch ( rxMsg.Id ){ |
| 211 | case MSG_CAL_SET: |
||
| 212 | // Set calibration value |
||
| 682 | eqlazer | 213 | if( 2 == rxMsg.DataLength ){ |
| 731 | eqlazer | 214 | |
| 682 | eqlazer | 215 | calibration( SET, rxMsg.Data.words[0] ); |
| 216 | } |
||
| 731 | eqlazer | 217 | |
| 682 | eqlazer | 218 | break; |
| 219 | |||
| 731 | eqlazer | 220 | case MSG_CAL_GET: |
| 221 | // Get calibration value |
||
| 682 | eqlazer | 222 | if( 0 == rxMsg.DataLength ){ |
| 731 | eqlazer | 223 | |
| 224 | txMsg.Id = MSG_CAL_RET; |
||
| 225 | txMsg.DataLength = 2; |
||
| 226 | txMsg.Data.words[0] = calibration( GET, 0 ); |
||
| 227 | |||
| 228 | BIOS_CanSend(&txMsg); |
||
| 682 | eqlazer | 229 | } |
| 731 | eqlazer | 230 | |
| 682 | eqlazer | 231 | break; |
| 232 | |||
| 731 | eqlazer | 233 | case MSG_ABS: |
| 234 | // Start turning to absolute position |
||
| 682 | eqlazer | 235 | if( 2 == rxMsg.DataLength ){ |
| 731 | eqlazer | 236 | |
| 237 | //FIXME implement this |
||
| 682 | eqlazer | 238 | } |
| 731 | eqlazer | 239 | |
| 682 | eqlazer | 240 | break; |
| 241 | |||
| 731 | eqlazer | 242 | case MSG_REL: |
| 243 | // Start turning to relative position |
||
| 682 | eqlazer | 244 | if( 2 == rxMsg.DataLength ){ |
| 731 | eqlazer | 245 | |
| 246 | //FIXME implement this |
||
| 682 | eqlazer | 247 | } |
| 731 | eqlazer | 248 | |
| 682 | eqlazer | 249 | break; |
| 250 | |||
| 731 | eqlazer | 251 | case MSG_START: |
| 252 | // Start turning rotor |
||
| 682 | eqlazer | 253 | if( 1 == rxMsg.DataLength ){ |
| 731 | eqlazer | 254 | |
| 682 | eqlazer | 255 | // First data byte decides direction |
| 256 | controlRelay( rxMsg.Data.bytes[0] ); |
||
| 257 | } |
||
| 731 | eqlazer | 258 | |
| 682 | eqlazer | 259 | break; |
| 260 | |||
| 731 | eqlazer | 261 | case MSG_STOP: |
| 262 | // Stop turning rotor |
||
| 263 | |||
| 264 | controlRelay( ROTATE_STOP ); |
||
| 265 | |||
| 682 | eqlazer | 266 | break; |
| 731 | eqlazer | 267 | |
| 268 | case MSG_STATUS: |
||
| 269 | // Get position |
||
| 682 | eqlazer | 270 | if( 0 == rxMsg.DataLength ){ |
| 271 | sendStatus(); |
||
| 272 | } |
||
| 731 | eqlazer | 273 | |
| 682 | eqlazer | 274 | break; |
| 275 | |||
| 276 | default: |
||
| 277 | break; |
||
| 278 | } |
||
| 279 | |||
| 731 | eqlazer | 280 | rxMsgFull = 0; |
| 638 | eqlazer | 281 | } |
| 282 | } |
||
| 283 | |||
| 284 | return 0; |
||
| 285 | } |
||
| 286 | |||
| 731 | eqlazer | 287 | /*----------------------------------------------------------------------------- |
| 288 | * Definitions |
||
| 289 | *---------------------------------------------------------------------------*/ |
||
| 638 | eqlazer | 290 | |
| 682 | eqlazer | 291 | int16_t calibration( uint8_t mode, uint16_t value ) |
| 638 | eqlazer | 292 | { |
| 293 | // set / get calibration value |
||
| 294 | if( SET == mode ){ |
||
| 682 | eqlazer | 295 | eeprom_write_word( CALIBRATE_AZIMUTH, value ); |
| 296 | azimuthCalibration = value; |
||
| 297 | |||
| 638 | eqlazer | 298 | }else if( GET == mode ){ |
| 682 | eqlazer | 299 | return azimuthCalibration; |
| 638 | eqlazer | 300 | } |
| 301 | return 0; |
||
| 302 | } |
||
| 303 | |||
| 682 | eqlazer | 304 | uint8_t turn( uint16_t position ) |
| 638 | eqlazer | 305 | { |
| 731 | eqlazer | 306 | //FIXME implement this |
| 638 | eqlazer | 307 | // start relay |
| 308 | // read feedback |
||
| 309 | // callibrate measurement |
||
| 310 | |||
| 311 | while(0){ |
||
| 312 | |||
| 313 | } |
||
| 314 | return 0; |
||
| 315 | } |
||
| 316 | |||
| 682 | eqlazer | 317 | uint16_t getPosition( void ) |
| 638 | eqlazer | 318 | { |
| 682 | eqlazer | 319 | uint16_t position = 0; |
| 638 | eqlazer | 320 | |
| 321 | // Get average value of position |
||
| 682 | eqlazer | 322 | for( uint8_t i=0; i < AVERAGE_SIZE; i++ ){ |
| 323 | position += azimuthReadout[i]; |
||
| 638 | eqlazer | 324 | } |
| 325 | |||
| 731 | eqlazer | 326 | // Calculate average |
| 682 | eqlazer | 327 | position = position >> AVERAGE_SIZE_SHIFT; |
| 731 | eqlazer | 328 | |
| 329 | // Convert to degrees, gives about 379 degrees |
||
| 330 | position /= 3; |
||
| 331 | |||
| 332 | // Calibrate |
||
| 333 | position += (int16_t)azimuthCalibration; |
||
| 638 | eqlazer | 334 | |
| 335 | return position; |
||
| 336 | } |
||
| 337 | |||
| 338 | void initAdcFeedback( void ) |
||
| 339 | { |
||
| 340 | // ADC4: Azimuth feedback |
||
| 341 | |||
| 342 | // Enable ADC4 |
||
| 343 | ADMUX |= ( 1 << MUX2 ); |
||
| 344 | ADMUX &= ~(( 1 << MUX0 )|( 1 << MUX1 )|( 1 << MUX3 )); |
||
| 345 | |||
| 346 | // Prescaler /128 |
||
| 347 | ADCSRA |= ( 1 << ADPS2)|( 1 << ADPS1)|( 1 << ADPS0); |
||
| 348 | |||
| 349 | // Enable AVcc as Voltage Reference |
||
| 350 | ADMUX |= ( 1 << REFS0 ); |
||
| 351 | ADMUX &= ~( 1 << REFS1 ); |
||
| 352 | |||
| 353 | // Right adjust the result |
||
| 354 | ADMUX &= ~( 1 << ADLAR ); |
||
| 355 | |||
| 356 | // Disable digital input |
||
| 357 | DIDR0 |= ( 1 << ADC5D )|( 1 << ADC4D ); |
||
| 358 | |||
| 682 | eqlazer | 359 | // Wake up ADC and enable it |
| 638 | eqlazer | 360 | PRR &= ~( 1 << PRADC ); |
| 361 | ADCSRA |= ( 1 << ADEN ); |
||
| 682 | eqlazer | 362 | |
| 363 | // Start first conversion |
||
| 364 | ADCSRA |= ( 1 << ADSC ); |
||
| 638 | eqlazer | 365 | } |
| 366 | |||
| 682 | eqlazer | 367 | void controlRelay( uint8_t direction ) |
| 638 | eqlazer | 368 | { |
| 731 | eqlazer | 369 | if( ROTATE_PLUS == direction ){ |
| 370 | // Turn CW |
||
| 682 | eqlazer | 371 | PORTD &= ~(1 << PD2); |
| 372 | PORTD |= (1 << PD1); |
||
| 731 | eqlazer | 373 | }else if ( ROTATE_MINUS == direction ){ |
| 374 | // Turn CCW |
||
| 682 | eqlazer | 375 | PORTD &= ~(1 << PD1); |
| 376 | PORTD |= (1 << PD2); |
||
| 638 | eqlazer | 377 | }else{ |
| 682 | eqlazer | 378 | // Stop azimuth rotor |
| 638 | eqlazer | 379 | PORTD |= (1 << PD1); |
| 380 | PORTD |= (1 << PD2); |
||
| 381 | } |
||
| 382 | } |
||
| 383 | |||
| 682 | eqlazer | 384 | void readFeedback( void ) |
| 638 | eqlazer | 385 | { |
| 386 | static uint8_t azimuthArrayPosition = 0; |
||
| 682 | eqlazer | 387 | |
| 731 | eqlazer | 388 | // Wait for ADC conversion to complete |
| 389 | while( ADCSRA & ( 1 << ADSC )){} |
||
| 638 | eqlazer | 390 | |
| 731 | eqlazer | 391 | // Put readout in ringbuffer |
| 682 | eqlazer | 392 | azimuthReadout[ azimuthArrayPosition ] = ADCW; |
| 393 | azimuthArrayPosition++; |
||
| 731 | eqlazer | 394 | |
| 682 | eqlazer | 395 | if( AVERAGE_SIZE <= azimuthArrayPosition ){ |
| 396 | azimuthArrayPosition = 0; |
||
| 638 | eqlazer | 397 | } |
| 398 | |||
| 682 | eqlazer | 399 | // Start next conversion |
| 400 | ADCSRA |= ( 1 << ADSC ); |
||
| 401 | } |
||
| 402 | |||
| 403 | void sendStatus( void ) |
||
| 404 | { |
||
| 731 | eqlazer | 405 | // Gets antennas position and sends on CAN bus |
| 682 | eqlazer | 406 | Can_Message_t txMsg; |
| 638 | eqlazer | 407 | |
| 731 | eqlazer | 408 | txMsg.Id = 0x1f8f0100UL; //FIXME id should not be defined here |
| 682 | eqlazer | 409 | |
| 410 | txMsg.DataLength = 2; |
||
| 411 | txMsg.RemoteFlag = 0; |
||
| 412 | txMsg.ExtendedFlag = 1; |
||
| 413 | |||
| 414 | txMsg.Data.words[ 0 ] = getPosition(); |
||
| 415 | |||
| 416 | BIOS_CanSend( &txMsg ); |
||
| 417 | |||
| 638 | eqlazer | 418 | } |
| 731 | eqlazer | 419 |