Вы находитесь на странице: 1из 17

Universidade Estadual de Campinas - UNICAMP

EA076 - Laboratório de Sistemas Embarcados


Turma M - 1s/2018 | Grupo A (placa 28)
Pedro Bassi (RA 157007)
Gabriela Shima (RA 135819)

Projeto Final - Analisador de Frequências e Volume

Introdução

O projeto final consiste de um analisador em frequências e volume de som. No projeto, para


uma dada uma entrada de áudio ele mostra respectivamente o seu espectro de frequências no LCD
e seu volume determina cores diferentes para os leds RGB.

Para isso utilizamos uma placa KL25Z e os seguintes módulos: display LCD, teclado
matricial, o conversor A/D com a entrada de áudio P2 (macho, a ser ligada no computador ou celular)
e LEDs RGB.
Na seção de metodologia analisaremos separadamente todos módulos usados e como foram
implementados. Na seção conclusão falaremos sobre o resultado do projeto e teremos um link para o
vídeo com este resultado (mas que também foi enviado em anexo com este relatório).

Por fim, temos as referências utilizadas para a elaboração deste projeto e, no final do
relatório, o código main.c utilizado.

Tela LCD

A tela LCD Nokia 5110 ​foi utilizada para apresentar informações do programa, imprimindo o
caractere ‘#’ para imprimir o espectrograma. Nas figuras 1, 2 e 3 é possível conferir as configurações
dos módulos no Processor Expert.

Para o gráfico a ser mostrado as colunas mais à direita representam frequências maiores.

A tela LCD foi implementada pelo código já disponibilizado pelo professor no site da
disciplina, todavia trocamos as portas da placa utilizadas, seguimos o esquema apresentado na
tabela 1 para configurar os módulos PDC e um BitIO (para a porta PTC0, controlando a luz de fundo).

Elemento Pino Nome Placa Função


1 RST PTC4 Reset do adaptador
2 CE PTC11 Chip enable, ou chip select da SPI
3 DC PTC10 Data/Command, ou RS (Register Select)
Display 4 DIN PTD3 Data In, ligado ao MOSI
LCD 5 CLK PTC5 Clock da SPI
6 VCC VCC Alimentação 3.3 V
7 LIGHT PTC0 Backlight, ativo em nível H
8 GND GND Terra do módulo
Tabela 1: Conexão de pinos para o LCD.
Figura 1: Configuração do PDC

Figura 2: Configuração BitIo

Figura 3: Configuração do Wait


Teclado Matricial

Figura 5: Esquema de conexões do teclado matricial.

O teclado foi implementado com uma topologia matricial, com chaves ligando as
linhas nas colunas da matriz. Uma vez que a chave esteja fechada, determinada coluna é
conectada a determinada linha, podendo-se fazer a passagem de bits. O esquema de
conexão pode ser visto na figura 5. Foi implementado um algoritmo de varredura, em que
cada linha tem uma saída GPIO e cada coluna tem uma entrada GPIO, com cada uma das
colunas conectadas com resistores de pull-up de 10000 Ohms, com as seguintes
configurações no process expert:

L1​: linha 1 PTC16

L2​: linha 2 PTC17

L3​: linha 3 PTA16

L4​: linha 4 PTA17

C1​: coluna 1 PTD2

C2​: coluna 2 PTD7

C3​: coluna 3 PTD0


Figura4:​ Components
implementados

Tabela 2:​ Pinagem do teclado


Figura5:​ Exemplo de Component implementado para linha (OUTPUT)

Figura6:​ Exemplo de Component implementado para coluna (INPUT)

void C3_OnInterrupt(void)
{ L1_SetVal();
//PDC1_WriteLineStr(2, "foi truta"); L2_SetVal();
extern volatile int tecla; L3_ClrVal();
extern volatile char botao; L4_SetVal();
tecla=1; WAIT1_Waitus(10);
bool col=FALSE; col=C3_GetVal();
extern volatile int coluna; if (!col){
extern volatile int linha; linha=3;
coluna=3; botao='9';
WAIT1_Waitms(10); col=FALSE;
L1_ClrVal(); }
L2_SetVal(); L1_SetVal();
L3_SetVal(); L2_SetVal();
L4_SetVal(); L3_SetVal();
WAIT1_Waitus(10); L4_ClrVal();
col=C3_GetVal(); WAIT1_Waitus(10);
if (!col){ col=C3_GetVal();
linha=1; if (!col){
botao='3'; linha=4;
col=FALSE; botao='*';
} col=FALSE;
}
L1_SetVal(); L1_ClrVal();
L2_ClrVal(); L2_ClrVal();
L3_SetVal(); L3_ClrVal();
L4_SetVal(); L4_ClrVal();
WAIT1_Waitus(10); WAIT1_Waitus(10);
col=C3_GetVal(); }
if (!col){
linha=2;
botao='6';
col=FALSE;
}

Anexo 1:​ Exemplo de código implementado para tratamento da interrupção do teclado matricial em Events.c

Comandos do teclado matricial:


Os comandos foram implementados de acordo com a tabela 2:

Tecla no teclado Comando

1 Liga/desliga
LCD

2 Liga/desliga
LEDS RGB

Tabela 3:​ Comandos implementados no programa.

Nota-se que poucos botões foram utilizados, mas o teclado adiciona uma possibilidade de
expansão fácil do projeto, para adicionar mais funções.
Os botões apenas invertem as flags graficoON e ledON, as quais, se forem FALSE, impedem
a execução da função graph, que atualiza o LCD e da função leds, que troca a cor dos LEDs. Além
disso, quando essas flags mudam de TRUE para FALSE elas apagam o conteúdo atual do LCD ou
dos LEDs. Tudo isso pode ser visto no main.c, no anexo 2, apenas a troca das flags ocorre no
events.c.

Conversor A/D - Entrada de áudio


Inicialmente tentamos utilizar os módulos de microfone disponibilizados pelo almoxarifado,
mas ambos foram testados em sala com um osciloscópio e ajuda do professor e não funcionavam
corretamente. Implementamos a segunda alternativa, que era ter como entrada um canal de áudio
que saísse do computador (saída P2) e fosse diretamente conectado ao microcontrolador. O circuito
da figura 7 é necessário para dar um offset no sinal, pois o componente ADC lê apenas valores
positivos de tensão (e a saída do computador possui valores negativos também), adicionando uma
tensão de 1,65V em DC.
A programação para o ADC pode ser vista no main.c e no events.c. Nota-se que os valores
lidos são guardados no vetor buffer.
Figura 7:​ Circuito para a entrada de áudio.

Figura 8:​ Configuração do ADC no Component

Módulo PWM e LEDS RGB


Foi utilizada uma fita de LEDs RGB simples, não endereçáveis individualmente. Seu
funcionamento pode ser visto em [Fitas de LED].

Os leds são controlados por três módulos PWM, que determinam o brilho de cada uma das
cores. Os módulos são configurados como nas figuras 10, 11 e 12. Observa-se que colocar um duty
cycle de 5ms gera o brilho mínimo da cor, e 0ms o brilho máximo.

O volume é representado pelo seguinte esquema de cores: vermelho significa volume alto,
azul baixo e verde intermediário. A mudança entre as cores é gradual em 5 níveis, cada um com
diferentes valores de vermelho, verde e azul.
Na figura 9 é possível ver o circuito para a fita de leds. Os mosfets são utilizados para
permitir a passagem maior de corrente, este circuito também poderia ser utilizado para fitas maiores.

Os LEDs indicam o volume do áudio baseado na intensidade média recebida pelo ADC em
128 amostras (as mesmas sobre as quais será calculado o espectro), que são guardadas em um
vetor chamado buffer (na main.c). essa média que calcula o volume é feita na própria função main na
main.c (anexo2) e, em seguida, é passado para a função leds, que acenderá os LEDs na cor correta.

Figura 9:​ Configuração do PMW para a porta G da fita de leds RGB.

Figura 10:​ Configuração do PMW para a porta G da fita de leds RGB.


Figura 11:​ Configuração do PMW para a porta R da fita de leds RGB.

Figura 12:​ Configuração do PMW para a porta B da fita de leds RGB.

Biblioteca de FFT
A biblioteca CMSIS DSP (documentada em [CMSIS_DSP] e disponível em [fonte CMSIS])
nos permite fazer o algoritmo de ​fast fourier transform (​ fft) nos processadores embarcados ARM,
como na placa KL25Z.

Sua instalação com o CodeWarrior não é muito simples, e, para isso, foi seguido o tutorial
em [Tutorial CMSIS_DSP]. Para realizar a transformada de fourier em tempo real no processador
embarcado foi utilizada a função arm_rfft_q15, que faz o algoritmo de fft pressupondo entradas reais
e utilizando ponto fixo de tamanho 15. A função é mostrada abaixo:

void arm_rfft_q15​ (const ​arm_rfft_instance_q15​ *S, ​q15_t​ *pSrc, ​q15_t


*pDst)

Nota-se que seu primeiro argumento é um parâmetro de inicialização com suas


configurações, gerado pela função arm_rfft_init_q15 (abaixo). Seu Segundo parâmetro é um buffer
de entrada e o terceiro um buffer de saída.Todos usam o formato q15.
arm_status arm_rfft_init_q15​ (​arm_rfft_instance_q15​ *S, uint32_t fftLenReal,
uint32_t ifftFlagR, uint32_t bitReverseFlag)

Esta função gera a estrutura de inicialização para a função anterior. Seu primeiro argumento
indica esta estrutura. Seu segundo argumento indica o tamanho da fft, vale ressaltar que utilizamos o
tamanho de 128. Seu terceiro parâmetro indica se a transformada é direta ou inversa, utilizamos 0,
para indicar direta. Seu último parâmetro indica se a saída deve estar em ordem normal ou inversa,
utilizamos 1, para indicar ordem normal.

Ao utilizar essas duas funções descritas no diagrama da figura 13, é importante destacar que
a transformada de Fourier tem tamanho 128 e utiliza ponto fixo para poupar processamento e ser
possível rodar o programa com a entrada em tempo real no microcontrolador sem qualquer
problema.

Após o cálculo da fft pela função arm_rfft_q15 ​mais alguns processos são necessários para
tratar seu vetor de saída (MicFFT, de tamanho 256), que são feitos na própria main(), logo após a
função. Primeiramente é feito um rescale do vetor (em um loop for). Depois é criado um vetor
magnitudes, pois o vetor atual é complexo. Para isso utiliza-se a função arm_cmplx_mag_q15 (que
tem como primeiro argumento o vetor complexo, como segundo a saída e como terceiro o tamanho
da saída). Esse vetor magnitudes se chama MicFFT_Mag, de tamanho 128. O próximo passo é
retirar seu primeiro valor, com MicFFT_Mag[0]=0, pois ele representa um erro DC relativamente
grande. Por fim, precisamos lembrar que apenas os primeiros 64 números deste vetor são de
interesse, pois os outros 64 são iguais aos primeiros.

O vetor de magnitudes, MicFFT_Mag, é então passado para a função graph, que fará o
gráfico de colunas do espectro. Para criar o valor representado por cada uma das 14 colunas do
display LCD somamos os valores do vetor de 4 em 4, a partir do primeiro até o 56. Assim, com 14
valores, desenhamos no display 14 colunas de altura proporcional a estes valores. Nota-se que as
colunas são formadas de caracteres #.

O diagrama da figura abaixo resume brevemente o processo explicado acima, desde o vetor
buffer até a escrita no LCD pela função PDC1_WriteLineStr() dentro da função graph().

Figura 13:​ Diagrama de explicação sobre o Processo de Fourier utilizando bibliotecas.


Resultados e Conclusão

Como pode ser visto no vídeo de demonstração ([Demonstração]) e como foi apresentado
em aula, o projeto funcionou corretamente e da maneira esperada. No LCD é possível visualizar o
espectro de frequência sendo plotado e os leds foram calibrados para a sensibilidade de detecção de
diferença de volume. Os botões de liga e desliga do gráfico e LEDs também estão funcionando.

A música utilizada como exemplo de entrada foi esta:


https://www.youtube.com/watch?v=HQ05lHoQtss

É possível observar um pequeno erro no gráfico, que advém ruído vindo da própria saída do
computador, pois mesmo sem música tocando é possível notar a impressão da primeira linha de
todas as colunas do gráfico. Todavia, se o conector P2 for desconectado ele desaparece.

Uma limitação observada durante o projeto foi o tamanho da FFT, que deveria ser um valor
pequeno pois todos os cálculos são feitos em tempo real pelo microcontrolador.

Com este projeto integramos vários assuntos estudados na disciplina. Utilizamos o LCD e o
teclado matricial com propósitos diferentes dos feitos antes, e utilizamos o ADC, antes utilizado para
ler temperatura, para ler um canal de áudio. Também fizemos coisas mais inéditas, como adicionar
um offset em uma entrada de áudio, e controlar LEDs RGB.

O maior desafio do projeto foi fazer uma transformada de fourier na placa KL25Z, mas
conseguimos fazê-lo com sucesso, instalando e aprendendo a usar a biblioteca CMSIS DSP, que
apresenta funções de fft bem otimizadas para este tipo de processador.

Vídeo:
[Demonstração]https://drive.google.com/open?id=1HdneZMtJp8Tum6PsPYggZtYENOpAZBts

Bibliografia

[CMSIS_DSP]​http://www.keil.com/pack/doc/CMSIS/DSP/html/group__RealFFT.html

[tutorial
CMSIS_DSP]​https://www.youtube.com/watch?v=gIh0pPsyGpI&index=9&list=PLWM8NW5LEukhCAv
E7voge_-L8waDyQSgo

[Tutorial CMSIS_DSP]​https://mcuoneclipse.com/2013/02/14/tutorial-using-the-arm-cmsis-library/

[Fitas de LED]​https://learn.adafruit.com/rgb-led-strips/usage

[Fonte CMSIS]https://github.com/ARM-software/CMSIS_5
/* ###################################################################
** Filename : main.c
** Project : Test5110
** Processor : MKL25Z128VLK4
** Version : Driver 01.01
** Compiler : GNU C Compiler
** Date/Time : 2017-08-05, 10:28, # CodeGen: 0
** Abstract :
** Main module.
** This module contains user's application code.
** Settings :
** Contents :
** No public methods
**
** ###################################################################*/
/*!
** @file main.c
** @version 01.01
** @brief
** Main module.
** This module contains user's application code.
*/
/*!
** @addtogroup main_module main module documentation
** @{
*/
/* MODULE main */

/* Including needed modules to compile this module/procedure */


#include "Cpu.h"
#include "Events.h"
#include "SM1.h"
#include "MCUC1.h"
#include "WAIT1.h"
#include "KSDK1.h"
#include "AD1.h"
#include "AdcLdd1.h"
#include "Bit1.h"
#include "BitIoLdd2.h"
#include "L1.h"
#include "BitIoLdd3.h"
#include "L2.h"
#include "BitIoLdd5.h"
#include "L3.h"
#include "BitIoLdd6.h"
#include "L4.h"
#include "BitIoLdd7.h"
#include "C1.h"
#include "ExtIntLdd1.h"
#include "C2.h"
#include "ExtIntLdd2.h"
#include "C3.h"
#include "ExtIntLdd3.h"
//#include "BitIoLdd8.h"
//#include "red.h"
//#include "BitIoLdd9.h"
#include "g.h"
#include "PwmLdd1.h"
#include "TU1.h"
#include "r.h"
#include "PwmLdd2.h"
#include "TU2.h"
#include "b.h"
#include "PwmLdd3.h"
#include "TU3.h"
#include "PDC1.h"
#include "RESpin1.h"
#include "SCEpin1.h"
#include "D_Cpin1.h"
#include "LT.h"
#include "BitIoLdd1.h"
/* Including shared modules, which are used for whole project */
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"
#include <string.h>
#include "Globals.h"
//includes da biblioteca CMSIS DSP para fazer a fft
#include "arm_math.h"
#include "arm_const_structs.h"

/* User includes (#include below this line is not maintained by Processor Expert) */
LDD_TDeviceData *MyRTCPtr;
LDD_RTC_TTime Time;
LDD_TError Error;

//variaveis globais
static uint16_t value;//valor para conversao adc
volatile bool AD_finished;//flag para termino da conversao do adc
volatile int coluna=0;//detecta coluna pressionada no teclado
volatile int linha=0;//detecta linha pressionada no teclado
volatile int tecla=0;//detecta que o teclado foi usado
volatile char botao; //botao que foi teclado
int colunas[14];//colunas do grafico a ser mostrado no LCD
/*lint -save -e970 Disable MISRA rule (6.3) checking. */
int atoi(char *str)//funcao comum, atoi, transforma char em int
{
int res = 0; // Initialize result
int i ;
// Iterate through all characters of input string and
// update result
for (i = 0; str[i] != '\0'; ++i)
res = res*10 + str[i] - '0';
// return result.
return res;
}
char* itoa(int i, char b[]){//funcao comum, itoa, transforma int em char
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
/*void measure(int sound){//funcao usada apenas para debbug, mostra o valor atual do ADC na
tela.
char str[1200]="0";
itoa(sound,str);
PDC1_WriteLineStr(1, " ");
PDC1_WriteLineStr(1, str);
}*/
void clear(){//limpa display (apaga todas linhas)
PDC1_WriteLineStr(1, " ");
PDC1_WriteLineStr(2, " ");
PDC1_WriteLineStr(3, " ");
PDC1_WriteLineStr(4, " ");
PDC1_WriteLineStr(5, " ");
PDC1_WriteLineStr(6, " ");
}
void graph(volatile q15_t MicFFT_Mag[128]){//recebe a transformada de fourier e desenha grafico
no LCD
int j=0;
int i=0;
int k=0;
int val=0;
char linha1[100]="";
char linha2[100]="";
char linha3[100]="";
char linha4[100]="";
char linha5[100]="";
char linha6[100]="";
//Utilizamos apenas os primeiros 56 valores do vetor da FFT, o vetor possui apenas os primeiros
64 valores
//diferentes, os ultimos 64 sao conjugados. usamos apenas 56 para utilizar 4 valores por coluna
em nosso
//display de 14 colunas
for(j=0;j<14;j++){//14 colunas
for(i=0;i<4;i++){//4 valores da fft agregados por coluna
k=4*j+i;
val=val+MicFFT_Mag[k];
}
val=val*10;//escala para melhorar grafico
colunas[j]=val;//vetor colunas representa amplitudes numa faixa de frequencia
val=0;
}
clear();
for(j=0;j<14;j++){
if(colunas[j]>=10000){
strcat(linha6, "#");//display nao aceita caracteres especiais, entao foi usado # para formar o
grafico
}
else{
strcat(linha6," ");
}
if(colunas[j]>=20000){
strcat(linha5, "#");
}
else{
strcat(linha5," ");
}
if(colunas[j]>=30000){
strcat(linha4, "#");
}
else{
strcat(linha4," ");
}
if(colunas[j]>=40000){
strcat(linha3, "#");
}
else{
strcat(linha3," ");
}
if(colunas[j]>=50000){
strcat(linha2, "#");
}
else{
strcat(linha2," ");
}
if(colunas[j]>=60000){
strcat(linha1, "#");
}
else{
strcat(linha1," ");
}
}
PDC1_WriteLineStr(6, linha6);
PDC1_WriteLineStr(5, linha5);
PDC1_WriteLineStr(4, linha4);
PDC1_WriteLineStr(3, linha3);
PDC1_WriteLineStr(2, linha2);
PDC1_WriteLineStr(1, linha1);
}
void leds(int altura){//acende os leds de acordo com o volume recebido (parametro altura)
//vermelho representa sons altos, verde sons intermediarios e azul sons baixos,
//transicao do minimo para o maximo feita em 5 passos
int red=0;//valores de 0 a 100%
int green=0;
int blue=100;
float cor=0;
int16_t r1=5;
int16_t g1=5;
int16_t b1=0;
cor=altura/10;//5000 é aproximadamente a altura maxima

if(cor>=30){
b1=2;
g1=4;
r1=5;
}
if(cor>=40){
b1=4;
g1=1;
r1=5;
}
if(cor>=60){
b1=5;
g1=1;
r1=4;
}
if(cor>=80){
b1=5;
g1=4;
r1=2;
}
if(cor>=100){
b1=5;
g1=5;
r1=0;
}
g_SetDutyMS(g1);
r_SetDutyMS(r1);
b_SetDutyMS(b1);
}

int main(void)
/*lint -restore Enable MISRA rule (6.3) checking. */
{
/* Write your local variable definition here */
arm_rfft_instance_q15 RealFFT_Instance;
arm_cfft_radix4_instance_q15 MyComplexFFT_Instance;
bool graficoON=TRUE;//flag que mostra o espectograma no lcd de for true
bool ledON=TRUE;//flag que acende os leds se for true

int som=0;//valor do volume instantaneo


volatile int16_t *Activebuffer;//buffer com serie dos volumes no tempo
volatile q15_t MicFFT[256];//fft complexa
volatile q15_t MicFFT_Mag[128];//fft real

//cria estrutura pra funcao fft, tamanho 128, real, ponto fixo
arm_rfft_init_q15(&RealFFT_Instance,
128,
0,
1); //Bit Reverse Flag enabled

/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
PE_low_level_init();
/*** End of Processor Expert internal initialization. ***/
Bit1_SetVal();//liga tecaldo ptb3

clear();
PDC1_SetContrast(50);
int buffer[128];//recebe serie temporal dos valores de som
int i=0;
int volume=0;//recebe volume, calculado como media de 128 valores (do buffer)

for(;;) {
AD_finished = FALSE; /* reseta flag */
(void)AD1_Measure(FALSE); /* AD_finished sera set como true uma vez */
while(!AD_finished) {//codigo para ser ralizado durante conversao ad
if(i<128){
buffer[i]=som;//preenche o buffer com valores temporais do som
i++;
if(i==128){//buffer completo com 128 valores
while(i>0){//calcula volume
i--;
volume=volume+abs(buffer[i]);
}
volume=volume/128;
i=0;
if (ledON==TRUE){
leds(volume);//mostra volume nos leds
}

Activebuffer=buffer;
arm_rfft_q15( &RealFFT_Instance, //faz tranformada de fourier real em ponto fixo
(q15_t *)Activebuffer,
(q15_t *)MicFFT);
int j=0;
for(j=0; j<256; j++)//rescale
{
MicFFT[j]<<=6;
}

//obtem magnitudes da transformada complexa MicFFT


arm_cmplx_mag_q15((q15_t *)MicFFT,
(q15_t *)MicFFT_Mag,
128);
MicFFT_Mag[0]=0;//retira erro DC
if(graficoON==TRUE){
graph(MicFFT_Mag);//mostra espectograma no LCD
}

}
}
if (tecla==1){

if(botao=='1'){//liga e desliga grafico


graficoON=!graficoON;
clear();
}
if(botao=='2'){//liga e desliga leds
ledON=!ledON;
g_SetDutyMS(5);
r_SetDutyMS(5);
b_SetDutyMS(5);
}
tecla=0;//sinal de interrupcao do teclado
}
}
/* AD_finished mostra que conversao ad terminou */
(void)AD1_GetValue16(&value);
som=value-32768;//le som em amplitude, 32768 deve ser o valor equivalente a nenhum som
}
/*** Don't write any code pass this line, or it will be deleted during code generation. ***/
/*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T
MODIFY THIS CODE!!! ***/
#ifdef PEX_RTOS_START
PEX_RTOS_START(); /* Startup of the selected RTOS. Macro is defined by the
RTOS component. */
#endif
/*** End of RTOS startup code. ***/
/*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
for(;;){}
/*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/
/* END main */
/*!
** @}
*/
/*
** ###################################################################
**
** This file was created by Processor Expert 10.3 [05.09]
** for the Freescale Kinetis series of microcontrollers.
**
** ###################################################################
*/

Anexo 2:​ Main.c

Вам также может понравиться