/** * @defgroup RGB leds DMX512 controller * @type firmware * @file main.c * @author Etienne Meleard * @creation 2015-06-09 * @tabsize 4 * @mcu ATMEGA8L * @fcpu 8MHz */ //============================ WARNING ===============================// //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!// // // // Fuses must be like SUT1 = 1, SUT0 = 1, CKSEL3 = 1, CKSEL2 = 1, // // CKSEL1 = 1 and CKSEL0 = 0 to ensure using the crystal as clock // // (otherwise it uses the 8MHz internal clock), full swing mode is // // mandatory to acheive 8MHz operation. // // // // CKDIV8 fuse must not be set to avoid clock frequency divided by 8 // // // // Fuse set or = 1 means checked in ponyprog // // // //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!// //====================================================================// // ================= Fuses ================= // /*FUSES = { // No success writing fuse this way, command line below ... .low = (FUSE_SUT1 & FUSE_SUT0 & FUSE_CKSEL3), .high = HFUSE_DEFAULT, .extended = EFUSE_DEFAULT, };*/ // Run // avrdude -c usbasp -p m8 -U lfuse:w:0xfe:m -U hfuse:w:0xd1:m -B250 // at first (-F may be needed) // ================ Includes =============== // #include #include #include #include // ================ Pinouts ================ // #define LED Bit(PORTD).bit7 #define RX Bit(PIND).bit0 #define RX_COMPLETE SIG_UART_RECV #define RX_TIMER TCNT0 #define BREAK_LEN 11 #define MAB_LEN 1 #define DIP_0 Bit(PINC).bit3 #define DIP_1 Bit(PINC).bit2 #define DIP_2 Bit(PINC).bit1 #define DIP_3 Bit(PINC).bit0 #define DIP_A Bit(PORTB).bit3 #define DIP_B Bit(PORTB).bit4 #define DIP_C Bit(PORTB).bit5 #define OUT_1 Bit(PORTD).bit1 #define OUT_2 Bit(PORTD).bit2 #define OUT_3 Bit(PORTD).bit5 #define OUT_4 Bit(PORTD).bit6 // ================ Macros ================= // #define BLINK LED=1; _delay_ms(150); LED=0; _delay_ms(150) #define PULSE10 LED=1; _delay_us(10); LED=0; _delay_us(10) #define PULSE5 LED=1; _delay_us(5); LED=0; _delay_us(5) #define PULSE3 LED=1; _delay_us(3); LED=0; _delay_us(3) #define PULSE1 LED=1; _delay_us(1); LED=0; _delay_us(1) #define RX_START UCSRB |= 0x10; receiving = 1 #define RX_STOP UCSRB &= 0xef; receiving = 0 #define DIP_SETTIME 10 // us, allows diodes to set (due to diode capacitance) // Outputs change logic #define TRIGGER_HALFWINDOW 10 #define TRIGGER_POINT 128 #define TRIGGER_HIGH TRIGGER_POINT + TRIGGER_HALFWINDOW #define TRIGGER_LOW TRIGGER_POINT - TRIGGER_HALFWINDOW #define RX_ERROR ((UCSRA & 0x10) || (UCSRA & 0x08)) #define MAX_TIME_WITHOUT_START 10000 #define ADDRESS_REFRESH_PERIOD 5000 // ================ Globals ================ // volatile uint16_t address = 0x0000; volatile uint8_t receiving = 0; volatile uint16_t received = 0; // ================ Functions ============== // void init(void) { // Led and relays as output DDRD = 0xe6; // USART UCSRA = 0x00; // No doubling of frequency UCSRB = 0x80; // Rx disabled, Rx irq enabled, 8bit mode UCSRC = 0x8e; // Async, no parity, 2 stops, 8 bits UBRRL = 1; // 250kbds // Detection timer (timer 0) TCCR0 = 0x03; // 64 prescaler, increment every 8us // Dip inputs DDRC &= 0xf0; PORTC |= 0x0f; // Pull-up // Dip outputs DDRB |= 0x38; DIP_A = 1; DIP_B = 1; DIP_C = 1; } void getAddress(void) { // Takes approx. 40us uint16_t buffer = 0x0000; DIP_A = 1; DIP_B = 1; DIP_C = 1; DIP_C = 0; _delay_us(DIP_SETTIME); if(!DIP_0) buffer += 1; if(!DIP_1) buffer += 2; if(!DIP_2) buffer += 4; if(!DIP_3) buffer += 8; DIP_C = 1; buffer <<= 4; DIP_B = 0; _delay_us(DIP_SETTIME); if(!DIP_0) buffer += 1; if(!DIP_1) buffer += 2; if(!DIP_2) buffer += 4; if(!DIP_3) buffer += 8; DIP_B = 1; buffer <<= 4; DIP_A = 0; _delay_us(DIP_SETTIME); if(!DIP_0) buffer += 1; if(!DIP_1) buffer += 2; if(!DIP_2) buffer += 4; if(!DIP_3) buffer += 8; DIP_A = 1; address = buffer; } uint8_t getFrameStart(void) { uint32_t t = 1500000; // 1.5s while(t && RX) { _delay_us(1); t--; } if(!t) return 0; RX_TIMER = 0; while(!RX && RX_TIMER < BREAK_LEN) _delay_us(1); if(RX_TIMER < BREAK_LEN) return 0; // Error, break too short while(!RX) _delay_us(1); // Wait for MAB RX_TIMER = 0; while(RX && RX_TIMER < MAB_LEN) _delay_us(1); if(RX_TIMER < MAB_LEN) return 0; // Error, break too short received = 0; RX_START; return 1; } // =============== Interrupts ============== // SIGNAL(RX_COMPLETE) { uint8_t data, channel, state; data = UDR; if(!receiving) return; if(UCSRA & 0x10) return; if(UCSRA & 0x08)return; received++; if(received == 1) { //if(data != 0x00) RX_STOP; // Should be 0x00, not a setting packet otherwise return; // Ignore first slot } if(received < address + 2) { return; // Address not reached } if(received > address + 4) { // Received enough packets, stop receiving and wait for next frame RX_STOP; return; } channel = received - address - 2; // Got channel, get current state switch(channel) { case 0: state = OUT_1; break; case 1: state = OUT_2; break; case 2: state = OUT_3; break; case 3: state = OUT_4; break; } // Apply schmitt trigger if(!state && data > TRIGGER_HIGH) state = 1; if(state && data < TRIGGER_LOW) state = 0; // Set output switch(channel) { case 0: OUT_1 = state; break; case 1: OUT_2 = state; break; case 2: OUT_3 = state; break; case 3: OUT_4 = state; break; } } // ================== Main ================= // int main(void) { uint16_t time_without_start = 0; uint16_t address_is_valid = 0; cli(); // WatchDog disabled AT ALL TIMES ! init(); sei(); BLINK; BLINK; while(1) { if(!address_is_valid) { getAddress(); address_is_valid = ADDRESS_REFRESH_PERIOD; } if(getFrameStart()) { LED = 0; time_without_start = 0; } if(time_without_start > MAX_TIME_WITHOUT_START) { if(receiving) RX_STOP; BLINK; } time_without_start++; if(address_is_valid) address_is_valid--; _delay_us(100); } return 0; }