/*************************************************************************** * 16ch RGB 8bit PWM controller * * * * Copyright (C) 2006 - 20011 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 /* * 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 * PB1 / PD5(OC1A) -> ROW3 * PB2 / PD4(OC1B) -> ROW2 * PB3(OC0) / PD6 -> ROW1 * PD0 -> RXD * PD1 -> TXD * PD2 -> /RX_TX * PD3 -> /LED */ #define TARGET_HW 1 #define ASM_IRQS 1 #if (TARGET_HW) #define F_CPU 8000000 #else #define F_CPU 7372800 #endif #include #define ROW1 PORTB3 #define ROW2 PORTB2 #define ROW3 PORTB1 #define ROW4 PORTB0 #define RXTX PORTD2 #define LED PORTD3 //#define BAUDRATE 115200 #if (!TARGET_HW) && (BAUDRATE) #define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1) static int uart_putchar(char c, FILE *stream) { if (c == '\n') { loop_until_bit_is_set(UCSRA, UDRE); UDR = '\r'; } loop_until_bit_is_set(UCSRA, UDRE); UDR = c; return 0; } static FILE uart = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); #endif /* (!TARGET_HW) && (BAUDRATE) */ static uint8_t valueR[16]; static uint8_t valueG[16]; static uint8_t valueB[16]; /* keep color in r4 */ register uint8_t nextColor asm("r4"); /* keep pointer in r2:r3 */ register uint8_t * pCurrentStep asm("r2"); /* 16 +1 * 4 values (portA, portC, OCR0, flags) per color */ static uint8_t data[17 * 4 * 3]; /* offsets in data array */ #define RED_OFFSET (17 * 4 * 0) #define GREEN_OFFSET (17 * 4 * 1) #define BLUE_OFFSET (17 * 4 * 2) #if ASM_IRQS void __attribute__ ((naked)) SIG_OVERFLOW0 (void) { 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( "clr r24 \n\t" "out %0, r24 \n\t" /* PORTA = 0x00; */ "out %1, r24 \n\t" /* PORTC = 0x00; */ : : "I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(PORTC)) ); asm volatile( /* switch color, assign pCurrentStep = data + RED/GREEN/BLUE_OFFSET */ "mov r24, %0 \n\t" "inc %0 \n\t" /* nextColor++ */ "cpi r24, 1 \n\t" "brlo L_red%= \n\t" /* if (nextColor < 1) -> red */ "breq L_green%= \n\t" /* if (nextColor == 1) -> green */ "clr %0 \n\t" /* else set nextColor = 0, add BLUE_OFFSET */ #if (TARGET_HW) "ldi r24, 8 \n\t" "out %5, r24 \n\t" #endif /* (TARGET_HW) */ "ldi r24, %4 \n\t" "rjmp L_add%= \n\t" "L_red%=: \n\t" /* red: add RED_OFFSET (do nothing) */ #if (TARGET_HW) "ldi r24, 2 \n\t" "out %5, r24 \n\t" #endif /* (TARGET_HW) */ "rjmp L_skip%= \n\t" "L_green%=: \n\t" /* green: add GREEN_OFFSET */ #if (TARGET_HW) "ldi r24, 1 \n\t" "out %5, r24 \n\t" #endif /* (TARGET_HW) */ "ldi r24, %3 \n\t" "L_add%=: \n\t" "add r30, r24 \n\t" "ldi r24, 0 \n\t" "adc r31, r24 \n\t" "L_skip%=: \n\t" "movw %2, r30 \n\t" : : "r" (nextColor), "z" (data), "r" (pCurrentStep), "M" (GREEN_OFFSET), "M" (BLUE_OFFSET), "I" (_SFR_IO_ADDR(PORTB)) : "r24" ); asm volatile( /* load table values */ "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 %4, r30 \n\t" /* check if IRQ must be enabled */ "or r24, r24 \n\t" /* if (*pCurrentStep++) { */ "breq L_skip%= \n\t" "ldi r24, %5 \n\t" /* TIFR = (1<> 8) & 0xFF); /* PORTC */ /* previous step needs timervalue and enable IRQ */ *(pData++ -4) = min_value -0; /* OCR0 */ /* FIXME: -1 ? */ *(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 */ } #if (!TARGET_HW) && (BAUDRATE) void print_values(uint8_t *data) { uint8_t i; for (i = 0; i < 17; i++) { fprintf(&uart, "%2d: %02X %02X %02X %02X\n", i, data[(i<<2)], data[(i<<2) +1], data[(i<<2) +2], data[(i<<2) +3]); if (data[(i<<2) +3] == 0x00) break; } } #endif /* (!TARGET_HW) && (BAUDRATE) */ int main(void) { /* 16 PWM Outputs */ PORTA = 0x00; DDRA = 0xFF; #if (TARGET_HW) PORTB = 0x00; DDRB = (1<>8) & 0xFF; UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF); /* enable usart with 8n1 */ UCSRB = (1< 0) { uint8_t tmp = (valueR[i] >> 5); valueR[i] -= (tmp > 0) ? tmp : 1; } if (valueG[i] > 0) { uint8_t tmp = (valueG[i] >> 5); valueG[i] -= (tmp > 0) ? tmp : 1; } if (valueB[i] > 0) { uint8_t tmp = (valueB[i] >> 5); valueB[i] -= (tmp > 0) ? tmp : 1; } } #else valueR[i] = color[0]; valueG[i] = color[1]; valueB[i] = color[2]; #endif } } return 0; }