collapse

* Posts Recentes

O que é isto ? por SerraCabo
[12 de Abril de 2024, 14:20]


Amplificador - Rockboard HA 1 In-Ear por almamater
[11 de Abril de 2024, 20:46]


Emulador NES em ESP32 por dropes
[10 de Abril de 2024, 15:30]


Meu novo robô por josecarlos
[29 de Março de 2024, 18:30]


Bateria - Portátil por almamater
[25 de Março de 2024, 22:14]


Escolher Osciloscópio por jm_araujo
[06 de Fevereiro de 2024, 23:07]


TP4056 - Dúvida por dropes
[31 de Janeiro de 2024, 14:13]


Leitura de dados por Porta Serie por jm_araujo
[22 de Janeiro de 2024, 14:00]


Distancia Cabo por jm_araujo
[08 de Janeiro de 2024, 16:30]


Laser Engraver - Alguém tem? por almamater
[16 de Dezembro de 2023, 14:23]

Autor Tópico: VU meter com Atmega328p e lcd16x2  (Lida 44500 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
VU meter com Atmega328p e lcd16x2
« em: 04 de Novembro de 2010, 00:40 »
  Boa noite a todos, não sabia bem onde colocar este tópico, mas aqui vai, no principio do ano lectivo como tinha um pouco mais de tempo livre e como acho graça a coisas que reajam a musica, decidi fazer um pequeno VU meter usando um comum Lcd 16x2 e um pequeno circuito para converter o sinal de áudio que varia no máximo entre -1,4v e 1,4v se não estou em erro, para uma variação entre 0-5v para ler numa entrada analógica do atmega(neste caso é um arduino, mas sem usar o IDE do arduino), por enquanto está algo simples e provavelmente até um pouco difícil de se perceber o código pois está tudo separado em funções, mas isso é para permitir no futuro acrescentar mais umas quantas funções, uma delas será um spectrum analyzer aplicando um FFT ao sinal de audio, mas tudo a seu tempo.
  Então, para começar temos o circuito que trata o sinal, como devem ter visto á uns tempos andava á procura de amplificadores operacionais(op-amps em inglês abreviado) para fazer um pequeno filtro/conversor, acabei por ficar com uma colecção de samples de variados modelos que irão ser úteis para outras brincadeiras e actualmente estou a usar um TLV2372 da Texas Instruments, configurado como um rectificador de precisão com um ganho de 10x, isto quer dizer que as partes do sinal que são negativas passam a positivas e multiplica o sinal 10 vezes para que tire o máximo partido da gama do ADC do atmega que são de 0-5v, esta foi a parte difícil do projecto, seguidamente usei uma biblioteca para interagir com o lcd, estando ela disponível aqui:
http://www.jump.to/fleury

  Por fim foi uma questão de ler os dois canais do conversor analógico-digital, fazer umas contas para converter os valores de 0-1023 que o adc gera para um valor entre 0 e 75, este valor pode parecer estranho, mas estou a usar o LCD na vertical, então como cada carácter tem um tamanho de 8x5 pixeis, ficamos com 5 linhas por carácter, como o lcd tem 16 colunas, mas a coluna inicial tem uma letra a dizer L e R logo sobram 15 colunas e 5*15=75. A leitura dos dados, calculos e escrita no lcd são todos realizados numa interrupção gerada pelo timer1, que gera interrupções a cerca de 15Hz (15 vezes por segundo) e o resultado até é relativamente fluido, mas na minha opinião ainda não está perfeito/como quero, e um dos objectivos deste pequeno projecto é aprender um pouco mais sobre condicionamento de sinais analógicos, por isso quando tiver tempo(lá para Dezembro, com sorte) queria experimentar mais alguns tipos diferentes de filtros e a sua influencia assim como implementar mais algumas funções como já referi acima.
  Para além de extras em software tambem tenciono adicionar controlo do contraste e da backlight do lcd via software usado PWM, assim como adicionar uns botões para poder alterar essas definições e para mudar de modo, adicionar uns caracteres diferentes para não serem umas simples barras, coisitas assim.
  O código penso que está relativamente bem comentado, mas se quiserem usar para os vossos projectos (em principio passar isto para arduino é muito simples, é meter tudo quanto é inits no setup, as declarações das funções e os includes ficam acima do setup no sitio normal deles, as funções abaixo do loop e no loop não é preciso meter nada) e tiverem duvidas digam.

Código: [Seleccione]
/*

****************************************************************************
** 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 "lcd.h"

/*
** Defines usados no programa
*/

#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 MyChars[] = { //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
};

uint8_t i=0; //Variavel de iterações
volatile uint8_t j=0; //variavel de iterações (só para a ISR)
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;
uint32_t mapped1 = 0; //Variavel para guardar o valor de adc_var_1*map
uint8_t sector1 = 0; //Coluna do lcd
uint8_t sectorRest1 = 0;
uint32_t mapped2 = 0; //Variavel para guardar o valor de adc_var_2*map
uint8_t sector2 = 0; //Coluna do lcd
uint8_t sectorRest2 = 0;
volatile uint8_t lcd_linha1[16];
volatile uint8_t lcd_linha2[16];

/*
***********************************************************************
** 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 timer1_init(void); //Inicialização do Timer1

/*
***********************************************************************
** 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)
vu_mode_init();
timer1_init();
sei(); //Inicia as interrupções

while(1){
//Loop infinito
//Em branco pois é tudo feito na interrupção
}

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){

vu_mode();
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; }

}

/*
***********************************************************************
** 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(&MyChars[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;

}

/*
***********************************************************************
** 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 = 1000; //Para gerar interrupções a ??Hz 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
}


Umas fotos do circuito, daqui a uns dias já passo o circuito para o Eagle para se perceber melhor:

Visão geral


Em pormenor

E aqui fica o esquema do filtro que estou actualmente a utilizar, os diodos devem ser schottky's para diminiur as perdas, mas estou a usar uns simples 1N4148, era o que havia na universidade, e funcionam bem.


E um filme do VU meter em funcionamento, a qualidade não é do melhor(apesar de o ficheiro original ser perfeitamente visível ainda não descobri qual a magia a fazer para o youtube não me comer a qualidade e o frame rate dos filmes), mas o filme não consegue retratar de todo o movimento das barras que é mais rápido e fluido que no filme, não muito, mas é.


EDIT: Esquema adicionado.
« Última modificação: 04 de Novembro de 2010, 13:47 por senso »
Avr fanboy

Offline ivitro

  • Mini Robot
  • *
  • Mensagens: 451
Re: VU meter com Atmega328p e lcd16x2
« Responder #1 em: 04 de Novembro de 2010, 09:45 »
Tá muito fixe senso, este trabalho que tiveste pode ser muito util noutros projectos ;) lembro me de ver algo parecido naquele fulano que fez o CB do carro a mostrar muitas coisas..


hmm so um pergunta. que esquema usas-te no condicionamento de sinal?  somador amplificador certo??

Offline Sérgio_Sena

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 1.649
    • Electronic Gear for Musicians
Re: VU meter com Atmega328p e lcd16x2
« Responder #2 em: 04 de Novembro de 2010, 10:33 »
very good ! :)

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: VU meter com Atmega328p e lcd16x2
« Responder #3 em: 04 de Novembro de 2010, 13:47 »
Já adicionei o esqema ;)
Avr fanboy

Offline ivitro

  • Mini Robot
  • *
  • Mensagens: 451
Re: VU meter com Atmega328p e lcd16x2
« Responder #4 em: 04 de Novembro de 2010, 13:51 »
Thx..


Ali os diodos é so para teres tensão positiva? não percebi muito bem :S

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: VU meter com Atmega328p e lcd16x2
« Responder #5 em: 04 de Novembro de 2010, 13:54 »
O sinal de audio normal, varia mais ou menos(consoante o volume) entre -1,4v e +1,4v, como a tensão negativa não é lida pelo adc do arduino, e até porque tensão negativa destroi o adc, os diodos estão a funcionar como uma ponte retificadora, e depois o sinal é amplificado 10 vezes ou seja, se entra um sinal de 0,5v sai um sinal de 5v, que é para aproveitar os 10bits do adc e não ter um sinal muito pequeno por assim dizer que nem se iam notar diferenças depois de convertido no adc.
Avr fanboy

Offline ivitro

  • Mini Robot
  • *
  • Mensagens: 451
Re: VU meter com Atmega328p e lcd16x2
« Responder #6 em: 04 de Novembro de 2010, 22:53 »
hmm ok então era o que pensava mais ou menos,  não sabia era que a tenção negativa dava cabo do adc.


se tivesses feito um amplificador somador não era melhor? tipo a tensão negativa agora ia tar positiva e na mesma entre 0 e 5, a menos que não de -1,4V a 0V seja indiferente.
« Última modificação: 04 de Novembro de 2010, 22:55 por ivitro »

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: VU meter com Atmega328p e lcd16x2
« Responder #7 em: 12 de Janeiro de 2011, 01:33 »
Finalmente!!!
Já tenho a parte do spectrum analyser a bombar!
A correr um FFT de 64 pontos e a actualizar o lcd a cada 10-15hz está super suave, a camera é que não capta bem o movimento.
Preciso de aumentar o ganho do meu filtro que o sinal está meio atenuado demais para o FFT.
Vou limpar o código e melhorar os comentários e mais logo ou amanhã coloco aqui junto com um filme.
E como a ideia é aprender talvez faça um pequeno circuito para juntar os dois sinais esquerdo e direito num sinal mono, pois o fft é só de um dos canais.
« Última modificação: 12 de Janeiro de 2011, 01:38 por senso »
Avr fanboy

Offline metRo_

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 3.753
Re: VU meter com Atmega328p e lcd16x2
« Responder #8 em: 12 de Janeiro de 2011, 09:22 »
Finalmente!!!
Já tenho a parte do spectrum analyser a bombar!
A correr um FFT de 64 pontos e a actualizar o lcd a cada 10-15hz está super suave, a camera é que não capta bem o movimento.
Preciso de aumentar o ganho do meu filtro que o sinal está meio atenuado demais para o FFT.
Vou limpar o código e melhorar os comentários e mais logo ou amanhã coloco aqui junto com um filme.
E como a ideia é aprender talvez faça um pequeno circuito para juntar os dois sinais esquerdo e direito num sinal mono, pois o fft é só de um dos canais.

Teres feito o FFT é muito bom pois é util na análise de montes de sinais.

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: VU meter com Atmega328p e lcd16x2
« Responder #9 em: 12 de Janeiro de 2011, 21:56 »
Efectivamente eu não fiz o FFT, todo o crédito do FFT vai para o elm-chan e o seu fantástico FFT feito em assembly, corre super rápido com 64 pontos, e mesmo com mais pontos não se queixa, mas como só tenho um pequeno lcd de 16 caracteres já estou a deitar fora metade dos pontos do FFT.
Quanto ao uso real de um FFT num avr, não é assim tanto quanto isso, porque para ter 10 bits de precisão estou a correr o adc com um clock de 125Khz, isto quer dizer que tenho uma frequência de conversão de aproximadamente 9,6Khz penso que como tenho um op-amp á entrada do adc que podia mandar um overclock no adc para os 250Khz, ai já teria uma amostragem a pouco mais de 19Khz, praticamente toda a banda audio, mas para mostrar tal coisa precisava de um lcd gráfico, e infelizmente o meu não dá sinais de vida.
Ainda tenho de implementar uma espécie de milis com um timer livre e ver quanto tempo demora a captura e execução do fft, mas deve ser 1ms ou menos.

Então aqui fica o código, no fim vou anexar um ficheiro com todo o projecto para AvrStudio pois estou a incluir vários ficheiros que já tem algumas modificações feitas para funcionar tudo certinho.

O código não é tão grande quanto parece, tem muitos comentários e penso que está simples de se ler e bem indentado.
Quanto a futuros extras, adicionar uns quantos botões, criar um mini menu para mudar entre FFT e VU meter, e poder controlar o contraste e a backlight do lcd via pwm, depois, bem dêem ideias de que mais posso adicionar a este mini projecto.


Código: [Seleccione]
/*
*****************************************************************************
** 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
}
« Última modificação: 14 de Janeiro de 2011, 02:01 por senso »
Avr fanboy

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: VU meter com Atmega328p e lcd16x2
« Responder #10 em: 14 de Janeiro de 2011, 02:01 »
E aqui está o video, mas mais uma vez ficou lento, entre 3-6x mais lento que na realidade :(
Avr fanboy

Offline ricardo-reis

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 1.338
Re: VU meter com Atmega328p e lcd16x2
« Responder #11 em: 14 de Janeiro de 2011, 03:53 »
gosto muito disto.. parabéns pelo projecto..

Offline metRo_

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 3.753
Re: VU meter com Atmega328p e lcd16x2
« Responder #12 em: 14 de Janeiro de 2011, 09:37 »
Está muito bom, tenho que analisar essa biblioteca ffft, sabes se só dá para o atmega328p ou se ele fez para vários?!

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: VU meter com Atmega328p e lcd16x2
« Responder #13 em: 14 de Janeiro de 2011, 16:53 »
Sim, dá, é o que estou a usar.
Avr fanboy

Offline metRo_

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 3.753
Re: VU meter com Atmega328p e lcd16x2
« Responder #14 em: 14 de Janeiro de 2011, 17:04 »
Sim, dá, é o que estou a usar.

A minha pergunta é se eles fez para outros micros?