Академический Документы
Профессиональный Документы
Культура Документы
Introdução
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).
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:
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
1 Liga/desliga
LCD
2 Liga/desliga
LEDS RGB
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.
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.
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:
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().
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.
É 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 */
/* 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
//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;
}
}
}
if (tecla==1){