Projeto Final

Geração onda triangular 0,1 Hz

A ideia agora é gerar uma onda triangular que leve 5 segundos para variar a tensão de 0 até 5 Volts e que gaste outros tantos segundos para reduzir a tensão novamente para 0 Volts, totalizando um ciclo de dura 10 segundos, resultando numa onda triangular de 0,1 Hz.

Detalhe: um botão (push-button NA) deve permitir que o usuário escolha quando a onda varia sua amplitude. Ou seja, a cada vez que este botão é pressionado, a onda pode ser “pausada” ou retomada do ponto em que foi pausada.

Está previsto o uso de saída PWM filtrada do Arduíno para gerar uma tensão a mais próxima possível de um nível DC entre 0 à 5 Volts.

Para gerar a onda triangular foi previsto algo como:

onda_triangular.drawio_red.png

Do esboço anterior se infere que:

new_y=last_y+Δv

Δv=Δttaxadir

Δt=currentMillispreviousMillis

e onde:

taxa=2555000

onde o valor 255 = 100% do PWM (ou % Volts) e o valor 5000 = T/2 (metade do período de tempo da onda, em milisegundos).

No momento de reiniciar a onda após um “pause”, é só fazer: previousMillis=currentMillis para que propositalmente Δt=0 e assim Δv=0.

O código fica algo como:

/***************************************************
 Teste do Serial Plotter da IDE 2.x com porta analógica

 Entrada analógica monitorada = A0
 botão para alternar entre geração/não de dados = pin 7, active LOW

 A própria placa gera onda trinagular com ciclo de 0,1 Hz com
 tensão (PWM filtrado) variando entre 0 à 5 Volts no pino 6

 Sugere-se curto-circuitar a saída filtrada do PWM (pino 6) com a
 entrada A0 do Arduino, para efeitos de teste.

 Fernando Passold, em 17/11/2022, 18/11/2022
 **************************************************/

#define pino_PWM 6      // pino da saída PWM (à ser filtrada)
#define pino_button 7   // pino onde está botão que alterna captura/não de dados
#define analog_pin A0   // entrada analógica usada
#define pino_led_satus   2    // led de "status", active HIGH

#define freq 0.1                                // Freq da onda triangular em Hz
const float taxa = (255.0*2.0*freq)/1000.0;     // taxa de subida/descida da onda triangular (valor %PWM/ms)
// Exemplo: Se freq = 0,1 Hz, T=1/01=10 segundos
// metada do ciclo da triangular T/2 = 5 segundos = 5000 ms
// taxa = 255.0/5000.0 = 0.051;                 // onda triangular deve atingir 100% duty-cycle (255) em 5 seg (5000 ms)
// para uso posterior em equação como:
// new_y = last_y + taxa*delta_t*dir
// dir=+1,-1 para indicar subida ou descida da onda triangular
// delta_t = passagem de tempo entre um ponto e outro da onda gerada (valor em ms)

const float converte_duty2volts = 5.0/256.0;    // para converter duty-cycle 100% = 255
const float converte_AD2volts = 5.0/1024.0;     // para converter de 2^10 (1024) = 5 Volts

float analog_value;                       // Valor original do AD (int) convertido para tensão em Volts
unsigned long currentMillis = 0;          // "tempo" atual do sistema, t[k]
unsigned long previousMillis = 0;         // "tempo" na amostra anterior, t[k-1]
unsigned long time_pressed = 0;           // will store last time buttom was pressed
bool debouncing = false;                  // realizando debouncig ou não (habilita/desabilita leiutura chave)
const unsigned long time_debounce = 150;  // periodo de tempo para debouncing de chave (milisegundos)
unsigned long delta_t;                    // diferença de passagem de tempo
bool pause = false;                       // pausar ou não onda triangular
bool stop = false;                        // parar onda trinagular (e zerar saída analógica/PWM)
float new_y;                              // novo valor (valor atual) amplitude onda triangular, y[k] (entre 0 à 255)
float last_y;                             // valor amostra passada da amplitude da trinagular, y[k-1]
int dir = 1;                              // indica "subida"(+1) ou descida (-1) da onda triangular
float y_volts;                            // valor convertido para tensão (0 ~ 5 Volts)
bool serial_out = true;                   // indica quando "imprimir" ou não info na porta serial

void prepara_onda_triangular(){
  // prepara variáveis para iniciar onda do zero
  dir = 1; // onda subindo
  last_y = 0;
  new_y = 0;
  delta_t = 0;
  currentMillis = millis();
  previousMillis = currentMillis;
}

void update_led_status(){
  // sinaliza "status" do sistema
  if (pause)  {
    digitalWrite(pino_led_satus, LOW);
    Serial.println("pause:");
    serial_out = false;
  }
  if (!pause) {
    digitalWrite(pino_led_satus, HIGH);
    serial_out = true;
  } 
}

void update_triangular(){
  // calcula próximo valor da onda triangular, atualiza amostra passada
  delta_t = currentMillis - previousMillis; // calcula intervalo de tempo em comparação com amostra (ou pausa) anterior
  new_y = last_y + taxa*delta_t*dir;
  if (new_y>255)  {
    // momento de inverter ciclo da triangular, de subida para descida
    dir = -1;
    new_y = 255;
  }
  if (new_y<0)  {
    // momento de inverter ciclo da triangular, de descida para subida
    dir = +1;
    new_y = 0;
  }
  // joga valor calculado para "fora"
  analogWrite(pino_PWM, new_y);
  last_y = new_y;                       // deixa preparado para próximo ponto
  previousMillis = currentMillis;       // deixa pronto para cálculo do próximo ponto
  y_volts = new_y*converte_duty2volts; // converte ssaída duty-cycle para Volts (atualiza valor)
}

void setup() {
  // put your setup code here, to run once:
  pinMode(pino_button, INPUT);
  pinMode(pino_PWM, OUTPUT);
  pinMode(pino_led_satus, OUTPUT);
  digitalWrite(pino_led_satus, HIGH);
  Serial.begin(9600);
  delay(50);
  Serial.println("Teste do Serial Plotter");
  Serial.print("               freq="); Serial.println(freq);
  Serial.print("               taxa="); Serial.println(taxa*1000);
  Serial.print("converte_duty2volts="); Serial.println(converte_duty2volts*1000);
  Serial.print("  converte_AD2volts="); Serial.println(converte_AD2volts*1000);
  debouncing = false;
  pause = true;
  stop = false;
  prepara_onda_triangular();
  update_triangular();
  digitalWrite(pino_led_satus, LOW);
  update_led_status();
  serial_out = false;
}

void loop() {
  // put your main code here, to run repeatedly:
  currentMillis = millis();
  if (!debouncing) {
    if (digitalRead(pino_button) == LOW) {
      // deveria deixar passar uns 50 ~ 150 ms a titulo de debouncing do botão
      // não podemos usar delay(100) porque influencia função analogWrite() - gerador PWM "interno" do Arduino
      debouncing = true;
      time_pressed = millis();
      // antes de atualizar pause, verifica se onda triangular está sendo re-iniciada
      // pause --> !pause; deve então garantir delta_t = 0
      if (pause){
        previousMillis = currentMillis;  
      }
      // atualiza pause
      pause = !pause;  // simplesmente alterna estado
      update_led_status();
    }
  }
  if (debouncing) {
    // Significa que estamos dentro de periodo de debouncing em que a chave é ignorada
    // falta verificar se passou tempo para "desligar" o debouncing
    if (currentMillis - time_pressed >= time_debounce) {
      debouncing = false;  // habilita novas leituras do botão
    }
  }
  if (!pause) update_triangular(); 
  // lendo sinal analógico
  analog_value = converte_AD2volts*(float)analogRead(analog_pin);
  // if (serial_out){ // <-- imprime apenas fora do pause
    Serial.print("y_max:"); Serial.print(5.5); // apenas "traço superior"
    Serial.print("\t");
    if (pause){
      Serial.print(" P ");
    }
    Serial.print("\t");
    Serial.print("delta_t:"); Serial.print(delta_t/10.0);
    Serial.print("\t");
    Serial.print("yd:"); Serial.print(y_volts);
    Serial.print("\t");
    Serial.print("y:"); Serial.print(analog_value);
    Serial.print("\t");
    Serial.print("y_min:"); Serial.println(-0.5); // apenas "traço inferior"
  // }
}

Saída no Monitor Serial:

21:20:42.656 ->                freq=0.10
21:20:42.689 ->                taxa=51.00
21:20:42.722 -> converte_duty2volts=19.53
21:20:42.754 ->   converte_AD2volts=4.88
21:20:42.787 -> pause:
21:20:42.787 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:42.853 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:42.886 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:42.951 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:43.016 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:43.082 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50

21:20:45.933 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:45.998 -> y_max:5.50     P     delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:46.031 -> y_max:5.50        delta_t:0.00    yd:0.00    y:0.00    y_min:-0.50
21:20:46.096 -> y_max:5.50        delta_t:5.40    yd:0.05    y:0.00    y_min:-0.50
21:20:46.162 -> y_max:5.50        delta_t:5.50    yd:0.11    y:0.05    y_min:-0.50
21:20:46.195 -> y_max:5.50        delta_t:5.60    yd:0.16    y:0.10    y_min:-0.50
21:20:46.260 -> y_max:5.50        delta_t:5.50    yd:0.22    y:0.17    y_min:-0.50
21:20:46.327 -> y_max:5.50        delta_t:5.50    yd:0.27    y:0.24    y_min:-0.50

21:20:48.915 -> y_max:5.50        delta_t:5.50    yd:2.85    y:2.75    y_min:-0.50
21:20:48.980 -> y_max:5.50        delta_t:5.40    yd:2.91    y:2.71    y_min:-0.50
21:20:49.013 -> pause:
21:20:49.013 -> y_max:5.50     P     delta_t:5.40    yd:2.91    y:2.80    y_min:-0.50
21:20:49.078 -> y_max:5.50     P     delta_t:5.40    yd:2.91    y:2.90    y_min:-0.50

21:20:58.720 -> y_max:5.50        delta_t:0.00    yd:4.83    y:4.84    y_min:-0.50
21:20:58.752 -> y_max:5.50        delta_t:5.50    yd:4.88    y:4.82    y_min:-0.50
21:20:58.818 -> y_max:5.50        delta_t:5.40    yd:4.94    y:4.88    y_min:-0.50
21:20:58.883 -> y_max:5.50        delta_t:5.60    yd:4.98    y:4.93    y_min:-0.50     <-- pico máximo
21:20:58.949 -> y_max:5.50        delta_t:5.50    yd:4.93    y:4.99    y_min:-0.50
21:20:58.982 -> y_max:5.50        delta_t:5.50    yd:4.87    y:4.94    y_min:-0.50
21:20:59.046 -> y_max:5.50        delta_t:5.60    yd:4.82    y:4.87    y_min:-0.50
21:20:59.113 -> y_max:5.50        delta_t:5.50    yd:4.76    y:4.80    y_min:-0.50
21:20:59.146 -> y_max:5.50        delta_t:5.40    yd:4.71    y:4.73    y_min:-0.50
21:20:59.211 -> y_max:5.50        delta_t:5.50    yd:4.65    y:4.73    y_min:-0.50

21:21:24.449 -> y_max:5.50        delta_t:5.50    yd:0.15    y:0.19    y_min:-0.50
21:21:24.488 -> y_max:5.50        delta_t:5.60    yd:0.09    y:0.15    y_min:-0.50
21:21:24.548 -> y_max:5.50        delta_t:5.50    yd:0.04    y:0.09    y_min:-0.50
21:21:24.614 -> y_max:5.50        delta_t:5.50    yd:0.00    y:0.03    y_min:-0.50
21:21:24.646 -> y_max:5.50        delta_t:5.50    yd:0.05    y:0.00    y_min:-0.50     <-- pico mínimo
21:21:24.711 -> y_max:5.50        delta_t:5.50    yd:0.11    y:0.04    y_min:-0.50
21:21:24.776 -> y_max:5.50        delta_t:5.50    yd:0.16    y:0.10    y_min:-0.50
21:21:24.810 -> y_max:5.50        delta_t:5.50    yd:0.22    y:0.17    y_min:-0.50
21:21:24.875 -> y_max:5.50        delta_t:5.60    yd:0.27    y:0.23    y_min:-0.50

Sinal gerado (IDE 2.x do Arduíno: Ferramentas >> Serial Plotter):

onda_triangular.gif

Observações:


Fernando Passold, 17/11/2022, 19/11/2022.