LCD VU meter and FFT spectrum analyser


/*
*****************************************************************************
**   LCD VU meter and FFT spectrum analyser                           *
**   Using Peter Fleury lcd lib and el-chan fft engine for avr            *
**   Made for Atmega328p/Arduino Duemilanove                           *
**   Tiago Angelo   12/01/2011                                    *
**   V0.6                                                   *
**                                                         *
*****************************************************************************

****************************************************************************
**
**   Pinos do lcd - 16x2
**   1   2   3   4   5   6   7   8   9   10    11   12   13   14    15   16
**   Gnd   Vcc   Ctr   RS   RW   En   D0   D1   D2   D3    D4     D5   D6   D7    An     Cat
**PB         4   5                   0     1    2    3
**PD               7
**
**   Ctr - Contrast
**   An - Anode(+)
**   Cat - Cathode(-)
****************************************************************************
*/

#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <math.h>
#include <inttypes.h>
#include <avr/pgmspace.h>

#include "lcd.h"
#include "ffft.h"

/*
** Defines usados no programa
*/
#define NUM_SAMPLES 64      //Samples usadas para calcular o FFT
#define FFT_SIZE (64/2)      //Numero de valores devolvidos pelo FFT

#define FULL 0xFF   //Caracter "cheio", consultar datasheet para perceber
#define BLANK 0xFE   //Caracter em branco

/*
***********************************************************************
** Constantes globais usadas no programa
***********************************************************************
*/

static const PROGMEM unsigned char vuChars[] = {   //Dados na flash que não são precisos na Ram para nada
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,   // 1 linha
   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,   // 2 linhas
   0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,   // 3 linhas
   0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,   // 4 linhas
   0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00,   //simbolo L
   0x00, 0x00, 0x1F, 0x05, 0x0D, 0x12, 0x00, 0x00,   //Simbolo R
   };
static const PROGMEM unsigned char fftChars[] = {   //Dados na flash que não são precisos na Ram para nada
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,   //1 coluna
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F,   //2 coluna
   0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F,   //3 coluna
   0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F,   //4 coluna
   0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,   //5 coluna
   0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,   //6 coluna
   0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,   //7 coluna
   0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,   //7 coluna
   };

uint8_t i,k;                  //Variaveis de iterações
uint8_t sector2 = 0;            //Numero de colunas para o vu meter, linha 2
uint8_t sectorRest2 = 0;         //Numero de colunas para o vu meter, linha 2
uint8_t sector1 = 0;            //Numero de colunas para o vu meter, linha 1
uint8_t sectorRest1 = 0;         //Numero de colunas para o vu meter, linha 1
uint8_t count = 0;
volatile uint8_t j=0;            //variavel de iterações (só para a ISR)
volatile uint8_t lcd_linha1[16];   //Dados da linha 1 do lcd
volatile uint8_t lcd_linha2[16];   //Dados da linha 2 do lcd
uint16_t newReading1 = 0;         //Variavel para guardar o valor lido pelo ADC
uint16_t newReading2 = 0;         //Variavel para guardar o valor lido pelo ADC
uint16_t lastReading1 = 0;
uint16_t lastReading2 = 0;
uint16_t adcVal= 0;               //Usado para guardar o valor lido pelo adc no modo fft
uint32_t mapped1 = 0;            //Variavel para guardar o valor de adc_var_1*map
uint32_t mapped2 = 0;            //Variavel para guardar o valor de adc_var_2*map

//Estas 3 são especificas para o FFT
int16_t capture[FFT_N];         //Buffer de captura
complex_t bfly_buff[FFT_N];      //Buffer do FFT
uint16_t spectrum[(FFT_N/2)];   //Buffer de saida do FFT


/*
***********************************************************************
** Declarações dos protótipos das funções
***********************************************************************
*/

int adc_read(char channel);      //Função usada para ler um canal arbitrário do ADC
void adc_init(void);         //Função para inicializar o ADC
void vu_mode(void);
void vu_mode_init(void);      //Inicialização do modo VU meter
void fft_mode_init(void);
void fft_mode(void);
void timer1_init(void);         //Inicialização do Timer1
void lcd_test(void);

/*
***********************************************************************
** Inicio do main
***********************************************************************
*/

int main(void){

   adc_init();
   lcd_init(LCD_DISP_ON);         //Inicializa o LCD, sem cursor visivel
   lcd_clrscr();               //Limpa o lcd e coloca o cursor em (0,0)
   fft_mode_init();            //Inicialização do modo fft
   //vu_mode_init();            //Inicialização do modo vu meter
   timer1_init();               //Inicialização/configuração do timer para gerar as interrupções
   sei();                     //Inicia as interrupções

   while(1){                  //Loop infinito
     
      //vu_mode();            //Modo vu meter
      fft_mode();               //Modo fft
      //lcd_test();            //Modo de teste do lcd
      }

   return 1;
}

/*
***********************************************************************
** ISR
** Corre todo o vu_mode e faz o refresh do display.
** Todo o trabalho é feito por interrupção, deixando o CPU livre
** entre interrupções.
** Actualiza as duas linhas na totalidade.
***********************************************************************
*/

ISR(TIMER1_COMPA_vect){

   lcd_gotoxy(0,0);
   for(j=0; j<16; j++){
      lcd_putc(lcd_linha1[j]); }

   lcd_gotoxy(0,1);
   for(j=0; j<16; j++){
      lcd_putc(lcd_linha2[j]); }
   }

/*
***********************************************************************
**                     Funções usadas
***********************************************************************
*/

/*
***********************************************************************
** Inicializa o ADC no modo 10bits a 125Khz
***********************************************************************
*/

void adc_init(void){

   ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));   //16Mhz/128 = 125Khz
   ADMUX |= (1<<REFS0);                     //Referencia de 5v, com condensador no pino Aref
   ADCSRA |= (1<<ADEN);                     //Adc ligada
   ADCSRA |= (1<<ADSC);                     //Fazer uma primeira conversão para iniciar o circuito e porque é a mais lenta
}

/*
***********************************************************************
** Passa-se o canal a ler e devolve um valor de 10bits do ADC
***********************************************************************
*/

int adc_read(char channel){

   ADMUX &= 0xF0;                  //Limpa o canal anterior
   ADMUX |= channel;               //Define o novo canal a ler do ADC
   ADCSRA |= (1<<ADSC);            //Inicia uma nova conversão
   while(ADCSRA & (1<<ADSC));         //Espera que a conversão seja feita
   return ADCW;                  //Retorna o valor do ADC, em modo 10 bits
}

/*
***********************************************************************
** Le o canal 0 e 1 do adc, faz uma detecção de pico e depois mapeia
** o valor 0..1023 do adc para 0..75 barras no display 16x2
***********************************************************************
*/

void vu_mode(void){
   
   newReading1 = adc_read(0);
   newReading2 = adc_read(1);
   
   if(newReading1 > lastReading1){
       lastReading1 = newReading1; }
   else{
      lastReading1 = (lastReading1*3 + newReading1)/4; }   //Decaimento "suave"

   mapped1 = ((lastReading1 * 75)/1024);                  //Pega nos 0..1023 e devolve 0..75
   sector1 = mapped1/5;                              //Segmentos FULL na linha 0
   sectorRest1 = mapped1 % 5;                            //Segmento final da linha 0

   if(newReading2 > lastReading2){
       lastReading2 = newReading2; }
   else{
      lastReading2 = (lastReading2*3 + newReading2)/4; }   //Decaimento "suave"

   mapped2 = ((lastReading2 * 75)/1024);                  //Pega nos 0..1023 e devolve 0..75
   sector2 = mapped2/5;                              //Segmentos FULL na linha 1
   sectorRest2 = mapped2 % 5;                            //Segmento final da linha 1
   

   //Linha 0
   for(i=0; i<(sector1); i++){
      lcd_linha1[i+1] = FULL; }
   if(sectorRest1>=1){
      lcd_linha1[i+1] = ((sectorRest1-1)); }
   for(i=(sector1 + 1);i<15; i++){
      lcd_linha1[i+1] = BLANK; }

   //Linha 1
   for(i=0; i<(sector2); i++){
      lcd_linha2[i+1] = FULL; }
   if(sectorRest2>=1){
      lcd_linha2[i+1] = ((sectorRest2-1)); }
   for(i=(sector2 + 1);i<15; i++){
      lcd_linha2[i+1] = BLANK; }

}

/*
***********************************************************************
** Le o canal 0 do adc, ao subtrair 512 á sample de 1023 bits cria um
** sinal positivo ou negativo centrado em 0, é preciso para o fft
** usando o FFT feito pelo elm-chan calcula um FFT de 64 pontos
** e preenche as duas linhas do lcd com barras
***********************************************************************
*/

void fft_mode(void){
   count = 0;
   adc_read(0);
   cli();
   while(count != NUM_SAMPLES){
      ADCSRA |= (1<<ADSC);
      while((ADCSRA & (1<<ADSC))){};
      adcVal = ADCW;
      capture[count] = ((int16_t)(adcVal)-512);
      count++;
      }
   sei();

   fft_input(capture,bfly_buff);
   fft_execute(bfly_buff);
   fft_output(bfly_buff,spectrum);
   
   k=0;
   for(i=1; i<17; i++){
      sector1 = spectrum[i]/16;

   if(sector1>7){
      lcd_linha2[k]=FULL;
      lcd_linha1[k]=(sector1-8);
      }
   else{
      lcd_linha2[k]=sector1;
      lcd_linha1[k]=BLANK;
      }

      k++;

   }
}

/*
***********************************************************************
** Função de teste usada para afinar o gerador de barras verticais
***********************************************************************
*/

void lcd_test(void){

   for(i=0; i<16; i++){
   
   sector1=i;

   if(sector1>7){
      lcd_linha2[i]=FULL;
      lcd_linha1[i]=(sector1-8);
      }
   else{
      lcd_linha2[i]=sector1;
      lcd_linha1[i]=BLANK;
      }

   }


}

/*
***********************************************************************
** Carrega da flash os caracteres especiais para fazer as barras e as
** letras L e R na CGRAM do display
***********************************************************************
*/

void vu_mode_init(void){

   lcd_command(_BV(LCD_CGRAM));                  //Coloca CG RAM no endereço 0
   for(i=0; i<48; i++){
      lcd_data(pgm_read_byte_near(&vuChars[i])); }   //Lê os dados da flash e carrega na Ram do LCD

   lcd_gotoxy(0,0);   //Linha 0 coluna 0
   lcd_putc(4);      //Escreve L na esquerda
   lcd_gotoxy(0,1);   //Linha 1 coluna 0
   lcd_putc(5);      //Escreve R na direita
   lcd_linha1[0]=4;
   lcd_linha2[0]=5;
}

/*
***********************************************************************
** Carrega da flash os caracteres especiais para fazer as barras e as
** na CGRAM do display
***********************************************************************
*/

void fft_mode_init(void){
   
   lcd_command(_BV(LCD_CGRAM));                  //Coloca CG RAM no endereço 0
   for(i=0; i<64; i++){
      lcd_data(pgm_read_byte_near(&fftChars[i]));   }   //Lê os dados da flash e carrega na Ram do LCD

   lcd_clrscr();
}

/*
***********************************************************************
** Inicializa o timer1(16 bits) no modo CTC com prescaller de 1024
***********************************************************************
*/

void timer1_init(void){

   TCCR1B |= (1 << WGM12);         // Configure timer 1 for CTC mode
   OCR1A = 1100;               //Para gerar interrupções a 14Hz para o refresh do display, valor obtido experimentalmente
   TIMSK1 |= (1 << OCIE1A);       // Enable CTC interrupt
   TCCR1B |= ((1<<CS12)|(1<<CS10));//Inicia timer 1 com clock div de 1024
}



Wyszukiwarka

Podobne podstrony:
Measuring power system harmincs and interharmonics by envelope spectrum analysis
Cherry Orchard, A Doll's House, and Galileo General Analys
Personality and divorce A genetic analysis
Duplex ultrasound scanning of the carotid arteries with velocity spectrum analysis
Death of a Salesman and The Price Analysis of Ideals
Affective inestability as rapid cycling Borderline personality and bipolar spectrum disorders Bipola
Cavanaugh and Wood, A Baraminological Analysis
Breman And Subrahmanyam Investment Analysis And Price Formation In Securities Markets
Infrared And Raman Spectroscopy (Dean s Analitical Chemistry Handbook)
Cultural Studies, Critical Theory and Critical Discourse Analysis Terry Threadgold (Cardiff)
With Microscope and Tweezers An Analysis of the Internet Virus of November 1988
Tom Swift and His Spectromarine Jim Lawrence
Appleton, Victor II Tom Swift Jr 015 Tom Swift and His Spectromarine Selector Jim Lawrence UC
Duplex ultrasound scanning of the carotid arteries with velocity spectrum analysis
Personality and divorce A genetic analysis
Lincoln, Religion, Empire, and the Spectre of Orientalism
Analysis of soil fertility and its anomalies using an objective model
In vivo MR spectroscopy in diagnosis and research of

więcej podobnych podstron