Rev 744 | Details | Compare with Previous | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 682 | eqlazer | 1 | /** |
| 638 | eqlazer | 2 | * AntennaControl |
| 3 | * |
||
| 744 | eqlazer | 4 | * Build for use at ETA, http://www.eta.chalmers.se/, controlling their 2 meter yagi antenna. |
| 638 | eqlazer | 5 | * |
| 744 | eqlazer | 6 | * @date 2007-10-28 |
| 638 | eqlazer | 7 | * @author Erik Larsson |
| 8 | * |
||
| 9 | */ |
||
| 10 | |||
| 11 | #include <inttypes.h> |
||
| 12 | #include <avr/interrupt.h> |
||
| 13 | #include <stdio.h> |
||
| 14 | #include <string.h> |
||
| 15 | #include <avr/eeprom.h> |
||
| 16 | #include <config.h> // All configuration parameters |
||
| 17 | #include <bios.h> // BIOS interface declarations, including CAN structure and ID defines. |
||
| 18 | #include <drivers/timer/timer.h> |
||
| 19 | |||
| 20 | #define APP_TYPE 0xf0a0 |
||
| 744 | eqlazer | 21 | #define APP_VERSION 0x0001 |
| 638 | eqlazer | 22 | |
| 23 | #define AZIMUTH 0 |
||
| 24 | |||
| 25 | #define ROTATE_STOP 0 |
||
| 26 | #define ROTATE_PLUS 1 |
||
| 27 | #define ROTATE_MINUS 2 |
||
| 28 | |||
| 29 | #define SET 0 |
||
| 30 | #define GET 1 |
||
| 31 | |||
| 744 | eqlazer | 32 | #define STATUS 0 |
| 33 | |||
| 682 | eqlazer | 34 | #define CALIBRATE_AZIMUTH 0 // EEPROM adress |
| 35 | |||
| 638 | eqlazer | 36 | // Make sure this is a power of 2 |
| 37 | #define AVERAGE_SIZE 16 |
||
| 38 | #define AVERAGE_SIZE_SHIFT 4 |
||
| 39 | |||
| 744 | eqlazer | 40 | // Tillfälliga defines |
| 731 | eqlazer | 41 | #define MSG_CAL_SET 0x11100001UL |
| 42 | #define MSG_CAL_GET 0x11100002UL |
||
| 43 | #define MSG_ABS 0x111000031UL |
||
| 44 | #define MSG_REL 0x11100004UL |
||
| 45 | #define MSG_START 0x11100005UL |
||
| 46 | #define MSG_STOP 0x11100006UL |
||
| 47 | #define MSG_STATUS 0x11100007UL |
||
| 744 | eqlazer | 48 | |
| 731 | eqlazer | 49 | #define MSG_CAL_RET 0x111000f2UL |
| 682 | eqlazer | 50 | |
| 51 | |||
| 744 | eqlazer | 52 | // ---------- |
| 53 | |||
| 54 | // Essential pins |
||
| 55 | // 0: PD2 azimuth plus |
||
| 56 | // 1: PD1 azimuth minus |
||
| 57 | // 8: PC4 ADC4 azimuth feedback |
||
| 58 | |||
| 59 | |||
| 60 | |||
| 638 | eqlazer | 61 | // A simple message "queue", with space for one message only. |
| 62 | // These are declared volatile to tell the compiler not to optimize away accesses. |
||
| 63 | volatile Can_Message_t rxMsg; // Message storage |
||
| 64 | volatile uint8_t rxMsgFull; // Synchronization flag |
||
| 65 | |||
| 744 | eqlazer | 66 | //uint16_t actualElevationValue; |
| 67 | //uint16_t desiredElevationValue; |
||
| 68 | //uint16_t actualAzimuthValue; |
||
| 69 | //uint16_t desiredAzimuthValue; |
||
| 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 | |||
| 744 | eqlazer | 76 | // CAN message reception callback. |
| 77 | // This function runs with interrupts disabled, keep it as short as possible. |
||
| 78 | void can_receive( Can_Message_t *msg ) { |
||
| 79 | if (!rxMsgFull) { |
||
| 80 | memcpy((void*)&rxMsg, msg, sizeof(rxMsg)); |
||
| 81 | rxMsgFull = 1; |
||
| 82 | } |
||
| 83 | } |
||
| 84 | |||
| 85 | // Calibration function |
||
| 86 | // mode: set / get |
||
| 87 | // value: |
||
| 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 |
| 682 | eqlazer | 92 | uint8_t turn( uint16_t position ); |
| 638 | eqlazer | 93 | |
| 744 | eqlazer | 94 | // Get position |
| 95 | // Gets the average value for compensation of distorsions |
||
| 682 | eqlazer | 96 | uint16_t getPosition( void ); |
| 638 | eqlazer | 97 | |
| 744 | eqlazer | 98 | // Initiate ADC |
| 638 | eqlazer | 99 | void initAdcFeedback( void ); |
| 100 | |||
| 744 | eqlazer | 101 | // Read antenna feedback from ADC |
| 682 | eqlazer | 102 | void readFeedback( void ); |
| 638 | eqlazer | 103 | |
| 744 | eqlazer | 104 | // Control rotation relays |
| 682 | eqlazer | 105 | void controlRelay( uint8_t direction ); |
| 638 | eqlazer | 106 | |
| 744 | eqlazer | 107 | // Sends antennas position on CAN |
| 108 | void sendStatus( uint8_t type ); |
||
| 638 | eqlazer | 109 | |
| 110 | |||
| 111 | int main( void ) |
||
| 112 | { |
||
| 113 | // Enable interrupts as early as possible |
||
| 114 | sei(); |
||
| 115 | |||
| 116 | Timer_Init(); |
||
| 731 | eqlazer | 117 | |
| 744 | eqlazer | 118 | // Set as outputs and stop rotor |
| 119 | DDRD |= (1 << PD1)|(1 << PD2); |
||
| 120 | PORTD &= ~((1 << PD1)|(1 << PD2)); |
||
| 121 | |||
| 682 | eqlazer | 122 | // Setup ADC |
| 123 | initAdcFeedback(); |
||
| 638 | eqlazer | 124 | |
| 125 | Can_Message_t txMsg; |
||
| 126 | txMsg.Id = (CAN_NMT_APP_START << CAN_SHIFT_NMT_TYPE) | (NODE_ID << CAN_SHIFT_NMT_SID); |
||
| 127 | txMsg.DataLength = 4; |
||
| 128 | txMsg.RemoteFlag = 0; |
||
| 129 | txMsg.ExtendedFlag = 1; |
||
| 130 | txMsg.Data.words[0] = APP_TYPE; |
||
| 131 | txMsg.Data.words[1] = APP_VERSION; |
||
| 132 | |||
| 744 | eqlazer | 133 | // Set up callback for CAN reception |
| 638 | eqlazer | 134 | BIOS_CanCallback = &can_receive; |
| 135 | // Send CAN_NMT_APP_START |
||
| 136 | BIOS_CanSend(&txMsg); |
||
| 137 | |||
| 682 | eqlazer | 138 | // Read calibration value from eeprom |
| 139 | azimuthCalibration = eeprom_read_word( CALIBRATE_AZIMUTH ); |
||
| 638 | eqlazer | 140 | |
| 682 | eqlazer | 141 | // Timer for reading position feedback |
| 744 | eqlazer | 142 | Timer_SetTimeout(0, 100, TimerTypeFreeRunning, 0); |
| 731 | eqlazer | 143 | Timer_SetTimeout(1, 1000, TimerTypeFreeRunning, 0); |
| 638 | eqlazer | 144 | |
| 744 | eqlazer | 145 | sendStatus( STATUS ); |
| 731 | eqlazer | 146 | |
| 638 | eqlazer | 147 | while (1) { |
| 148 | if (Timer_Expired(0)) { |
||
| 682 | eqlazer | 149 | // Periodicly read antennas position |
| 150 | readFeedback(); |
||
| 638 | eqlazer | 151 | } |
| 731 | eqlazer | 152 | if (Timer_Expired(1)) { |
| 744 | eqlazer | 153 | sendStatus(STATUS); |
| 731 | eqlazer | 154 | } |
| 638 | eqlazer | 155 | |
| 156 | if (rxMsgFull) { |
||
| 744 | eqlazer | 157 | switch (rxMsg.Id){ |
| 158 | case MSG_CAL_SET: // Set calibration value |
||
| 682 | eqlazer | 159 | if( 2 == rxMsg.DataLength ){ |
| 160 | calibration( SET, rxMsg.Data.words[0] ); |
||
| 161 | } |
||
| 162 | break; |
||
| 163 | |||
| 744 | eqlazer | 164 | case MSG_CAL_GET: // Get calibration value |
| 682 | eqlazer | 165 | if( 0 == rxMsg.DataLength ){ |
| 731 | eqlazer | 166 | txMsg.Id = MSG_CAL_RET; |
| 167 | txMsg.DataLength = 2; |
||
| 168 | txMsg.Data.words[0] = calibration( GET, 0 ); |
||
| 169 | BIOS_CanSend(&txMsg); |
||
| 682 | eqlazer | 170 | } |
| 171 | break; |
||
| 172 | |||
| 744 | eqlazer | 173 | case MSG_ABS: // Start turning to absolute position |
| 682 | eqlazer | 174 | if( 2 == rxMsg.DataLength ){ |
| 744 | eqlazer | 175 | |
| 682 | eqlazer | 176 | } |
| 177 | break; |
||
| 178 | |||
| 744 | eqlazer | 179 | case MSG_REL: // Start turning to relative position |
| 682 | eqlazer | 180 | if( 2 == rxMsg.DataLength ){ |
| 744 | eqlazer | 181 | |
| 682 | eqlazer | 182 | } |
| 183 | break; |
||
| 184 | |||
| 744 | eqlazer | 185 | case MSG_START: // Start turning |
| 682 | eqlazer | 186 | if( 1 == rxMsg.DataLength ){ |
| 187 | // First data byte decides direction |
||
| 188 | controlRelay( rxMsg.Data.bytes[0] ); |
||
| 189 | } |
||
| 190 | break; |
||
| 191 | |||
| 744 | eqlazer | 192 | case MSG_STOP: // Stop turning |
| 193 | //if( 1 == rxMsg.DataLength ){ |
||
| 194 | controlRelay( ROTATE_STOP ); |
||
| 195 | //} |
||
| 682 | eqlazer | 196 | break; |
| 744 | eqlazer | 197 | case MSG_STATUS: // Get position |
| 682 | eqlazer | 198 | if( 0 == rxMsg.DataLength ){ |
| 744 | eqlazer | 199 | sendStatus(STATUS); |
| 682 | eqlazer | 200 | } |
| 201 | break; |
||
| 202 | |||
| 203 | default: |
||
| 204 | break; |
||
| 205 | } |
||
| 206 | |||
| 744 | eqlazer | 207 | rxMsgFull = 0; // |
| 638 | eqlazer | 208 | } |
| 209 | } |
||
| 210 | |||
| 211 | return 0; |
||
| 212 | } |
||
| 213 | |||
| 214 | |||
| 682 | eqlazer | 215 | int16_t calibration( uint8_t mode, uint16_t value ) |
| 638 | eqlazer | 216 | { |
| 217 | // set / get calibration value |
||
| 218 | if( SET == mode ){ |
||
| 682 | eqlazer | 219 | eeprom_write_word( CALIBRATE_AZIMUTH, value ); |
| 220 | azimuthCalibration = value; |
||
| 221 | |||
| 638 | eqlazer | 222 | }else if( GET == mode ){ |
| 682 | eqlazer | 223 | return azimuthCalibration; |
| 638 | eqlazer | 224 | } |
| 225 | return 0; |
||
| 226 | } |
||
| 227 | |||
| 682 | eqlazer | 228 | uint8_t turn( uint16_t position ) |
| 638 | eqlazer | 229 | { |
| 230 | // start relay |
||
| 231 | // read feedback |
||
| 232 | // callibrate measurement |
||
| 233 | |||
| 234 | while(0){ |
||
| 235 | |||
| 236 | } |
||
| 237 | return 0; |
||
| 238 | } |
||
| 239 | |||
| 682 | eqlazer | 240 | uint16_t getPosition( void ) |
| 638 | eqlazer | 241 | { |
| 682 | eqlazer | 242 | uint16_t position = 0; |
| 744 | eqlazer | 243 | |
| 638 | eqlazer | 244 | // Get average value of position |
| 682 | eqlazer | 245 | for( uint8_t i=0; i < AVERAGE_SIZE; i++ ){ |
| 246 | position += azimuthReadout[i]; |
||
| 638 | eqlazer | 247 | } |
| 248 | |||
| 731 | eqlazer | 249 | // Calculate average |
| 682 | eqlazer | 250 | position = position >> AVERAGE_SIZE_SHIFT; |
| 731 | eqlazer | 251 | |
| 744 | eqlazer | 252 | // Convert to degrees, gives about 361 degrees |
| 253 | // start 508, stop 1023 => 516 bits per rotation |
||
| 254 | // 516 * 7 / 10 = 361 |
||
| 731 | eqlazer | 255 | |
| 744 | eqlazer | 256 | // Antennen rör sig inom 180 -> 360 (=0) -> 180 |
| 257 | // Norr = mittläget = 0 / 360 grader = 766 decimalt innan omräkning |
||
| 258 | // Söder = ändlägena = 180 grader = 508 och 1023 innan omräkning |
||
| 259 | |||
| 260 | |||
| 261 | // Make it between 508 and 1023 |
||
| 262 | if( position < 508 ){ |
||
| 263 | position = 0; |
||
| 264 | }else{ |
||
| 265 | position -= 508; |
||
| 266 | } |
||
| 267 | |||
| 268 | // if( 0x8000&position ){ |
||
| 269 | // position = -position; |
||
| 270 | // position *= 7; |
||
| 271 | // position /= 10; |
||
| 272 | // //position = -position; |
||
| 273 | // }else{ |
||
| 274 | |||
| 275 | // Make it between 0 and 360 |
||
| 276 | position *= 7; |
||
| 277 | position /= 10; |
||
| 278 | // } |
||
| 279 | |||
| 280 | // Move it to 180 and 540 |
||
| 281 | position += 180; |
||
| 282 | |||
| 731 | eqlazer | 283 | // Calibrate |
| 284 | position += (int16_t)azimuthCalibration; |
||
| 638 | eqlazer | 285 | |
| 744 | eqlazer | 286 | // North is now at 360 |
| 287 | if( position > 360 ){ |
||
| 288 | // Antenna is between 0 and 180 |
||
| 289 | position -= 360; |
||
| 290 | } |
||
| 291 | |||
| 638 | eqlazer | 292 | return position; |
| 293 | } |
||
| 294 | |||
| 295 | void initAdcFeedback( void ) |
||
| 296 | { |
||
| 297 | // ADC4: Azimuth feedback |
||
| 298 | |||
| 299 | // Enable ADC4 |
||
| 300 | ADMUX |= ( 1 << MUX2 ); |
||
| 301 | ADMUX &= ~(( 1 << MUX0 )|( 1 << MUX1 )|( 1 << MUX3 )); |
||
| 302 | |||
| 303 | // Prescaler /128 |
||
| 304 | ADCSRA |= ( 1 << ADPS2)|( 1 << ADPS1)|( 1 << ADPS0); |
||
| 305 | |||
| 306 | // Enable AVcc as Voltage Reference |
||
| 307 | ADMUX |= ( 1 << REFS0 ); |
||
| 308 | ADMUX &= ~( 1 << REFS1 ); |
||
| 309 | |||
| 310 | // Right adjust the result |
||
| 311 | ADMUX &= ~( 1 << ADLAR ); |
||
| 312 | |||
| 313 | // Disable digital input |
||
| 314 | DIDR0 |= ( 1 << ADC5D )|( 1 << ADC4D ); |
||
| 315 | |||
| 682 | eqlazer | 316 | // Wake up ADC and enable it |
| 638 | eqlazer | 317 | PRR &= ~( 1 << PRADC ); |
| 318 | ADCSRA |= ( 1 << ADEN ); |
||
| 682 | eqlazer | 319 | |
| 320 | // Start first conversion |
||
| 321 | ADCSRA |= ( 1 << ADSC ); |
||
| 638 | eqlazer | 322 | } |
| 323 | |||
| 682 | eqlazer | 324 | void controlRelay( uint8_t direction ) |
| 638 | eqlazer | 325 | { |
| 744 | eqlazer | 326 | if( ROTATE_PLUS == direction ){ // Turn clockwise |
| 327 | PORTD &= ~(1 << PD1); |
||
| 328 | PORTD |= (1 << PD2); |
||
| 329 | }else if ( ROTATE_MINUS == direction ){ // Turn counter clockwise |
||
| 682 | eqlazer | 330 | PORTD &= ~(1 << PD2); |
| 331 | PORTD |= (1 << PD1); |
||
| 638 | eqlazer | 332 | }else{ |
| 682 | eqlazer | 333 | // Stop azimuth rotor |
| 744 | eqlazer | 334 | PORTD &= ~(1 << PD1); |
| 335 | PORTD &= ~(1 << PD2); |
||
| 638 | eqlazer | 336 | } |
| 337 | } |
||
| 338 | |||
| 682 | eqlazer | 339 | void readFeedback( void ) |
| 638 | eqlazer | 340 | { |
| 341 | static uint8_t azimuthArrayPosition = 0; |
||
| 682 | eqlazer | 342 | |
| 744 | eqlazer | 343 | while( ADCSRA & ( 1 << ADSC )){} // Wait for conversion to complete |
| 638 | eqlazer | 344 | |
| 744 | eqlazer | 345 | azimuthReadout[ azimuthArrayPosition ] = ADCW; // Store result in ring buffer |
| 682 | eqlazer | 346 | azimuthArrayPosition++; |
| 347 | if( AVERAGE_SIZE <= azimuthArrayPosition ){ |
||
| 348 | azimuthArrayPosition = 0; |
||
| 638 | eqlazer | 349 | } |
| 744 | eqlazer | 350 | |
| 682 | eqlazer | 351 | // Start next conversion |
| 352 | ADCSRA |= ( 1 << ADSC ); |
||
| 353 | } |
||
| 354 | |||
| 744 | eqlazer | 355 | void sendStatus( uint8_t type ) |
| 682 | eqlazer | 356 | { |
| 357 | Can_Message_t txMsg; |
||
| 638 | eqlazer | 358 | |
| 744 | eqlazer | 359 | //txMsg.Id = 0x1f8f0100UL; //(( CAN_SNS << CAN_SHIFT_CLASS )|( SNS_TYPE_STATUS << CAN_SHIFT_SNS_TYPE )|( SNS_ID_ANTENNA_STATUS << CAN_SHIFT_SNS_ID )|( NODE_ID << CAN_SHIFT_SNS_SID ); |
| 682 | eqlazer | 360 | |
| 361 | txMsg.DataLength = 2; |
||
| 362 | txMsg.RemoteFlag = 0; |
||
| 363 | txMsg.ExtendedFlag = 1; |
||
| 364 | |||
| 744 | eqlazer | 365 | // txMsg.Data.words[ 0 ] = getPosition(); |
| 366 | |||
| 367 | if(STATUS == type){ |
||
| 368 | txMsg.Id = 0x1f8f0100UL; |
||
| 369 | txMsg.Data.words[ 0 ] = getPosition(); |
||
| 370 | txMsg.DataLength = 2; |
||
| 371 | }else{ |
||
| 372 | txMsg.Id = 0x12020202UL; |
||
| 373 | txMsg.Data.bytes[ 0 ] = type; |
||
| 374 | txMsg.DataLength = 1; |
||
| 375 | } |
||
| 376 | |||
| 377 | // Try to resend up to four times |
||
| 378 | for(int i=0; i<3;i++){ |
||
| 379 | if( CAN_OK == BIOS_CanSend( &txMsg )){ |
||
| 380 | return; |
||
| 381 | } |
||
| 382 | } |
||
| 682 | eqlazer | 383 | |
| 638 | eqlazer | 384 | } |