|
|
@ -1,7 +1,7 @@ |
|
|
|
/*************************************************************************** |
|
|
|
* 16ch RGB 8bit PWM controller * |
|
|
|
* * |
|
|
|
* Copyright (C) 2006 - 20011 by Olaf Rempel * |
|
|
|
* 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 * |
|
|
@ -20,6 +20,7 @@ |
|
|
|
***************************************************************************/ |
|
|
|
#include <avr/io.h> |
|
|
|
#include <avr/interrupt.h> |
|
|
|
#include <avr/wdt.h> |
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
|
|
@ -30,10 +31,10 @@ |
|
|
|
* |
|
|
|
* PA0..7 -> COL1..8 |
|
|
|
* PC0..7 -> COL9..16 |
|
|
|
* PB0 / PD7(OC2) -> ROW4 |
|
|
|
* PB1 / PD5(OC1A) -> ROW3 |
|
|
|
* PB2 / PD4(OC1B) -> ROW2 |
|
|
|
* PB3(OC0) / PD6 -> ROW1 |
|
|
|
* 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 |
|
|
@ -41,7 +42,6 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
#define F_CPU 8000000 |
|
|
|
|
|
|
|
#include <util/delay.h> |
|
|
|
|
|
|
|
#define ROW1 PORTB1 /* RED */ |
|
|
@ -49,43 +49,47 @@ |
|
|
|
#define ROW3 PORTB3 /* BLUE */ |
|
|
|
#define ROW4 PORTB2 /* not used */ |
|
|
|
|
|
|
|
#define RXTX PORTD2 |
|
|
|
#define RXTX PORTD2 /* RS485 TX enable */ |
|
|
|
#define LED PORTD3 |
|
|
|
|
|
|
|
//#define BAUDRATE 115200 |
|
|
|
/* running without mpmboot? */ |
|
|
|
#define STANDALONE 0 |
|
|
|
|
|
|
|
#if (BAUDRATE) |
|
|
|
#define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1) |
|
|
|
#if (STANDALONE) |
|
|
|
#define OSCCAL 0xAA |
|
|
|
#define BAUDRATE 115200 |
|
|
|
#define MPM_ADDRESS 0x11 |
|
|
|
|
|
|
|
static int uart_putchar(char c, FILE *stream) |
|
|
|
{ |
|
|
|
if (c == '\n') { |
|
|
|
loop_until_bit_is_set(UCSRA, UDRE); |
|
|
|
UDR = '\r'; |
|
|
|
} |
|
|
|
#define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1) |
|
|
|
#endif /* STANDALONE */ |
|
|
|
|
|
|
|
loop_until_bit_is_set(UCSRA, UDRE); |
|
|
|
UDR = c; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
const static uint8_t versioninfo[16] = "rgb16mpm v0.99"; |
|
|
|
|
|
|
|
static FILE uart = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); |
|
|
|
#endif /* (BAUDRATE) */ |
|
|
|
#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 */ |
|
|
|
/* (16 +1) * 4 values (portA, portC, OCR0, flags) per color */ |
|
|
|
static uint8_t chan_rawdata[3][17 * 4]; |
|
|
|
|
|
|
|
/* used only in ISR */ |
|
|
|
/* used only in softpwm ISRs */ |
|
|
|
register uint8_t * pCurrentStep asm("r2"); /* r3:r2 */ |
|
|
|
|
|
|
|
/* used to sync ISR and color_update() */ |
|
|
|
/* 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 */ |
|
|
|
void __attribute__ ((naked)) SIG_OVERFLOW0 (void) |
|
|
|
ISR(TIMER0_OVF_vect, ISR_NAKED) |
|
|
|
{ |
|
|
|
asm volatile( |
|
|
|
/* save registers 2+1+2+2+2 = 9 */ |
|
|
@ -106,30 +110,29 @@ void __attribute__ ((naked)) SIG_OVERFLOW0 (void) |
|
|
|
); |
|
|
|
|
|
|
|
asm volatile( |
|
|
|
/* switch color and assign pCurrentStep */ |
|
|
|
/* 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, %8 \n\t" /* PORTB = (1<<ROW4); */ // - - 7 |
|
|
|
"ldi r30, lo8(%4) \n\t" /* pCurrentStep = &rawdata[2]; */ // - - 8 |
|
|
|
"ldi r31, hi8(%4) \n\t" // - - 9 |
|
|
|
"ldi r24, %7 \n\t" /* PORTB = (1<<ROW4); */ // - - 7 |
|
|
|
"ldi r30, lo8(%3) \n\t" /* pCurrentStep = &rawdata[2]; */ // - - 8 |
|
|
|
"ldi r31, hi8(%3) \n\t" // - - 9 |
|
|
|
"rjmp L_end%= \n\t" // - - 11 |
|
|
|
"L_red%=: \n\t" /* red: */ |
|
|
|
"ldi r24, %6 \n\t" /* PORTB = (1<<ROW1); */ // 6 - - |
|
|
|
"ldi r30, lo8(%2) \n\t" /* pCurrentStep = &rawdata[0]; */ // 7 - - |
|
|
|
"ldi r31, hi8(%2) \n\t" // 8 - - |
|
|
|
"ldi r24, %5 \n\t" /* PORTB = (1<<ROW1); */ // 6 - - |
|
|
|
"ldi r30, lo8(%1) \n\t" /* pCurrentStep = &rawdata[0]; */ // 7 - - |
|
|
|
"ldi r31, hi8(%1) \n\t" // 8 - - |
|
|
|
"rjmp L_end%= \n\t" // 10 - - |
|
|
|
"L_green%=: \n\t" /* green: */ |
|
|
|
"ldi r24, %7 \n\t" /* PORTB = (1<<ROW2); */ // - 7 - |
|
|
|
"ldi r30, lo8(%3) \n\t" /* pCurrentStep = &rawdata[1]; */ // - 8 - |
|
|
|
"ldi r31, hi8(%3) \n\t" // - 9 - |
|
|
|
"ldi r24, %6 \n\t" /* PORTB = (1<<ROW2); */ // - 7 - |
|
|
|
"ldi r30, lo8(%2) \n\t" /* pCurrentStep = &rawdata[1]; */ // - 8 - |
|
|
|
"ldi r31, hi8(%2) \n\t" // - 9 - |
|
|
|
"L_end%=: \n\t" |
|
|
|
"out %5, r24 \n\t" /* set PORTB */ // 11 10 12 |
|
|
|
"out %4, r24 \n\t" /* set PORTB */ // 11 10 12 |
|
|
|
:: "r" (nextColor), |
|
|
|
"r" (pCurrentStep), |
|
|
|
"i" (&chan_rawdata[0]), /* RED */ |
|
|
|
"i" (&chan_rawdata[1]), /* GREEN */ |
|
|
|
"i" (&chan_rawdata[2]), /* BLUE */ |
|
|
@ -184,7 +187,7 @@ void __attribute__ ((naked)) SIG_OVERFLOW0 (void) |
|
|
|
} |
|
|
|
|
|
|
|
/* worst case: 9+9+5+13 = 36 clks -> 4.5us @ 8MHz */ |
|
|
|
void __attribute__ ((naked)) SIG_OUTPUT_COMPARE0 (void) |
|
|
|
ISR(TIMER0_COMP_vect, ISR_NAKED) |
|
|
|
{ |
|
|
|
asm volatile( |
|
|
|
/* save registers 2+1+2+2+2 = 9 */ |
|
|
@ -237,7 +240,7 @@ void __attribute__ ((naked)) SIG_OUTPUT_COMPARE0 (void) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* calc chan_valueX => chan_rawdataX */ |
|
|
|
static void calculate_timer_values(uint8_t *value, uint8_t *pDataStart) |
|
|
|
{ |
|
|
|
uint8_t *pData = pDataStart +4; /* skip first entry (init) */ |
|
|
@ -285,7 +288,7 @@ static void calculate_timer_values(uint8_t *value, uint8_t *pDataStart) |
|
|
|
*pData++ = ((chan_used >> 8) & 0xFF); /* PORTC */ |
|
|
|
|
|
|
|
/* previous step needs timervalue and enable IRQ */ |
|
|
|
*(pData++ -4) = min_value -0; /* OCR0 */ /* FIXME: -1 ? */ |
|
|
|
*(pData++ -4) = min_value; /* OCR0 */ |
|
|
|
*(pData++ -4) = 0x01; /* flags */ |
|
|
|
} |
|
|
|
|
|
|
@ -311,55 +314,149 @@ static void calculate_timer_values(uint8_t *value, uint8_t *pDataStart) |
|
|
|
*pData++ = ((chan_init >> 8) & 0xFF); /* PORTC */ |
|
|
|
} |
|
|
|
|
|
|
|
#if (BAUDRATE) |
|
|
|
void print_values(uint8_t *data) |
|
|
|
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 i; |
|
|
|
uint8_t data = UDR; |
|
|
|
sei(); |
|
|
|
|
|
|
|
if (rx_bcnt == 0xFF) { |
|
|
|
/* MPM address stored in TWI address register by bootloader */ |
|
|
|
if (data == TWAR) { |
|
|
|
UCSRA &= ~(1<<MPCM); |
|
|
|
rx_bcnt = 0; |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
if (rx_bcnt == 0) { |
|
|
|
rx_cmd = data; |
|
|
|
|
|
|
|
} else if ((rx_bcnt == 1) || (rx_bcnt == 2)) { |
|
|
|
rx_length = (rx_length << 8) | data; |
|
|
|
|
|
|
|
} else if ((rx_bcnt -3) < rx_length) { |
|
|
|
// TODO: get data |
|
|
|
} |
|
|
|
|
|
|
|
if ((rx_bcnt -2) == rx_length) { |
|
|
|
/* enable RS485 TX */ |
|
|
|
PORTD |= (1<<RXTX); |
|
|
|
|
|
|
|
/* first byte */ |
|
|
|
tx_cmd = rx_cmd; |
|
|
|
UDR = rx_cmd; |
|
|
|
|
|
|
|
/* prepare header */ |
|
|
|
tx_bcnt = 1; |
|
|
|
tx_cause = 0; |
|
|
|
tx_length = 0; |
|
|
|
|
|
|
|
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 (tx_cmd == CMD_GET_VERSION) { |
|
|
|
tx_length = sizeof(versioninfo); |
|
|
|
} |
|
|
|
|
|
|
|
if (data[(i<<2) +3] == 0x00) |
|
|
|
break; |
|
|
|
/* enable interrupt */ |
|
|
|
UCSRB |= (1<<UDRIE); |
|
|
|
} |
|
|
|
rx_bcnt++; |
|
|
|
} |
|
|
|
} |
|
|
|
#else |
|
|
|
#define print_values(data) |
|
|
|
#endif /* (BAUDRATE) */ |
|
|
|
|
|
|
|
ISR(USART_UDRE_vect) |
|
|
|
{ |
|
|
|
/* enable IRQs again, but prevent multiple UDRE IRQs */ |
|
|
|
UCSRB &= ~(1<<UDRIE); |
|
|
|
sei(); |
|
|
|
|
|
|
|
if ((tx_bcnt < 4) || (tx_bcnt -4) < tx_length) { |
|
|
|
uint16_t pos = (tx_bcnt -4); |
|
|
|
uint8_t data = 0xFF; |
|
|
|
|
|
|
|
if (tx_bcnt == 1) { |
|
|
|
data = tx_cause; |
|
|
|
|
|
|
|
} else if (tx_bcnt == 2) { |
|
|
|
data = (tx_length >> 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<<UDRIE); |
|
|
|
} |
|
|
|
|
|
|
|
tx_bcnt++; |
|
|
|
} |
|
|
|
|
|
|
|
ISR(USART_TXC_vect, ISR_NOBLOCK) |
|
|
|
{ |
|
|
|
/* disable RS485 TX */ |
|
|
|
PORTD &= ~(1<<RXTX); |
|
|
|
|
|
|
|
/* enable MP mode again */ |
|
|
|
UCSRA |= (1<<MPCM); |
|
|
|
rx_bcnt = 0xFF; // FIXME: cli? |
|
|
|
|
|
|
|
if (tx_cmd == CMD_SWITCH_MODE) { // FIXME: check mode |
|
|
|
wdt_enable(WDTO_15MS); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int main(void) __attribute__ ((noreturn)); |
|
|
|
int main(void) |
|
|
|
{ |
|
|
|
/* 16 PWM Outputs */ |
|
|
|
PORTA = 0x00; |
|
|
|
DDRA = 0xFF; |
|
|
|
|
|
|
|
PORTB = 0x00; |
|
|
|
DDRB = (1<<ROW1) | (1<<ROW2) | (1<<ROW3) | (1<<ROW4); |
|
|
|
|
|
|
|
PORTC = 0x00; |
|
|
|
DDRC = 0xFF; |
|
|
|
|
|
|
|
PORTD = (1<<RXTX) | (1<<LED); |
|
|
|
/* color ROWs */ |
|
|
|
PORTB = 0x00; |
|
|
|
DDRB = (1<<ROW1) | (1<<ROW2) | (1<<ROW3) | (1<<ROW4); |
|
|
|
|
|
|
|
PORTD = (1<<LED); |
|
|
|
DDRD = (1<<RXTX) | (1<<LED); |
|
|
|
|
|
|
|
#if (STANDALONE) |
|
|
|
OSCCAL = OSCCAL_VALUE; |
|
|
|
#endif /* (STANDALONE) */ |
|
|
|
|
|
|
|
/* timer0, FCPU/64, overflow interrupt */ |
|
|
|
TCCR0 = (1<<CS01) | (1<<CS00); /* FCPU/64 */ |
|
|
|
TIMSK = (1<<TOIE0); |
|
|
|
TCNT0 = 0x00; |
|
|
|
|
|
|
|
#if (BAUDRATE) |
|
|
|
/* Set baud rate */ |
|
|
|
/* USART config */ |
|
|
|
/* Multi Drop Mode, 9n1 */ |
|
|
|
UCSRA = (1<<MPCM); |
|
|
|
UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<TXCIE) | (1<<UCSZ2); |
|
|
|
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0); |
|
|
|
#if (STANDALONE) |
|
|
|
UBRRH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF; |
|
|
|
UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF); |
|
|
|
|
|
|
|
/* enable usart with 8n1 */ |
|
|
|
UCSRB = (1<<TXEN) | (1<<RXEN); |
|
|
|
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0); |
|
|
|
|
|
|
|
fprintf(&uart, "good morning dave\n"); |
|
|
|
#endif /* (BAUDRATE) */ |
|
|
|
/* MPM address stored in TWI address register */ |
|
|
|
TWAR = MPM_ADDRESS; |
|
|
|
#endif /* (STANDALONE) */ |
|
|
|
|
|
|
|
sei(); |
|
|
|
|
|
|
@ -368,29 +465,37 @@ int main(void) |
|
|
|
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); |
|
|
|
print_values(chan_rawdata[0]); |
|
|
|
|
|
|
|
} else if ((color_update & 0x02) && (nextColor == 0)) { |
|
|
|
calculate_timer_values(chan_value[1], chan_rawdata[1]); |
|
|
|
color_update &= ~(0x02); |
|
|
|
print_values(chan_rawdata[1]); |
|
|
|
|
|
|
|
} else if ((color_update & 0x04) && (nextColor == 1)) { |
|
|
|
calculate_timer_values(chan_value[2], chan_rawdata[2]); |
|
|
|
color_update &= ~(0x04); |
|
|
|
print_values(chan_rawdata[2]); |
|
|
|
} |
|
|
|
} |
|
|
|
#if 1 |
|
|
|
|
|
|
|
PORTD ^= (1<<LED); |
|
|
|
|
|
|
|
_delay_ms(100); |
|
|
|
// _delay_ms(100); |
|
|
|
|
|
|
|
step++; |
|
|
|
if (step == 16) { |
|
|
@ -473,7 +578,6 @@ int main(void) |
|
|
|
#endif |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |