Skip to content

Instantly share code, notes, and snippets.

@ngarbezza
Created July 11, 2013 05:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ngarbezza/5972642 to your computer and use it in GitHub Desktop.
Save ngarbezza/5972642 to your computer and use it in GitHub Desktop.
/*
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