Willian Mateus Ferreira dos Santos, Autor em Blog Ryndack Componentes
Rua Jovelina Claudino Buhrer, 440 - São José dos Pinhais - PR (41) 3383-3034
Projetos


Os hamsters são animais conhecidos por seu habito de correr incessantemente em suas rodinhas, desse modo é normal se perguntar que velocidade eles alcançam, ou ainda, querer saber que distancia eles percorrem. Pensando nisso construímos um velocímetro e contador de voltas para rodinhas de hamsters. Curioso para saber como? Não se preocupe que contamos neste post.

Antes de mais nada vamos aos materiais usados, que são os seguintes:

  1. Arduino Uno;

  2. Display LCD 16×2;

  3. Sensor infravermelho TCRT5000;

  4. Resistores de 330Ω, 3,3kΩ e 10kΩ (1 de cada);

Montando o velocímetro

A ideia aqui é usar um sensor infravermelho TCRT5000  para encontrar o momento em que o hamster completa uma volta na sua rodinha, e com o tempo de cada volta, juntamente ao tamanho da rodinha calcularmos a velocidade e a distancia percorrida por ele, para em seguida exibir esses valores em um display LCD. Para isso, vamos montar o circuito como mostrado na figura abaixo.

Nessa figura temos que o display LCD esta ligado com os pinos D4 a D7 nos pinos de mesmo nome do Arduino (ou seja, nos pinos PD4 a PD7 no Atmega328p), além disso, os pinos  E, R/W e RS estão conectados nos pinos A0, A1 e A2 (ou PC0, PC1 e PC2) respectivamente. Para a alimentação do display, assim como o TCRT5000, esta sendo utilizado os 5V fornecidos pelo próprio Arduino.

O funcionamento do TCRT5000 se da por meio de um fototransistor acoplado a um LED infravermelho, assim, quando um objeto é posto próximo desse sensor, a luz infravermelha é refletida para o fototransistor, que atuara como um circuito fechado. No nosso circuito, o emissor do fototransistor esta conectado ao terra (gnd), portanto quando ele atuar como um circuito fechado temos que a tensão no coletor do fototransistor e consequentemente pino D8 do Arduino (ou PB0 para o Atmega, pino onde o conector esta ligado) sera de 0V. Já quando não existir nenhum objeto próximo ao TCRT5000, o fototransistor funciona como um circuito aberto, e a tensão de seu coletor sera de 5V, ja que o mesmo esta ligado nessa tensão por meio de um resistor de 10kΩ.

Programando nosso velocímetro

Agora que já temos os materiais em mãos e montamos o nosso circuito, iremos programar o Arduino. O código completo usado nesse projeto, além da biblioteca usada para controlar o LCD estão presentes no nosso GitHub.

O que vamos fazer no código é usar o timer0 para nos fornecer uma medida de tempo para o calculo da velocidade de nosso velocímetro, para isso, precisamos que ele seja reiniciado a cada volta completada pelo hamster. Com esse fim vamos usar a interrupção por mudança de estado do pino PB0, assim, sempre que o microcontrolador receber um sinal do TCRT5000, ele sofrera uma interrupção, e nela ocorrera a contagem da quantidade de voltas dada pelo Hamster e a inicialização do timer, sendo que após duas interrupções suscetivas, ou seja, duas voltas completas, a velocidade é calculada. Entretanto, se a rodinha ficar inativa por muito tempo (mais que 10 segundos), usaremos a interrupção por estouro do timer para desliga-lo.

Para simplificar a escrita e a compreensão do nosso código, usamos 5 funções (além da main e das interrupções), que servem para ligar o LCD, configurar o pino PB0 como entrada habilitando as interrupções, configurar o timer0, realizar os cálculos necessários e criar um loop que mantem o circuito sempre em funcionamento. Cada função usada esta explicada abaixo.

Função “Liga_LCD()”

A primeira função que vamos discutir é a função “Liga_LCD()”, ela foi implementada como presente abaixo.

void Liga_LCD(void){
          LCD_init();
          LCD_clear();
          LCD_move_cursor(0,1);
          LCD_write("Hodometro para");
          LCD_move_cursor(1,4);
          LCD_write("Hamsters");
          _delay_ms(2000);
          LCD_clear();
}

Nela iniciamos o display usando “LCD_init()”, então limpamos o display com “LCD_clear()” e finalmente a mensagem “Hodometro para Hamsters” é exibida por dois segundos com as funções “LCD_write()”, “LCD_move_cursor()” e “_delay_ms()”. 

A função LCD_init() envia um conjunto de instruções que configuram o display no modo de 4 bits com o cursor ligado. Enquanto isso a função LCD_clear() apaga tudo o que esta exibido no display e retorna o cursor para a primeira posição da primeira linha. Já a função LCD_move_cursor() posiciona o cursor na posição desejada no display, essas funções fazem parte da biblioteca LCD.h. Já a ultima função _delay_ms() pertence a biblioteca <util/delay.h> e pausa a execução do programa pelo tempo desejado.

Função “configura_contador()”

A próxima função configura PB0 como entrada no registrador DDRB, em seguida, o pull up é ativado, isso mantém o nível de tensão do pino PB0 em 5V, a menos que a entrada seja 0V, o que facilita a identificação de transições para baixo. Logo após, usamos os registradores PCICR e PCMSK0, para habilitar as interrupções por mudança de estado do pino PB0, assim, sempre que o pino PB0 muda de estado, ocorre uma interrupção. Essa função ficou como mostra o exemplo abaixo:

void configura_contador(void){
          DDRB &= ~(1<<PB0);
          PORTB |= (1<<PB0);

          PCICR |= (1<<PCIE0);
          PCMSK0 |= (1<<PCINT0);
}

A rotina da interrupção para a mudança de estado do pino PB0 em primeiro lugar realiza a leitura desse pino, e se este sofreu transição para nível alto, apenas um pequeno delay de 50ms é realizado, mas se a transição é para nível baixo, então o timer0 é reiniciado, a quantidade de voltas é incrementada, e o valor do tempo é salvo na variável “tempo”. Essa interrupção foi implementada como mostrado a seguir:

ISR(PCINT0_vect){
          if(PINB & (1<<PB0)){
          _delay_ms(50);
          }
          else{
                    voltas +=1;
                    voltas_total +=1;
                    tempo = i;
                    i = 0;
_delay_ms(50);

TCCR0B |= (1<<CS00);
TCNT0 = 0; } }

Função “configura_timer()”

Da mesma forma que a função configura contador, essa função configura uma interrupção, mas desta vez por estouro do timer0. Para isso, primeiro configura-se o timer0 no modo de operação normal e desconectado. Essa configuração foi selecionada, pois com ela é possível obter uma precisão de 0,16ms, que é o suficiente para a construção do velocímetro. Por fim a interrupção por estouro do timer é habilitada. A função que configura o timer esta presente abaixo.

void configura_timer(void){
          TCCR0A = 0;
          TCCR0B = 0;
          TIMSK0 |= (1<<TOIE0);
}

Com essa configuração temos 62500 interrupções por estouro por segundo, sendo que a cada interrupção a variável “i” é acrescida em 1 e quando ocorre uma interrupção por mudança de estado do pino PB0, o valor de “i” é salvo em “tempo” e “i” é reiniciada com zero. Entretanto, se não ocorre nenhuma mudança de estado em PB0 dentro de 10 segundos, então i>624999 e quando isso ocorre, a interrupção desconecta o timer e reinicia as variáveis “voltas” e “i”. Essa interrupção está abaixo.

ISR(TIMER0_OVF_vect){
          i++;
          if (i>624999){
                    i=0;
                 
                    voltas = 0;
                    TCNT0 = 0;
                    TCCR0B &= ~(1<<CS00);
          }
}

Ao se desconectar o timer o Arduino fica aguardando uma interrupção por mudança de estado do PB0 para reiniciar o timer. Além disso o velocímetro é zerado.

Função “calcula_velocidade()”

O código desta função está abaixo:


void calcula_velocidade(float tempo, float raio){
    if (voltas > 1){
        tempo = tempo/(62500);
        distancia = (voltas_total-1)*2*3.1416*raio;
        velocidade = 3.6*2*3.1416*raio/tempo;
    }
    else {
        velocidade = 0;
    }
}

Esta função é a responsável pelo calculo da velocidade do hamster em km/h. Para isso, primeiro é verificado se houveram 2 incrementos na variável “voltas”, uma vez que na primeira vez que o TCRT5000 é ativado, o timer está zerado. Se já houveram 2 incrementos em voltas, então o tempo em segundos é calculado usando:

(1)   \begin{equation*} tempo=\frac{tempo}{62500} \end{equation*}

Com o tempo em segundos calculamos a velocidade com a equação abaixo.

(2)   \begin{equation*} velocidade=3.6*\frac{2*\pi*raio}{tempo} \end{equation*}

Nessa equação temos que 2*\pi*raio é o comprimento da circunferência da rodinha do HHamster, ou seja, a distancia que ele percorre em uma volta completa. Assim a velocidade será obtida dividindo este valor pelo tempo. O fator 3.6 é usado para converter a velocidade (encontrada em metros por segundo) para quilômetros por hora.

Além disso, para calcular a distancia percorrida pelo Hamster basta multiplicar a circunferência de sua rodinha pelo numero de voltas dada, como abaixo.

(3)   \begin{equation*} distância=2*\pi*raio*numero-de-voltas \end{equation*}

Função “loop()”

Por fim temos a função loop(), nossa ultima função, que ficará sempre em execução, sendo implementada como abaixo:



void loop(void){
    char str_distancia[20];
    char str_velocidade[20];
    int vel_int;
    int vel_dec;
    int distancia_int;
    int distancia_dec;
   
    while(1){
                calcula_velocidade(tempo, raio);
                distancia_int = floor(distancia);
                distancia_dec = 100*(distancia-distancia_int);
                sprintf(str_distancia, "Dis: %d,%d[m]   ", distancia_int, distancia_dec);
                vel_int = floor(velocidade);
                vel_dec = 100*velocidade - 100*vel_int;
                sprintf(str_velocidade, "Vel: %d,%d[km/h]   ", vel_int, vel_dec);
                LCD_move_cursor(0,0);
                LCD_write(str_distancia);
                LCD_move_cursor(1,0);
                LCD_write(str_velocidade);
                voltas_cont=voltas;
         }
    }   
}

Nessa função, primeiro temos a declaração das variáveis a serem utilizadas sendo duas strings e quatro variáveis int. Em seguida, temos um loop infinito (while(1)), nele utiliza-se a função calcula_velocidade() para se calcular a velocidade e a distancia total percorridas pelo Hamster. Em seguida, estes valores são gravados em números inteiros, primeiro a parte inteira, depois as duas primeiras casas depois da virgula. Isso porque a função sprintf, usada para transformar números em strings, não funciona com variáveis do tipo float. Finalmente esses valores são exibidos no display do nosso velocímetro.

Função “main()”

Por fim, temos a função main do nosso programa, que ficou como a seguir:

void main(void){   
    SREG   |=  (1<<7);
    voltas  = 1;
    Liga_LCD();
    configura_contador();
    configura_timer();

    loop();
}

Na função main, apenas habilita-se as interrupções globais e chama-se as funções Liga_LCD(), configura_contador(), configura_timer() e loop().

Testando

Para testar o nosso projeto contamos com a ajuda do Antônio, o nosso hamster atleta, e ao final de uma noite ele correu por 3200 metros. No link abaixo você encontra o nosso projeto funcionando.
https://youtube.com/shorts/fL2wfnrB3ng?feature=share
E então, o que vocês acharam deste projeto? Conte pra gente nos comentários.


0

Conhecimento, Sem categoria

Os encoders são botões giratórios, similares aos potenciômetros, e que podem ser girados indefinidamente. Eles estão presentes em rádios, mouses, impressoras 3D, equipamentos de bancada, entre vários outros dispositivos eletrônicos. Assim, entender o seu funcionamento e aprender a usa-lo é algo muito interessante e útil, por isso vamos te ensinar mais sobre eles!

Podemos classificar os encoders em dois tipos, os absolutos e os incrementais. Aqui não iremos nos aprofundar muito nos encoders absolutos, mas vale apena uma breve explicação sobre eles. De modo simplificado, os encoders absolutos são construídos com um disco com diversas trilhas, cada uma com um sensor e uma marcação específica de modo que, ao se combinar as informações captadas pelos sensores de todas as trilhas é possível se formar um código, que é único para a posição em que o encoder se encontra, ou seja, em um encoder absoluto podemos determinar exatamente sua posição através de um código.

O segundo tipo de encoder, o incremental é um pouco mais simples. Ele é formado por uma unica trilha com marcações igualmente espaçadas e dois sensores postos lado a lado, assim, quando o seu disco é girado, com o auxilio do seu eixo, temos a ordem com a qual os sensores são ativados e que dependem do sentido de rotação.

Conhecendo o encoder incremental

Um exemplo de encoder incremental é o EC11, que é o modelo que usaremos neste artigo. Ele possui cinco terminais, sendo dois deles usados por uma chave, que pode ser ativada pressionando o seu eixo. Enquanto isso os demais terminais são usados para controlar o encoder. A imagem abaixo representa o simbolo esquemático de um encorder desse tipo.

Aqui temos os terminais S1 e S2 para a chave, enquanto os terminais A e B permitem acesso a saída dos sensores. O terminal C é o comum aos dois sensores e geralmente é aterrado. O acionamento desse encoder ocorre de forma mecânica, sendo que, quando giramos o seu eixo, o contato entre um dos terminais das extremidades (A ou B) é fechado com o terminal comum (C). Como já foi dito, a ordem com a qual os sensores são acionados dependem do sentido de rotação do encoder, assim, se ele é rotacionado no sentido horário, sabemos que o contato entre A e C é fechado e em seguida entre B e C. Mas o inverso ocorre se girado no sentido anti-horário, quando fecha-se primeiro o contato entre B e C, e depois entre A e C.

Isso pode nos descrever duas ondas quadradas em quadratura, como abaixo.

Por quadratura nos referimos a diferença de fase entre os sinais, isto é, temos que um sinal esta adiantado em relação ao outro por 90° (aqui vale ressaltar que esse ângulo não se refere ao ângulo físico do encoder, mas sim a defasagem dos sinais).

Mapeando as transições do encoder

As ondas apresentadas mostram os sinais nos canais A e B de acordo com a posição do encoder. Se percorrermos as ondas da esquerda para a direita vemos que o sinal A está adiantado em relação ao sinal B. Isso ocorre quando giramos o encoder no sentido horário. Entretanto, se percorrermos as ondas da direita para a esquerda, veremos que o sinal B fica adiantado em relação ao A, o que acontece quando giramos o encoder no sentido anti-horário. Assim, para saber o sentido de rotação, precisamos apenas encontrar o sinal adiantado.

Agora que conhecemos seu funcionamento, pensamos em como identificar o sinal adiantado. Com as formas de onda em mãos, podemos mapear as transições realizadas, ou seja, encontrar todas as transições possíveis e determinar em quais condições ela ocorre. 

Vamos usar como exemplo o caso em que A e B estão em nível alto, ou seja, AB=11, os próximos estados possíveis são AB=01, quando girado no sentido horário e AB=10, quando girado no sentido anti-horário. Isso fica mais claro na imagem abaixo.

Além disso, se AB permaneceu em 11 ele ficou parado, e para AB=00 temos um erro de leitura. Assim, para cada posição temos 4 estados futuros, sendo 1 deles impossível (ou um erro de leitura). Isso está sintetizado na tabela abaixo.

Nessa tabela, temos um número binário de quatro bits para cada movimento do encoder. Além disso podemos escrever horário como sendo 1, anti-horário como -1 e parado ou impossível como 0, assim conseguimos escrever um vetor de 16 posições com as transições possíveis. Esse vetor é {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0} e ele é muito útil, uma vez que podemos usa-lo para incrementar ou decrementar uma variável, como veremos logo mais.

Montando um teste

Para entender como usar um encoder preparamos um exemplo prático, mas, antes de mais nada precisamos de alguns materiais básicos, são eles:

  1. Display LCD 16X2;
  2. Arduíno;
  3. Resistores de 330Ω e de 3,3kΩ (1 de cada);
  4. Encoder rotativo EC11;
  5. LED;

O que faremos é controlar o brilho de um LED, para isso, ajustaremos a razão cíclica de um PWM gerado com um Arduino. O valor da razão cíclica será determinado pelo encoder e exibido no display LCD, assim, precisamos montar o circuito como abaixo.

 

Aqui temos os pinos 0 e 1 (PD0 e PD1 para o ATmega328p) para a entrada dos sinais A e B do encoder, enquanto os pinos de 4 a 7 (PD4 ao PD7) são conectados nos pinos de mesmo valor do LCD, para o envio de dados. Já os pinos A0, A1 e A2 (PC0 ao PC2) são conectados nos pinos En, R/W e RS respectivamente, para configurar o display. Por fim temos o pino 11 (PB3), que é por onde saíra o PWM gerado. 

Lendo o encoder

Com o hardware montado, podemos programar o Arduino. O programa completo com as bibliotecas utilizadas estão disponíveis em nosso github. Para o programa criamos uma função denominada “le_encoder()”, que como o nome já diz, é responsável pela leitura do encoder. Essa função ficou como mostrado abaixo:


int le_encoder(void){

    static int8_t A_B = 3;                                                                                     // A_B |==> Armazena os estados anterior e atual do encoder.
    static int8_t incremento;                                                                                // incremeto|==> Variavel retornada pela função, indica o sentido de rotação do encoder.
    static int8_t entradas[]= {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};          // entradas[] |==> Vetor com as mudanças de estados possíveis.

// Cria um binario de 4 bits com os 2 MSDs indicando o estado passado e os 2 LSDs indicando o estado atual do encoder.
    A_B<<=2;
    A_B |= (PIND & 0x03);
    incremento += entradas[A_B & 0x0f];

// Evita incrementos e decrementos descontrolados.
    if(incremento>1){
        incremento=0;
    }
    if(incremento<-1){
        incremento=0;
    }
    return incremento;
}

Primeiro temos a declaração das variáveis locais, sendo todas estáticas para não serem reiniciadas a cada chamada da função. Em seguida, o valor antigo da variável A_B, responsável por armazenar o estado dos pinos PD0 e PD1, sofre um deslocamento de dois bits para a esquerda (A_B<<=2) e o valor dos pinos PD0 e PD1 são lidos com o auxilio de uma mascara e colocados nos 2 bits menos significativos (A_B |= (PIND & 0x03)). Isso forma um binário que representa a mudança de estado do encoder.

Logo após, soma-se na variável incremento 1, -1 ou 0, de acordo com o valor dos quatro bits menos significativos de A_B. Para isso lê-se a posição A_B & 0X0f do vetor entradas[], que é o vetor com as transições possíveis, que já discutimos aqui. Aplica-se a mascará 0x0f para se descartar os bits mais significativos de A_B, e ler apenas os 4 menos significativos, que são formados pelo valor de A_B anterior e atual.
Finalmente temos um “if”, que verifica se o valor do incremento é maior que 1 ou menor que -1, e se sim zera esse valor. Isso evita incrementos sucessivos de forma descontrolada.

Controlando o PWM

Agora que já conseguimos identificar o sentido de rotação do nosso encoder, vamos realizar o controle do PWM. Para isso, usaremos um inteiro de 8 bits e sem sinal, assim, temos uma variável que ira de 0 a 255. Fazemos isso porque o registrador OCR2A, responsável pelo PWM no pino PB3, é de 8 bits. O código fica como segue abaixo.

void calcula_PWM(int incremento){

static uint8_t D;                    // D = duty cicle em percentual
static uint8_t duty = 0;             // duty = duty cicle valor passado para o OCR2A para dutyrole do PWM
char s_duty[20];                     // s_duty = string com o duty cicle em percentual para exibir no LCD.

if (incremento){ if(duty>10){ if(duty<245){ duty += 3*incremento; } else if(duty<253){ duty += incremento; } else if(incremento>0){ duty = 255; } else{ duty += incremento; } } else if(duty>2){ duty += incremento; } else if(incremento<0){ duty = 0; } else{ duty += incremento; } D=100*duty/255; if(duty > 254){ sprintf(s_duty, "Duty: %d%% Max ", D); } else if(duty < 1){ sprintf(s_duty, "Duty: %d%% Min ", D); } else{ sprintf(s_duty, "Duty: %d%% ", D); } OCR2A = duty; LCD_move_cursor(0,0); LCD_write(s_duty); _delay_ms(20); } }

Nessa função primeiro se verifica se existe algum incremento (ou decremento) e se sim, verifica se a variável duty (que será passada para o OCR2A) está entre 10 e 245. Em caso afirmativo será somado 3 vezes o incremento em duty, mas caso essa variável esteja entre 2 e 10 ou 245 e 253, esse incremento será de apenas uma unidade. No caso dessa variável ser menor que dois ou maior que 253 então a saída será 0 ou 255 respectivamente.

O incremento de 3 quando duty está entre 10 e 245 permite um controle veloz da razão cíclica, mas mantem o controle do brilho do led suave, já que uma mudança dessa grandeza no PWM não é perceptível. Além disso, quando o valor está próximo de 255 o controle do PWM não permite um novo incremento, já que isso acarretaria em uma transição abrupta de nível máximo para mínimo. O mesmo ocorre nas proximidades de 0.

Em seguida calculamos o valor percentual da razão cíclica na variável D, exibindo esse valor no display LCD na forma “Duty: XXX”. Além disso, imprimimos na tela Máx quando a razão cíclica é 100% e Min quando essa é 0%. Por fim enviamos o valor de duty para o registrador OCR2A.

Main

Finalmente temos a função main, que realiza a configuração das entradas e saídas, além de configurar o PWM. Ela ficou como abaixo:


int main(void){
// Configura os pinos de entrada. 
    DDRD &= ~((1<<PD0)+(1<<PD1));
    PORTD |= ((1<<PD0)+(1<<PD1));

// Configura PWM.
    DDRB |= (1<<PB3);
    TCCR2A |= ((1<<COM2A1)+(1<<WGM21)+(1<<WGM20));
    TCCR2B |= (1<<CS20);
    OCR2A = 0;

// Inicia o LCD.
    LCD_init();
    LCD_move_cursor(0,0);

// Loop de execução.
    while(1){
        calcula_PWM(le_encoder());
    }
return 0;
}

O que fazemos aqui é configurar os pinos PD0 e PD1 como entradas (no registrador DDRD) e habilitar o resistor de pull up interno (no PORTD). Em seguida, habilitamos PB3 como saída (com DDRB), e configuramos o timer 2 no modo fast PWM, sem prescale. Aqui também iniciamos o LCD, configurando as entradas e saídas pertinentes com a função LCD_init(), e posicionamos o cursor na primeira casa e na primeira coluna. Finalmente, temos um loop infinito que realiza a constante chamada da função calcula_PWM().

Nós montamos este circuito para testarmos na prática e ele ficou funcionando como no vídeo deste link. O que você achou desse artigo? Pretende usar o encoder em algum projeto? Nos conte aqui nos comentários.

0

Projetos

É provável que você já tenha desejado reproduzir um áudio com o Arduino, mas como se faz isso? Vem com a gente, que nesse post você vai aprender!

A ideia por traz do projeto desse post é usar uma rede R2R, que é um conversor digital analógico formado por alguns resistores (se você ainda não conhece, confira o nosso post sobre ela), para reproduzir um áudio salvo na memória de um Arduino, de maneira simples e barata, usando poucos componentes.

Para esse projeto você não irá precisar de muita coisa, os materiais são:

  1. 25 resistores de 1kΩ

  2. 1 capacitor de 100nF

  3. Uma caixa de som

  4. Um Arduino

Esse projeto pode ser dividido em 3 etapas, sendo elas:

  1. Escolha e extração de um arquivo de áudio

  2. Escrita do código a ser executado

  3. Montagem do circuito


Escolha e Extração do Áudio 

A primeira etapa é a escolha de um arquivo de áudio. Para isso, precisamos levar em conta uma limitação comum em microcontroladores, a memória. No caso do Arduino, ela é de 32Kb, então precisamos garantir que o arquivo de áudio utilizado e o programa escrito não excedam este valor.

Assim, escolhemos um arquivo que seja curto, e com uma baixa frequência de amostragem. Para este artigo foi escolhido um áudio de cerca de 16 segundos com uma frequência de amostragem de 2000 amostras por segundo.
Com um arquivo de áudio selecionado, é preciso transformar ele em um vetor, que será gravado no Arduino. Para isso, usamos a função audioread do octave. O resultado dessa função é uma matriz [y fs], na qual y são os dados de áudio e fs a taxa de amostragem.

Os dados y são dispostos na forma de uma matriz que tem os frames de áudio colocados nas linhas e os canais nas colunas. A fim de simplificar o processo e de gerar um arquivo menor usamos um áudio mono, que fará com que y possua apenas uma coluna.

O próximo passo é transformar y em um vetor de uma unica linha com valores inteiros de 0 a 255, o que é possível com algumas contas simples e com a função reshape. Por fim, esse arquivo é salvo como um arquivo “.h”, que sera usado no código do Arduino. Isso esta demonstrado no script abaixo.

# Le o arquivo de audio, aqui usamos inicio e fim de modo que fim-inicio = numero_de_amostras. 
[y fs] = audioread("nome_do_arquivo_de_audio", [inicio fim]) 

# Determina o numero de linhas e o numero de colunas (1 para áudios mono) de y. 
[linha coluna] = size(y) 

# Transpõe y, em um vetor de 1 linha. 
y = reshape(y, 1, linha) 

# Transforma y em um vetor de 0 a 255 com números inteiros e salva em audio. 
audio = round(255*y+128) 

# Cria um arquivo .h com "const unsigned char pontos[ ] PROGMEM = {" salvo na primeira linha. 
dlmwrite("Audio.h", "const unsigned char pontos[ ] PROGMEM = {", "") 

# Adiciona o vetor com o audio ao arquivo .h criado. 
dlmwrite("Audio.h", audio, ",", "-append") 

# Fecha a chave. 
dlmwrite("Audio.h", "}", "", "-append")



Importante lembrar que para este script funcionar adequadamente, o arquivo de áudio deve estar salvo na mesma pasta que o octave. A fim de melhor aproveitar a memória do microcontrolador, foi usado o comando PROGMEM, que faz com que a variável, quando gravada, seja salva na memória flash, e não na RAM do Arduino.


Código do Arduino

O código implementado no Arduino ficou como está a seguir

#define F_CPU 16000000

#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "Pontos.h"

int i=0, Min=130;

//Interrupção quando há um estouro do timer0.
ISR(TIMER0_OVF_vect){ 

     //Reinicia o timer0.
     TCNT0 = Min; 

     //Manda o valor da posição i do vetor com o audio para o PORTD.
     PORTD = pgm_read_byte_near(pontos + i); 

     //incrementa a posição do vetor com o audio.
     if(i>30998){ 
          i = 0;
     }
     else{
          i += 1;
     }
}

//Configura o timer0 e as saídas.
void main (void){
     //Habilita interruoções globais.
     SREG |= (1<<7); 

     //Configura o timer0 para operação normal, não conectada.
     TCCR0A = 0; 
     TCCR0B = 0;

     //Define o prescale como sendo 64.
     TCCR0B |= ((1 << CS01)+(1 << CS00)); 

     //Inicia o timer0.
     TCNT0 = Min; 

     //Habilita interrupção por estouro do timer 0.
     TIMSK0 |= (1<<TOIE0); 

     //Configura o PORTD (pinos 0 ao 7), como saídas.
     DDRD = 0xFF; 

     //Loop vazio
     while (1){

     }
}

No código acima, vimos que o registrador TCNT0 incrementa seu valor em uma unidade uma vez a cada \frac{N}{f_{CPU}} segundo com N sendo o valor do prescale (64). Além disso, haverá um estouro no timer sempre que o valor de TCNT0 atingir 255. Dessa forma, temos que a frequência do timer (f) será:

(1)   \begin{equation*} f=\frac{f_{CPU}}{N*(255-Min)} \end{equation*}

Com Min sendo o menor valor de TCNT0. A fim de que o audio seja reproduzido adequadamente, precisamos que f seja igual a fs. Usando fs=2000, f_{CPU} = 16MHz e N igual a 64, temos:

(2)   \begin{equation*} 2000=\frac{16000000}{64*(255-Min)} \end{equation*}

Resolvendo para Min encontramos Min = 130. Desse modo, temos um estouro do timer a cada 0,5ms, fazendo com que a rotina de interrupção ISR seja acionada. Nessa interrupção, primeiramente atualizamos o valor presente na saída (PORTD) e reiniciamos o valor de TCNT0.

Em seguida, atualizamos a posição do vetor que será mandado ao PORTD na próxima iteração.
Já na função main temos a configuração do PORTD como saída, configuração do timer0 no modo desconectado com o prescale definido e configuração da interrupção por estouro do timer.
Por fim temos o loop vazio, que mantem o microcontrolador ativo aguardando a próxima interrupção. O código usado e um arquivo .h com um áudio exemplo estão disponíveis em nosso GitHub.

Montagem

A montagem é bem simples, como mostra a imagem abaixo:


Ela basicamente consiste em uma rede R2R, com a saída ligada em um capacitor e uma caixa de som para computador. Já os pinos de 0 a 7 são ligados aos pinos de mesmos valores do Arduino.
O funcionamento desse circuito é simples, já que ele consiste basicamente em um conversor digital analógico. Esse conversor transforma a saída do Arduino em um sinal de tensão analógico que é usado na caixa de som. O capacitor presente é um capacitor de desacoplamento, já que a saída do R2R apresenta um nível de tensão CC e a componente de CA.
É importante observar que esse circuito não apresenta um amplificador, já que a caixa de som usada já possui um amplificador interno. Viu só como foi fácil? Agora me conta nos comentários, que som que você vai usar para esse projeto? 

0

Conhecimento

Você já deve ter precisado converter um sinal digital, gerado por um microcontrolador, em um sinal analógico, mas como fazemos isso? É para isso que serve o R2R, um circuito simples, mas muito útil, que usa apenas alguns resistores. Para entendermos seu funcionamentos, primeiro precisamos conhecer sua construção, que esta mostrada no esquemático abaixo.



O nome R2R é fácil de entender quando olhamos para o circuito, uma vez que os valores de resistência do circuito alternam entre um resistor de valor R e um com dobro de resistência, 2R. As entradas rotuladas de V0 até V3 são entradas digitais e Vout é a saída desse circuito.


Para usar esse circuito você insere uma palavra binária com V0 sendo o bit menos significativo, e V3 sendo o mais significativo. Como resultado, será obtido uma tensão em Vout, cujo o valor esta entre 0V e um valor de referencia, equivalente a tensão de um bit em nível lógico alto.


Para realizar a analise desse circuito vamos recorrer a 2 princípios bem interessantes da eletrônica, o principio da superposição, e o equivalente de Thévenin.


Calculo da Tensão de Saída em Função das Entradas Para 4 Bits

Considerando, em um primeiro momento, apenas o efeito de V0, temos V1=V2=V3=0. Assim o circuito fica como a imagem abaixo.



Determinando o equivalente de Thévenin para o ponto A, precisamos primeiro desconectar o restante do circuito, o que nos deixa com o que esta mostrado abaixo.



Para este circuito, a resistência de Thévenin é obtida substituindo V0 por 0V, assim a resistência será o paralelo dos dois resistores, ou seja R.


Já a tensão no ponto A (V_A) será obtida através do divisor resistivo formado pelos dois resistores de 2R, obtendo-se V_A=\frac{2R}{2R+2R}V0=\frac{V0}{2}, assim, o circuito fica como abaixo.



Repetindo essa analise, temos que a tensão no ponto B será \frac{V0}{4}, para o ponto C, \frac{V0}{8}, e por fim, temos que a tensão de saída será Vout=\frac{V0}{16}, quando consideramos o efeito de V0 apenas.


Agora consideraremos apenas a fonte V1, desprezando as demais fontes, o que nos deixa com o circuito abaixo.




A resistência equivalente até A é dada pelo paralelo dos dois resistores que antecedem esse nó, resultando em um resistor de resistência R, portanto a resistência antes do ponto B é de 2R, o que nos deixa com o circuito abaixo.




Esse circuito é quase igual ao que tínhamos para V0, logo, de maneira similar ao caso anterior a tensão no ponto B será \frac{V1}{2}. Para o ponto C teremos \frac{V1}{4}, e por fim Vout=\frac{V1}{8} para a saída.


Nesse ponto você já deve ter percebido que o mesmo raciocínio serve para V2 e V3. Considerando cada uma dessas fontes individualmente, teremos Vout=\frac{V2}{4} para V2 e Vout=\frac{V3}{2} para V3.


A tensão na saída considerando todas as fontes é dada pela soma de todas as contribuições individuais, uma vez que aplicamos o principio da superposição. O que resulta na equação abaixo:


(1)   \begin{equation*}Vout=\frac{V0}{16}+\frac{V1}{8}+\frac{V2}{4}+\frac{V3}{2}\end{equation*}




Generalizando…


Se quisermos colocar mais entradas no nosso circuito, basta adicionar um novo conjunto de resistores R2R, como mostra a imagem abaixo.



Ao adicionar esses novos resistores à sua rede R2R, a tensão na nova saída (Vout_novo) sem considerar a nova fonte de tensão (V4) será a metade da saída antiga (Vout_antigo). Mas se desprezarmos as demais fontes de tensão, e calcularmos a tensão de saída apenas para V4, encontraremos \frac{V4}{2}. Por fim, aplicamos o principio da superposição novamente, encontrando:


(2)   \begin{equation*}Vout=\frac{V0}{32}+\frac{V1}{16}+\frac{V2}{8}+\frac{V3}{4}+\frac{V4}{2}\end{equation*}



Se você observar os denominadores das equações 1 e 2, você verá que temos potências de base 2, de forma que 32=2^5, 16=2^4 e assim por diante. Então, a equação anterior pode ser escrita como sendo:


(3)   \begin{equation*}Vout=\frac{V0}{2^5}+\frac{V1}{2^{4}}+\frac{V2}{2^3}+\frac{V3}{2^2}+\frac{V4}{2^1}\end{equation*}



A fim de generalizar essa expressão, tomaremos o circuito abaixo.



Se você observar as equações 1 e 3, você verá que, quando temos 4 entradas digitais, a contribuição de V0 é dividida por 2^4. Já para 5 entradas, temos V0 dividido por 2^5, disso concluímos que para n entradas, teremos \frac{V0}{2^n}.


Para V1, temos, \frac{V1}{2^3} para 4 entradas, e \frac{V1}{2^4} para 5 entradas. Assim, para n entradas, ficamos com \frac{V1}{2^{n-1}}.


A entrada mais a direita, sempre ficou dividida por dois, assim, para n entradas, teremos \frac{Vn-1}{2}. Por fim a expressão 3 pode ser reescrita de modo geral como sendo:


(4)   \begin{equation*}Vout=\frac{V0}{2^n}+\frac{V1}{2^{n-1}}+\frac{V2}{2^n-2}+...+\frac{Vn-3}{2^3}+\frac{Vn-2}{2^2}+\frac{Vn-1}{2^1}\end{equation*}


O R2R é um circuito simples, porém muito útil, capaz de converter sinais digitais em analógicos. E você, já conhecia ele? Deixe seu comentário.

1

Conhecimento


Os retificadoras são os responsáveis pela transformação de corrente alternada em corrente contínua, mas o que isso significa?

Para entendermos isso, primeiro precisamos lembrar o que significam corrente alternada (CA) e corrente contínua (CC). De modo simples, a corrente alternada, como o nome já diz, alterna constantemente o seu sentido, sendo ora positiva e ora negativa, seguindo um comportamento senoidal, como a figura a seguir nos mostra. Por outro lado, a corrente contínua possui um sentido único, que se mantém constante, sempre fluindo do positivo para o negativo (este é o sentido convencional).

Os sinais CA são muito usados na geração e distribuição de eletricidade, sendo o tipo de sinal que chega nas tomadas de nossas casas. Entretanto a maior parte dos eletrônicos que possuímos funcionam com corrente contínua, isso porque a corrente contínua pode ser facilmente armazenada em pilhas e baterias. Além disso, computadores, celulares, entre outros funcionam através da lógica binária, e para isso precisam internamente de tensões estáveis para servirem como níveis baixo (0) e alto (1).

Então, como ligamos circuitos de corrente continua (CC) com a energia das tomadas (CA)?

É aqui que entram os retificadores, que são circuitos elétricos com a função de retificar a tensão CA, isto é, transformá-la em corrente contínua.


Retificadores de Meia Onda

De modo geral, para a retificação de uma senoide apenas se usa uma característica bastante conhecida de um diodo, que é sua capacidade de condução em uma única direção. A imagem abaixo mostra a esquerda um circuito retificador simples, nele o diodo conduz corrente para a carga apenas quando a tensão entre o seu catodo e anodo (V_{21}) é positiva, bloqueando a corrente quando V_{21} é negativa. Já na imagem a direita temos a tensão na carga R, esta tensão é uma réplica do sinal de entrada, porém com a parte negativa removida.

Esse tipo de circuito é chamado de retificador de meia onda, uma vez que ele elimina metade da onda de entrada. Além disso, embora sem a parte negativa, este sinal ainda não é um sinal CC puro, já que ele não é estável em uma única tensão.

Para aumentar a estabilidade da tensão costuma-se usar um capacitor, ligado paralelo com a carga, como no circuito a seguir.


A ideia aqui é bem simples, ela consiste em carregar o capacitor, durante os picos de tensão, e usar o capacitor para fornecer energia para a carga quando a tensão está em baixa. Assim é possível alcançar maior estabilidade na tensão. Entretanto, com esse tipo de circuito ainda temos alguns problemas, sendo os principais o baixo aproveitamento da tensão, já que ele descarta metade do sinal, e que, mesmo com o capacitor, o sinal ainda continua uma grande variação de tensão, o que fica visível na próxima imagem.


Um modo de contornar estes problemas é o uso de retificadores de onda completa.

Retificadores de Onda Completa

O circuito de retificação mais utilizado é a ponte retificadora, sendo possível construí-la com diodos discretos, como o 1N4007, 1N4148, 1N5402, entre outros, ou podendo ser comprado já montado, como nas pontes KBPC5010, MB10F, e outros. Este circuito é um retificador de onda completa, ou seja, ele não só mantém a parte positiva da onda de entrada como também realiza a reflexão da parte negativa. Sua montagem é simples, envolvendo apenas 4 diodos, ligados como mostrado abaixo.


O funcionamento deste circuito pode ser separado em 2 momentos diferentes. Primeiro, quando a tensão é positiva. Nessa condição os diodos
D1 e D2 conduzem corrente elétrica, enquanto os diodos 3 e 4 a bloqueiam, isto está ilustrado na imagem a direita. A imagem a esquerda representa o segundo momento, quando a tensão de entrada é negativa. Nessa condição o inverso ocorre, isto é, os diodos 3 e 4 conduzem e os diodos 1 e 2 bloqueiam a corrente elétrica.

 

Testes Experimentais

Na imagem a seguir temos a tensão usada para alimentar uma carga de 10kΩ após um retificador de onda completa construído com os diodos 1N4148. Essa imagem foi obtida realizando medições com um osciloscópio. Nela vemos que a tensão fica em 0V por alguns instantes, mesmo em um retificador de onda completa. Isso ocorre porque o diodo usado apresenta uma queda de tensão mínima antes da condução de aproximadamente 0,7V. Note que o circuito montado não apresenta o capacitor presente no circuito retificador de onda completa apresentado anteriormente.

Quando usamos um retificador de meia onda, para uma mesma carga, temos como resultado a imagem abaixo. Note que a tensão de pico para ambos os retificadores é a mesma (9,30V). Porém frequência reduz pela metade, quando comparamos com um retificador de onda completa, apesar da fonte ser a mesma.

A diferença nas frequências são provocadas uma vez que o retificador de meia onda conserva a frequência da fonte. Enquanto o retificador de onda completa dobra a frequência, já que o sinal se repete a cada semi ciclo.


Colocando um capacitor de 100uF no retificador de onda completa obtêm-se uma tensão plana como mostra a imagem a seguir.


Note que a tensão é quase constante, já que o capacitor usado possui um valor muito alto. 
Pontes retificadoras são de grande importância na eletrônica. E você, já conhecia elas? Deixe seu comentário sobre o que achou do post. 

0