Created
July 11, 2013 05:02
-
-
Save ngarbezza/5972642 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Vumetro para Matriz 8x8 de LEDs RGB. | |
Codigo basado en: | |
* Visualizacion de la matriz: http://www.instructables.com/id/64-pixel-RGB-LED-Display-Another-Arduino-Clone/ | |
* Manejo de la entrada de audio: http://neuroelec.com/2011/03/fft-library-for-arduino/ | |
*/ | |
#include <stdint.h> | |
#include <ffft.h> | |
#define IR_AUDIO 0 // Pin entrada de audio | |
#define __spi_clock 13 // Pin de clock | |
#define __spi_latch 10 // Pin de latch | |
#define __spi_data 11 // Pin de salida de datos | |
#define __display_enable 9 // Pin 9 (para controlar encendido/apagado de la matriz completa) | |
#define __rows 8 // Cantidad de filas | |
#define __max_row __rows-1 | |
#define __columns 8 // Cantidad de columnas | |
#define __max_led __columns-1 | |
#define __brightness_levels 32 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! ) | |
#define __max_brightness __brightness_levels-1 | |
#define __fade_delay 4 | |
#define __TIMER1_MAX 0xFFFF // 16 bit CTR | |
#define __TIMER1_CNT 0x0130 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker) | |
#include <avr/interrupt.h> | |
#include <avr/io.h> | |
byte brightness_red[__columns][__rows]; /* memory for RED LEDs */ | |
byte brightness_green[__columns][__rows]; /* memory for GREEN LEDs */ | |
byte brightness_blue[__columns][__rows]; /* memory for BLUE LEDs */ | |
volatile byte position = 0; | |
volatile long zero = 0; | |
int16_t capture[FFT_N]; /* Wave captureing buffer */ | |
complex_t bfly_buff[FFT_N]; /* FFT buffer */ | |
uint16_t spektrum[FFT_N/2]; /* Spectrum output buffer */ | |
float frequencyAdjustValues[] = {0.8,1.5,1.5,1.5,3,2.5,3,4}; | |
void setup(void) { | |
randomSeed(555); | |
pinMode(__spi_clock,OUTPUT); | |
pinMode(__spi_latch,OUTPUT); | |
pinMode(__spi_data,OUTPUT); | |
pinMode(__display_enable,OUTPUT); | |
digitalWrite(__spi_latch,HIGH); | |
digitalWrite(__spi_data,HIGH); | |
digitalWrite(__spi_clock,HIGH); | |
setup_hardware_spi(); | |
delay(10); | |
set_matrix_rgb(0,0,0); /* set the display to BLACK */ | |
setup_timer1_ovf(); /* enable the framebuffer display */ | |
Serial.begin(57600); | |
adcInit(); | |
adcCalb(); | |
} | |
void loop(void) { | |
vumeter(); | |
} | |
void vumeter() { | |
if (position == FFT_N) { | |
fft_input(capture, bfly_buff); // prepara la funcion FFFT | |
fft_execute(bfly_buff); // ejecuta la funcion FFFT | |
fft_output(bfly_buff, spektrum); // guarda los resultados en el array spektrum (64 valores) | |
for (int i = 0; i < __columns; i++) { | |
// como tenemos 64 bandas de frecuencia, y 8 columnas, promediamos de a 8 en cada vuelta | |
float bandAverage = ((float)spektrum[8*i] + (float)spektrum[8*i+1] + (float)spektrum[8*i+2] + (float)spektrum[8*i+3] + (float)spektrum[8*i+4] + (float)spektrum[8*i+5] + (float)spektrum[8*i+6] + (float)spektrum[8*i+7]) / 8; | |
// el promedio de cada banda queda ponderado por unos ajustes para que la escala sea mas adecuada | |
float audioIn = map(bandAverage * frequencyAdjustValues[i], 0, 7, 0, 8); | |
Serial.print(bandAverage); | |
Serial.print("-"); | |
for (int j = 0; j < __rows; j++) { | |
if (j < audioIn) { | |
set_led_rgb(i, j, 255, 0, 0); | |
} else { | |
set_led_rgb(i, j, 0, 0, 5); | |
} | |
} | |
} | |
Serial.println(""); | |
position = 0; | |
} | |
} | |
// Funciones utilitarias para el manejo de LEDs en la matriz | |
void set_led_red(byte row, byte led, byte red) { | |
brightness_red[row][led] = red; | |
} | |
void set_led_green(byte row, byte led, byte green) { | |
brightness_green[row][led] = green; | |
} | |
void set_led_blue(byte row, byte led, byte blue) { | |
brightness_blue[row][led] = blue; | |
} | |
void set_led_rgb(byte row, byte led, byte red, byte green, byte blue) { | |
set_led_red(row,led,red); | |
set_led_green(row,led,green); | |
set_led_blue(row,led,blue); | |
} | |
void set_matrix_rgb(byte red, byte green, byte blue) { | |
byte ctr1; | |
byte ctr2; | |
for(ctr2 = 0; ctr2 <= __max_row; ctr2++) { | |
for(ctr1 = 0; ctr1 <= __max_led; ctr1++) { | |
set_led_rgb(ctr2,ctr1,red,green,blue); | |
} | |
} | |
} | |
void set_row_rgb(byte row, byte red, byte green, byte blue) { | |
byte ctr1; | |
for(ctr1 = 0; ctr1 <= __max_led; ctr1++) { | |
set_led_rgb(row,ctr1,red,green,blue); | |
} | |
} | |
void set_column_rgb(byte column, byte red, byte green, byte blue) { | |
byte ctr1; | |
for(ctr1 = 0; ctr1 <= __max_row; ctr1++) { | |
set_led_rgb(ctr1,column,red,green,blue); | |
} | |
} | |
void set_row_hue(byte row, int hue) { | |
byte ctr1; | |
for(ctr1 = 0; ctr1 <= __max_led; ctr1++) { | |
set_led_hue(row,ctr1,hue); | |
} | |
} | |
void set_column_hue(byte column, int hue) { | |
byte ctr1; | |
for(ctr1 = 0; ctr1 <= __max_row; ctr1++) { | |
set_led_hue(ctr1,column,hue); | |
} | |
} | |
void set_matrix_hue(int hue) { | |
byte ctr1; | |
byte ctr2; | |
for(ctr2 = 0; ctr2 <= __max_row; ctr2++) { | |
for(ctr1 = 0; ctr1 <= __max_led; ctr1++) { | |
set_led_hue(ctr2,ctr1,hue); | |
} | |
} | |
} | |
void set_led_hue(byte row, byte led, int hue) { | |
// see wikipeda: HSV | |
float S=100.0,V=100.0,s=S/100.0,v=V/100.0,h_i,f,p,q,t,R,G,B; | |
hue = hue%360; | |
h_i = hue/60; | |
f = (float)(hue)/60.0 - h_i; | |
p = v*(1-s); | |
q = v*(1-s*f); | |
t = v*(1-s*(1-f)); | |
if ( h_i == 0 ) { | |
R = v; | |
G = t; | |
B = p; | |
} | |
else if ( h_i == 1 ) { | |
R = q; | |
G = v; | |
B = p; | |
} | |
else if ( h_i == 2 ) { | |
R = p; | |
G = v; | |
B = t; | |
} | |
else if ( h_i == 3 ) { | |
R = p; | |
G = q; | |
B = v; | |
} | |
else if ( h_i == 4 ) { | |
R = t; | |
G = p; | |
B = v; | |
} | |
else { | |
R = v; | |
G = p; | |
B = q; | |
} | |
set_led_rgb(row,led,byte(R*(float)(__max_brightness)),byte(G*(float)(__max_brightness)),byte(B*(float)(__max_brightness))); | |
} | |
void set_row_byte_hue(byte row, byte data_byte, int hue) { | |
byte led; | |
for(led = 0; led <= __max_led; led++) { | |
if( (data_byte>>led)&(B00000001) ) { | |
set_led_hue(row,led,hue); | |
} | |
else { | |
set_led_rgb(row,led,0,0,0); | |
} | |
} | |
} | |
// Funciones relacionadas a hardware SPI y Timers | |
void setup_hardware_spi(void) { | |
byte clr; | |
// spi prescaler: | |
// SPI2X SPR1 SPR0 | |
// 0 0 0 fosc/4 | |
// 0 0 1 fosc/16 | |
// 0 1 0 fosc/64 | |
// 0 1 1 fosc/128 | |
// 1 0 0 fosc/2 | |
// 1 0 1 fosc/8 | |
// 1 1 0 fosc/32 | |
// 1 1 1 fosc/64 | |
SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master | |
//SPCR |= ( (1<<SPR1) ); // set prescaler bits | |
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits | |
clr=SPSR; // clear SPI status reg | |
clr=SPDR; // clear SPI data reg | |
SPSR |= (1<<SPI2X); // set prescaler bits | |
//SPSR &= ~(1<<SPI2X); // clear prescaler bits | |
} | |
void setup_timer1_ovf(void) { | |
// Arduino runs at 16 Mhz... | |
// Timer1 (16bit) Settings: | |
// prescaler (frequency divider) values: CS12 CS11 CS10 | |
// 0 0 0 stopped | |
// 0 0 1 /1 | |
// 0 1 0 /8 | |
// 0 1 1 /64 | |
// 1 0 0 /256 | |
// 1 0 1 /1024 | |
// 1 1 0 external clock on T1 pin, falling edge | |
// 1 1 1 external clock on T1 pin, rising edge | |
// | |
TCCR1B &= ~ ( (1<<CS11) ); | |
TCCR1B |= ( (1<<CS12) | (1<<CS10) ); | |
//normal mode | |
TCCR1B &= ~ ( (1<<WGM13) | (1<<WGM12) ); | |
TCCR1A &= ~ ( (1<<WGM11) | (1<<WGM10) ); | |
//Timer1 Overflow Interrupt Enable | |
TIMSK1 |= (1<<TOIE1); | |
TCNT1 = __TIMER1_MAX - __TIMER1_CNT; | |
// enable all interrupts | |
sei(); | |
} | |
ISR(TIMER1_OVF_vect) { /* Framebuffer interrupt routine */ | |
TCNT1 = __TIMER1_MAX - __TIMER1_CNT; | |
byte cycle; | |
digitalWrite(__display_enable,LOW); // enable display inside ISR | |
for(cycle = 0; cycle < __max_brightness; cycle++) { | |
byte led; | |
byte row = B00000000; // row: current source. on when (1) | |
byte red; // current sinker, on when (0) | |
byte green; // current sinker, on when (0) | |
byte blue; // current sinker, on when (0) | |
for(row = 0; row <= __max_row; row++) { | |
red = B11111111; // off | |
green = B11111111; // off | |
blue = B11111111; // off | |
for(led = 0; led <= __max_led; led++) { | |
if(cycle < brightness_red[row][led]) { | |
red &= ~(1<<led); | |
} | |
if(cycle < brightness_green[row][led]) { | |
green &= ~(1<<led); | |
} | |
if(cycle < brightness_blue[row][led]) { | |
blue &= ~(1<<led); | |
} | |
} | |
digitalWrite(__spi_latch,LOW); | |
// aca se controla el orden de los registros de desplazamiento: | |
// para nuestro circuito es: verde -> rojo -> azul -> columnas | |
spi_transfer(B00000001<<row); | |
spi_transfer(blue); | |
spi_transfer(red); | |
spi_transfer(green); | |
digitalWrite(__spi_latch,HIGH); | |
} | |
} | |
digitalWrite(__display_enable,HIGH); // disable display outside ISR | |
} | |
byte spi_transfer(byte data) { | |
SPDR = data; // Start the transmission | |
while (!(SPSR & (1<<SPIF))) {} // Wait the end of the transmission | |
return SPDR; // return the received byte. (we don't need that here) | |
} | |
// Funciones para inicializar FFFT | |
void adcInit(){ | |
/* REFS0 : VCC use as a ref, IR_AUDIO : channel selection, ADEN : ADC Enable, ADSC : ADC Start, ADATE : ADC Auto Trigger Enable, ADIE : ADC Interrupt Enable, ADPS : ADC Prescaler */ | |
// free running ADC mode, f = ( 16MHz / prescaler ) / 13 cycles per conversion | |
ADMUX = _BV(REFS0) | IR_AUDIO; // | _BV(ADLAR); | |
//ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) //prescaler 64 : 19231 Hz - 300Hz per 64 divisions | |
ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // prescaler 128 : 9615 Hz - 150 Hz per 64 divisions, better for most music | |
sei(); | |
} | |
void adcCalb(){ | |
Serial.println("Start to calc zero"); | |
long midl = 0; | |
// get 2 meashurment at 2 sec | |
// on ADC input must be NO SIGNAL!!! | |
for (byte i = 0; i < 2; i++) | |
{ | |
position = 0; | |
delay(100); | |
midl += capture[0]; | |
delay(900); | |
} | |
zero = -midl/2; | |
Serial.println("Done."); | |
} | |
// free running ADC fills capture buffer | |
ISR(ADC_vect) { | |
if (position >= FFT_N) | |
return; | |
capture[position] = ADC + zero; | |
if (capture[position] == -1 || capture[position] == 1) | |
capture[position] = 0; | |
position++; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment