Problema que emite 2.5 Khz Signal ATTiny85 con Digispark

4

Soy relativamente nuevo trabajando con el IDE de Digispark y tengo algunos problemas con algo que debería ser realmente simple.

Estoy intentando emitir una señal de onda cuadrada de 2,5 kHz, que oscilará +/- 500 Hz. Así que va de 2000Hz a 3000 Hz desde un microcontrolador que usa un ATTiny85 con el IDE de Digispark.

Encontré este tutorial en línea: enlace que explica lo que quiero hacer. Como dijeron, para modificar la frecuencia HW PWM cambié el valor de 64 a 8 de MS_TIMER_TICK_EVERY_X_CYCLES dentro de wiring.c

Debido a esto, logré obtener un valor de frecuencia más alto. Pero no pude ajustarlo, es por eso que continué con la manipulación del software PWM

He usado este código:

#include <TinySoftPwm.h>
#include <DigiUSB.h>

#define HW_PWM_PIN               0 /* Used to check HW PWM with analogWrite() */
#define SW_PWM_BUILT_IN_LED_PIN  1 /* Digispark Model A (Rev2) built-in LED pin number (Change it to 2 for Model B) */
#define TIME_TEST_PIN            5 /* Used to check with oscilloscope micros(), millis() are still OK */

void setup()
{
   TinySoftPwm_begin(128, 0); /* 128 x TinySoftPwm_process() calls before overlap (Frequency tuning), 0 = PWM init for all declared pins */
   pinMode(TIME_TEST_PIN, OUTPUT);
   DigiUSB.begin(); 
}

void loop()
{
static uint32_t PwmStartUs=micros();
static uint32_t PwmStartMs=millis();
static uint8_t  Pwm=0;
static int8_t   Dir=1;
static boolean  State=LOW;
static uint32_t BlinkStartMs=millis();

  /***********************************************************/
  /* Call TinySoftPwm_process() with a period of 60 us       */
  /* The PWM frequency = 128 x 60 # 7.7 ms -> F # 130Hz      */
  /* 128 is the first argument passed to TinySoftPwm_begin() */
  /***********************************************************/
  if((micros() - PwmStartUs) >= 60)
  {
    /* We arrive here every 60 microseconds */
    PwmStartUs=micros();
    TinySoftPwm_process(); /* This function shall be called periodically (like here, based on micros(), or in a timer ISR) */
  }

  /*************************************************************/
  /* Increment/decrement PWM on LED Pin with a period of 10 ms */
  /*************************************************************/
  if((millis()-PwmStartMs) >= 10)
  {
    /* We arrived here every 10 milliseconds */
    PwmStartMs=millis();
    Pwm+=Dir; /* increment or decrement PWM depending of sign of Dir */
    TinySoftPwm_analogWrite(SW_PWM_BUILT_IN_LED_PIN, Pwm); /* Software PWM: Update built-in LED for Digispark */
    analogWrite(HW_PWM_PIN, Pwm); /* Copy Pwm duty cycle to Hardware PWM */
    if(Pwm==255) Dir=-1; /* if PWM reaches the maximum: change direction */
    if(Pwm==0)   Dir=+1; /* if PWM reaches the minimum: change direction */
  }

  /* Blink half period = 5 ms */
  if(millis()-BlinkStartMs>=5)
  {
    BlinkStartMs=millis();
    digitalWrite(TIME_TEST_PIN, State);
    State=!State;
  }

  /* Check USB is still working */
  if(DigiUSB.available()) /* Just hit "Enter" in the digiterm to check USB */
  {
    DigiUSB.read(); /* just to clear the Rx buffer */
    DigiUSB.println(F("DigiUSB is still alive!"));
  }
  DigiUSB.refresh();

}

No importa lo que haga, estoy leyendo un valor oscilante de 4.065 kHz a 4.049 kHz.

¿Qué estoy haciendo mal? ¿Está roto mi osciloscopio? ¿Debería estar haciendo esto de otra manera?

    
pregunta Gioben

1 respuesta

1

Puede hacer esto en el temporizador 0 en modo CTC. Si su MCU se está ejecutando a 8MHz, necesita un ajuste de preescalador de 8 para alcanzar el rango de frecuencia que está buscando. Invertir la fórmula de frecuencia de la hoja de datos le brinda los conteos correctos para el desbordamiento. Lo hacemos por separado usando nuestro idioma favorito (el mío es Haskell):

module Main (main) where

f_cpu = 8e6
freq n c = f_cpu / (2 * n * (1 + c))
count n f = f_cpu / (f * 2 * n) - 1

main :: IO ()
main = mapM_ (print . round . count 8) [ 2e3, 2.5e3, 3e3 ]

que nos da 249, 199, 166 respectivamente. Ahora estamos listos para escribir el programa:

#include <avr/io.h>
#include <util/delay.h>

void setup()
{
    TCCR0A = _BV(WGM01) | _BV(COM0A0);  // CTC mode w toggle channel A on compare match
    TCCR0B = _BV(CS01);                 // clock select prescale = 8
    DDRB |= _BV(0);                     // enable channel A = OC0A (PB0) output
}

void loop()
{
    static uint8_t i = 0;

    switch (++i & 0x3)
    {
        case 0: OCR0A = 249; break;     // 2kHz
        case 1: OCR0A = 199; break;     // 2.5kHz
        case 2: OCR0A = 166; break;     // 3kHz
        case 3: OCR0A = 199; break;     // 2.5kHz
    }

    _delay_ms(2000);
}

int main()
{
    setup();
    for (;;)
        loop();
}

Esto recorrerá las frecuencias 2, 2.5 y 3kHz en pasos de 500Hz 2 segundos cada uno. He probado en un ATTiny85 desnudo que funciona a 8 MHz y lo he medido en mi alcance: 2.01 kHz, 2.52 kHz y 3.01 kHz es lo que obtengo.

Si no puede usar el temporizador 0, debería ser sencillo lograr lo mismo en el temporizador 1 (tiene más opciones de preescalado y un rango más amplio).

    
respondido por el marangisto

Lea otras preguntas en las etiquetas