/* * Copyright 2005, 2006 Glen Overby. All Rights Reserved. * * Atmel AVR Altitude-Only Altimeter based on a Tiny45 * - Sample data on 1 A/D @ 200hz, filter with a recursive average * - software-driven serial port for debugging and detailed data * - status on a Buzzer (in morse code) * - Record Base and Peak voltages * * Pinout: Tiny45 * reset 1 8 vcc * Sensor ADC3 /PB3 2 7 PB2 SCK / ADC1 * Buzzer ADC4 /PB4 3 6 PB1 MISO / TxD * gnd 4 5 PB0 MOSI Mode? * * PB4 Buzzer * PB3 (ADC3) Pressure Sensor * PB2 empty * PB1 Mode: Run vs Report * PB0 TxD * * --------------------------------------------------------------------------- * * Baud Rate Calculation * * _delay_loop_1( count ) 3 cycles per loop * _delay_loop_2( count ) 4 cycles per loop * * Cycles per bit = System clock / (Baudrate - Timer Prescaler) * * CPU Clock 1mhz 8mhz * Baud Rate 9600 19200 9600 19200 * Time Per Bit 9.6 ms 250us * * Cycles Per Bit 104.16 52.08 434 * delay_loop_1 34.722 17.36 144 * delay_loop_2 23 108.5 500? * * 2400 bps = 2ms 2100us * 19200 bps = 250us * Measured: * on/off = 10us 1.2us * delay1(1) = 25us 3us * delay1(10) = 160us 8us * delay1(16) = ? 30us * * delay2(1) 4us * delay2(10) 26us * * --------------------------------------------------------------------------- * Recursive Filter * Described in David Schultz's 2002 NARAM paper. * http://home.earthlink.net/~david.schultz/ under NAR R&D * * P(n+1) = x * ADC + ( 1 - x ) * P(n) * * x = 1 gives the current value * x = 0 gives the old value (?) * x = 1/2 gives an average of the two values: * * P(n+1) = (ADC + Pn) / 2 * * Rearrange & Optimise: * * P(n+1) = x * ADC + Pn - x * Pn * = Pn + x * ADC - x * Pn * = Pn + x (ADC - Pn) * * Rearrange to get: * P(n+1) = P(n) + x (ADC - P(n)) * * 200 samples/second, a weight of x = 1/32 worked well for him. * * P(n+1) = Pn + (ADC - Pn) / 32 * * Spreadsheet: starting at zero, it takes 632 steps to hit 255. * Precision may be very important. More bits would be better! * * Binary Point: 8 bits for fractional part. * A 'long' is 32 bits on an AVR. Use that. * * P(n+1) = Pn + ((ADC<<8)>>5) - (Pn>>5) * = Pn + (ADC<<3) - (Pn>>5) * * or * P(n+1) = Pn + ( ( (ADC<<8) - Pn ) >> 5 ) * * old += ( (ADC << 3) - (old >> 5) ) * * x = 1/32 * newalt = oldalt + ((( atod-data << 8) >> 5) - * (oldalt >> 5)) * * 16 bits for fractional part * P(n+1) = Pn + ( ( (ADC<<16) - Pn ) >> 5 ) * old += (ADC << 11) - (old >> 5) * */ # define F_CPU 8000000UL #include #include #include #include #include #include #include "morse.h" /* * EEPROM Memory Map (in bytes) */ #define EE_STATUS 0 #define EE_GROUND 2 #define EE_APOGEE 4 #define EE_ATIME 6 /* * event bits: * - 200hz count for 10hz * - 10hz buzzer looks at state * - 1hz not sure what to do * - a/d complete run calculations */ volatile unsigned char events; #define E_CLOCK 0x01 #define E_ADC 0x02 #define E_04 0x04 #define E_08 0x08 #define E_T1O 0x10 #define E_T1C 0x20 #define E_T0O 0x40 #define E_T0C 0x80 /* * Timer 0: 200hz */ ISR(SIG_OUTPUT_COMPARE0A) { events |= E_CLOCK | E_T0C; ADCSRA |= _BV(ADSC); } #if 0 ISR(SIG_TIM0_OVF) /* interrupts enabled */ { TIFR = _BV(TOV0); events |= E_CLOCK | E_T0O; ADCSRA |= _BV(ADSC); } ISR(SIG_TIM1_OVF) /* interrupts enabled */ { TIFR = _BV(TOV1); events |= E_T1O; } ISR(SIG_TIM1_COMPA) { /*TIFR = _BV(OCF1A);*/ events |= E_T1C; } #endif volatile unsigned int adc_v0; /* A/D input value */ ISR(SIG_ADC) { unsigned char c; unsigned int i; c = ADCL; i = ADCH; i <<= 8; i |= (unsigned int)c; if (!(events & E_ADC)) { adc_v0 = i; events |= E_ADC; } } /* * Software serial port */ #define SERIALPORT PORTB #define TXBIT 0x02 /*#define BIT_DELAY 15*/ /* 1mhz 19200bps */ #define BIT_DELAY 100 /* 8mhz 19200bps */ void tx(unsigned char c) { unsigned char bc; /* bit count */ cli(); SERIALPORT &= ~TXBIT; _delay_loop_2(BIT_DELAY); for (bc=8; bc > 0; bc--) { if (c & 0x01) { SERIALPORT |= TXBIT; } else { SERIALPORT &= ~TXBIT; } _delay_loop_2(BIT_DELAY); c >>= 1; } SERIALPORT |= TXBIT; sei(); _delay_loop_2(BIT_DELAY); _delay_loop_2(BIT_DELAY); } void sput(char *s) { while (*s) { tx(*s); s++; } tx('\r'); tx('\n'); } /* * 16-bit hex output * Goal: more efficient than itoa * s = 0xXXXX\0 */ void hex16(unsigned int i, char *str) { char c, count; #ifdef X0 str += 6; *str-- = '\0'; #else str += 5; *str-- = '\0'; *str-- = ' '; #endif for( count=4; count; count--) { c = i & 0xf; if( c > 9 ) c += 0x37; else c += 0x30; *str-- = c; i >>= 4; } #ifdef X0 *str-- = 'x'; *str-- = '0'; #endif } /* * Print to a buffer. */ char str[22]; int s; void px(int i) { if (s > 16) return; hex16(i, &str[s]); s += 5; } void pc(char c) { str[s] = c; s++; } void pflush() { pc('\0'); sput(str); s = 0; } /* * Peripheral initialization */ void ioinit() { events = 0; /* PB5 PB4 PB3 PB2 PB1 PB0 Output = 1 */ /* I O I I O I */ /* I O O I O I */ /* 20 10 8 4 2 1 */ PORTB = 0; DDRB = 0x12; /*DDRB = 0x1a;*/ PORTB |= TXBIT; /* * Set system clock divisor */ CLKPR = _BV(CLKPCE); CLKPR = 0x0; /* Divisor = 0 */ /* * Timer 0 - 200hz */ #if 1 /* 8 mhz */ OCR0A = 156; /* 8M / 256 = 31250 / 200 = 156 */ /* 31 shows 5ms on & off times on scope */ OCR0B = 157; TCCR0A = 0x02; /* CTC mode is WGM01: = 010 */ TCCR0B = 0x04; /* clk/256 */ #else /* 1 mhz */ #if 0 /* count up */ OCR0A = 0xff; OCR0B = 0xff; TCCR0A = 0; TCCR0B = 0x03; /* clk/64 */ /* reload = 78 */ reload = 0xff - 78; #else /* count down */ OCR0A = 19; /*78;*/ OCR0B = 0xff; TCCR0A = 0x02; /* CTC mode is WGM01: = 010 */ TCCR0B = 0x03; /* clk/64 */ #endif #endif #if 0 TCCR1 = 0x88; /* CTC1 | CK/128 */ OCR1A = 39; /* >>> OCF1A */ OCR1B = 42; OCR1C = 39; /* resets cntr 1? */ #endif TIMSK = _BV(OCIE0A) | _BV(OCIE0B) | _BV(TOIE0); /* >>> buggy? Only an interrupt handler for OCIE0A is provided */ /* * A/D converter */ ADMUX = 0x03; /* REFS = Vcc */ /*ADMUX = 0x0c;*/ ADCSRA = _BV(ADEN) | _BV(ADIE) | 0x6 ; /* ADPS = / 64 */ ADCSRA |= _BV(ADSC); sei(); } /* * Buzzer with cycle counter */ #define BUZZERMASK 0x10 char bzcount; /* Count */ char bzon, bzoff; /* on & off time */ char bz; /* timer time */ void buzzer(char count, char on, char off) { bzcount = count; bz = bzon = on; bzoff = off; PORTB |= BUZZERMASK; } void buzz() { if (PORTB & BUZZERMASK) { PORTB &= ~BUZZERMASK; bz = bzoff; if (bzcount == 1) bz += bzoff; /*tx('B');*/ } else { if (--bzcount) { PORTB |= BUZZERMASK; bz = bzon; /*tx('b');*/ } } } /* * Morse code output to a buzzer */ unsigned char word; void buzzm() { if ((PORTB & BUZZERMASK)) { PORTB &= ~BUZZERMASK; bz = bzon; if (word == 0x80) bz = bzoff+bzoff+bzoff; /*tx('B');*/ } else { if( word != 0x80 ) { bz = ( word & 0x80 ) ? bzoff : bzon; PORTB |= BUZZERMASK; word <<= 1; /*tx('b');*/ } else { bzcount = 0; } } } void morse(unsigned char w) { word = w; bzcount = 1; PORTB &= ~BUZZERMASK; buzzm(); } /* * Number to Morse Code translation table */ prog_int8_t digits[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8, m9 }; /* * Altitude Table * * a table is a compromise with a logarithm for the altitude. * With adequate memory, the altitude can be represented close enough for my * needs. Anything more detailed should simply use Hpa and calulate it * after the flight. */ prog_int16_t alt[] = { 51246, /* 0x0 0 0.0000 V 10.3314 hpa 51245.8521 ft -51245.9 delta */ 50002, /* 0x8 8 0.0391 V 11.0692 hpa 50001.5923 ft 1244.3 delta */ 48823, /* 0x10 16 0.0781 V 11.8069 hpa 48822.7672 ft 1178.8 delta */ 47702, /* 0x18 24 0.1172 V 12.5446 hpa 47702.1523 ft 1120.6 delta */ 46634, /* 0x20 32 0.1562 V 13.2823 hpa 46633.6907 ft 1068.5 delta */ 45612, /* 0x28 40 0.1953 V 14.0201 hpa 45612.2510 ft 1021.4 delta */ 44633, /* 0x30 48 0.2344 V 14.7578 hpa 44633.4454 ft 978.8 delta */ 43693, /* 0x38 56 0.2734 V 15.4955 hpa 43693.4908 ft 940.0 delta */ 42789, /* 0x40 64 0.3125 V 16.2332 hpa 42789.1010 ft 904.4 delta */ 41917, /* 0x48 72 0.3516 V 16.9710 hpa 41917.4022 ft 871.7 delta */ 41076, /* 0x50 80 0.3906 V 17.7087 hpa 41075.8659 ft 841.5 delta */ 40262, /* 0x58 88 0.4297 V 18.4464 hpa 40262.2546 ft 813.6 delta */ 39475, /* 0x60 96 0.4688 V 19.1841 hpa 39474.5785 ft 787.7 delta */ 38711, /* 0x68 104 0.5078 V 19.9219 hpa 38711.0597 ft 763.5 delta */ 37970, /* 0x70 112 0.5469 V 20.6596 hpa 37970.1025 ft 741.0 delta */ 37250, /* 0x78 120 0.5859 V 21.3973 hpa 37250.2692 ft 719.8 delta */ 36550, /* 0x80 128 0.6250 V 22.1350 hpa 36550.2595 ft 700.0 delta */ 35869, /* 0x88 136 0.6641 V 22.8728 hpa 35868.8932 ft 681.4 delta */ 35205, /* 0x90 144 0.7031 V 23.6105 hpa 35205.0958 ft 663.8 delta */ 34558, /* 0x98 152 0.7422 V 24.3482 hpa 34557.8861 ft 647.2 delta */ 33926, /* 0xa0 160 0.7812 V 25.0859 hpa 33926.3653 ft 631.5 delta */ 33310, /* 0xa8 168 0.8203 V 25.8237 hpa 33309.7080 ft 616.7 delta */ 32707, /* 0xb0 176 0.8594 V 26.5614 hpa 32707.1543 ft 602.6 delta */ 32118, /* 0xb8 184 0.8984 V 27.2991 hpa 32118.0032 ft 589.2 delta */ 31542, /* 0xc0 192 0.9375 V 28.0368 hpa 31541.6061 ft 576.4 delta */ 30977, /* 0xc8 200 0.9766 V 28.7746 hpa 30977.3620 ft 564.2 delta */ 30425, /* 0xd0 208 1.0156 V 29.5123 hpa 30424.7126 ft 552.6 delta */ 29883, /* 0xd8 216 1.0547 V 30.2500 hpa 29883.1387 ft 541.6 delta */ 29352, /* 0xe0 224 1.0938 V 30.9877 hpa 29352.1560 ft 531.0 delta */ 28831, /* 0xe8 232 1.1328 V 31.7254 hpa 28831.3127 ft 520.8 delta */ 28320, /* 0xf0 240 1.1719 V 32.4632 hpa 28320.1858 ft 511.1 delta */ 27818, /* 0xf8 248 1.2109 V 33.2009 hpa 27818.3794 ft 501.8 delta */ 27326, /* 0x100 256 1.2500 V 33.9386 hpa 27325.5219 ft 492.9 delta */ 26841, /* 0x108 264 1.2891 V 34.6763 hpa 26841.2644 ft 484.3 delta */ 26365, /* 0x110 272 1.3281 V 35.4141 hpa 26365.2786 ft 476.0 delta */ 25897, /* 0x118 280 1.3672 V 36.1518 hpa 25897.2551 ft 468.0 delta */ 25437, /* 0x120 288 1.4062 V 36.8895 hpa 25436.9023 ft 460.4 delta */ 24984, /* 0x128 296 1.4453 V 37.6272 hpa 24983.9448 ft 453.0 delta */ 24538, /* 0x130 304 1.4844 V 38.3650 hpa 24538.1222 ft 445.8 delta */ 24099, /* 0x138 312 1.5234 V 39.1027 hpa 24099.1882 ft 438.9 delta */ 23667, /* 0x140 320 1.5625 V 39.8404 hpa 23666.9092 ft 432.3 delta */ 23241, /* 0x148 328 1.6016 V 40.5781 hpa 23241.0639 ft 425.8 delta */ 22821, /* 0x150 336 1.6406 V 41.3159 hpa 22821.4421 ft 419.6 delta */ 22408, /* 0x158 344 1.6797 V 42.0536 hpa 22407.8443 ft 413.6 delta */ 22000, /* 0x160 352 1.7188 V 42.7913 hpa 22000.0804 ft 407.8 delta */ 21598, /* 0x168 360 1.7578 V 43.5290 hpa 21597.9697 ft 402.1 delta */ 21201, /* 0x170 368 1.7969 V 44.2668 hpa 21201.3399 ft 396.6 delta */ 20810, /* 0x178 376 1.8359 V 45.0045 hpa 20810.0269 ft 391.3 delta */ 20424, /* 0x180 384 1.8750 V 45.7422 hpa 20423.8740 ft 386.2 delta */ 20043, /* 0x188 392 1.9141 V 46.4799 hpa 20042.7314 ft 381.1 delta */ 19666, /* 0x190 400 1.9531 V 47.2177 hpa 19666.4563 ft 376.3 delta */ 19295, /* 0x198 408 1.9922 V 47.9554 hpa 19294.9118 ft 371.5 delta */ 18928, /* 0x1a0 416 2.0312 V 48.6931 hpa 18927.9671 ft 366.9 delta */ 18565, /* 0x1a8 424 2.0703 V 49.4308 hpa 18565.4967 ft 362.5 delta */ 18207, /* 0x1b0 432 2.1094 V 50.1686 hpa 18207.3806 ft 358.1 delta */ 17854, /* 0x1b8 440 2.1484 V 50.9063 hpa 17853.5036 ft 353.9 delta */ 17504, /* 0x1c0 448 2.1875 V 51.6440 hpa 17503.7550 ft 349.7 delta */ 17158, /* 0x1c8 456 2.2266 V 52.3817 hpa 17158.0289 ft 345.7 delta */ 16816, /* 0x1d0 464 2.2656 V 53.1195 hpa 16816.2232 ft 341.8 delta */ 16478, /* 0x1d8 472 2.3047 V 53.8572 hpa 16478.2400 ft 338.0 delta */ 16144, /* 0x1e0 480 2.3438 V 54.5949 hpa 16143.9850 ft 334.3 delta */ 15813, /* 0x1e8 488 2.3828 V 55.3326 hpa 15813.3676 ft 330.6 delta */ 15486, /* 0x1f0 496 2.4219 V 56.0703 hpa 15486.3004 ft 327.1 delta */ 15163, /* 0x1f8 504 2.4609 V 56.8081 hpa 15162.6994 ft 323.6 delta */ 14842, /* 0x200 512 2.5000 V 57.5458 hpa 14842.4834 ft 320.2 delta */ 14526, /* 0x208 520 2.5391 V 58.2835 hpa 14525.5745 ft 316.9 delta */ 14212, /* 0x210 528 2.5781 V 59.0212 hpa 14211.8971 ft 313.7 delta */ 13901, /* 0x218 536 2.6172 V 59.7590 hpa 13901.3787 ft 310.5 delta */ 13594, /* 0x220 544 2.6562 V 60.4967 hpa 13593.9489 ft 307.4 delta */ 13290, /* 0x228 552 2.6953 V 61.2344 hpa 13289.5399 ft 304.4 delta */ 12988, /* 0x230 560 2.7344 V 61.9721 hpa 12988.0862 ft 301.5 delta */ 12690, /* 0x238 568 2.7734 V 62.7099 hpa 12689.5244 ft 298.6 delta */ 12394, /* 0x240 576 2.8125 V 63.4476 hpa 12393.7932 ft 295.7 delta */ 12101, /* 0x248 584 2.8516 V 64.1853 hpa 12100.8334 ft 293.0 delta */ 11811, /* 0x250 592 2.8906 V 64.9230 hpa 11810.5876 ft 290.2 delta */ 11523, /* 0x258 600 2.9297 V 65.6608 hpa 11523.0001 ft 287.6 delta */ 11238, /* 0x260 608 2.9688 V 66.3985 hpa 11238.0173 ft 285.0 delta */ 10956, /* 0x268 616 3.0078 V 67.1362 hpa 10955.5869 ft 282.4 delta */ 10676, /* 0x270 624 3.0469 V 67.8739 hpa 10675.6584 ft 279.9 delta */ 10398, /* 0x278 632 3.0859 V 68.6117 hpa 10398.1829 ft 277.5 delta */ 10123, /* 0x280 640 3.1250 V 69.3494 hpa 10123.1128 ft 275.1 delta */ 9850, /* 0x288 648 3.1641 V 70.0871 hpa 9850.4019 ft 272.7 delta */ 9580, /* 0x290 656 3.2031 V 70.8248 hpa 9580.0056 ft 270.4 delta */ 9312, /* 0x298 664 3.2422 V 71.5626 hpa 9311.8804 ft 268.1 delta */ 9046, /* 0x2a0 672 3.2812 V 72.3003 hpa 9045.9840 ft 265.9 delta */ 8782, /* 0x2a8 680 3.3203 V 73.0380 hpa 8782.2756 ft 263.7 delta */ 8521, /* 0x2b0 688 3.3594 V 73.7757 hpa 8520.7152 ft 261.6 delta */ 8261, /* 0x2b8 696 3.3984 V 74.5135 hpa 8261.2642 ft 259.5 delta */ 8004, /* 0x2c0 704 3.4375 V 75.2512 hpa 8003.8850 ft 257.4 delta */ 7749, /* 0x2c8 712 3.4766 V 75.9889 hpa 7748.5408 ft 255.3 delta */ 7495, /* 0x2d0 720 3.5156 V 76.7266 hpa 7495.1960 ft 253.3 delta */ 7244, /* 0x2d8 728 3.5547 V 77.4644 hpa 7243.8162 ft 251.4 delta */ 6994, /* 0x2e0 736 3.5938 V 78.2021 hpa 6994.3674 ft 249.4 delta */ 6747, /* 0x2e8 744 3.6328 V 78.9398 hpa 6746.8168 ft 247.6 delta */ 6501, /* 0x2f0 752 3.6719 V 79.6775 hpa 6501.1325 ft 245.7 delta */ 6257, /* 0x2f8 760 3.7109 V 80.4153 hpa 6257.2833 ft 243.8 delta */ 6015, /* 0x300 768 3.7500 V 81.1530 hpa 6015.2389 ft 242.0 delta */ 5775, /* 0x308 776 3.7891 V 81.8907 hpa 5774.9696 ft 240.3 delta */ 5536, /* 0x310 784 3.8281 V 82.6284 hpa 5536.4467 ft 238.5 delta */ 5300, /* 0x318 792 3.8672 V 83.3661 hpa 5299.6420 ft 236.8 delta */ 5065, /* 0x320 800 3.9062 V 84.1039 hpa 5064.5280 ft 235.1 delta */ 4831, /* 0x328 808 3.9453 V 84.8416 hpa 4831.0782 ft 233.4 delta */ 4599, /* 0x330 816 3.9844 V 85.5793 hpa 4599.2663 ft 231.8 delta */ 4369, /* 0x338 824 4.0234 V 86.3170 hpa 4369.0668 ft 230.2 delta */ 4140, /* 0x340 832 4.0625 V 87.0548 hpa 4140.4551 ft 228.6 delta */ 3913, /* 0x348 840 4.1016 V 87.7925 hpa 3913.4066 ft 227.0 delta */ 3688, /* 0x350 848 4.1406 V 88.5302 hpa 3687.8979 ft 225.5 delta */ 3464, /* 0x358 856 4.1797 V 89.2679 hpa 3463.9057 ft 224.0 delta */ 3241, /* 0x360 864 4.2188 V 90.0057 hpa 3241.4074 ft 222.5 delta */ 3020, /* 0x368 872 4.2578 V 90.7434 hpa 3020.3810 ft 221.0 delta */ 2801, /* 0x370 880 4.2969 V 91.4811 hpa 2800.8048 ft 219.6 delta */ 2583, /* 0x378 888 4.3359 V 92.2188 hpa 2582.6578 ft 218.1 delta */ 2366, /* 0x380 896 4.3750 V 92.9566 hpa 2365.9193 ft 216.7 delta */ 2151, /* 0x388 904 4.4141 V 93.6943 hpa 2150.5692 ft 215.4 delta */ 1937, /* 0x390 912 4.4531 V 94.4320 hpa 1936.5878 ft 214.0 delta */ 1724, /* 0x398 920 4.4922 V 95.1697 hpa 1723.9557 ft 212.6 delta */ 1513, /* 0x3a0 928 4.5312 V 95.9075 hpa 1512.6541 ft 211.3 delta */ 1303, /* 0x3a8 936 4.5703 V 96.6452 hpa 1302.6645 ft 210.0 delta */ 1094, /* 0x3b0 944 4.6094 V 97.3829 hpa 1093.9689 ft 208.7 delta */ 887, /* 0x3b8 952 4.6484 V 98.1206 hpa 886.5496 ft 207.4 delta */ 680, /* 0x3c0 960 4.6875 V 98.8584 hpa 680.3892 ft 206.2 delta */ 475, /* 0x3c8 968 4.7266 V 99.5961 hpa 475.4708 ft 204.9 delta */ 272, /* 0x3d0 976 4.7656 V 100.3338 hpa 271.7778 ft 203.7 delta */ 69, /* 0x3d8 984 4.8047 V 101.0715 hpa 69.2940 ft 202.5 delta */ -132, /* 0x3e0 992 4.8438 V 101.8093 hpa -131.9966 ft 201.3 delta */ -332, /* 0x3e8 1000 4.8828 V 102.5470 hpa -332.1096 ft 200.1 delta */ -531, /* 0x3f0 1008 4.9219 V 103.2847 hpa -531.0602 ft 199.0 delta */ -729, /* 0x3f8 1016 4.9609 V 104.0224 hpa -728.8635 ft 197.8 delta */ }; /* * Altitude Lookup * * Input is a 10-bit altitude. It should already be rounded. * * The table holds every 8th value. The output altitude can be imprecise * because the difference between two values is a fraction, and this uses * integer arithmetic. * * Solution: * j = i - j; simple integer * j <<= 8; fixed point: 8 bit fraction * j >>= 3; divide by 8 * k = 0; sucker multiply * for (l = value & 0x7; l--; ) * k += j; * j = k >> 8; convert to simple integer * if (k & 0x80) * j++; round * i -= j; finish calculation */ int altitude(int value) { int i, j, l, t1, t2; long a, b; i = value >> 3; /* value/8 = index */ t1 = pgm_read_word_near(&alt[i]); /*pc('$'); px(i); px(t1);*/ /* * if value & 0x7 != 0 * read (value >> 3) and ((value >> 3) + 1) and interpolate */ if (value & 0x7) { t2 = pgm_read_word_near(&alt[i+1]); /*px(t2);*/ a = t1 - t2; /* simple integer */ a <<= 8; /* fixed point: 8 bit fraction */ a >>= 3; /* divide by 8 */ b = 0; /* sucker multiply */ for (l = value & 0x7; l--; ) b += a; j = b >> 8; /* convert to simple integer */ if (a & 0x80) j++; /* round */ /*px(j);*/ t1 -= j; /* finish calculation */ } return t1; } /* * Binary Coded Decimal * Brute force method, with a fixed number of digits */ char bcdvalue[5]; int bcdigit(int v, int i, int j) { bcdvalue[i] = 0; while ( v >= j) { bcdvalue[i]++; v -= j; } return v; } void bcd(int v) { v = bcdigit(v, 0, 10000); v = bcdigit(v, 1, 1000); v = bcdigit(v, 2, 100); v = bcdigit(v, 3, 10); v = bcdigit(v, 4, 1); } int main() { unsigned char fstate; #define S_R0 0 #define S_STARTUP 1 #define S_GROUND 2 #define S_ASCENT 3 #define S_APOGEE 4 #define S_DONE 5 /* reporting */ char cnt10hz; unsigned char report; unsigned char rstate; #define R_R0 0 #define R_STARTUP 1 #define R_GROUND 2 #define R_LAUNCH 3 #define R_APOGEE 4 #define R_ALT 5 #define R_BCDALT 6 #define R_DONE 7 unsigned char bstate; #define B_LAST 0 #define B_FSTATE 1 #define B_DONE 2 int timer; long adc_last0; /* Last A/D value */ long adc_ground; /* "ground" value */ long adc_apogee; /* apogee value */ int alt; /* apogee altitude */ int ascent_time; /* "up" time */ int t; int b; ioinit(); sei(); tx('!'); adc_last0 = adc_ground = adc_apogee = 0; ascent_time = 0; fstate = rstate = bstate = 0; cnt10hz = 20; timer = 0; s = 0; report = 200; bzon = 1; bzoff = 3; morse(0x01); adc_ground = eeprom_read_word((uint16_t*)EE_GROUND); adc_apogee = eeprom_read_word((uint16_t*)EE_APOGEE); alt = altitude(adc_apogee); /* >>> pflush(); <<< */ alt -= altitude(adc_ground); /* >>> pflush(); <<< */ pc('R'); px(adc_ground); px(adc_apogee); px(alt); pflush(); /* >>> */ /* buzzer: altitude */ bcd(alt); for(b = 0; bcdvalue[b] == 0; b++) ; while (1) { sei(); if (events & E_CLOCK) { events &= ~E_CLOCK; /*tx('t');*/ if (--cnt10hz == 0) { /*tx('T');*/ cnt10hz = 20; timer++; if (bzcount && --bz == 0) { buzzm(); } } if (report) { report--; } } if (events & E_ADC) { adc_last0 += ((long)( adc_v0 << 3 )) - (adc_last0 >> 5); cli(); events &= ~E_ADC; sei(); /*tx('&');*/ switch(fstate) { /* * Wait for pressure average to stabilize */ case S_R0: fstate = S_STARTUP; break; case S_STARTUP: if (timer > 10) { adc_ground = adc_last0; adc_apogee = adc_ground - (3<<8); tx('g'); fstate = S_GROUND; } break; /* Watch for launch */ case S_GROUND: /* overload _apogee as the launch detect alt */ if (adc_last0 < adc_apogee) { fstate = S_ASCENT; timer = 0; ascent_time = 0; tx('l'); } break; /* Watch for Apogee */ case S_ASCENT: ascent_time++; if (adc_last0 < adc_apogee) { adc_apogee = adc_last0; tx('+'); } else if (adc_last0 > (adc_apogee + 0x300)) { fstate = S_APOGEE; ascent_time = timer; tx('a'); eeprom_write_word((uint16_t*)EE_GROUND, adc_ground>>8); eeprom_write_word((uint16_t*)EE_APOGEE, adc_apogee>>8); eeprom_write_word((uint16_t*)EE_ATIME, ascent_time); alt = altitude(adc_apogee>>8); alt -= altitude(adc_ground>>8); bcd(alt); for(b = 0; bcdvalue[b] == 0; b++) ; } break; case S_APOGEE: fstate++; break; } } /* * Serial Port Status Reporting * * - Last Altitude & Time * - Current Ground Level * - This Flight's Altitude & Time */ if (s == 0) { if (report == 0) { report = 200; } switch (rstate) { case S_R0: case R_STARTUP: /* Startup (S_STARTUP) */ /* when waiting to pass S_STARTUP: * buzzer 1hz * a/d value */ /* Serial: ground level */ if (report == 200) { pc('S'); px(adc_last0>>4); tx(str[0]); s = 1; } if (fstate > S_STARTUP) { rstate = R_GROUND; pc('G'); px(adc_ground); tx(str[0]); s = 1; } break; case R_GROUND: /* Wait for launch */ /* short beep every n seconds */ if (fstate > S_GROUND) { /* buzzer: fast */ rstate = R_LAUNCH; tx('L'); } break; case R_LAUNCH: /* Launch Detected */ if (fstate >= S_APOGEE) { rstate = R_APOGEE; } break; case R_APOGEE: pc('A'); px(adc_apogee); tx(str[0]); s = 1; rstate++; break; case R_ALT: pc('H'); px(alt); tx(str[0]); s = 1; rstate++; break; case R_BCDALT: pc('B'); for(t = 0; t < 5; t++) { pc('0' + bcdvalue[t]); } pc('\0'); tx(str[0]); s = 1; rstate++; break; case R_DONE: break; } } else { tx(str[s]); s++; if (str[s] == '\0') { str[1] = '\r'; str[2] = '\n'; str[3] = '\1'; s = 1; } else if (str[s] == '\1') { s = 0; } } /* * Status reporting by buzzer */ if (bzcount == 0) { switch(bstate) { case B_LAST: /* last altitude */ if (b < 5) { t = bcdvalue[b]; morse(pgm_read_byte_near(&digits[t])); b++; } else { bstate = B_FSTATE; morse(mStop); } break; case B_FSTATE: switch(fstate) { case S_STARTUP: morse(mS); break; case S_GROUND: morse(mE); break; case S_ASCENT: morse(mH); break; case S_APOGEE: morse(mA); break; case S_DONE: morse(mA); bstate = B_DONE; break; } break; case B_DONE: /* * buzzer altitude */ if (b >= 5) { for(b = 0; bcdvalue[b] == 0; b++) ; morse(mStop); } else { t = bcdvalue[b]; morse(pgm_read_byte_near(&digits[t])); b++; } break; } } } }