/*************************************************************************** * 16ch RGB 8bit PWM controller * * * * Copyright (C) 2011 - 2012 by Olaf Rempel * * razzor AT kopf MINUS tisch DOT de * * * * 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; version 2 of the License, * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include /* * using ATmega32 @8MHz: * Fuse H: 0xD9 (no bootloader, jtag disabled) * Fuse L: 0xD4 (int. 8MHz Osz, fast rising power, no BOD) * * PA0..7 -> COL1..8 * PC0..7 -> COL9..16 * PB0 / PD7(OC2) -> ROW4 (OC2 not used) * PB1 / PD5(OC1A) -> ROW3 (OC1A not used) * PB2 / PD4(OC1B) -> ROW2 (OC1B not used) * PB3(OC0) / PD6 -> ROW1 (OC0 not used) * PD0 -> RXD * PD1 -> TXD * PD2 -> /RX_TX * PD3 -> /LED */ #define F_CPU 8000000 #include #define ROW1 PORTB1 /* RED */ #define ROW2 PORTB0 /* GREEN */ #define ROW3 PORTB3 /* BLUE */ #define ROW4 PORTB2 /* not used */ #define RXTX PORTD2 /* RS485 TX enable */ #define LED PORTD3 /* running without mpmboot? */ #define STANDALONE 0 #if (STANDALONE) #define OSCCAL 0xAA #define BAUDRATE 115200 #define MPM_ADDRESS 0x11 #define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1) #endif /* STANDALONE */ const static uint8_t versioninfo[16] = "rgb16mpm v0.99"; #define CMD_WAIT 0x00 #define CMD_SWITCH_MODE 0x01 #define CMD_GET_VERSION 0x02 // #define CMD_GET_CHIPINFO 0x03 // #define CMD_READ_MEMORY 0x11 // #define CMD_WRITE_MEMORY 0x12 #define CMD_WRITE_COLOR 0x81 #define CMD_WRITE_RAW_COLOR 0x82 #define CMD_READ_RAW_COLOR 0x83 #define CMD_WRITE_CONFIG 0x81 /* 16 values per color */ static uint8_t chan_value[3][16]; /* (16 +1) * 4 values (portA, portC, OCR0, flags) per color */ static uint8_t chan_rawdata[3][17 * 4]; /* used only in softpwm ISRs */ register uint8_t * pCurrentStep asm("r2"); /* r3:r2 */ /* used to sync softpwm ISRs and color_update() */ register uint8_t nextColor asm("r4"); /* r4 */ /* worst case: 9+2+12+8+7+13 = 61 clks -> 7.625us @8MHz */ ISR(TIMER0_OVF_vect, ISR_NAKED) { asm volatile( /* save registers 2+1+2+2+2 = 9 */ "push r24 \n\t" "in r24, __SREG__ \n\t" "push r24 \n\t" "push r30 \n\t" "push r31 \n\t" :: ); asm volatile( /* disable outputs 1+1 = 2 */ "out %0, r1 \n\t" /* PORTA = 0x00; */ "out %1, r1 \n\t" /* PORTC = 0x00; */ :: "I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(PORTC)) ); asm volatile( /* switch color and assign pCurrentStep */ // R G B "mov r24, %0 \n\t" // 1 1 1 "inc %0 \n\t" /* nextColor++ */ // 2 2 2 "cpi r24, 1 \n\t" // 3 3 3 "brlo L_red%= \n\t" /* if (nextColor < 1) -> red */ // 5 4 4 "breq L_green%= \n\t" /* if (nextColor == 1) -> green */ // - 6 5 "clr %0 \n\t" /* nextColor = 0; */ // - - 6 "ldi r24, %7 \n\t" /* PORTB = (1< 4.5us @ 8MHz */ ISR(TIMER0_COMP_vect, ISR_NAKED) { asm volatile( /* save registers 2+1+2+2+2 = 9 */ "push r24 \n\t" "in r24, __SREG__ \n\t" "push r24 \n\t" "push r30 \n\t" "push r31 \n\t" :: ); asm volatile( /* load table values 1+1+1+1+1+1+1+1+1 = 9 */ "movw r30, %0 \n\t" "ld r24, z+ \n\t" "out %1, r24 \n\t" /* PORTA = *pCurrentStep++; */ "ld r24, z+ \n\t" "out %2, r24 \n\t" /* PORTC = *pCurrentStep++; */ "ld r24, z+ \n\t" "out %3, r24 \n\t" /* OCR0 = *pCurrentStep++; */ "ld r24, z+ \n\t" "movw %0, r30 \n\t" :: "r" (pCurrentStep), "I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(PORTC)), "I" (_SFR_IO_ADDR(OCR0)) ); asm volatile( /* check if IRQ must be disabled 1+1+1+1+1 = 3/5 */ "or r24, r24 \n\t" /* if (!(*pCurrentStep++)) { */ "brne L_skip%= \n\t" "in r24, %0 \n\t" /* TIMSK &= ~(1< chan_rawdataX */ static void calculate_timer_values(uint8_t *value, uint8_t *pDataStart) { uint8_t *pData = pDataStart +4; /* skip first entry (init) */ uint8_t index = 0; uint16_t chan_used = 0xFFFF; uint16_t chan_init = 0xFFFF; /* loop until all channels are calculated */ while (chan_used) { uint8_t i; uint8_t min_value = 0xFF; uint16_t chan_tmp = chan_used; uint16_t chan_mask = 0x0001; for (i = 0; i < 16; i++) { /* skip if channel already used */ if (chan_used & chan_mask) { /* channel is not used (value 0x00) */ if (value[i] == 0x00) { chan_init &= (~chan_mask); chan_used &= (~chan_mask); /* found a new lower value */ } else if (value[i] < min_value) { min_value = value[i]; chan_tmp = chan_used & (~chan_mask); /* found another value with the same value */ } else if (value[i] == min_value) { chan_tmp &= (~chan_mask); } } chan_mask <<= 1; } chan_used &= chan_tmp; if (min_value < 0xFF) { /* set new outputs */ *pData++ = (chan_used & 0xFF); /* PORTA */ *pData++ = ((chan_used >> 8) & 0xFF); /* PORTC */ /* previous step needs timervalue and enable IRQ */ *(pData++ -4) = min_value; /* OCR0 */ *(pData++ -4) = 0x01; /* flags */ } index++; } /* fill all remaining slots */ while (index < 16) { /* repeat enabled outputs */ *pData++ = (chan_used & 0xFF); /* PORTA */ *pData++ = ((chan_used >> 8) & 0xFF); /* PORTC */ /* previous step was last one (no timevalue / disable IRQ) */ *(pData++ -4) = 0x00; /* OCR0 */ *(pData++ -4) = 0x00; /* flags */ index++; } /* first slot/init: enable only channels that are > 0 */ pData = pDataStart; *pData++ = (chan_init & 0xFF); /* PORTA */ *pData++ = ((chan_init >> 8) & 0xFF); /* PORTC */ } static uint8_t rx_cmd; static uint16_t rx_bcnt = 0xFF; static uint16_t rx_length; static uint8_t tx_cmd; static uint8_t tx_cause; static uint16_t tx_length; static uint16_t tx_bcnt; ISR(USART_RXC_vect) { uint8_t data = UDR; sei(); if (rx_bcnt == 0xFF) { /* MPM address stored in TWI address register by bootloader */ if (data == TWAR) { UCSRA &= ~(1<> 8); } else if (tx_bcnt == 3) { data = (tx_length & 0xFF); } else if (tx_cmd == CMD_GET_VERSION) { data = versioninfo[pos]; } else { data = 0xAA; } UDR = data; /* re-enable for next round */ UCSRB |= (1<>8) & 0xFF; UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF); /* MPM address stored in TWI address register */ TWAR = MPM_ADDRESS; #endif /* (STANDALONE) */ sei(); uint8_t x = 0; uint8_t xdir = 1; uint16_t ramp = 0; uint8_t step = 0; #if 0 /* create worst case for R+G, relaxed for B */ for (x = 0; x < 16; x++) { chan_value[0][x] = 254 - x; chan_value[1][x] = x +1; chan_value[2][x] = x * 16; } x = 0; #endif while (1) { uint8_t color_update = 0x07; while (color_update) { if ((color_update & 0x01) && (nextColor == 2)) { calculate_timer_values(chan_value[0], chan_rawdata[0]); color_update &= ~(0x01); } else if ((color_update & 0x02) && (nextColor == 0)) { calculate_timer_values(chan_value[1], chan_rawdata[1]); color_update &= ~(0x02); } else if ((color_update & 0x04) && (nextColor == 1)) { calculate_timer_values(chan_value[2], chan_rawdata[2]); color_update &= ~(0x04); } } #if 1 PORTD ^= (1<> 8) { case 6: ramp = 0x0000; /* no break */ case 0: /* red: on, green: ramp up, blue: off */ color[0] = 0xFF; color[1] = ramp & 0xFF; color[2] = 0x00; break; case 1: /* red: ramp down, green: on, blue:off */ color[0] = 0xFF - (ramp & 0xFF); color[1] = 0xFF; color[2] = 0x00; break; case 2: /* red: off, green: on, blue: ramp up */ color[0] = 0x00; color[1] = 0xFF; color[2] = (ramp & 0xFF); break; case 3: /* red: off, green: ramp down: blue: on */ color[0] = 0x00; color[1] = 0xFF - (ramp & 0xFF); color[2] = 0xFF; break; case 4: /* red: ramp up, green: off, blue: on */ color[0] = (ramp & 0xFF); color[1] = 0x00; color[2] = 0xFF; break; case 5: /* red: on, green: off, blue: ramp down */ color[0] = 0xFF; color[1] = 0x00; color[2] = 0xFF - (ramp & 0xFF); break; } uint8_t i, j; for (i = 0; i < 16; i++) { for (j = 0; j < 3; j++) { #if 0 if (x == i) { chan_value[j][i] = color[j]; } else if (chan_value[j][i] > 0) { uint8_t tmp = (chan_value[j][i] >> 5); chan_value[j][i] -= (tmp > 0) ? tmp : 1; } #else chan_value[j][i] = color[j]; #endif } } #endif } }