#include #include #include #include "avrlibdefs.h" #include "presepio.h" /* AVR8515 - not present */ #include "crib162.h" byte dimmer_status; /* stato del dimmer */ byte dimmer_ticks; /* numero di ticks per conteggiare il tempo del ciclo */ byte steps; /* nuovo step */ byte percent; /* percentuale del ciclo giorno-notte */ byte scale; /* velocita' del ciclo - solo per modo stand-alone */ byte random; /* valore del generatore pseudo random per il fuoco */ byte fire_ch; /* canale del fuoco */ byte fire_flag; /* flag del fuoco attivo */ word oc_start; /* valore di partenza del timer 1 */ word oc_min; /* valore min di oc - partenza a crescere */ word oc_max; /* valore max di oc - partenza a decrescere */ byte end_pulse; /* and mask per terminare gli impulsi */ byte pulse_a; /* or mask canale da attivare con oc1a */ byte pulse_b; /* or mask canale da attivare con oc1b */ word compare_a; /* valore aggiornato da inserire in oc1a al trigger */ word compare_b; /* valore aggiornato da inserire in oc1a al trigger */ byte remainder; /* resto del comparatore a */ word step_inc; /* unita' d'incremento per regolare la velocita' di dissolvenza */ word step_cent; /* centesimi di incremento */ byte direction; /* direzione - UP = oc1a */ volatile byte end_cycle; /* indicazione fine impulso - inizio dimmer handler */ /**************************************** INIT_PORTS ****************************************/ void init_ports(void) { DDRA = 0xFF; /* PORTA tutti outputs */ DDRB = 0x00; /* PORTB tutti inputs */ #ifdef EMUL DDRC = 0x45; /* PORTC tutti inputs tranne PC.6 (led) - PC.2 mains sim - PC.0 testout */ #else DDRC = 0x41; /* PORTC tutti inputs tranne PC.6 (led) - PC.0 testout */ #endif PORTB = 0xff; /* AUTO PULLUP */ PORTC = 0xfe; /* AUTO PULLUP - PC.0 testout = 0 */ cbi(PORTC, LED); /* accendi il led */ PORTA = 0x40; /* luce giorno accesa - tutte le altre spente */ return; } /**************************************** INIT_VARS ****************************************/ void init_vars(void) { dimmer_status = DIMMER_OFF; dimmer_ticks = 0; steps = 0; percent = 20; /* stato = giorno */ fire_flag = OFF; fire_ch = 0; end_pulse = 0xff; pulse_a = 0; pulse_b = 0; compare_a = 0; compare_b = 0; remainder = 0; step_inc = 50; step_cent = 0; direction = UP; end_cycle = OFF; random = 0x55; /* Calcolo del controllo dimmer <- min -> inizio oc_start oc_min oc_max tout fine |------|---------|-----------------|--------|-------| trigger <- start pulse ---> end_pulse - valori in us - tout = 65536 - oc_min = lmax - oc_max = lmin - in salita = diminuzione della luminosita' - in discesa = aumento della luminosita' - fine-inizio = durata ciclo = 10000 us - oc_start - inizio = ritardo per la rivelazione inizio ciclo (da verificare) - margine = 500 us (per tener conto del ritardo inizio e del margine tra tout e fine ciclo) - oc_start - oc_min = margine per l'inizio duty cicle = 100 us - tout - oc_max = margine per la fine duty cicle = larghezza minima dell'impulso = 100 us */ oc_start = 65535 - 10000 + 500; oc_min = oc_start + 100; oc_max = 65535 - 100; if (bit_is_set(PINB, MASTER)) { fire_flag = ON; fire_ch = 0; get_scale(); } return; } /**************************************** INIT_COM ****************************************/ void init_com(void) { /* AVR8515 */ /* UCR = 0x10; */ /* rx enable - tx disabled - all irq disabled */ /* UBRR = BR2400; */ /* baud rate = 2400 */ UBRR0H = (unsigned char)((CLK_RATE / BAUD_RATE / 16 - 1) >> 8); UBRR0L = (unsigned char)((CLK_RATE / BAUD_RATE / 16 - 1) & 0xff); UCSR0C = _BV(URSEL0) | _BV(UCSZ01) | _BV(UCSZ00); UCSR0B = _BV(RXEN0); return; } #ifdef EMUL /**************************************** INIT_TIMER0 ****************************************/ void init_timer0(void) { TCNT0 = 256 -79; /* reset TCNT0 */ TCCR0 = 5; /* count with cpu clock/1024 */ return; } #endif /**************************************** INIT_TIMER1 ****************************************/ void init_timer1(void) { TCCR1A = 0xa0; /* toggle OC1A and OC1B */ TCCR1B = 0x00; /* stop counter */ return; } /**************************************** INIT_TIMER ****************************************/ void init_timers(void) { #ifdef EMUL init_timer0(); #endif init_timer1(); return; } /**************************************** IS_CHAR_AV ****************************************/ int is_char_av(void) { /* AVR8515 */ /* if (bit_is_set(USR, RXC)) return(1); */ if (bit_is_set(UCSR0A, RXC0)) return(1); else return(0); } /**************************************** INIT ****************************************/ void init(void) { init_ports(); init_vars(); init_timers(); init_com(); return; } /***************************************** SETUP_DIMMER *****************************************/ /* - DAYBREAK e SUNSET controllati da OC1A - DAY e NIGHT controllati da OC1B - DIRECTION e' riferita a OC1A - UP diminuisce la luminosita' - DOWN aumenta la luminosita' */ void setup_dimmer(byte event_type) { remainder = 0; switch (event_type) { case DAYBREAK_STATE : direction = DOWN; dimmer_status = DAYBREAK_STATE; pulse_a = BV(DAYBREAK_CH); pulse_b = BV(NIGHT_CH); compare_a = oc_max; compare_b = oc_min; end_pulse = END_PULSE_DAYBREAK; break; case DAY_STATE : direction = UP; dimmer_status = DAY_STATE; pulse_a = BV(DAYBREAK_CH); pulse_b = BV(DAY_CH); compare_a = oc_min; compare_b = oc_max; end_pulse = END_PULSE_DAY; break; case SUNSET_STATE : direction = DOWN; dimmer_status = SUNSET_STATE; pulse_a = BV(SUNSET_CH); pulse_b = BV(DAY_CH); compare_a = oc_max; compare_b = oc_min; end_pulse = END_PULSE_SUNSET; break; case NIGHT_STATE : direction = UP; dimmer_status = NIGHT_STATE; pulse_a = BV(SUNSET_CH); pulse_b = BV(NIGHT_CH); compare_a = oc_min; compare_b = oc_max; end_pulse = END_PULSE_NIGHT; break; case DIMMER_OFF : default: STOP_TIMER; pulse_a = pulse_b = 0; dimmer_status = DIMMER_OFF; end_pulse = 0xff; break; } return; } /****************************************** STOP_DIMMER ******************************************/ void stop_dimmer(void) { switch (dimmer_status) { case DAYBREAK_STATE : sbi(PORTA, DAYBREAK_CH); cbi(PORTA, NIGHT_CH); break; case DAY_STATE : sbi(PORTA, DAY_CH); cbi(PORTA, DAYBREAK_CH); break; case SUNSET_STATE : sbi(PORTA, SUNSET_CH); cbi(PORTA, DAY_CH); break; case NIGHT_STATE : sbi(PORTA, NIGHT_CH); cbi(PORTA, SUNSET_CH); break; case DIMMER_OFF : default: STOP_TIMER; /* stop timer */ pulse_a = pulse_b = 0; dimmer_status = DIMMER_OFF; break; } STOP_TIMER; /* stop timer */ end_pulse = 0xff; pulse_a = pulse_b = 0; dimmer_status = DIMMER_OFF; return; } /******************************************* IS_REMAINDER *******************************************/ int is_remainder(void) { remainder = remainder + step_cent; if (remainder < 100) return(0); else remainder = remainder - 100; return(1); } /******************************************** UPDATE_DIMMER ********************************************/ void update_dimmer(void) { if (dimmer_status == DIMMER_OFF) return; if (direction == UP) { compare_a = compare_a + step_inc; compare_b = compare_b - step_inc; if (is_remainder()) { compare_a++; compare_b--; } if (compare_a > oc_max) stop_dimmer(); } else { compare_b = compare_b + step_inc; compare_a = compare_a - step_inc; if (is_remainder()) { compare_b++; } if (compare_b > oc_max) stop_dimmer(); } return; } /******************************************** TIMER_1_OVERFLOW - END PULSES ********************************************/ SIGNAL (SIG_OVERFLOW1) { byte temp; temp = PINA; temp = temp & end_pulse; PORTA = temp; end_cycle = ON; cbi(PORTC, TEST_OUT); } /******************************************** TIMER_0_OVERFLOW - ********************************************/ SIGNAL(SIG_OVERFLOW0) /* signal handler for tcnt0 overflow interrupt */ { byte temp; temp = PINC; if(temp & 0x04) PORTC = temp & 0xfb; else PORTC = temp | 0x04; TCNT0 = 256-79; } /******************************************** TIMER_1_OUTPUT_COMPAREA_A - SET PULSE ********************************************/ SIGNAL (SIG_OUTPUT_COMPARE1A) { byte temp; temp = PINA; temp = temp | pulse_a; PORTA = temp; sbi(PORTC, TEST_OUT); } /******************************************** TIMER_1_OUTPUT_COMPARE_B - SET PULSE ********************************************/ SIGNAL (SIG_OUTPUT_COMPARE1B) { byte temp; temp = PINA; temp = temp | pulse_b; PORTA = temp; } /******************************************** COMMAND COMMAND COMMAND COMMAND COMMAND COMMAND COMMAND COMMAND COMMAND ********************************************/ /******************************************** CHECK_FOR_COMMAND ********************************************/ void check_for_command(void) { byte command; byte value; if (is_char_av()) { /* AVR8515 */ /* command = UDR; */ command = UDR0; value = command % 16; command = command / 16; switch(command) { case CHA_OFF : do_choff(value); break; case CHA_ON : do_chon(value); break; case SET_SCALE : do_setscale(value); break; case DAYBREAK_ON : setup_dimmer(DAYBREAK_STATE); break; case DAY_ON : setup_dimmer(DAY_STATE); break; case SUNSET_ON : setup_dimmer(SUNSET_STATE); break; case NIGHT_ON : setup_dimmer(NIGHT_STATE); break; case FIRE_ON : do_rand(value); break; default : break; } } return; } /******************************************** GET_SCALE pos scale ciclo in sec 0 2 40 sec 1 4 80 sec 2 6 120 sec 3 8 160 sec 4 10 200 sec 5 12 240 sec 6 14 280 sec ********************************************/ void get_scale(void) { byte temp, i; temp = PINB; for (i = 0; i < 7; i++, temp = temp >> 1) { if ((temp & 0x01) == 0) break; } if (i > 7) i = 0; scale = (i + 1) * 2; /* velocita' del ciclo giorno-notte */ do_setscale(i); /* velocita' dissolvenza */ return; } /******************************************** GET_KEYS Controllo del pulsante di test ********************************************/ void get_keys(void) { byte temp; if (bit_is_clear(PINC, TEST)) { temp = PINA; PORTA = 0xff; loop_until_bit_is_set(PINC, TEST); PORTA = temp; } return; } /******************************************** DO_CHOFF ********************************************/ void do_choff(byte ch_no) { byte temp; if (ch_no == fire_ch) fire_flag = 0; temp = PINA; temp = temp & ~(BV(ch_no)); PORTA = temp; return; } /******************************************** DO_CHON ********************************************/ void do_chon(byte ch_no) { byte temp; if (ch_no > 7) return; if (ch_no == fire_ch) fire_flag = 0; temp = PINA; temp = temp | BV(ch_no); PORTA = temp; return; } /******************************************** DO_SETSCALE ********************************************/ void do_setscale(byte scale_value) { switch (scale_value) { case 0 : step_inc = 50 ; remainder = 0; break; case 1 : step_inc = 25; remainder = 0; break; case 2 : step_inc = 16; remainder = 67; break; case 3 : step_inc = 12; remainder = 50; break; case 4 : step_inc = 10; remainder = 0; break; case 5 : step_inc = 8; remainder = 33; break; case 6 : step_inc = 7; remainder = 14; break; case 7 : step_inc = 6; remainder = 25; break; case 8 : step_inc = 5; remainder = 56; break; case 9 : step_inc = 5; remainder = 0; break; case 10 : step_inc = 4; remainder = 55; break; case 11 : step_inc = 4; remainder = 17; break; case 12 : step_inc = 3; remainder = 85; break; case 13 : step_inc = 3; remainder = 57; break; case 14 : step_inc = 3; remainder = 33; break; case 15 : step_inc = 3; remainder = 13; break; } return; } /********************************************* DO_RAND *********************************************/ void do_rand(byte ch_no) { if (ch_no > 7) return; fire_flag = 1; fire_ch = ch_no; return; } /*********************************************** EVENTS - Solo in modo stand-alone! ***********************************************/ void events(void) { switch (percent) { case START_DAYBREAK : setup_dimmer(DAYBREAK_STATE); break; case ZONE2_OFF : cbi(PORTA, ZONE2_CH); break; case ZONE3_OFF : cbi(PORTA, ZONE3_CH); break; case START_DAY : setup_dimmer(DAY_STATE); break; case START_SUNSET : setup_dimmer(SUNSET_STATE); break; case ZONE1_ON : sbi(PORTA, ZONE1_CH); break; case START_NIGHT : setup_dimmer(NIGHT_STATE); break; case ZONE2_ON : sbi(PORTA, ZONE2_CH); break; case ZONE3_ON : sbi(PORTA, ZONE3_CH); break; case ZONE1_OFF : cbi(PORTA, ZONE1_CH); break; } return; } /******************************************** WAIT_NEG_EDGE ********************************************/ void wait_neg_edge(void) { byte count; for (count = 0; count < PMAX; ) { if (bit_is_clear(PINC, MAINS)) count++; else count = 0; } /* loop_until_bit_is_clear(PINC, MAINS); */ return; } /******************************************** WAIT_POS_EDGE ********************************************/ void wait_pos_edge(void) { byte count; for (count = 0; count < PMAX; ) { if (bit_is_set(PINC, MAINS)) count++; else count = 0; } /* loop_until_bit_is_set(PINC, MAINS); */ return; } /******************************************** DIMMER_HANDLER ********************************************/ void dimmer_handler(void) { /* modo asservito */ if (bit_is_clear(PINB, MASTER)) check_for_command(); /* modo non asservito = master */ else { dimmer_ticks++; if (dimmer_ticks >= scale) { dimmer_ticks = 0; steps++; if (steps >= 20) { steps = 0; percent++; if (percent == 100) percent = 0; events(); } } } update_dimmer(); /* valido per ambedue i modi */ return; } /******************************************** TRIGGER ********************************************/ void trigger(void) { if (dimmer_status != DIMMER_OFF) { end_cycle = OFF; OCR1A = compare_a; OCR1B = compare_b; TCNT1 = oc_start; /* __outw(compare_a, OCR1AL); __outw(compare_b, OCR1BL); __outw(oc_start, TCNT1L); */ START_TIMER; /* start timer */ } return; } /******************************************** SCHEDULER ********************************************/ void scheduler(void) { if (dimmer_status != DIMMER_OFF) { while (end_cycle == OFF) { if (end_cycle == ON) break; } } STOP_TIMER; end_cycle = OFF; dimmer_handler(); /* MASTER MODE */ if (bit_is_set(PINB, MASTER)) get_scale(); return; } /******************************************** IS_ODD_PARITY ********************************************/ byte is_odd_parity(byte value) { byte temp = 0; byte i; for (i = 0; i < 8; i++) { if (value & 0x01) temp++; value = value >> 1; } return(temp % 2); } /******************************************** RAND8 ********************************************/ void rand8(void) { byte parity, temp; if (fire_flag) { if (random == 0) random = ~random; temp = random & 0xb8; parity = is_odd_parity(temp); if (random & 0x01) { temp = PINA; temp = temp | BV(fire_ch); PORTA = temp; } else { temp = PINA; temp = temp & ~(BV(fire_ch)); PORTA = temp, PORTA; } random = random >> 1; if (parity) random = random | 0x80; return; } return; } /******************************************** MAIN ********************************************/ int main(void) { byte edge; init(); #ifdef EMUL ENABLE_TINT_EMU; /* abililita tim1_ovfl, oc1a, oc1b */ #else ENABLE_TIM_INT; #endif sei(); wait_neg_edge(); /* Prima sincronizzazione */ edge = NEG_EDGE; /* loop principale */ while(1) { get_keys(); if (edge == POS_EDGE) { wait_neg_edge(); edge = NEG_EDGE; sbi(PORTC, LED); /* spegni il led */ } else { wait_pos_edge(); edge = POS_EDGE; cbi(PORTC, LED); /* accendi il led */ } trigger(); /* start il timer1 se dimmer_status e' ON */ scheduler(); /* */ rand8(); /* gestione del generatore pseudorandom */ } }