/* * Sequencer and Pulsed Relay Driver * * Copyright 2006 Glen Overby, All Rights Reserved * * This program may be freely used and distributed on a non-commercial * basis. * * This program is designed to drive an output sequence using four output * ports on an Atmel(R) Tiny AVR microprocessor. The typical use for this * is to sequence amateur radio transverters and amplifiers or preamplifiers. * * The port delays (and on time for the pulsed ports) is selected in 1ms * intervals. * * Memory Output Function * Location * 0 0 mode * 1 0 on delay * 2 0 off delay * 3 0 pulse length * * 4 1 mode * 5 1 on delay * 6 1 off delay * 7 1 pulse length * * 8 2 mode * 9 2 on delay * 10 2 off delay * 11 2 pulse length * * 12 2 mode * 13 3 on delay * 14 3 off delay * 15 3 pulse length * * Mode * This is a bit mask of: * * Postive 0x000 Port is set to '1' when PTT is on * Negative 0x001 Port is set to '0' when PTT is on * Pulse On 0x002 Port is pulsed when PTT turns on * Pulse Off 0x004 Port is pulsed when PTT turns off * * Note that the combination of Pulse and Postive/Negative is permitted. * * Delays are in milliseconds (1 khz) * * The configuration I use is: * ------- delay --------- * Output type on off pulse * 0 POS 0 75 0 VN2222 to Ground * 1 POS 75 0 0 VN2222 to Ground * 2 P-ON-N 1 0 255 IRF9Z14 to 12V * 3 P-OFF-N 0 1 255 IRF9Z14 to 12V * * * avrdude -p t45 -c bsd -E noreset,novcc -U * eeprom:w:00,75,00,00,03,01,00,255,05,00,01,255,00,00,75,00:m * | M N F P| M N F P | M N F P | M N F P | * | 1111111111| 2222222222 | 3333333333 | 4444444444 | * * * Tiny26 * Timer/Counter 1 - 1khz timing * * Ports: * PORT A 0 output 1 * PORT A 1 output 2 * PORT A 2 output 3 * PORT A 3 output 4 * PORT A 4 * PORT A 5 status: port timer state change * PORT A 6 status: interrupt * PORT A 7 PTT * * Tiny45 * Ports: * PORT B 0 output 2 (2nd from top: VN2222) * PORT B 1 output 3 (3rd from top: IRF9Z14) * PORT B 2 output 4 (4th from top: IRF9Z14) * PORT B 3 PTT * PORT B 4 output 1 (top of board: VN2222) * * The outputs that connect to a VN2222 are negative (0 = on) * The outputs that connect to a IRF9Z14 are positive (1 = on) */ # define F_CPU 1000000UL #include #include #include #include #include #include #if defined (__AVR_ATtiny26__) #define DEBUG #endif volatile unsigned char events; #define E_TIMER 0x01 /* Timer 0 */ #if defined (__AVR_ATtiny45__) ISR(SIG_OUTPUT_COMPARE0A) { events |= E_TIMER; #ifdef DEBUG if (PORTB & 0x2) PORTB &= ~0x2; else PORTB |= 0x2; #endif } #endif /* ATtiny45 */ /* * Timer 1: 1khz */ ISR(SIG_OUTPUT_COMPARE1A) { events |= E_TIMER; #ifdef DEBUG if (PORTB & 0x10) PORTB &= ~0x10; else PORTB |= 0x10; #endif } /* * Per-Channel data: * mode: POS, NEG, PULSE-ON, PULSE-OFF * on-delay * off-delay * on-time for pulse ports */ struct portinfo { unsigned char mode; unsigned char ondelay; unsigned char offdelay; unsigned char pulse; unsigned char mask; }; #define PORT_POS 0x000 #define PORT_NEG 0x001 #define PORT_PULSE_ON 0x002 #define PORT_PULSE_OFF 0x004 #define PTT_ON 0x010 #define PTT_OFF 0x020 #define PORT_TIMER 0x040 #define PORT_ABORT 0x080 /* PTT changed partway through sequence */ #define PIN_ON 0x100 #define PIN_OFF 0x200 #define PTT 0x08 /* PB3 */ struct portinfo portinfo[4]; unsigned char timers[4]; /* * Change port state: turn on or off. (Version 3) * * POS set port bit to 0 to turn on * * NEG set port bit to 1 to turn on * * PTT is 0 when 'activated' (PTT-L) * * PIN the current state of the output pin * * Timer is True when call came from the timer * * ToPin is what to set the ping to * * StTimer is 1 if the timer should be restarted * * * MODE PTT Timer PIN ToPin StTimer * ------------------------------------------------------------ * POS 0 0 0 * POS 1 1 0 * * P-ON POS 0 1 0 1 * P-ON POS 0 0 1 0 * P-ON POS 1 1 0 * * P-OFF POS 0 1 0 * P-OFF POS 1 1 0 1 * P-OFF POS 1 0 1 0 * * NEG 0 1 0 * NEG 1 0 0 * * P-ON NEG 0 0 1 1 * P-ON NEG 0 1 0 0 * P-ON NEG 1 0 0 * * P-OFF NEG 0 0 0 * P-OFF NEG 1 1 0 0 * P-OFF NEG 1 0 1 1 * * * Make this logic table POS/NEG neutral: (To is the state to set the pin to) * * POS on on no To=PTT * POS off off no To=PTT * * P-ON on off on yes To=PTT&!PIN * P-ON on on off no * P-ON off any off no * * P-OFF on any off no * P-OFF off off on yes To=!PTT&!PIN * P-OFF off on off no * * To convert the on/off logc to a 1 or 0 on the port, respecting the POS/NEG * pin state: * * PIN = ToPIN mode&PORT_NEG * ---------------------------------------- * 0 on (T) 0 POS * 1 on (T) 1 NEG To && !pos * 1 off (F) 0 POS * 0 off (F) 1 NEG !To && pos * */ char turn(int port, char what) { unsigned char bits, mode, pin, ptt, to, r, pos; to = r = 0; mode = portinfo[port].mode; pos = (mode&PORT_NEG) == 0; bits = portinfo[port].mask; pin = (PORTB & bits) == 0; /* True when on */ if (!pos) pin = !pin; /* False when on */ ptt = (PINB & PTT) == 0; /* True when 'on' */ switch(mode & (~PORT_NEG)) { case PORT_POS: to = ptt; break; case PORT_PULSE_ON: to = r = ptt && !pin; break; case PORT_PULSE_OFF: to = r = !ptt && !pin; break; } if ( (to && !pos) || (!to && pos) ) { PORTB |= bits; } else { PORTB &= ~bits; } return r; } int main() { unsigned char pcbits; int a, d, p; struct portinfo *info; char *t; /* DDRn: 0 = Input, 1 = Output */ #if defined (__AVR_ATtiny26__) DDRA = 0x7f; DDRB = 0x17; #elif defined (__AVR_ATtiny45__) DDRB = 0x17; #endif #if defined (__AVR_ATtiny45__) /* Set clock to 8mhz */ CLKPR = _BV(CLKPCE); CLKPR = 0x0; #endif /* * Timer 0 * I enabled this while debugging timer interrupts */ #if defined (__AVR_ATtiny45__) TCCR0A = 0x2; /* WGM = CTC */ TCCR0B = 0x3; /* clk/? */ OCR0A = 125; TIMSK |= _BV(OCIE0A); #endif /* * Timer 1: 1khz */ #if defined (__AVR_ATtiny26__) /* 8,000,000 / 64 / 1000 = 125 */ TCCR1B = _BV(CTC1) | _BV(PSR1) | 0x7; /* CTC1 | CK/64 */ OCR1A = OCR1B = 126; OCR1C = 125; TIMSK |= _BV(OCIE1A) | _BV(TOIE1); #elif defined (__AVR_ATtiny45__) TCCR1 = _BV(CTC1) | 0x7; /* CTC1 | CK/64 */ OCR1A = 125; /* interrupt on this value */ OCR1C = 125; /* Holds max value: clear on compare match */ TIMSK |= _BV(OCIE1A); #endif sei(); /* * Read configuration from nvram */ a = 0; for (p = 0, info = portinfo; p < 4; p++, info++) { info->mode = eeprom_read_byte ((uint8_t *)a); a++; info->ondelay = eeprom_read_byte ((uint8_t *)a); a++; info->offdelay = eeprom_read_byte ((uint8_t *)a); a++; info->pulse = eeprom_read_byte ((uint8_t *)a); a++; info->mask = (1 << p); } portinfo[3].mask = 0x10; /* Output #4 = PB4 */ pcbits = ~(PINB & PTT); while (1) { sei(); if ((PINB & PTT) != pcbits) { _delay_loop_2( 1024 ); if ((PINB & PTT) == pcbits) { continue; } pcbits = (PINB & PTT); a = (pcbits) ? PTT_OFF : PTT_ON; for (p = 0, info = portinfo, t = timers; p < 4; p++, info++, t++) { d = (a == PTT_ON) ? portinfo[p].ondelay : portinfo[p].offdelay; if (*t || !d) turn(p, a); else *t = d; } #ifdef DEBUG if (PORTB & 0x10) PORTB &= ~0x10; else PORTB |= 0x10; #endif } if (events) { events = 0; for (p = 0, info = portinfo, t = timers; p < 4; p++, info++, t++) { if (*t == 0) { continue; } if (--(*t) == 0) { if(turn(p, PORT_TIMER)) *t = info->pulse; #if 0 if (PORTB & 0x2) PORTB &= ~0x2; else PORTB |= 0x2; #endif } } } } }