/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. NMRA ACCESSORY DCC Decoder for ATMEL AtTiny45. (C) 2007, Heiko Schroeter Samples are taken every 21us interupt driven by timer0. Achtung: Auch in Makefile anpassen ! */ #define ATTiny44 /* F_CPU declared here because it is needed for delay.h */ #define F_CPU 8000000 #include #include #include #include #include #include #include #include "attiny44_accessory_pin_06.h" void do_pins(); /****************** INTERRUPT CAPSULATION *************************************/ void prmbl () { if(samples >= 0) { numbits = 0; // No Zero allowed in PREAMBLE } else { if(numbits >= NumPrmbl) { state++; // Advance if we have enough 'ones' } numbits++; } } void wtlo () { if(samples >= 0) // Advance if ZERO received state++; } void tstlo () { if(samples < 0) // A ONE signals end of DCC command { if(config & (1<= 0) // check if second half = first half, otherwise error { if(bitrec & (1<<0)) state=0xFF; // Reset state=0 if not equal } else { if(!(bitrec & (1<<0))) state=0xFF; // Reset state=0 if not equal } state++; } void bit12 () { if(samples >= 0) { CLC(); // Clear Carry -> ZERO received } else { SEC(); // Set Carry -> ONE received } ROL(bitrec); // Move received BIT into data container state++; // Advance state } void wtxlo () { // We have to semaphore end of DCC Command, because of Interrupt driven reception. // Jump to decode has to happen in main loop. if(numbytes > MaxNumBytes - 1) { numbytes = MaxNumBytes - 1; } dcc_data[numbytes] = bitrec; // Save the received byte numbytes++; // Count the bytes received state++; // Advance state if(samples < 0) // If a DCC One here, than End of DCC packet { config |= (1< analyze DCC stream, else keep on sampling. if((SREG & _BV(SREG_T) && (!(config & (1< 285 times // 30ms -> 857 times // Save Level for next round. Don't change ! asm volatile ("bld %0,%1" :: "r" (config), "i" (LastLevel)); // Increment the DCC stream samples samples++; } } /****************** END INTERRUPT CAPSULATION *********************************/ /****************** NO INT HERE ***********************************************/ void acknowledge(void) { // Acknowledge SET_ACKMASK; _delay_ms(50); CLR_ACKMASK; } /* RP 9.21 Extended Accessory Decoder Control Packet Format The Extended Accessory Decoder Control Packet is included for the purpose of transmitting aspect control to signal decoders or data bytes to more complex accessory decoders. Each signal head can display one aspect at a time. {preamble} 0 10AAAAAA 0 0AAA0AA1 0 000XXXXX 0 EEEEEEEE 1 XXXXX is for a single head. A value of 00000 for XXXXX indicates the absolute stop aspect. All other aspects represented by the values for XXXXX are determined by the signaling system used and the prototype being modeled. {preamble} 0 10AAAAAA 0 0AAA0AA1 0 000XXXXX 0 EEEEEEEE 1 43210 Bit4: On(1)/Off(0) Bit3-0: Number of Function */ /* Extended Decoder Control Packet address for operations mode programming 10AAAAAA 0 0AAA0AA1 Please note that the use of 0 in bit 3 of byte 2 is to ensure that this packet cannot be confused with the legacy accessory-programming packets. The resulting packet would be: {preamble} 10AAAAAA 0 0AAA0AA1 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1 Signal Decoder Address (Configuration Variable Access Instruction) Error Byte 11Bit-Address as following AAA AAAAAA AA ||| Bit0-5 in Byte1 Bit1&2 in Byte2 ||| ||| |||_ ||__\ Complement and store in Byte2 Bit4-6 |___/ Complementing and Bit fiddling is done in Service Mode. Therefore we can check Byte1 and Byte2 directly during transmission. */ void decode_packet_acc() { // 11Bit extended Accessory Decoder unsigned char temp; unsigned char temp1; switch(dcc_data[2] & 0xE0) { case 0: temp = dcc_data[2]; if(((temp & 0xF) <= NumIoPins) && (temp & (1<<4))) // {preamble} 0 10AAAAAA 0 0AAA0AA1 0 000XXXXX 0 EEEEEEEE 1 CVs_ram[(temp & 0xF) + PioConfig] |= (1 << PORTSTATUS); // Semaphore On else CVs_ram[(temp & 0xF)] &= ~(1 << PORTSTATUS); // Semaphore Off config &= ~((1<> 2) { /* The defined values for Instruction type (CC) are: CC=00 Reserved for future use CC=01 Verify byte CC=11 Write byte CC=10 Bit manipulation */ case 0: break; case 1: // Verify temp = cv2sram_offset(dcc_data[3] + 1); //??????????????????? if(!(dcc_data[4] = eeprom_read_byte(&temp))) acknowledge(); break; case 2: break; case 3: // Write temp = cv2sram_offset(dcc_data[3] + 1); if(temp == 1) // CV1 --> Bits 2-7 as 0-5 in CV1, Bits0&1 as 1&2 in CV9 { temp = (dcc_data[4]>>2) & (1<<7) & ~(1<<6); temp1 = ((((dcc_data[4] & 0x3) << 1) & ~(1<<3)) | (1<<0)); eeprom_write_byte(&temp,1); eeprom_write_byte(&temp1,9); } else if(temp == 9) // CV9 --> 3MSB complemented in Bits 4-6 { temp = cv2sram(9); temp = eeprom_read_byte(&temp) & 0xF0; temp1 = (~(dcc_data[4] >> 4)) & 0xF0; temp = temp | temp1; eeprom_write_byte(&temp,9); } else { eeprom_write_byte(&dcc_data[4],temp); } acknowledge(); break; default: break; } break; default: break; } } void make_func(unsigned char mask,unsigned char wert) { // mask = Func Wert vom DCC Kommando // wert = Func Nummer im CV des PortPins while(wert > 0) { mask >>= 1; wert--; } do_pins(); if(mask & (1<<0)) { (CVs_ram[stati + PioConfig]) |= (1<= lowlimit) && (temp2 <= uplimit)) { do_pins(); temp2 -= lowlimit; // Align func value to be shiftable make_func(temp1,temp2); } stati += 8; } while(stati < num_cvs); } void susi(void) { PORTB = ~((1< CVs_ram[0] 00010 --> CVs_ram[1] 00100 --> CVs_ram[2] 01000 --> CVs_ram[3] 10000 --> CVs_ram[4] F1-F3,FL -- 100DDDDD F1=0,F2=1,F3=2,F3=3,F4=4,FL=5 */ do_pins(); lowlimit=1; uplimit=5; cycle_func(lowlimit,uplimit); break; case Funktion2: case Funktion22: /* This instruction has the format 101SDDDD S=0 -> F5-F8 S=1 -> F9-F12 F5=6,F6=7,F7=8,F8=9, F9=10,F10=11,F11=12,F12=13 F5=6,F6=7,F7=8,F8=9, F9=A ,F10=B ,F11=C, F12=D 0110 1110 1000 1001 1010 1011 1100 1101 */ do_pins(); if(!(dcc_data[1] & (1<<4))) // F5-F8 { lowlimit = 6; uplimit = 9; } else // F9-F12 { lowlimit = 10; uplimit = 13; } cycle_func(lowlimit,uplimit); break; case CVAccessFunc: /* {preamble} 0 [ AAAAAAAA ] 0 {instruction-bytes} 0 EEEEEEEE 1 instruction bytes: Byte 1 Byte 2 Byte 3 1110CCAA 0 AAAAAAAA 0 DDDDDDDD The defined values for Instruction type (CC) are: CC=00 Reserved for future use CC=01 Verify byte CC=11 Write byte CC=10 Bit manipulation */ do_pins(); if((dcc_data[1] & 0x0C) == 4) // Verify { if(dcc_data[3] == CVs_ram[cv2sram_offset(dcc_data[2] + 1)]) { acknowledge(); } } if(config & (1<= 112) && (dcc_data[0] < 128) && (numbytes == 4)) // Service Mode { do_pins(); servicemode(0); // Call Servicemode return; } if(cv2sram(29) & (1<<7)) // Multi Function or Accesseroy Mode ? { // Accessory if((((dcc_data[0] == 0b10111111) && ((dcc_data[1] & 0xF0) == 0x80)) || // 11bit Accessory Broadcast (dcc_data[0] == cv2sram(1) && dcc_data[1] == cv2sram(9))) && // Compare Address 128-191 extended Accessory Decoder (dcc_data[0] & (1<<7))) // MSB = 1 { do_pins(); decode_packet_acc(); return; } } else { // Multi Function if(((cv2sram(29) & (1<<5)) && (dcc_data[0] & 0xC0)) && (dcc_data[0] == cv2sram(17)) && (dcc_data[1] == cv2sram(18))) // 14 Bit { do_pins(); dcc_data[0] = dcc_data[1]; dcc_data[1] = dcc_data[2]; dcc_data[2] = dcc_data[3]; dcc_data[3] = dcc_data[4]; goto multi1; } else if ( (!(dcc_data[0] & (1<<7))) && (!(cv2sram(29) & (1<<5))) && (dcc_data[0] == cv2sram(1))) // 7 Bit { goto multi1; } if(dcc_data[0] == 0) // Broadcast { multi1: do_pins(); decode_packet_multi(); return; } } } } //--------------------------------------------------------------------------------------------------------- #ifdef ATTiny44 void turnoff_porta(mask) unsigned char mask; { PORTA &= ~mask; } void turnon_porta(mask) unsigned char mask; { PORTA |= mask; } #endif void turnoff_portb(mask) unsigned char mask; { PORTB &= ~mask; } void turnon_portb(mask) unsigned char mask; { PORTB |= mask; } void turnon(mask) unsigned char mask; { #ifdef ATTiny44 if(porter == 1) { turnon_porta(mask); } else { turnon_portb(mask); } #endif #ifdef ATTiny45 turnon_portb(mask); #endif } void turnoff(mask) unsigned char mask; { #ifdef ATTiny44 if(porter == 1) { turnoff_porta(mask); } else { turnoff_portb(mask); } #endif #ifdef ATTiny45 turnoff_portb(mask); #endif } unsigned char DoDim(void) { if(!(CVs_ram[step_io + PioConfig] & (1<= DIM_ON_TIME) { DIM_DT_COUNTER = 0; return 0; } } else { if(DIM_DT_COUNTER >= DIM_OFF_TIME) { DIM_DT_COUNTER = 0; return 1; } } return 0; } void toggle(mask) unsigned char mask; { #ifdef ATTiny44 if(porter == 1) { if(PORTA & mask) { turnoff_porta(mask); CVs_ram[step_io + PioConfig] &= ~(1<<3); } else { CVs_ram[step_io + PioConfig] |= (1<<3); turnon_porta(mask); } } else { if(PORTB & mask) { turnoff_portb(mask); CVs_ram[step_io + PioConfig] &= ~(1<<3); } else { CVs_ram[step_io + PioConfig] |= (1<<3); turnon_portb(mask); } } #endif #ifdef ATTiny45 if(PORTB & mask) { turnoff_portb(mask); } else { turnon_portb(mask); } #endif } unsigned char CheckDir() { unsigned char mask = CVs_ram[step_io + PioConfig]; if(mask & (1<> 4) & 0x0F; switch(CVs_ram[step_io + PioConfig] & 0x7) { case 0: // Simple On/Off wird in DECODE gesetzt if(temp & (1<= cv2sram(NEON_ON_CV)) { NEON_COUNT = 0; NEON_STATE++; // next state 2 or 4 TURNOFF; } NEON_COUNT++; } NEON_DELAY++; break; case 2: case 4: case 6: if(NEON_DELAY == 0) { if(NEON_COUNT >= cv2sram(NEON_OFF_CV)) { NEON_COUNT = 0; NEON_STATE++; // next state 3 TURNON; } NEON_COUNT++; } NEON_DELAY++; break; default: if ((DoDim() != 0) && (CheckDir() != 0)) TURNON; else TURNOFF; break; } break; case 4: // Blink. Count von 64 ist ca. 1Sek on, 1Sek off. also 2Sek tutto. if(!(temp & (1< Simple On/Off // 1 -> On/Off according to DIR // 2 -> Dimming // 3 -> Neon // 4 -> Blink // Durch PORTB und PORTA steppen if(porterPin == 0) { porterPin = 1; } do_pins(); // Call the work routine; Maske in porterPin porterPin = porterPin << 1; work_io_port_counter++; step_io += num_block_cvs; if(porter == SET_PORTA) { if(work_io_port_counter > 7) // NumPinPortA) { porter = SET_PORTB; // PORTB porterPin = 0; work_io_port_counter = 0; } } if(porter == SET_PORTB) { if(work_io_port_counter >= 4) // NumPinPortB) { work_io_port_counter = 0; // start all over again porterPin = 0; porter = SET_PORTA; // PORTA step_io = num_base_cvs; } } // if(step_io >= num_cvs) step_io = num_base_cvs; } /*********************************************************************************/ /* DCCIN is sampled every 21us via TIMER0 Overflow Int. The DCC stream analysation is within this INT. A semaphore (Register config => GoDecode) is raised when a complete DCC packet is received. The Program jumps to the decoding routines. */ int main (void) { unsigned char temp; eeprom_read_block(&CVs_ram, &CVs_defaults, num_cvs); // Copy our setting to SRAM #ifdef ATTiny45 DDRB = IO_DIR_Mask_PORTB; // Set Directions on Port B #endif #ifdef ATTiny44 DDRB = 0x0F; // Set Directions on Port B DDRA = IO_DIR_Mask_PORTA; // Set Directions on Port A #endif // Set TIMER0 for 21us sampling TCCR0A = (1<= DCC_TIME_LIMIT || config & (1<