/* -------------------------------------------------------------- Byte Encoding system for ultraluminous v5 This library includes functions to send 8-bit byte encoded brightnesses out to port A on the ultraluminous illuminator v5 for 7 colors. Author: Brian Neltner Copyright: 2008 Changelog: 2008-06-30 Brian Neltner Made RED, ORANGE, GREEN, etc aliases for BENC[n] elements so that users can abstract to other colors, or even pick colors using the random number generator. 2008-06-22 Brian Neltner I got everything working, and put it in this library to aid modularity. Everything seems to work as advertized, although a few compiler directives could help with BENC redefining values, etc. The abstraction barrier at the prescaler will hopefully hold, but it's borderline. For now, in the unlikely event that it is needed, it will have to be changed in this library file. Details: This library implements 7-channel, 8-bit resolution byte encoding as a method for controlling the intensities of each channel. It uses the Timer 3 - Comparator A to time the length of each bit pulse. To avoid timing glitches, only the MSB is timed using an actual interrupt -- all of the LSBs are done by polling the timer count in a while loop. This is necessary because the length of the pulse associated with each bit is actually the desired length plus the interrupt overhead. In other words, if the overhead is 1us and the LSB is supposed to be 5us long, while the 2LSB is 10us long, you will actually see 6us and 11us. So, the 2LSB is not actually quite twice the length of the LSB, resulting in a non-monotonically increasing brightness with desired value. The only clean way around this is to remove the interrupt overhead by using polling. The result of this compromise is that only half of the clock cycles, at a maximum, are available for other computation. However, this compromise also means a clean monotonically increasing brightness, as well as insanely high overall byte encoding cycles -- of order 100kHz easily. This allows all sorts of neat possibilities in the persistance of vision realm. Updating BENC values is done with double buffering, and waits to update until the next BENC cycle begins to avoid glitches as well as put requiBENC[6] computation in the largest timer interrupt gap. Use: All you should need to do to use this is to define: #define F_CPU 8000000UL #define benc_freq 200 which says that the clock speed is 8MHz and to make the overall byte encoding cycle frequency 200 Hz. This library will do the rest of the setup. Call init_timer() from the main function to set up PORTA as an output and configure the timer interrupts. Change brightnesses by updating, respectively: RBLUE = BENC[0] BLUE = BENC[1] CYAN = BENC[2] GREEN = BENC[3] AMBER = BENC[4] ORANGE = BENC[5] RED = BENC[6] with your desired 8-bit brightness. After you make all desired changes to light brightnesses, call update_benc(); to load those values into the output registers on the beginning of the next cycle. For a light with fewer lights, some of the BENC[n] values will not be meaningful and will be outputting to non-existant power modules. -----------------------------------------------------------------*/ #define prescale 1UL // CS1 = 0b010 on timer 1 = 1:8 prescale #define benc_period 0x2000 //F_CPU/(benc_freq*prescale) #define max_interrupt benc_period>>1 #define max_benc_index 7 #include #include void benc_handler(); void init_timer(); void update_benc(void); ISR(TIMER3_COMPA_vect) { //timer1 comparator A interrupt // TCNT3 = 0x0000; // Reset timer. benc_handler(); } volatile uint8_t benc_index; volatile uint8_t update_ready; volatile uint8_t longest_cycle; // BENCn is the actual values to be written to PORTA on index n volatile uint8_t BENC7 = 0b00000000; volatile uint8_t BENC6 = 0b00000000; volatile uint8_t BENC5 = 0b00000000; volatile uint8_t BENC4 = 0b00000000; volatile uint8_t BENC3 = 0b00000000; volatile uint8_t BENC2 = 0b00000000; volatile uint8_t BENC1 = 0b00000000; volatile uint8_t BENC0 = 0b00000000; // After update_benc(), the new BENCn values are stored here until // the start of the next brightness loop volatile uint8_t BENC7_TEMP = 0b00000000; volatile uint8_t BENC6_TEMP = 0b00000000; volatile uint8_t BENC5_TEMP = 0b00000000; volatile uint8_t BENC4_TEMP = 0b00000000; volatile uint8_t BENC3_TEMP = 0b00000000; volatile uint8_t BENC2_TEMP = 0b00000000; volatile uint8_t BENC1_TEMP = 0b00000000; volatile uint8_t BENC0_TEMP = 0b00000000; // These are the desired 8-bit brightnesses for each color. volatile uint8_t BENC[7]; #define RBLUE BENC[6] #define BLUE BENC[5] #define CYAN BENC[4] #define GREEN BENC[3] #define AMBER BENC[2] #define ORANGE BENC[1] #define RED BENC[0] void update_benc(void) { uint16_t brightness; uint8_t i; if(update_ready != 2) { brightness = BENC[6]+BENC[5]+BENC[4]+BENC[3]+BENC[2]+BENC[1]+BENC[0]; if(brightness>512) { BENC[6] = (BENC[6]*512UL)/brightness; BENC[5] = (BENC[5]*512UL)/brightness; BENC[4] = (BENC[4]*512UL)/brightness; BENC[3] = (BENC[3]*512UL)/brightness; BENC[2] = (BENC[2]*512UL)/brightness; BENC[1] = (BENC[1]*512UL)/brightness; BENC[0] = (BENC[0]*512UL)/brightness; } // for(i=0;i<7;i++) { // if(BENC[i]==16) BENC[i]=17; // To make monotonic due to interrupt time. // } BENC7_TEMP = ((BENC[6] & 0b10000000) >> 7) | (((BENC[0] & 0b10000000) >> 7) << 6) | (((BENC[1] & 0b10000000) >> 7) << 5) | (((BENC[2] & 0b10000000) >> 7) << 4) | (((BENC[3] & 0b10000000) >> 7) << 3) | (((BENC[4] & 0b10000000) >> 7) << 2) | (((BENC[5] & 0b10000000) >> 7) << 1); // Because of the timing, BENC7 is special. BENC6_TEMP = ((BENC[6] & 0b01000000) >> 6) | (((BENC[0] & 0b01000000) >> 6) << 6) | (((BENC[1] & 0b01000000) >> 6) << 5) | (((BENC[2] & 0b01000000) >> 6) << 4) | (((BENC[3] & 0b01000000) >> 6) << 3) | (((BENC[4] & 0b01000000) >> 6) << 2) | (((BENC[5] & 0b01000000) >> 6) << 1); BENC5 = ((BENC[6] & 0b00100000) >> 5) | (((BENC[0] & 0b00100000) >> 5) << 6) | (((BENC[1] & 0b00100000) >> 5) << 5) | (((BENC[2] & 0b00100000) >> 5) << 4) | (((BENC[3] & 0b00100000) >> 5) << 3) | (((BENC[4] & 0b00100000) >> 5) << 2) | (((BENC[5] & 0b00100000) >> 5) << 1); BENC4_TEMP = ((BENC[6] & 0b00010000) >> 4) | (((BENC[0] & 0b00010000) >> 4) << 6) | (((BENC[1] & 0b00010000) >> 4) << 5) | (((BENC[2] & 0b00010000) >> 4) << 4) | (((BENC[3] & 0b00010000) >> 4) << 3) | (((BENC[4] & 0b00010000) >> 4) << 2) | (((BENC[5] & 0b00010000) >> 4) << 1); BENC3_TEMP = ((BENC[6] & 0b00001000) >> 3) | (((BENC[0] & 0b00001000) >> 3) << 6) | (((BENC[1] & 0b00001000) >> 3) << 5) | (((BENC[2] & 0b00001000) >> 3) << 4) | (((BENC[3] & 0b00001000) >> 3) << 3) | (((BENC[4] & 0b00001000) >> 3) << 2) | (((BENC[5] & 0b00001000) >> 3) << 1); BENC2_TEMP = ((BENC[6] & 0b00000100) >> 2) | (((BENC[0] & 0b00000100) >> 2) << 6) | (((BENC[1] & 0b00000100) >> 2) << 5) | (((BENC[2] & 0b00000100) >> 2) << 4) | (((BENC[3] & 0b00000100) >> 2) << 3) | (((BENC[4] & 0b00000100) >> 2) << 2) | (((BENC[5] & 0b00000100) >> 2) << 1); BENC1_TEMP = ((BENC[6] & 0b00000010) >> 1) | (((BENC[0] & 0b00000010) >> 1) << 6) | (((BENC[1] & 0b00000010) >> 1) << 5) | (((BENC[2] & 0b00000010) >> 1) << 4) | (((BENC[3] & 0b00000010) >> 1) << 3) | (((BENC[4] & 0b00000010) >> 1) << 2) | (((BENC[5] & 0b00000010) >> 1) << 1); BENC0_TEMP = (BENC[6] & 0b00000001) | ((BENC[0] & 0b00000001) << 6) | ((BENC[1] & 0b00000001) << 5) | ((BENC[2] & 0b00000001) << 4) | ((BENC[3] & 0b00000001) << 3) | ((BENC[4] & 0b00000001) << 2) | ((BENC[5] & 0b00000001) << 1); update_ready = 1; } } void benc_handler() { switch(benc_index) { case 7: // Reordering the bits. PORTA = BENC7; OCR3A = (uint16_t)benc_period*160UL/255; benc_index--; if(update_ready==2) { BENC6=BENC6_TEMP; BENC4=BENC4_TEMP; BENC3=BENC3_TEMP; BENC2=BENC2_TEMP; BENC1=BENC1_TEMP; BENC0=BENC0_TEMP; update_ready=0; } break; case 6: // Take care of all the LSBs with polling. PORTA = BENC6; while(TCNT3<(uint16_t)benc_period*224UL/255);// Wait for next bit. PORTA = BENC0; while(TCNT3<(uint16_t)benc_period*225UL/255);// Wait for next bit. PORTA = BENC1; while(TCNT3<(uint16_t)benc_period*227UL/255);// Wait for next bit. PORTA = BENC2; while(TCNT3<(uint16_t)benc_period*231UL/255);// Wait for next bit. PORTA = BENC3; while(TCNT3<(uint16_t)benc_period*239UL/255);// Wait for next bit. PORTA = BENC4; while(TCNT3<(uint16_t)benc_period*255UL/255);// Wait for next bit. TCNT3=0x0000; PORTA = BENC5; OCR3A = (uint16_t)benc_period*32UL/255; if(update_ready==1) { BENC7=BENC7_TEMP; update_ready=2; } benc_index=7; break; default: break; } } void init_timer() { PORTA=0x00; // Start with all off. DDRA=0xFF; // outputs for lights // Timer setup. benc_index = max_benc_index; longest_cycle = 0; TCCR3A=0x00; // Timer control register A. TCCR3B=0x00 | (0<